/* ==========================================================================
   Estate ERP — layer on top of the Notika template
   Loaded after css/style.css. Keeps the template's visual language but
   stretches the shell to full width and adds the ERP building blocks.
   ========================================================================== */

:root {
    --erp-primary: #00c292;
    --erp-primary-dark: #009a76;
    /* "Available / done / passing" — the same teal .erp-pill.is-available has
       always used. It was referenced in ten places but never defined, and an
       invalid var() makes the whole declaration compute to its initial value:
       an Active project's progress bar was rendering fully transparent. */
    --erp-success: #00c292;
    --erp-success-dark: #00926e;
    --erp-info: #03a9f3;
    --erp-warning: #ff9800;
    --erp-danger: #e91e63;
    --erp-purple: #6c63ff;
    --erp-ink: #2c3e50;
    --erp-muted: #8a94a6;
    --erp-line: #e9edf2;
    --erp-bg: #f4f6fa;
    --erp-radius: 9px;
    --erp-shadow: 0 1px 2px rgba(16, 30, 54, .04), 0 4px 14px rgba(16, 30, 54, .05);
    --erp-gutter: 18px;
}

/* --------------------------------------------------------------------------
   Tone palette

   Eleven hues as a set of custom properties, worn by any component that wants
   colour to mean something: the land pipeline rail, the stat strips, the
   contractor register. One class, four properties, and the component decides
   what it paints with them.

   ADDING A COMPONENT: read the properties with a fallback --
   var(--erp-tone, var(--erp-muted)) -- and never declare --erp-tone defaults in
   the component's own rule. A default there has the same specificity as the
   palette class and would fight it depending on source order.

   ADDING A HUE: add it here and in the dark block below, and give it a
   text-safe --erp-tone-ink. Do not reach for a hex inside a component.
   -------------------------------------------------------------------------- */

.is-tone-slate {
    --erp-tone-deep: #334155;
    --erp-tone-deeper: #1e293b;
    --erp-tone: #64748b;
    --erp-tone-soft: rgba(100, 116, 139, .12);
    --erp-tone-line: rgba(100, 116, 139, .32);
    --erp-tone-ink: #475569;
}

.is-tone-indigo {
    --erp-tone-deep: #4338ca;
    --erp-tone-deeper: #312e81;
    --erp-tone: #4f46e5;
    --erp-tone-soft: rgba(79, 70, 229, .12);
    --erp-tone-line: rgba(79, 70, 229, .32);
    --erp-tone-ink: #4338ca;
}

.is-tone-blue {
    --erp-tone-deep: #0369a1;
    --erp-tone-deeper: #075985;
    --erp-tone: #0aa2e0;
    --erp-tone-soft: rgba(10, 162, 224, .13);
    --erp-tone-line: rgba(10, 162, 224, .34);
    --erp-tone-ink: #0369a1;
}

.is-tone-cyan {
    --erp-tone-deep: #0e7490;
    --erp-tone-deeper: #155e75;
    --erp-tone: #0891b2;
    --erp-tone-soft: rgba(8, 145, 178, .12);
    --erp-tone-line: rgba(8, 145, 178, .32);
    --erp-tone-ink: #0e7490;
}

.is-tone-violet {
    --erp-tone-deep: #4f46e5;
    --erp-tone-deeper: #3730a3;
    --erp-tone: #6c63ff;
    --erp-tone-soft: rgba(108, 99, 255, .12);
    --erp-tone-line: rgba(108, 99, 255, .32);
    --erp-tone-ink: #574fd6;
}

.is-tone-purple {
    --erp-tone-deep: #7e22ce;
    --erp-tone-deeper: #581c87;
    --erp-tone: #a855f7;
    --erp-tone-soft: rgba(168, 85, 247, .12);
    --erp-tone-line: rgba(168, 85, 247, .32);
    --erp-tone-ink: #7e22ce;
}

.is-tone-amber {
    --erp-tone-deep: #b45309;
    --erp-tone-deeper: #78350f;
    --erp-tone: #d97706;
    --erp-tone-soft: rgba(217, 119, 6, .13);
    --erp-tone-line: rgba(217, 119, 6, .32);
    --erp-tone-ink: #b45309;
}

.is-tone-orange {
    --erp-tone-deep: #c2410c;
    --erp-tone-deeper: #7c2d12;
    --erp-tone: #ea580c;
    --erp-tone-soft: rgba(234, 88, 12, .12);
    --erp-tone-line: rgba(234, 88, 12, .32);
    --erp-tone-ink: #c2410c;
}

.is-tone-teal {
    --erp-tone-deep: #0f766e;
    --erp-tone-deeper: #115e59;
    --erp-tone: #0d9488;
    --erp-tone-soft: rgba(13, 148, 136, .13);
    --erp-tone-line: rgba(13, 148, 136, .34);
    --erp-tone-ink: #0f766e;
}

.is-tone-green {
    --erp-tone-deep: #047857;
    --erp-tone-deeper: #065f46;
    --erp-tone: #00a37a;
    --erp-tone-soft: rgba(0, 163, 122, .14);
    --erp-tone-line: rgba(0, 163, 122, .36);
    --erp-tone-ink: #047857;
}

.is-tone-rose {
    --erp-tone-deep: #be123c;
    --erp-tone-deeper: #881337;
    --erp-tone: #e11d48;
    --erp-tone-soft: rgba(225, 29, 72, .11);
    --erp-tone-line: rgba(225, 29, 72, .3);
    --erp-tone-ink: #be123c;
}

/* Dark mode lifts the hue and strengthens the wash. The HUE DOES NOT CHANGE
   identity -- a stage must not mean teal by day and green by night -- and the
   ink becomes the lit hue itself, because text is now light on dark. */
:root[data-theme="dark"] .is-tone-slate {
    --erp-tone: #94a3b8;
    --erp-tone-soft: rgba(148, 163, 184, .18);
    --erp-tone-line: rgba(148, 163, 184, .34);
    --erp-tone-ink: #94a3b8;
}

:root[data-theme="dark"] .is-tone-indigo {
    --erp-tone: #8b84f8;
    --erp-tone-soft: rgba(139, 132, 248, .18);
    --erp-tone-line: rgba(139, 132, 248, .34);
    --erp-tone-ink: #8b84f8;
}

:root[data-theme="dark"] .is-tone-blue {
    --erp-tone: #4cc3f5;
    --erp-tone-soft: rgba(76, 195, 245, .18);
    --erp-tone-line: rgba(76, 195, 245, .34);
    --erp-tone-ink: #4cc3f5;
}

:root[data-theme="dark"] .is-tone-cyan {
    --erp-tone: #3ab7d4;
    --erp-tone-soft: rgba(58, 183, 212, .18);
    --erp-tone-line: rgba(58, 183, 212, .34);
    --erp-tone-ink: #3ab7d4;
}

:root[data-theme="dark"] .is-tone-violet {
    --erp-tone: #9a94ff;
    --erp-tone-soft: rgba(154, 148, 255, .18);
    --erp-tone-line: rgba(154, 148, 255, .34);
    --erp-tone-ink: #9a94ff;
}

:root[data-theme="dark"] .is-tone-purple {
    --erp-tone: #c489fa;
    --erp-tone-soft: rgba(196, 137, 250, .18);
    --erp-tone-line: rgba(196, 137, 250, .34);
    --erp-tone-ink: #c489fa;
}

:root[data-theme="dark"] .is-tone-amber {
    --erp-tone: #f0a936;
    --erp-tone-soft: rgba(240, 169, 54, .18);
    --erp-tone-line: rgba(240, 169, 54, .34);
    --erp-tone-ink: #f0a936;
}

:root[data-theme="dark"] .is-tone-orange {
    --erp-tone: #f5854a;
    --erp-tone-soft: rgba(245, 133, 74, .18);
    --erp-tone-line: rgba(245, 133, 74, .34);
    --erp-tone-ink: #f5854a;
}

:root[data-theme="dark"] .is-tone-teal {
    --erp-tone: #3ec2b4;
    --erp-tone-soft: rgba(62, 194, 180, .18);
    --erp-tone-line: rgba(62, 194, 180, .34);
    --erp-tone-ink: #3ec2b4;
}

:root[data-theme="dark"] .is-tone-green {
    --erp-tone: #33c99f;
    --erp-tone-soft: rgba(51, 201, 159, .2);
    --erp-tone-line: rgba(51, 201, 159, .36);
    --erp-tone-ink: #33c99f;
}

:root[data-theme="dark"] .is-tone-rose {
    --erp-tone: #f2557a;
    --erp-tone-soft: rgba(242, 85, 122, .16);
    --erp-tone-line: rgba(242, 85, 122, .32);
    --erp-tone-ink: #f2557a;
}

/* --------------------------------------------------------------------------
   1. Full-width shell
   The template ships fixed-width .container wrappers; the ERP runs edge to
   edge, so every container variant is unpinned in one place.
   -------------------------------------------------------------------------- */

.container,
.container-sm,
.container-md,
.container-lg,
.container-xl,
.container-xxl {
    max-width: 100% !important;
    width: 100%;
    padding-left: var(--erp-gutter);
    padding-right: var(--erp-gutter);
}

/* Record screens (forms, detail views) read badly when fields are stretched
   across an ultrawide monitor, so they cap to a measure and centre. The page
   header shares the cap, keeping it aligned with the content beneath it. */
.container.erp-container-narrow {
    max-width: 100% !important;
}

.container.erp-container-form {
    max-width: 1060px !important;
}

@media (max-width: 767.98px) {
    :root {
        --erp-gutter: 14px;
    }
}

body {
    background: var(--erp-bg);
    color: var(--erp-ink);
    font-family: Roboto, "Segoe UI", system-ui, -apple-system, sans-serif;
}

.erp-shell {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    /* dvh accounts for mobile browser chrome; the vh above is the fallback. */
    min-height: 100dvh;
    /* Clears the fixed footer. */
    padding-bottom: 24px;
}

.erp-main {
    flex: 1 0 auto;
    padding-bottom: 8px;
    /* The module bar used to carry this gap as a margin; it now lives here so
       the sticky block has no trailing whitespace pinned to the viewport. */
    padding-top: var(--erp-gutter);
}

/* --------------------------------------------------------------------------
   2. Top bar + horizontal menu (no sidebar)
   -------------------------------------------------------------------------- */

/* The utility header runs deliberately short — it is a strip, not a banner —
   and can be collapsed away entirely for data-entry heavy screens. */
.notika-header {
    padding: 3px 0;
}

html.erp-header-collapsed .notika-header {
    display: none;
}

.notika-header .notika-nav-link {
    border-radius: 7px;
    font-size: 15px;
    height: 34px;
    width: 34px;
}

.notika-header .notika-badge {
    font-size: 9.5px;
    height: 15px;
    right: 1px;
    top: 1px;
    width: 15px;
}

.notika-header .btn-erp {
    font-size: 12.5px;
    padding: 6px 12px;
}

.notika-navbar {
    margin-bottom: 0;
}

/* Collapse control, carried as the last item of the module bar so it can
   never overlap the tabs and stays reachable once the header is hidden. */
.erp-toggle-item {
    align-items: center;
    display: flex;
    margin-left: 6px;
    padding-left: 10px;
    position: relative;
}

.erp-toggle-item:before {
    background: var(--erp-line);
    content: "";
    height: 18px;
    left: 0;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 1px;
}

.erp-header-toggle {
    align-items: center;
    background: transparent;
    border: 1px solid var(--erp-line);
    border-radius: 7px;
    color: var(--erp-muted);
    display: flex;
    font-size: 11px;
    height: 26px;
    justify-content: center;
    transition: all .16s ease;
    width: 26px;
}

.erp-header-toggle:hover {
    background: rgba(0, 194, 146, .1);
    border-color: rgba(0, 194, 146, .45);
    color: var(--erp-primary-dark);
}

.erp-header-toggle .fa-chevron-down {
    display: none;
}

html.erp-header-collapsed .erp-header-toggle .fa-chevron-down {
    display: block;
}

html.erp-header-collapsed .erp-header-toggle .fa-chevron-up {
    display: none;
}

/* Header and module bar stick as one block, so neither needs a hand-tuned
   offset that would drift whenever their heights change. Sticky only from lg
   up: below that the header must not sit above the off-canvas drawer. */
@media (min-width: 992px) {
    .erp-topbar-stack {
        position: sticky;
        top: 0;
        z-index: 1090;
    }
}

/* The ERP carries ten modules in the bar, so the tabs run tighter than the
   template default and wrap instead of overflowing on narrower desktops. */
.notika-navbar .navbar-nav {
    flex-wrap: wrap;
}

.notika-navbar .nav-link {
    border-bottom-width: 2px;
    font-size: 12.5px;
    gap: 6px;
    padding: 8px 13px;
}

.notika-navbar .nav-link i {
    font-size: 13.5px;
}

@media (min-width: 992px) and (max-width: 1399.98px) {
    .notika-navbar .nav-link {
        font-size: 12px;
        gap: 5px;
        padding: 8px 9px;
    }

    .notika-navbar .nav-link i {
        font-size: 13px;
    }
}

/* The caret is sized in em, so it tracks the smaller font on its own — only
   the gap in front of it needs pulling in. */
.notika-navbar .dropdown-toggle:after {
    margin-left: 4px;
}

.notika-navbar .dropdown-menu {
    min-width: 232px;
}

.erp-brand-logo {
    height: 29px;
    width: auto;
}

.erp-brand-icon {
    height: 26px;
    width: 26px;
}

/* Company / branch switcher sitting next to the logo */
.erp-context {
    align-items: center;
    border-left: 1px solid var(--erp-line);
    display: flex;
    gap: 10px;
    margin-left: 14px;
    padding-left: 14px;
}

.erp-context-btn {
    align-items: center;
    background: #f7f9fc;
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    color: var(--erp-ink);
    display: flex;
    font-size: 12px;
    font-weight: 500;
    gap: 8px;
    line-height: 1.2;
    padding: 4px 10px;
    text-align: left;
    transition: all .18s ease;
}

.erp-context-btn:hover {
    background: #eef7f4;
    border-color: rgba(0, 194, 146, .45);
    color: var(--erp-primary-dark);
}

.erp-context-btn small {
    color: var(--erp-muted);
    display: block;
    font-size: 10px;
    font-weight: 400;
}

/* Read-only company banner shown at the top of every record form, standing
   in for the company select those forms used to carry. */
.erp-context-banner {
    align-items: center;
    background: #f4faf8;
    border: 1px solid rgba(0, 194, 146, .3);
    border-radius: 9px;
    display: flex;
    gap: 12px;
    margin-bottom: 20px;
    padding: 10px 14px;
}

.erp-context-banner-icon {
    align-items: center;
    background: rgba(0, 194, 146, .14);
    border-radius: 8px;
    color: var(--erp-primary);
    display: flex;
    flex: 0 0 34px;
    height: 34px;
    justify-content: center;
}

.erp-context-banner-body strong {
    color: var(--erp-ink);
    display: block;
    font-size: 13px;
    line-height: 1.3;
}

.erp-context-banner-body span {
    color: var(--erp-muted);
    font-size: 11.5px;
}

/* Profile chip in the top-right cluster */
.erp-user-chip {
    align-items: center;
    background: transparent;
    border: none;
    border-radius: 8px;
    display: flex;
    gap: 8px;
    padding: 2px 8px 2px 2px;
    transition: background .18s ease;
}

.erp-user-chip:hover {
    background: #f8f9fa;
}

.erp-user-chip img,
.erp-user-chip .erp-avatar {
    border-radius: 50%;
    font-size: 12px;
    height: 30px;
    object-fit: cover;
    width: 30px;
}

/* Initials avatar — used wherever a real photo is not available */
.erp-avatar {
    align-items: center;
    background: linear-gradient(135deg, var(--erp-primary), var(--erp-primary-dark));
    border-radius: 50%;
    color: #fff;
    display: flex;
    flex: 0 0 auto;
    font-size: 14px;
    font-weight: 600;
    height: 38px;
    justify-content: center;
    letter-spacing: .3px;
    width: 38px;
}

.erp-avatar-lg {
    font-size: 17px;
    height: 48px;
    width: 48px;
}

.erp-user-chip .erp-user-meta {
    line-height: 1.3;
    text-align: left;
}

.erp-user-chip .erp-user-name {
    color: var(--erp-ink);
    display: block;
    font-size: 12.5px;
    font-weight: 600;
}

.erp-user-chip .erp-user-role {
    color: var(--erp-muted);
    display: block;
    font-size: 10.5px;
}

.erp-user-menu {
    min-width: 260px;
}

.erp-user-menu .dropdown-item {
    align-items: center;
    display: flex;
    font-size: 14px;
    gap: 10px;
    padding: 10px 18px;
}

.erp-user-menu .dropdown-item i {
    color: var(--erp-muted);
    width: 16px;
}

.erp-user-menu .dropdown-item:hover {
    background: #f2faf8;
    color: var(--erp-primary-dark);
}

.erp-user-menu .dropdown-item:hover i {
    color: var(--erp-primary);
}

/* --------------------------------------------------------------------------
   3. Page header (title, breadcrumb, actions)
   -------------------------------------------------------------------------- */

.erp-pagehead {
    align-items: center;
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    box-shadow: var(--erp-shadow);
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    justify-content: space-between;
    margin-bottom: var(--erp-gutter);
    padding: 11px 16px;
}

.erp-pagehead-main {
    align-items: center;
    display: flex;
    gap: 12px;
    min-width: 0;
}

.erp-pagehead-icon {
    align-items: center;
    background: linear-gradient(135deg, var(--erp-primary), var(--erp-primary-dark));
    border-radius: 8px;
    color: #fff;
    display: flex;
    flex: 0 0 auto;
    font-size: 15px;
    height: 38px;
    justify-content: center;
    width: 38px;
}

.erp-pagehead h1 {
    color: var(--erp-ink);
    font-size: 17px;
    font-weight: 600;
    line-height: 1.25;
    margin: 0;
}

.erp-pagehead .breadcrumb {
    font-size: 11.5px;
    margin: 2px 0 0;
}

.erp-pagehead .breadcrumb-item,
.erp-pagehead .breadcrumb-item a {
    color: var(--erp-muted);
    text-decoration: none;
}

.erp-pagehead .breadcrumb-item a:hover {
    color: var(--erp-primary);
}

.erp-pagehead-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    /* Centred, not stretched. The default let a single taller child — a
       two-line status chip, say — pull every button in the row up to its
       height, which read as broken styling rather than as one odd element. */
    align-items: center;
}

/* --------------------------------------------------------------------------
   3b. Module submenu bar (in-page quick nav)
   Rendered by layouts/partials/module-submenu.blade.php for any module that
   sets `submenu_bar` in config/erp.php. It sits above the page header and
   lists the module's sibling pages as buttons.
   -------------------------------------------------------------------------- */

.erp-submenubar {
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    box-shadow: var(--erp-shadow);
    margin-bottom: 10px;
    padding: 7px 8px;
}

/* The strip scrolls sideways rather than wrapping into three tall rows on a
   laptop; a module can carry a dozen pages and the page header must stay in
   view. */
.erp-submenubar-scroll {
    display: flex;
    gap: 6px;
    overflow-x: auto;
    scrollbar-width: thin;
}

.erp-submenubar-scroll::-webkit-scrollbar {
    height: 5px;
}

.erp-submenubar-scroll::-webkit-scrollbar-thumb {
    background: var(--erp-line);
    border-radius: 3px;
}

.erp-submenu-btn {
    align-items: center;
    background: var(--erp-bg);
    border: 1px solid transparent;
    border-radius: 7px;
    color: var(--erp-ink);
    display: inline-flex;
    flex: 0 0 auto;
    font-size: 12.5px;
    font-weight: 500;
    gap: 7px;
    line-height: 1.2;
    padding: 7px 12px;
    text-decoration: none;
    transition: background .15s ease, color .15s ease, border-color .15s ease;
    white-space: nowrap;
}

.erp-submenu-btn i {
    color: var(--erp-muted);
    font-size: 12px;
    transition: color .15s ease;
}

.erp-submenu-btn:hover,
.erp-submenu-btn:focus-visible {
    background: #fff;
    border-color: var(--erp-primary);
    color: var(--erp-primary);
}

.erp-submenu-btn:hover i,
.erp-submenu-btn:focus-visible i {
    color: var(--erp-primary);
}

/* The page you are on stays obviously marked — this strip doubles as a
   "where am I" indicator inside the module. */
.erp-submenu-btn.is-active {
    background: linear-gradient(135deg, var(--erp-primary), var(--erp-primary-dark));
    border-color: transparent;
    box-shadow: 0 2px 6px rgba(0, 0, 0, .12);
    color: #fff;
}

.erp-submenu-btn.is-active i,
.erp-submenu-btn.is-active:hover i {
    color: #fff;
}

.erp-submenu-btn.is-active:hover,
.erp-submenu-btn.is-active:focus-visible {
    color: #fff;
}

@media (max-width: 575.98px) {
    .erp-submenubar {
        padding: 6px;
    }

    .erp-submenu-btn {
        font-size: 12px;
        padding: 6px 10px;
    }
}

/* --------------------------------------------------------------------------
   4. Cards
   -------------------------------------------------------------------------- */

.erp-card {
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    box-shadow: var(--erp-shadow);
    display: flex;
    flex-direction: column;
    height: 100%;
    margin-bottom: var(--erp-gutter);
    overflow: hidden;
}

.erp-card-head {
    align-items: center;
    background: linear-gradient(180deg, #fbfcfe, #f7f9fc);
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: space-between;
    padding: 11px 18px;
}

.erp-card-head h2 {
    color: var(--erp-ink);
    font-size: 13.5px;
    font-weight: 600;
    margin: 0;
}

.erp-card-head p {
    color: var(--erp-muted);
    font-size: 11.5px;
    margin: 1px 0 0;
}

.erp-card-body {
    flex: 1 1 auto;
    padding: 16px 18px;
}

.erp-card-body.is-flush {
    padding: 0;
}

.erp-card-foot {
    background: #fafbfd;
    border-top: 1px solid var(--erp-line);
    padding: 9px 16px;
}

/* --------------------------------------------------------------------------
   4b. Pagination — modern pill style layered over Bootstrap 5 markup
   Laravel renders Bootstrap-5 paginators (Paginator::useBootstrapFive), so we
   restyle its .pagination/.page-item/.page-link nodes rather than a partial.
   Everything is scoped under .pagination to out-specify Bootstrap's own
   first/last-child border-radius rules and its -1px border collapse.
   -------------------------------------------------------------------------- */

.pagination {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 0;
    padding: 0;
}

.pagination .page-item {
    margin: 0;
}

/* Undo Bootstrap's border-collapsing so each control is a standalone pill. */
.pagination .page-item:not(:first-child) .page-link {
    margin-left: 0;
}

.pagination .page-link {
    align-items: center;
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    color: var(--erp-ink);
    display: inline-flex;
    font-size: 12.5px;
    font-weight: 500;
    height: 34px;
    justify-content: center;
    line-height: 1;
    min-width: 34px;
    padding: 0 10px;
    transition: transform .15s ease, box-shadow .15s ease, background .15s ease,
        border-color .15s ease, color .15s ease;
}

.pagination .page-link:hover {
    background: color-mix(in srgb, var(--erp-primary) 8%, #fff);
    border-color: color-mix(in srgb, var(--erp-primary) 45%, var(--erp-line));
    box-shadow: 0 4px 12px rgba(16, 30, 54, .08);
    color: var(--erp-primary-dark);
    transform: translateY(-1px);
    z-index: 1;
}

.pagination .page-link:focus {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--erp-primary) 25%, transparent);
    outline: none;
    z-index: 1;
}

.pagination .page-item.active .page-link {
    background: linear-gradient(135deg, var(--erp-primary), var(--erp-primary-dark));
    border-color: transparent;
    box-shadow: 0 6px 16px rgba(0, 194, 146, .32);
    color: #fff;
}

/* An active page is where you already are — it should not invite a click. */
.pagination .page-item.active .page-link:hover {
    box-shadow: 0 6px 16px rgba(0, 194, 146, .32);
    transform: none;
}

.pagination .page-item.disabled .page-link {
    background: var(--erp-bg);
    border-color: var(--erp-line);
    box-shadow: none;
    color: var(--erp-muted);
    cursor: not-allowed;
    opacity: .7;
    transform: none;
}

/* --------------------------------------------------------------------------
   5. KPI tiles
   -------------------------------------------------------------------------- */

.erp-kpi {
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    box-shadow: var(--erp-shadow);
    height: 100%;
    margin-bottom: var(--erp-gutter);
    overflow: hidden;
    padding: 15px 16px;
    position: relative;
    transition: transform .18s ease, box-shadow .18s ease;
}

.erp-kpi:hover {
    box-shadow: 0 10px 28px rgba(16, 30, 54, .1);
    transform: translateY(-2px);
}

.erp-kpi:before {
    background: var(--erp-primary);
    content: "";
    height: 100%;
    left: 0;
    position: absolute;
    top: 0;
    width: 3px;
}

.erp-kpi.is-info:before { background: var(--erp-info); }
.erp-kpi.is-warning:before { background: var(--erp-warning); }
.erp-kpi.is-danger:before { background: var(--erp-danger); }
.erp-kpi.is-purple:before { background: var(--erp-purple); }

.erp-kpi-top {
    align-items: flex-start;
    display: flex;
    gap: 14px;
    justify-content: space-between;
}

.erp-kpi-label {
    color: var(--erp-muted);
    font-size: 12px;
    font-weight: 500;
    letter-spacing: .4px;
    margin: 0;
    text-transform: uppercase;
}

.erp-kpi-value {
    color: var(--erp-ink);
    font-size: 22px;
    font-weight: 700;
    letter-spacing: -.5px;
    line-height: 1.2;
    margin: 6px 0 0;
}

.erp-kpi-value small {
    color: var(--erp-muted);
    font-size: 12px;
    font-weight: 500;
}

.erp-kpi-icon {
    align-items: center;
    border-radius: 12px;
    display: flex;
    flex: 0 0 auto;
    font-size: 18px;
    height: 46px;
    justify-content: center;
    width: 46px;
}

.erp-kpi-icon.is-primary { background: rgba(0, 194, 146, .12); color: var(--erp-primary); }
.erp-kpi-icon.is-info { background: rgba(3, 169, 243, .12); color: var(--erp-info); }
.erp-kpi-icon.is-warning { background: rgba(255, 152, 0, .14); color: var(--erp-warning); }
.erp-kpi-icon.is-danger { background: rgba(233, 30, 99, .12); color: var(--erp-danger); }
.erp-kpi-icon.is-purple { background: rgba(108, 99, 255, .12); color: var(--erp-purple); }

.erp-kpi-foot {
    align-items: center;
    border-top: 1px dashed var(--erp-line);
    color: var(--erp-muted);
    display: flex;
    flex-wrap: wrap;
    font-size: 11.5px;
    gap: 7px;
    margin-top: 12px;
    padding-top: 9px;
}

.erp-delta {
    align-items: center;
    border-radius: 20px;
    display: inline-flex;
    font-size: 12px;
    font-weight: 600;
    gap: 4px;
    padding: 3px 9px;
}

.erp-delta.is-up { background: rgba(0, 194, 146, .12); color: var(--erp-primary-dark); }
.erp-delta.is-down { background: rgba(233, 30, 99, .1); color: var(--erp-danger); }
.erp-delta.is-flat { background: #f1f3f7; color: var(--erp-muted); }

/* --------------------------------------------------------------------------
   5b. Executive summary banner, section labels & module snapshot cards
   -------------------------------------------------------------------------- */

.erp-summary {
    align-items: center;
    background: linear-gradient(120deg, rgba(0, 194, 146, .07), rgba(108, 99, 255, .06));
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    display: flex;
    flex-wrap: wrap;
    gap: 14px 20px;
    justify-content: space-between;
    margin-bottom: var(--erp-gutter);
    padding: 14px 18px;
}

.erp-summary-lead {
    align-items: center;
    display: flex;
    flex: 1 1 420px;
    gap: 12px;
    min-width: 0;
}

.erp-summary-lead p {
    color: var(--erp-ink);
    font-size: 13.5px;
    line-height: 1.5;
    margin: 0;
}

.erp-summary-health {
    align-items: center;
    border-radius: 20px;
    display: inline-flex;
    flex: 0 0 auto;
    font-size: 12px;
    font-weight: 600;
    gap: 5px;
    padding: 5px 11px;
}

.erp-summary-health.is-up { background: rgba(0, 194, 146, .14); color: var(--erp-primary-dark); }
.erp-summary-health.is-down { background: rgba(233, 30, 99, .12); color: var(--erp-danger); }
.erp-summary-health.is-flat { background: #f1f3f7; color: var(--erp-muted); }

.erp-summary-flags {
    display: flex;
    flex-wrap: wrap;
    gap: 7px;
}

.erp-section-label {
    align-items: baseline;
    display: flex;
    flex-wrap: wrap;
    gap: 4px 12px;
    margin: 6px 0 12px;
}

.erp-section-label h3 {
    color: var(--erp-ink);
    font-size: 15px;
    font-weight: 700;
    margin: 0;
}

.erp-section-label span {
    font-size: 12.5px;
}

.erp-snap {
    align-items: flex-start;
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    box-shadow: var(--erp-shadow);
    display: flex;
    gap: 12px;
    height: 100%;
    margin-bottom: var(--erp-gutter);
    padding: 14px;
    position: relative;
    transition: transform .18s ease, box-shadow .18s ease;
}

.erp-snap:hover {
    box-shadow: 0 10px 28px rgba(16, 30, 54, .1);
    transform: translateY(-2px);
}

.erp-snap-icon {
    align-items: center;
    border-radius: 11px;
    display: flex;
    flex: 0 0 auto;
    font-size: 16px;
    height: 42px;
    justify-content: center;
    width: 42px;
}

.erp-snap-icon.is-primary { background: rgba(0, 194, 146, .12); color: var(--erp-primary); }
.erp-snap-icon.is-info { background: rgba(3, 169, 243, .12); color: var(--erp-info); }
.erp-snap-icon.is-warning { background: rgba(255, 152, 0, .14); color: var(--erp-warning); }
.erp-snap-icon.is-danger { background: rgba(233, 30, 99, .12); color: var(--erp-danger); }
.erp-snap-icon.is-purple { background: rgba(108, 99, 255, .12); color: var(--erp-purple); }

.erp-snap-body { min-width: 0; }

.erp-snap-label {
    color: var(--erp-muted);
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: .3px;
    margin: 0;
    text-transform: uppercase;
}

.erp-snap-value {
    color: var(--erp-ink);
    font-size: 20px;
    font-weight: 700;
    letter-spacing: -.4px;
    line-height: 1.2;
    margin: 3px 0 6px;
}

.erp-snap-value small {
    color: var(--erp-muted);
    font-size: 11.5px;
    font-weight: 500;
    margin-left: 3px;
}

.erp-snap-foot {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.erp-snap-foot .erp-delta { font-size: 11px; padding: 2px 8px; }

.erp-snap-caption {
    color: var(--erp-muted);
    font-size: 11.5px;
}

/* Dashboard grid rhythm. Spacing is driven by row gutters and a margin between
   the top-level rows, so the per-card margin-bottom is folded away here to stop
   the two stacking into an uneven, cramped grid. Scoped to the dashboard. */
.erp-dashboard .row {
    --bs-gutter-x: var(--erp-gutter);
    --bs-gutter-y: var(--erp-gutter);
}

.erp-dashboard .erp-kpi,
.erp-dashboard .erp-snap,
.erp-dashboard .erp-card {
    margin-bottom: 0;
}

.erp-dashboard > .row {
    margin-bottom: var(--erp-gutter);
}

/* --------------------------------------------------------------------------
   6. Status pills
   -------------------------------------------------------------------------- */

.erp-pill {
    /* Reads the tone palette when a tone class is present. The named variants
       below are more specific, so every existing pill is untouched. */
    background: var(--erp-tone-soft, transparent);
    border-radius: 20px;
    color: var(--erp-tone-ink, inherit);
    display: inline-block;
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: .2px;
    line-height: 1.6;
    padding: 3px 11px;
    white-space: nowrap;
}

.erp-pill.is-available { background: rgba(0, 194, 146, .12); color: #00926e; }
.erp-pill.is-booked { background: rgba(3, 169, 243, .12); color: #0281bb; }
.erp-pill.is-sold { background: rgba(108, 99, 255, .12); color: #574fd6; }
.erp-pill.is-hold { background: rgba(255, 152, 0, .16); color: #b96f00; }
.erp-pill.is-overdue { background: rgba(233, 30, 99, .12); color: #c2185b; }
.erp-pill.is-neutral { background: #f1f3f7; color: #5b6675; }

/* A solid, colourful count chip — pops against the muted accordion header. */
.erp-count-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    padding: 0 7px;
    border-radius: 999px;
    background: linear-gradient(135deg, var(--erp-info), var(--erp-primary));
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    line-height: 1;
    box-shadow: 0 2px 6px rgba(3, 169, 243, .3);
}

/* --------------------------------------------------------------------------
   7. Tables

   One colour scheme, driven by tokens, used by every table in the ERP.

   The tokens exist so the whole system re-skins from a single place. There are
   260-odd list screens; a scheme hard-coded into each of them is a scheme that
   drifts, and within a release you have four slightly different greys and
   nobody able to say which is correct.

   WHY THE COLOUR SITS WHERE IT DOES. The header carries the strong colour and
   the body stays quiet. These tables are read, not admired: a user scanning
   forty rows for one number needs the data to be the loudest thing on the row,
   so colour is used to separate STRUCTURE (which band is the header, where does
   one row end) from CONTENT, never to compete with it. Rows alternate in a tint
   barely off the card so the eye can track across a wide table without a ruler,
   and hover states are strong enough to be unambiguous about which row is under
   the cursor before somebody clicks its delete button.

   Semantic colour still wins. A row that is overdue, failed or on hold sets its
   own cell background further down this file, and those rules must keep beating
   the stripe — see the note on Bootstrap below, which is what makes that work.
   -------------------------------------------------------------------------- */

:root {
    --erp-table-head-bg: #07b8ff82;
    /* The same colour composited over a white card. Four tables in the system
       have a sticky header, and a translucent band would show the rows sliding
       underneath it -- so those rules use this instead. Identical to the eye,
       opaque where it has to be. Recompute it if --erp-table-head-bg changes. */
    --erp-table-head-solid: #80dbff;
    /* Dark, not white: the header is now a pale tint, and white text on it is
       unreadable. */
    --erp-table-head-ink: #0c4a63;
    /* The divider between header cells. White reads as a gap in the tint rather
       than a crack, which is what a column edge should look like. */
    --erp-table-head-rule: rgba(255, 255, 255, .55);
    --erp-table-stripe: #f4fbff;
    --erp-table-hover: #e4f5ff;
    --erp-table-accent: var(--erp-info);
    --erp-table-line: #eef1f6;
    --erp-table-ink: #46556a;
    --erp-table-foot: #f0f9ff;
    --erp-table-group: #e7f4fb;
}

:root[data-theme="dark"] {
    /* The same tint, but the ink goes light: over a dark card the composite is
       a deep blue, not a pale one, so dark text would disappear into it. */
    --erp-table-head-bg: #07b8ff82;
    --erp-table-head-solid: #116f9a;
    --erp-table-head-ink: #eaf7ff;
    --erp-table-head-rule: rgba(255, 255, 255, .16);
    --erp-table-stripe: rgba(255, 255, 255, .03);
    --erp-table-hover: rgba(7, 184, 255, .14);
    --erp-table-line: var(--erp-line);
    --erp-table-ink: var(--erp-ink);
    --erp-table-foot: var(--erp-surface-2);
    --erp-table-group: rgba(255, 255, 255, .06);
}

.erp-table {
    margin: 0;
    width: 100%;
}

/* Bootstrap paints EVERY cell with --bs-table-bg and an inset shadow of
   --bs-table-accent-bg. Both are opaque, so anything set on the <tr> is hidden
   behind them and row striping silently does nothing.

   Clearing them here is what makes the layering below work: colour set on a row
   shows through, and colour set on a cell still paints on top of it. That is
   the whole reason a semantic state (.is-overdue td, .is-qc-open td) keeps
   beating the stripe without needing !important anywhere.

   The selector deliberately mirrors Bootstrap's own so the two have equal
   specificity and load order decides — erp.css is loaded after bootstrap. */
.erp-table > :not(caption) > * > * {
    background-color: transparent;
    box-shadow: none;
}

.erp-table thead th {
    background-color: var(--erp-table-head-bg);
    border-bottom: 0;
    color: var(--erp-table-head-ink);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .6px;
    padding: 9px 16px;
    text-transform: uppercase;
    white-space: nowrap;
}

/* Column edges, drawn inside the band rather than as a border so the tint stays
   continuous across the whole header. */
.erp-table thead th + th {
    box-shadow: inset 1px 0 0 var(--erp-table-head-rule);
}

.erp-table tbody td {
    border-bottom: 1px solid var(--erp-table-line);
    color: var(--erp-table-ink);
    font-size: 12.5px;
    padding: 8px 16px;
    vertical-align: middle;
}

/* The stripe is set on the ROW, not the cell. A cell-level stripe would need
   higher specificity than the semantic row states in this file and would
   therefore override them, quietly stripping the colour off every overdue and
   failed row on an even index — a bug that only shows on half the data. */
.erp-table tbody tr:nth-child(even) {
    background-color: var(--erp-table-stripe);
}

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

/* Hover is on the cell, so it reads over the stripe. Strong enough to be
   unmistakable: this is the row somebody is about to act on. */
.erp-table tbody tr:hover > td {
    background-color: var(--erp-table-hover);
}

/* And an accent bar on the leading edge, which tells you where the row STARTS
   on a table wide enough to scroll sideways. */
.erp-table tbody tr:hover > td:first-child {
    box-shadow: inset 3px 0 0 var(--erp-table-accent);
}

.erp-table tfoot td,
.erp-table tfoot th {
    background-color: var(--erp-table-foot);
    border-top: 2px solid var(--erp-table-line);
    color: var(--erp-ink);
    font-size: 12.5px;
    font-weight: 700;
    padding: 9px 16px;
    vertical-align: middle;
}

.erp-table .erp-cell-strong {
    color: var(--erp-ink);
    font-weight: 600;
}

.erp-table-wrap {
    overflow-x: auto;
}

/* Per-module accents ------------------------------------------------------- */

/* A module that wants its own hue sets one class on the table rather than
   redefining the rules. Used sparingly and never inside one module's own
   screens at random: the point of a scheme is that a table looks the same
   wherever you meet it, so these are for tables that mean something different
   — a money table against a register, a warning list against a normal one. */

.erp-table.is-accent-purple {
    --erp-table-head-bg: #6c63ff59;
    --erp-table-head-solid: #c4c1ff;
    --erp-table-head-ink: #2a2472;
    --erp-table-stripe: #f6f6fe;
    --erp-table-hover: #ececfe;
    --erp-table-accent: var(--erp-purple);
    --erp-table-foot: #f4f4fe;
}

.erp-table.is-accent-teal {
    --erp-table-head-bg: #00c29259;
    --erp-table-head-solid: #99e7d3;
    --erp-table-head-ink: #0a4f3e;
    --erp-table-stripe: #f5fcfa;
    --erp-table-hover: #e6f8f3;
    --erp-table-accent: var(--erp-primary);
    --erp-table-foot: #f1fbf8;
}

.erp-table.is-accent-amber {
    --erp-table-head-bg: #ff980059;
    --erp-table-head-solid: #ffd699;
    --erp-table-head-ink: #6b3b00;
    --erp-table-stripe: #fffaf2;
    --erp-table-hover: #fff3e2;
    --erp-table-accent: var(--erp-warning);
    --erp-table-foot: #fff7ec;
}

.erp-table.is-accent-rose {
    --erp-table-head-bg: #e91e6359;
    --erp-table-head-solid: #f6a5c1;
    --erp-table-head-ink: #780e33;
    --erp-table-stripe: #fff6f8;
    --erp-table-hover: #ffecf1;
    --erp-table-accent: var(--erp-danger);
    --erp-table-foot: #fff5f7;
}

/* A quiet table, for the places where a saturated band would be shouting: a
   two-row summary inside a modal, a nested breakdown under a parent row. */
.erp-table.is-plain thead th {
    background: var(--erp-surface-2, #f7f9fc);
    color: var(--erp-muted);
    font-weight: 600;
}

.erp-table.is-plain thead th + th {
    box-shadow: none;
}

.erp-table.is-plain tbody tr:nth-child(even) {
    background-color: transparent;
}

/* A banner row inside the body: a stage, a group, a section heading that spans
   the table. Colour is set on the CELLS, not the row, so it sits above the
   stripe -- a banner that is tinted on odd rows and plain on even ones reads as
   a rendering bug.

   There were three separate inventions of this row before it was named. If you
   are about to write a fourth, use this one. */
.erp-table tr.erp-group-row > td,
.erp-table tr.erp-group-row > th {
    background-color: var(--erp-table-group);
    border-bottom: 1px solid var(--erp-table-line);
    border-top: 1px solid var(--erp-table-line);
    color: var(--erp-ink);
    font-size: 11.5px;
    font-weight: 700;
}

.erp-table tr.erp-group-row:first-child > td,
.erp-table tr.erp-group-row:first-child > th {
    border-top: 0;
}

/* A banner is a heading, not a record, so it does not respond to hover. */
.erp-table tbody tr.erp-group-row:hover > td {
    background-color: var(--erp-table-group);
    box-shadow: none;
}

/* Print -------------------------------------------------------------------- */

/* A gradient header prints as grey mush on most office printers and costs a
   surprising amount of toner doing it. Reports are printed and signed in this
   business, so print gets ink on paper instead of colour on screen. */
@media print {
    .erp-table thead th {
        background: #fff !important;
        border-bottom: 1.5px solid #666 !important;
        color: #000 !important;
    }

    .erp-table thead th + th {
        box-shadow: none !important;
    }

    .erp-table tbody tr:nth-child(even) {
        background-color: transparent !important;
    }

    .erp-table tfoot td,
    .erp-table tfoot th {
        background: #fff !important;
        border-top: 1.5px solid #666 !important;
    }

    .erp-table tr.erp-group-row > td,
    .erp-table tr.erp-group-row > th {
        background-color: transparent !important;
        border-top: 1px solid #999 !important;
    }
}

/* Two-line identity cell: avatar + name/meta */
.erp-identity {
    align-items: center;
    display: flex;
    gap: 10px;
}

.erp-identity img,
.erp-identity .erp-initials {
    border-radius: 50%;
    flex: 0 0 auto;
    height: 28px;
    object-fit: cover;
    width: 28px;
}

/* .erp-identity span sets display:block, which outranks .erp-initials' own
   flex and would knock the text/icon off-centre — restore the centring here. */
.erp-identity .erp-initials {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.erp-initials {
    align-items: center;
    background: rgba(0, 194, 146, .12);
    color: var(--erp-primary-dark);
    display: flex;
    font-size: 11px;
    font-weight: 600;
    justify-content: center;
}

.erp-identity span {
    display: block;
    line-height: 1.35;
}

.erp-identity .erp-identity-name {
    color: var(--erp-ink);
    font-weight: 600;
}

.erp-identity .erp-identity-meta {
    color: var(--erp-muted);
    font-size: 11px;
}

.btn-erp.btn-sm,
.btn-erp-ghost.btn-sm {
    border-radius: 5px;
    font-size: 11.5px;
    padding: 4px 9px;
}

/* A sober brand-tinted wash so the small ghost actions read as interactive
   at a glance, without shouting like a solid primary button. */
.btn-erp-ghost.btn-sm {
    background: rgba(0, 194, 146, .10);
    border-color: rgba(0, 194, 146, .30);
    color: var(--erp-primary-dark);
}

.btn-erp-ghost.btn-sm:hover,
.btn-erp-ghost.btn-sm:focus {
    background: rgba(0, 194, 146, .18);
    border-color: rgba(0, 194, 146, .55);
    color: var(--erp-primary-dark);
}

.btn-erp-ghost.btn-sm.text-danger {
    background: rgba(220, 53, 69, .09);
    border-color: rgba(220, 53, 69, .28);
}

.btn-erp-ghost.btn-sm.text-danger:hover,
.btn-erp-ghost.btn-sm.text-danger:focus {
    background: rgba(220, 53, 69, .16);
    border-color: rgba(220, 53, 69, .5);
}

/* --------------------------------------------------------------------------
   8. Lists — project progress, milestones, activity
   -------------------------------------------------------------------------- */

.erp-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.erp-list > li {
    border-bottom: 1px solid #f2f4f8;
    padding: 11px 16px;
}

.erp-list > li:last-child {
    border-bottom: 0;
}

.erp-progress-head {
    align-items: baseline;
    display: flex;
    gap: 10px;
    justify-content: space-between;
    margin-bottom: 8px;
}

.erp-progress-head strong {
    color: var(--erp-ink);
    font-size: 13.5px;
    font-weight: 600;
}

.erp-progress-head span {
    color: var(--erp-muted);
    font-size: 12.5px;
}

.erp-list .progress {
    background: #eef1f6;
    border-radius: 20px;
    height: 7px;
}

.erp-list .progress-bar {
    border-radius: 20px;
}

.erp-meta-row {
    color: var(--erp-muted);
    display: flex;
    flex-wrap: wrap;
    font-size: 12px;
    gap: 14px;
    margin-top: 8px;
}

.erp-meta-row i {
    margin-right: 4px;
    opacity: .8;
}

/* Timeline used by the activity feed */
.erp-timeline {
    list-style: none;
    margin: 0;
    padding: 4px 20px 4px 20px;
}

.erp-timeline > li {
    border-left: 2px solid var(--erp-line);
    padding: 0 0 20px 20px;
    position: relative;
}

.erp-timeline > li:last-child {
    border-left-color: transparent;
    padding-bottom: 0;
}

.erp-timeline > li:before {
    background: #fff;
    border: 2px solid var(--erp-primary);
    border-radius: 50%;
    content: "";
    height: 11px;
    left: -6.5px;
    position: absolute;
    top: 3px;
    width: 11px;
}

.erp-timeline > li.is-info:before { border-color: var(--erp-info); }
.erp-timeline > li.is-warning:before { border-color: var(--erp-warning); }
.erp-timeline > li.is-danger:before { border-color: var(--erp-danger); }

.erp-timeline h6 {
    color: var(--erp-ink);
    font-size: 13.5px;
    font-weight: 600;
    margin: 0 0 2px;
}

.erp-timeline p {
    color: #6b7686;
    font-size: 12.5px;
    margin: 0;
}

.erp-timeline time {
    color: var(--erp-muted);
    display: block;
    font-size: 11.5px;
    margin-top: 3px;
}

/* --------------------------------------------------------------------------
   9. Charts + legends
   -------------------------------------------------------------------------- */

.erp-chart {
    position: relative;
    width: 100%;
}

.erp-chart-lg { height: 320px; }
.erp-chart-md { height: 260px; }
.erp-chart-sm { height: 210px; }

/* The four-up dashboard grid. 180px is the floor at which a pie still reads and
   a bar chart still has room for four value ticks; below it the axis labels
   start colliding and the chart stops being worth the space it takes. */
.erp-chart-xs { height: 180px; }

/* --- Compact chart card --------------------------------------------------- */

.erp-card-head.is-tight {
    padding-bottom: 8px;
}

.erp-card-head.is-tight h2 {
    font-size: 14px;
}

.erp-card-head.is-tight p {
    font-size: 11.5px;
    margin-bottom: 0;
}

.erp-legend.is-tight {
    font-size: 11px;
    gap: 4px 12px;
    margin-bottom: 6px;
}

/* The value list under each small chart.
   This is not a caption — it is the readable form of the same data. The marks
   in a 180px chart sit below a 3:1 contrast ratio against the card, which is
   only acceptable where the figures are ALSO available as text, and it is what
   makes the charts usable for anyone who cannot separate the slices by hue. */
.erp-chart-values {
    list-style: none;
    margin: 10px 0 0;
    padding: 0;
}

.erp-chart-values li {
    align-items: center;
    display: flex;
    font-size: 11.5px;
    justify-content: space-between;
    padding: 3px 0;
}

.erp-chart-values li + li {
    border-top: 1px solid var(--erp-line);
}

.erp-chart-values-key {
    align-items: center;
    color: var(--erp-muted);
    display: flex;
    gap: 6px;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-chart-values-key i {
    border-radius: 2px;
    flex: 0 0 auto;
    height: 9px;
    width: 9px;
}

/* Values wear text ink, never the series colour — the swatch beside the label
   already carries identity, and a coloured number is a number that fails
   contrast for the sake of a decoration. */
.erp-chart-values strong {
    color: var(--erp-ink);
    font-size: 11.5px;
    font-weight: 600;
    text-align: right;
    white-space: nowrap;
}

.erp-donut-center.is-sm strong {
    font-size: 16px;
}

.erp-donut-center.is-sm span {
    font-size: 10.5px;
}

.erp-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 20px;
    margin: 0;
    padding: 0;
}

.erp-legend li {
    align-items: center;
    color: #5b6675;
    display: flex;
    font-size: 12.5px;
    gap: 7px;
    list-style: none;
}

.erp-legend i {
    border-radius: 3px;
    display: inline-block;
    height: 10px;
    width: 10px;
}

/* Donut centre readout */
.erp-donut-wrap {
    position: relative;
}

.erp-donut-center {
    left: 50%;
    pointer-events: none;
    position: absolute;
    text-align: center;
    top: 50%;
    transform: translate(-50%, -50%);
}

.erp-donut-center strong {
    color: var(--erp-ink);
    display: block;
    font-size: 24px;
    font-weight: 700;
    line-height: 1.1;
}

.erp-donut-center span {
    color: var(--erp-muted);
    font-size: 11.5px;
    letter-spacing: .4px;
    text-transform: uppercase;
}

/* --------------------------------------------------------------------------
   10. Buttons + form controls
   -------------------------------------------------------------------------- */

.btn-erp {
    background: var(--erp-primary);
    border: 1px solid var(--erp-primary);
    border-radius: 6px;
    color: #fff;
    font-size: 12.5px;
    font-weight: 500;
    padding: 6px 13px;
    transition: all .18s ease;
}

.btn-erp:hover,
.btn-erp:focus {
    background: var(--erp-primary-dark);
    border-color: var(--erp-primary-dark);
    box-shadow: 0 6px 16px rgba(0, 194, 146, .28);
    color: #fff;
}

.btn-erp-ghost {
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: 6px;
    color: #5b6675;
    font-size: 12.5px;
    font-weight: 500;
    padding: 6px 13px;
    transition: all .18s ease;
}

.btn-erp-ghost:hover,
.btn-erp-ghost:focus {
    background: #f7f9fc;
    border-color: rgba(0, 194, 146, .45);
    color: var(--erp-primary-dark);
}

.erp-select {
    background-color: #fff;
    background-position: right 8px center;
    background-size: 13px 10px;
    border: 1px solid #dde3ec;
    border-radius: 6px;
    color: #5b6675;
    font-size: 12.5px;
    min-height: 32px;
    padding: 5px 26px 5px 10px;
    width: auto;
}

.erp-select:focus {
    border-color: rgba(0, 194, 146, .65);
    box-shadow: 0 0 0 2.5px rgba(0, 194, 146, .15);
}

/* --------------------------------------------------------------------------
   11. Footer
   -------------------------------------------------------------------------- */

/* A premium, hairline-thin one-liner centred at the very bottom. No
   background by design; it reads as a quiet imprint, not a bar. pointer-events
   are off so the decorative line never intercepts a click on content beneath.
   .erp-shell carries matching bottom padding so nothing hides under it. */
.erp-footer {
    background: transparent;
    bottom: 0;
    color: var(--erp-muted);
    font-size: 10.5px;
    font-weight: 500;
    left: 0;
    letter-spacing: .04em;
    line-height: 1;
    padding: 7px 18px;
    pointer-events: none;
    position: fixed;
    right: 0;
    text-align: center;
    white-space: nowrap;
    z-index: 5;
}

.erp-footer-copy strong {
    color: var(--erp-ink);
    font-weight: 600;
    letter-spacing: .02em;
}

/* --------------------------------------------------------------------------
   12. Sign-in screen
   -------------------------------------------------------------------------- */

.erp-auth .nk-auth-brand h1 {
    font-size: 30px;
}

.erp-auth-modules {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 30px;
}

.erp-auth-modules span {
    background: rgba(255, 255, 255, .14);
    border: 1px solid rgba(255, 255, 255, .2);
    border-radius: 20px;
    color: #fff;
    font-size: 12px;
    padding: 5px 13px;
}

.erp-auth-mark {
    display: block;
    margin-bottom: 26px;
    text-align: left;
}

.erp-auth-mark img {
    height: 44px;
    max-width: 100%;
    width: auto;
}

.erp-auth-note {
    background: #f7f9fc;
    border: 1px dashed var(--erp-line);
    border-radius: 10px;
    color: #5b6675;
    font-size: 12.5px;
    margin-top: 26px;
    padding: 12px 14px;
}

.erp-auth-note strong {
    color: var(--erp-ink);
}

/* --------------------------------------------------------------------------
   13. Record forms — sectioned layout with a sticky section rail
   -------------------------------------------------------------------------- */

.erp-form-layout {
    align-items: start;
    display: grid;
    gap: var(--erp-gutter);
    grid-template-columns: 252px minmax(0, 1fr);
}

@media (max-width: 1199.98px) {
    .erp-form-layout {
        grid-template-columns: minmax(0, 1fr);
    }
}

.erp-form-rail {
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    box-shadow: var(--erp-shadow);
    overflow: hidden;
    position: sticky;
    top: calc(var(--erp-topbar-h, 108px) + 16px);
}

@media (max-width: 1199.98px) {
    .erp-form-rail {
        position: static;
    }
}

.erp-form-rail-head {
    border-bottom: 1px solid var(--erp-line);
    padding: 14px 18px;
}

.erp-form-rail-head h3 {
    color: var(--erp-ink);
    font-size: 13px;
    font-weight: 600;
    letter-spacing: .3px;
    margin: 0;
    text-transform: uppercase;
}

.erp-form-rail-head p {
    color: var(--erp-muted);
    font-size: 12px;
    margin: 3px 0 0;
}

.erp-form-rail nav {
    padding: 8px;
}

.erp-rail-link {
    align-items: center;
    border-radius: 6px;
    color: #5b6675;
    display: flex;
    font-size: 12.5px;
    font-weight: 500;
    gap: 10px;
    padding: 7px 10px;
    text-decoration: none;
    transition: background .16s ease, color .16s ease;
}

.erp-rail-link:hover {
    background: #f5f8fb;
    color: var(--erp-primary-dark);
}

.erp-rail-link.is-active {
    background: rgba(0, 194, 146, .1);
    color: var(--erp-primary-dark);
    font-weight: 600;
}

.erp-rail-step {
    align-items: center;
    background: #eef1f6;
    border-radius: 50%;
    color: var(--erp-muted);
    display: flex;
    flex: 0 0 auto;
    font-size: 10.5px;
    font-weight: 600;
    height: 21px;
    justify-content: center;
    transition: background .16s ease, color .16s ease;
    width: 21px;
}

.erp-rail-link.is-active .erp-rail-step {
    background: var(--erp-primary);
    color: #fff;
}

.erp-rail-link.has-error .erp-rail-step {
    background: rgba(233, 30, 99, .14);
    color: var(--erp-danger);
}

.erp-rail-link.has-error {
    color: var(--erp-danger);
}

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

.erp-form-section {
    scroll-margin-top: calc(var(--erp-topbar-h, 108px) + 16px);
}

/* The tinted strip is what separates one section from the next once the
   fields themselves are packed tight. */
.erp-section-head {
    align-items: center;
    background: linear-gradient(180deg, #fbfcfe, #f7f9fc);
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    gap: 11px;
    padding: 10px 16px;
}

.erp-section-icon {
    align-items: center;
    background: rgba(0, 194, 146, .12);
    border-radius: 7px;
    color: var(--erp-primary);
    display: flex;
    flex: 0 0 auto;
    font-size: 12.5px;
    height: 30px;
    justify-content: center;
    width: 30px;
}

.erp-section-head h2 {
    color: var(--erp-ink);
    font-size: 13px;
    font-weight: 600;
    letter-spacing: .1px;
    margin: 0;
}

.erp-section-head p {
    color: var(--erp-muted);
    font-size: 11.5px;
    margin: 1px 0 0;
}

/* Fields ----------------------------------------------------------------- */

/* Density target: a ~32px control, the way a native desktop client sizes its
   fields. Rows are tightened with it so the grid stays visually balanced. */
.erp-form .row {
    --bs-gutter-x: 16px;
}

.erp-form .form-label {
    color: #55627a;
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: .15px;
    margin-bottom: 4px;
}

.erp-required {
    color: var(--erp-danger);
    margin-left: 2px;
}

.erp-form .form-control,
.erp-form .form-select {
    border: 1px solid #dde3ec;
    border-radius: 6px;
    box-shadow: none;
    color: var(--erp-ink);
    font-size: 12.5px;
    line-height: 1.45;
    min-height: 32px;
    padding: 5px 10px;
    transition: border-color .16s ease, box-shadow .16s ease;
}

/* Bootstrap reserves the right padding for the caret; the shorthand above
   would otherwise let long option labels run underneath it. */
.erp-form .form-select {
    background-position: right 8px center;
    background-size: 13px 10px;
    padding-right: 26px;
}

.erp-form textarea.form-control {
    line-height: 1.55;
    min-height: 0;
    padding: 7px 10px;
}

.erp-form .form-control::placeholder {
    color: #b6bfcd;
}

.erp-form .form-control:focus,
.erp-form .form-select:focus {
    border-color: rgba(0, 194, 146, .65);
    box-shadow: 0 0 0 2.5px rgba(0, 194, 146, .15);
}

.erp-form .form-control.is-invalid,
.erp-form .form-select.is-invalid {
    border-color: rgba(233, 30, 99, .6);
    background-image: none;
}

.erp-form .form-control.is-invalid:focus,
.erp-form .form-select.is-invalid:focus {
    box-shadow: 0 0 0 2.5px rgba(233, 30, 99, .13);
}

.erp-form .input-group-text {
    background: #f4f7fb;
    border: 1px solid #dde3ec;
    border-radius: 6px;
    color: var(--erp-muted);
    font-size: 11.5px;
    min-width: 32px;
    padding: 5px 9px;
}

.erp-form .invalid-feedback {
    font-size: 11.5px;
    margin-top: 4px;
}

.erp-help {
    color: var(--erp-muted);
    display: block;
    font-size: 11px;
    line-height: 1.45;
    margin-top: 4px;
}

.erp-field {
    margin-bottom: 14px;
}

/* Side-by-side form layout ------------------------------------------------

   A form laid out in two columns is trying to fit on one screen, so it buys
   back vertical space everywhere it can: tighter field rhythm, tighter card
   padding, and a fieldset label that separates without costing a whole row.
   Scoped to .erp-form-split so every other form keeps the roomier default.

   The two cards are equal height because .erp-card is already height: 100%
   inside its column — the shorter side stretches rather than leaving a step
   halfway down the page. */

.erp-form-split .erp-field {
    margin-bottom: 9px;
}

.erp-form-split .erp-card-body {
    padding: 11px 16px;
}

.erp-form-split .erp-section-head {
    padding: 8px 16px;
}

.erp-form-split .erp-fieldset-label {
    border-top: 1px solid var(--erp-line);
    margin: 3px 0 10px;
    padding-top: 9px;
}

.erp-form-split .erp-help {
    margin-top: 2px;
}

/* The chrome around the two cards, trimmed for the same reason the cards
   themselves were: on a 900px-tall screen the banner, the card gutter and the
   action bar's margin together cost more than a whole field row. */

.erp-form-split .erp-context-banner {
    gap: 10px;
    margin-bottom: 12px;
    padding: 6px 12px;
}

.erp-form-split .erp-context-banner-icon {
    height: 28px;
    width: 28px;
}

.erp-form-split .erp-card {
    margin-bottom: 6px;
}

.erp-form-split .erp-form-bar {
    margin-top: 0;
}

/* The section subtitle explains the card once. It is worth its line on a tall
   screen and not on a short one, where the same 16px is a field row. */
@media (max-height: 950px) {
    .erp-form-split .erp-section-head p {
        display: none;
    }
}

/* The page chrome around a split form, reached through :has() because the
   padding belongs to the layout and the decision belongs to the form. The
   alternative was a second container class threaded through every page that
   renders one, which is a lot of plumbing to move 16px. */
.erp-main:has(.erp-form-split) {
    padding-top: 10px;
}

.erp-main:has(.erp-form-split) .erp-pagehead {
    margin-bottom: 10px;
}

/* The slider carries its own value in the label, so it needs no reserved
   space beneath it the way a text field does. */
.erp-form-split .form-range {
    margin-top: 2px;
}

/* Switches --------------------------------------------------------------- */

.erp-switch-row {
    align-items: center;
    background: #fafbfd;
    border: 1px solid var(--erp-line);
    border-radius: 7px;
    display: flex;
    gap: 14px;
    justify-content: space-between;
    padding: 9px 13px;
}

.erp-switch-row + .erp-switch-row {
    margin-top: 8px;
}

.erp-switch-row h6 {
    color: var(--erp-ink);
    font-size: 12.5px;
    font-weight: 600;
    margin: 0;
}

.erp-switch-row p {
    color: var(--erp-muted);
    font-size: 11.5px;
    margin: 1px 0 0;
}

.erp-form .form-switch .form-check-input {
    border-color: #d5dbe5;
    cursor: pointer;
    height: 19px;
    margin: 0;
    width: 34px;
}

.erp-form .form-switch .form-check-input:checked {
    background-color: var(--erp-primary);
    border-color: var(--erp-primary);
}

.erp-form .form-switch .form-check-input:focus {
    border-color: rgba(0, 194, 146, .6);
    box-shadow: 0 0 0 3px rgba(0, 194, 146, .14);
}

/* Logo drop zone --------------------------------------------------------- */

.erp-logo-zone {
    align-items: center;
    background: #fafbfd;
    border: 2px dashed #d9e0ea;
    border-radius: 12px;
    cursor: pointer;
    display: flex;
    gap: 18px;
    padding: 18px;
    transition: border-color .18s ease, background .18s ease;
}

.erp-logo-zone:hover,
.erp-logo-zone.is-dragging {
    background: rgba(0, 194, 146, .05);
    border-color: rgba(0, 194, 146, .6);
}

.erp-logo-preview {
    align-items: center;
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: 12px;
    display: flex;
    flex: 0 0 auto;
    height: 92px;
    justify-content: center;
    overflow: hidden;
    width: 92px;
}

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

.erp-logo-preview .erp-logo-placeholder {
    color: #c3cbd8;
    font-size: 26px;
}

.erp-logo-copy h6 {
    color: var(--erp-ink);
    font-size: 13.5px;
    font-weight: 600;
    margin: 0 0 3px;
}

.erp-logo-copy p {
    color: var(--erp-muted);
    font-size: 12.5px;
    margin: 0;
}

.erp-logo-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 10px;
}

/* Sticky action bar ------------------------------------------------------ */

.erp-form-bar {
    align-items: center;
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    bottom: 14px;
    box-shadow: 0 -2px 20px rgba(16, 30, 54, .1);
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: space-between;
    margin-top: 16px;
    padding: 9px 16px;
    position: sticky;
    z-index: 5;
}

.erp-form-bar-note {
    color: var(--erp-muted);
    font-size: 11.5px;
}

.erp-form-bar-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

/* --------------------------------------------------------------------------
   14. Index toolbar, empty state, detail lists
   -------------------------------------------------------------------------- */

.erp-toolbar {
    align-items: center;
    background: linear-gradient(180deg, #fbfcfe, #f7f9fc);
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    justify-content: space-between;
    padding: 10px 16px;
}

.erp-search {
    max-width: 340px;
    position: relative;
    width: 100%;
}

.erp-search input {
    border: 1px solid #dde3ec;
    border-radius: 6px;
    font-size: 12.5px;
    min-height: 32px;
    padding: 5px 10px 5px 31px;
    width: 100%;
}

.erp-search input:focus {
    border-color: rgba(0, 194, 146, .65);
    box-shadow: 0 0 0 2.5px rgba(0, 194, 146, .15);
    outline: none;
}

.erp-search i {
    color: var(--erp-muted);
    font-size: 11.5px;
    left: 11px;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
}

.erp-empty {
    padding: 56px 24px;
    text-align: center;
}

.erp-empty-icon {
    align-items: center;
    background: rgba(0, 194, 146, .08);
    border-radius: 50%;
    color: var(--erp-primary);
    display: inline-flex;
    font-size: 26px;
    height: 74px;
    justify-content: center;
    margin-bottom: 18px;
    width: 74px;
}

.erp-empty h3 {
    color: var(--erp-ink);
    font-size: 16px;
    font-weight: 600;
    margin: 0 0 6px;
}

.erp-empty p {
    color: var(--erp-muted);
    font-size: 13.5px;
    margin: 0 auto 20px;
    max-width: 420px;
}

.erp-logo-thumb {
    align-items: center;
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: 9px;
    display: flex;
    flex: 0 0 auto;
    height: 40px;
    justify-content: center;
    overflow: hidden;
    width: 40px;
}

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

.erp-detail-grid {
    display: grid;
    gap: 0 28px;
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (max-width: 767.98px) {
    .erp-detail-grid {
        grid-template-columns: minmax(0, 1fr);
    }
}

.erp-detail {
    border-bottom: 1px solid #f2f4f8;
    padding: 9px 0;
}

/* The tabbed-workspace .erp-detail further down shares this name; inside a
   detail grid it must stay a plain row, not a 420px flex column. */
.erp-detail-grid > .erp-detail {
    display: block;
    height: auto;
    min-height: 0;
}

.erp-detail dt {
    color: var(--erp-muted);
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .4px;
    margin-bottom: 2px;
    text-transform: uppercase;
}

.erp-detail dd {
    color: var(--erp-ink);
    font-size: 12.5px;
    margin: 0;
    word-break: break-word;
}

.erp-detail dd.is-empty {
    color: #b3bccb;
}

.erp-profile-head {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 18px;
}

.erp-profile-logo {
    align-items: center;
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: 14px;
    display: flex;
    flex: 0 0 auto;
    height: 84px;
    justify-content: center;
    overflow: hidden;
    width: 84px;
}

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

/* --------------------------------------------------------------------------
   15. Utilities
   -------------------------------------------------------------------------- */

.erp-divider-y {
    border-right: 1px solid var(--erp-line);
}

@media (max-width: 991.98px) {
    .erp-divider-y {
        border-bottom: 1px solid var(--erp-line);
        border-right: 0;
    }

    /* The template gives the header z-index 1090, which would paint it over
       the off-canvas drawer (1045). Static drops it out of the z-order. */
    .notika-header {
        position: static;
    }
}

.erp-scroll-y {
    overflow-y: auto;
}

/* Lets flex children with long text truncate instead of pushing siblings out. */
.min-w-0 {
    min-width: 0;
}

.erp-h-320 { max-height: 320px; }
.erp-h-380 { max-height: 380px; }

/* ==========================================================================
   Holidays & Shifts
   ========================================================================== */

/* Scope picker — shared by the holiday and shift forms */
.erp-scope-choices {
    display: grid;
    gap: 10px;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}

.erp-scope-choice {
    cursor: pointer;
    display: block;
    margin: 0;
    /* Scopes the visually-hidden radio below to this label. */
    position: relative;
}

.erp-scope-choice input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.erp-scope-choice-body {
    align-items: flex-start;
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    display: flex;
    gap: 10px;
    height: 100%;
    padding: 12px 14px;
    transition: border-color .18s ease, background .18s ease, box-shadow .18s ease;
}

.erp-scope-choice-body i {
    color: var(--erp-muted);
    margin-top: 2px;
    transition: color .18s ease;
}

.erp-scope-choice-body strong {
    color: var(--erp-ink);
    display: block;
    font-size: 13px;
    font-weight: 600;
}

.erp-scope-choice-body small {
    color: var(--erp-muted);
    display: block;
    font-size: 11.5px;
    line-height: 1.45;
}

.erp-scope-choice:hover .erp-scope-choice-body {
    border-color: rgba(0, 194, 146, .45);
}

.erp-scope-choice input:checked + .erp-scope-choice-body {
    background: #f4faf8;
    border-color: var(--erp-primary);
    box-shadow: 0 0 0 3px rgba(0, 194, 146, .12);
}

.erp-scope-choice input:checked + .erp-scope-choice-body i {
    color: var(--erp-primary);
}

.erp-scope-choice input:focus-visible + .erp-scope-choice-body {
    outline: 2px solid var(--erp-primary);
    outline-offset: 2px;
}

.erp-scope-panel {
    background: #fbfcfe;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    margin-top: 16px;
    padding: 14px;
}

.erp-scope-panel-head {
    align-items: center;
    color: var(--erp-ink);
    display: flex;
    font-size: 13px;
    font-weight: 600;
    justify-content: space-between;
    margin-bottom: 12px;
}

.erp-scope-actions {
    display: flex;
    gap: 4px;
}

.erp-scope-empty {
    color: var(--erp-muted);
    font-size: 12.5px;
    margin: 0;
}

.erp-check-grid {
    display: grid;
    gap: 8px;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}

.erp-check-item {
    align-items: center;
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    cursor: pointer;
    display: flex;
    gap: 9px;
    margin: 0;
    padding: 9px 11px;
    transition: border-color .18s ease, background .18s ease;
}

.erp-check-item:hover {
    border-color: rgba(0, 194, 146, .45);
}

.erp-check-item input {
    accent-color: var(--erp-primary);
    flex: 0 0 auto;
    height: 15px;
    width: 15px;
}

.erp-check-item strong {
    color: var(--erp-ink);
    display: block;
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1.3;
}

.erp-check-item small {
    color: var(--erp-muted);
    font-size: 11px;
}

.erp-check-item:has(input:checked) {
    background: #f4faf8;
    border-color: var(--erp-primary);
}

/* Compact scope label used in list rows */
.erp-scope-tag {
    align-items: center;
    color: var(--erp-ink);
    display: inline-flex;
    font-size: 12.5px;
    gap: 6px;
}

.erp-scope-tag i {
    color: var(--erp-muted);
    font-size: 11px;
}

/* Colour swatches on the holiday form */
.erp-swatches {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.erp-swatch {
    cursor: pointer;
    display: inline-block;
    margin: 0;
    /* Scopes the visually-hidden radio below to this label. */
    position: relative;
}

.erp-swatch input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.erp-swatch span {
    background: var(--swatch);
    border: 2px solid #fff;
    border-radius: 50%;
    box-shadow: 0 0 0 1px var(--erp-line);
    display: block;
    height: 26px;
    transition: box-shadow .18s ease, transform .18s ease;
    width: 26px;
}

.erp-swatch:hover span {
    transform: scale(1.08);
}

.erp-swatch input:checked + span {
    box-shadow: 0 0 0 2px var(--swatch);
    transform: scale(1.12);
}

.erp-swatch input:focus-visible + span {
    outline: 2px solid var(--erp-ink);
    outline-offset: 2px;
}

/* Working-day picker on the shift form */
.erp-daypicker {
    display: grid;
    gap: 8px;
    grid-template-columns: repeat(auto-fit, minmax(92px, 1fr));
}

.erp-day {
    cursor: pointer;
    margin: 0;
    position: relative;
}

.erp-day input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.erp-day span {
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    display: block;
    padding: 10px 8px;
    text-align: center;
    transition: border-color .18s ease, background .18s ease, box-shadow .18s ease;
}

.erp-day strong {
    color: var(--erp-ink);
    display: block;
    font-size: 13px;
    font-weight: 600;
}

.erp-day small {
    color: var(--erp-muted);
    font-size: 10.5px;
}

.erp-day.is-weekend span {
    background: #fbfcfe;
}

.erp-day input:checked + span {
    background: #f4faf8;
    border-color: var(--erp-primary);
    box-shadow: 0 0 0 3px rgba(0, 194, 146, .12);
}

.erp-day input:checked + span strong {
    color: var(--erp-primary-dark);
}

.erp-day input:focus-visible + span {
    outline: 2px solid var(--erp-primary);
    outline-offset: 2px;
}

/* Weekday chips in the shift list */
.erp-daychips {
    display: inline-flex;
    gap: 3px;
}

.erp-daychip {
    align-items: center;
    background: #f1f4f8;
    border-radius: 4px;
    color: var(--erp-muted);
    display: inline-flex;
    font-size: 10px;
    font-weight: 600;
    height: 19px;
    justify-content: center;
    width: 19px;
}

.erp-daychip.is-on {
    background: rgba(0, 194, 146, .16);
    color: var(--erp-primary-dark);
}

/* Weekly pattern on the shift detail page */
.erp-weekgrid {
    display: grid;
    gap: 8px;
    grid-template-columns: repeat(auto-fit, minmax(84px, 1fr));
}

.erp-weekgrid-day {
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    padding: 12px 8px;
    text-align: center;
}

.erp-weekgrid-day strong {
    color: var(--erp-ink);
    display: block;
    font-size: 13px;
}

.erp-weekgrid-day span {
    color: var(--erp-muted);
    font-size: 11px;
}

.erp-weekgrid-day.is-on {
    background: #f4faf8;
    border-color: rgba(0, 194, 146, .4);
}

.erp-weekgrid-day.is-on span {
    color: var(--erp-primary-dark);
    font-weight: 600;
}

.erp-weekgrid-day.is-off {
    background: #fbfcfe;
}

/* Coloured dot in the holiday list */
.erp-dot {
    border-radius: 50%;
    display: inline-block;
    flex: 0 0 10px;
    height: 10px;
    width: 10px;
}

/* ==========================================================================
   Holiday calendar
   ========================================================================== */

.erp-calnav {
    align-items: center;
    display: flex;
    gap: 6px;
}

.erp-calnav-title {
    color: var(--erp-ink);
    font-size: 15px;
    font-weight: 600;
    margin: 0 6px;
    min-width: 150px;
    text-align: center;
}

.erp-calendar-head,
.erp-calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
}

.erp-calendar-head {
    gap: 6px;
    margin-bottom: 6px;
}

.erp-calendar-weekday {
    color: var(--erp-muted);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .04em;
    padding-bottom: 4px;
    text-align: center;
    text-transform: uppercase;
}

.erp-calendar-grid {
    gap: 6px;
}

.erp-calendar-day {
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-height: 92px;
    padding: 6px;
}

.erp-calendar-daynum {
    align-self: flex-start;
    color: var(--erp-ink);
    font-size: 12px;
    font-weight: 600;
    line-height: 1;
    padding: 2px;
}

.erp-calendar-day.is-outside {
    background: #fbfcfe;
}

.erp-calendar-day.is-outside .erp-calendar-daynum {
    color: var(--erp-muted);
    opacity: .6;
}

.erp-calendar-day.is-weekly-off {
    background: #f7f9fc;
}

.erp-calendar-day.is-today {
    border-color: var(--erp-primary);
    box-shadow: 0 0 0 2px rgba(0, 194, 146, .16);
}

.erp-calendar-day.is-today .erp-calendar-daynum {
    background: var(--erp-primary);
    border-radius: 5px;
    color: #fff;
    padding: 2px 6px;
}

.erp-calendar-chip {
    background: color-mix(in srgb, var(--chip) 14%, transparent);
    border-left: 3px solid var(--chip);
    border-radius: 4px;
    color: var(--erp-ink);
    display: block;
    font-size: 11px;
    line-height: 1.3;
    overflow: hidden;
    padding: 3px 5px;
    text-decoration: none;
    text-overflow: ellipsis;
    transition: background .18s ease;
    white-space: nowrap;
}

.erp-calendar-chip:hover {
    background: color-mix(in srgb, var(--chip) 26%, transparent);
    color: var(--erp-ink);
}

.erp-calendar-legend {
    color: var(--erp-muted);
    display: flex;
    flex-wrap: wrap;
    font-size: 11.5px;
    gap: 14px;
    margin-top: 12px;
}

.erp-calendar-legend span {
    align-items: center;
    display: inline-flex;
    gap: 6px;
}

.erp-legend-swatch {
    border-radius: 3px;
    display: inline-block;
    height: 11px;
    width: 11px;
}

.erp-legend-swatch.is-today { background: var(--erp-primary); }
.erp-legend-swatch.is-off { background: #f0f3f8; border: 1px solid var(--erp-line); }
.erp-legend-swatch.is-holiday { background: rgba(0, 194, 146, .3); border-left: 3px solid var(--erp-primary); }

/* Side panel listing the holidays for the month on view */
.erp-calendar-panel {
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    overflow: hidden;
}

.erp-calendar-panel-head {
    align-items: center;
    background: #fbfcfe;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    justify-content: space-between;
    padding: 12px 14px;
}

.erp-calendar-panel-head h3 {
    color: var(--erp-ink);
    font-size: 14px;
    font-weight: 600;
    margin: 0;
}

.erp-calendar-entry {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    gap: 12px;
    padding: 11px 14px;
    text-decoration: none;
    transition: background .18s ease;
}

.erp-calendar-entry:last-child {
    border-bottom: none;
}

.erp-calendar-entry:hover {
    background: #f7f9fc;
}

.erp-calendar-entry-date {
    background: color-mix(in srgb, var(--chip) 14%, transparent);
    border-radius: 8px;
    color: var(--chip);
    flex: 0 0 42px;
    padding: 6px 0;
    text-align: center;
}

.erp-calendar-entry-date strong {
    display: block;
    font-size: 15px;
    font-weight: 700;
    line-height: 1;
}

.erp-calendar-entry-date small {
    font-size: 10px;
    text-transform: uppercase;
}

.erp-calendar-entry-body {
    min-width: 0;
}

.erp-calendar-entry-body strong {
    color: var(--erp-ink);
    display: block;
    font-size: 13px;
    font-weight: 600;
}

.erp-calendar-entry-body small {
    color: var(--erp-muted);
    display: block;
    font-size: 11.5px;
}

.erp-calendar-entry-scope i {
    font-size: 10px;
}

.erp-calendar-entry-flag {
    color: var(--erp-muted);
    font-size: 11px;
    margin-left: auto;
}

.erp-calendar-panel-empty {
    color: var(--erp-muted);
    padding: 28px 16px;
    text-align: center;
}

.erp-calendar-panel-empty i {
    color: var(--erp-line);
    font-size: 26px;
}

.erp-calendar-panel-empty p {
    font-size: 12.5px;
    margin: 10px 0;
}

@media (max-width: 767.98px) {
    .erp-calendar-day {
        min-height: 68px;
        padding: 4px;
    }

    .erp-calendar-chip {
        padding: 3px;
    }

    .erp-calendar-chip span {
        display: none;
    }
}

/* ==========================================================================
   Module bar accents
   ========================================================================== */

/* Each module carries a deep accent from config/erp.php as --nav-accent. The
   icon wears it at full strength; the label stays neutral so the bar still
   reads as one row rather than a rainbow of text. */
.notika-navbar .nav-link i:first-child,
.erp-nav-icon {
    color: var(--nav-accent, var(--erp-primary));
    transition: color .18s ease, transform .18s ease;
}

.notika-navbar .nav-link:hover i:first-child {
    transform: translateY(-1px);
}

/* The active module gets a tinted plate and a matching underline, so the
   accent identifies where you are rather than just decorating. */
.notika-navbar .nav-link.active {
    background: color-mix(in srgb, var(--nav-accent, var(--erp-primary)) 10%, transparent);
    border-bottom-color: var(--nav-accent, var(--erp-primary));
    border-radius: 7px 7px 0 0;
    color: var(--nav-accent, var(--erp-primary));
}

.notika-navbar .nav-link.active i:first-child {
    color: var(--nav-accent, var(--erp-primary));
}

/* ==========================================================================
   Dark theme
   ==========================================================================
   Driven by data-theme on <html>, set server-side from the user's saved
   preference and corrected before paint by the boot script when that
   preference is "system". Everything below re-points the design tokens the
   rest of the stylesheet already builds on. */

:root[data-theme="dark"] {
    --erp-primary: #1ad3a6;
    --erp-primary-dark: #00b283;
    --erp-info: #38bdf8;
    --erp-warning: #fbbf24;
    --erp-danger: #fb7185;
    --erp-purple: #a78bfa;
    --erp-ink: #e6edf5;
    --erp-muted: #93a2b7;
    --erp-line: #2a3442;
    --erp-bg: #131a23;
    --erp-surface: #1b2430;
    --erp-surface-2: #212c3a;
    --erp-shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 4px 14px rgba(0, 0, 0, .28);

    color-scheme: dark;
}

/* Light keeps the same two surface tokens so components can reference them
   unconditionally. */
:root[data-theme="light"],
:root:not([data-theme]) {
    --erp-surface: #ffffff;
    --erp-surface-2: #f7f9fc;
}

:root[data-theme="dark"] body {
    background: var(--erp-bg);
    color: var(--erp-ink);
}

/* Surfaces ---------------------------------------------------------------- */

:root[data-theme="dark"] .erp-card,
:root[data-theme="dark"] .erp-submenubar,
:root[data-theme="dark"] .notika-header,
:root[data-theme="dark"] .notika-navbar,
:root[data-theme="dark"] .dropdown-menu,
:root[data-theme="dark"] .modal-content,
:root[data-theme="dark"] .offcanvas,
:root[data-theme="dark"] .erp-calendar-day,
:root[data-theme="dark"] .erp-scope-choice-body,
:root[data-theme="dark"] .erp-check-item,
:root[data-theme="dark"] .erp-snap,
:root[data-theme="dark"] .erp-day span {
    background: var(--erp-surface);
    border-color: var(--erp-line);
    color: var(--erp-ink);
}

/* Submenu bar. Only the resting and hover states are restated — the active
   button keeps its accent gradient in both themes, so it is excluded rather
   than folded into the surface rules above. */
:root[data-theme="dark"] .erp-submenu-btn:not(.is-active) {
    background: var(--erp-surface-2);
    color: var(--erp-ink);
}

:root[data-theme="dark"] .erp-submenu-btn:not(.is-active):hover,
:root[data-theme="dark"] .erp-submenu-btn:not(.is-active):focus-visible {
    background: var(--erp-bg);
    border-color: var(--erp-primary);
    color: var(--erp-primary);
}

:root[data-theme="dark"] .erp-toolbar,
:root[data-theme="dark"] .erp-card-foot,
:root[data-theme="dark"] .erp-section-head,
:root[data-theme="dark"] .erp-calendar-panel-head,
:root[data-theme="dark"] .erp-scope-panel,
:root[data-theme="dark"] .erp-context-btn,
:root[data-theme="dark"] .erp-calendar-day.is-weekly-off,
:root[data-theme="dark"] .erp-calendar-day.is-outside,
:root[data-theme="dark"] .erp-weekgrid-day.is-off,
:root[data-theme="dark"] .erp-day.is-weekend span {
    background: var(--erp-surface-2);
    border-color: var(--erp-line);
    color: var(--erp-ink);
}

/* The dark header and hover come from the --erp-table-* tokens, which the
   dark theme redefines in section 7. Repainting them here would undo that and
   leave dark mode on the old flat grey while light mode moved on. */
:root[data-theme="dark"] .erp-table tbody tr:hover > td {
    background-color: var(--erp-table-hover);
}

:root[data-theme="dark"] .erp-table td,
:root[data-theme="dark"] .erp-table th,
:root[data-theme="dark"] .erp-list li,
:root[data-theme="dark"] .erp-calendar-entry,
:root[data-theme="dark"] .erp-switch-row + .erp-switch-row {
    border-color: var(--erp-line);
}

/* Typography -------------------------------------------------------------- */

:root[data-theme="dark"] h1,
:root[data-theme="dark"] h2,
:root[data-theme="dark"] h3,
:root[data-theme="dark"] h4,
:root[data-theme="dark"] h5,
:root[data-theme="dark"] h6,
:root[data-theme="dark"] .erp-identity-name,
:root[data-theme="dark"] .erp-cell-strong,
:root[data-theme="dark"] .dropdown-item {
    color: var(--erp-ink);
}

:root[data-theme="dark"] .text-muted {
    color: var(--erp-muted) !important;
}

:root[data-theme="dark"] .dropdown-item:hover,
:root[data-theme="dark"] .dropdown-item:focus {
    background: var(--erp-surface-2);
    color: var(--erp-ink);
}

:root[data-theme="dark"] .breadcrumb-item,
:root[data-theme="dark"] .breadcrumb-item a {
    color: var(--erp-muted);
}

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

:root[data-theme="dark"] .form-control,
:root[data-theme="dark"] .form-select,
:root[data-theme="dark"] .erp-search input,
:root[data-theme="dark"] .input-group-text {
    background: var(--erp-surface-2);
    border-color: var(--erp-line);
    color: var(--erp-ink);
}

:root[data-theme="dark"] .form-control:focus,
:root[data-theme="dark"] .form-select:focus {
    background: var(--erp-surface-2);
    border-color: var(--erp-primary);
    color: var(--erp-ink);
}

:root[data-theme="dark"] .form-control::placeholder,
:root[data-theme="dark"] .erp-search input::placeholder {
    color: var(--erp-muted);
}

/* The browser paints native date/time pickers dark once color-scheme is set,
   but the calendar glyph still needs inverting. */
:root[data-theme="dark"] input[type="date"]::-webkit-calendar-picker-indicator,
:root[data-theme="dark"] input[type="time"]::-webkit-calendar-picker-indicator {
    filter: invert(1) opacity(.7);
}

:root[data-theme="dark"] .btn-erp-ghost {
    background: transparent;
    border-color: var(--erp-line);
    color: var(--erp-ink);
}

:root[data-theme="dark"] .btn-erp-ghost:hover {
    background: var(--erp-surface-2);
    color: var(--erp-ink);
}

:root[data-theme="dark"] .btn-erp-ghost.btn-sm {
    background: rgba(0, 194, 146, .16);
    border-color: rgba(0, 194, 146, .38);
    color: var(--erp-primary);
}

:root[data-theme="dark"] .btn-erp-ghost.btn-sm:hover,
:root[data-theme="dark"] .btn-erp-ghost.btn-sm:focus {
    background: rgba(0, 194, 146, .26);
    border-color: rgba(0, 194, 146, .6);
    color: var(--erp-primary);
}

/* Accents ---------------------------------------------------------------- */

/* Deep print colours go muddy on a dark ground, so lift them without losing
   the hue that identifies each module. */
:root[data-theme="dark"] .notika-navbar .nav-link i:first-child,
:root[data-theme="dark"] .erp-nav-icon {
    filter: brightness(1.55) saturate(1.15);
}

:root[data-theme="dark"] .notika-navbar .nav-link.active {
    background: color-mix(in srgb, var(--nav-accent, var(--erp-primary)) 22%, transparent);
    color: #fff;
}

:root[data-theme="dark"] .erp-context-banner {
    background: color-mix(in srgb, var(--erp-primary) 12%, var(--erp-surface));
    border-color: color-mix(in srgb, var(--erp-primary) 35%, transparent);
}

:root[data-theme="dark"] .erp-scope-choice input:checked + .erp-scope-choice-body,
:root[data-theme="dark"] .erp-check-item:has(input:checked),
:root[data-theme="dark"] .erp-day input:checked + span,
:root[data-theme="dark"] .erp-weekgrid-day.is-on {
    background: color-mix(in srgb, var(--erp-primary) 16%, var(--erp-surface));
    border-color: var(--erp-primary);
}

:root[data-theme="dark"] .erp-daychip {
    background: var(--erp-surface-2);
}

:root[data-theme="dark"] .erp-daychip.is-on {
    background: color-mix(in srgb, var(--erp-primary) 24%, transparent);
    color: var(--erp-primary);
}

:root[data-theme="dark"] .erp-swatch span {
    border-color: var(--erp-surface);
}

:root[data-theme="dark"] .erp-empty-icon,
:root[data-theme="dark"] .erp-calendar-panel-empty i {
    color: var(--erp-line);
}

/* Brand marks are drawn for a light ground. */
:root[data-theme="dark"] .erp-brand-logo,
:root[data-theme="dark"] .offcanvas-header img {
    filter: brightness(0) invert(1);
}

:root[data-theme="dark"] .alert-danger {
    background: color-mix(in srgb, var(--erp-danger) 16%, var(--erp-surface));
    border-color: color-mix(in srgb, var(--erp-danger) 40%, transparent);
    color: var(--erp-ink);
}

:root[data-theme="dark"] .alert-warning {
    background: color-mix(in srgb, var(--erp-warning) 16%, var(--erp-surface));
    border-color: color-mix(in srgb, var(--erp-warning) 40%, transparent);
    color: var(--erp-ink);
}

:root[data-theme="dark"] .pagination .page-link {
    background: var(--erp-surface-2);
    border-color: var(--erp-line);
    color: var(--erp-ink);
}

:root[data-theme="dark"] .pagination .page-link:hover {
    background: color-mix(in srgb, var(--erp-primary) 16%, var(--erp-surface));
    border-color: color-mix(in srgb, var(--erp-primary) 50%, var(--erp-line));
    color: #fff;
}

:root[data-theme="dark"] .pagination .page-item.active .page-link {
    background: linear-gradient(135deg, var(--erp-primary), var(--erp-primary-dark));
    border-color: transparent;
    color: #06231c;
}

:root[data-theme="dark"] .pagination .page-item.disabled .page-link {
    background: var(--erp-surface);
    border-color: var(--erp-line);
    color: var(--erp-muted);
}

/* ==========================================================================
   Profile
   ========================================================================== */

.erp-profile-tabs {
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    gap: 2px;
    margin-bottom: var(--erp-gutter);
}

.erp-profile-tab {
    border-bottom: 2px solid transparent;
    color: var(--erp-muted);
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 13px;
    font-weight: 500;
    padding: 10px 14px;
    text-decoration: none;
    transition: color .18s ease, border-color .18s ease;
}

.erp-profile-tab:hover {
    color: var(--erp-ink);
}

.erp-profile-tab.is-active {
    border-bottom-color: var(--erp-primary);
    color: var(--erp-primary);
}

/* Avatar drop zone, same interaction as the company logo picker */
.erp-avatar-zone {
    align-items: center;
    border: 1px dashed var(--erp-line);
    border-radius: var(--erp-radius);
    cursor: pointer;
    display: flex;
    gap: 16px;
    padding: 16px;
    transition: border-color .18s ease, background .18s ease;
}

.erp-avatar-zone:hover,
.erp-avatar-zone.is-dragging {
    background: color-mix(in srgb, var(--erp-primary) 6%, transparent);
    border-color: var(--erp-primary);
}

.erp-avatar-preview {
    align-items: center;
    background: var(--erp-surface-2);
    border-radius: 50%;
    display: flex;
    flex: 0 0 76px;
    height: 76px;
    justify-content: center;
    overflow: hidden;
    width: 76px;
}

.erp-avatar-preview img {
    height: 100%;
    object-fit: cover;
    width: 100%;
}

.erp-avatar-preview .erp-avatar-initials {
    color: var(--erp-primary);
    font-size: 24px;
    font-weight: 600;
}

.erp-avatar-copy h6 {
    color: var(--erp-ink);
    font-size: 13.5px;
    font-weight: 600;
    margin: 0 0 3px;
}

.erp-avatar-copy p {
    color: var(--erp-muted);
    font-size: 11.5px;
    margin: 0 0 8px;
}

/* Theme picker on the preferences screen */
.erp-theme-choices {
    display: grid;
    gap: 10px;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}

.erp-theme-choice {
    cursor: pointer;
    display: block;
    margin: 0;
    /* Scopes the visually-hidden radio below to this label. */
    position: relative;
}

.erp-theme-choice input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

/* A span, so it needs an explicit box — padding, borders and text-align do
   not lay out on an inline element, and the block-level icon inside would
   break the inline box apart. */
.erp-theme-choice-body {
    background: var(--erp-surface);
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    display: block;
    height: 100%;
    padding: 14px;
    text-align: center;
    transition: border-color .18s ease, background .18s ease, box-shadow .18s ease;
}

.erp-theme-choice-body i {
    color: var(--erp-muted);
    display: block;
    font-size: 20px;
    margin-bottom: 8px;
}

.erp-theme-choice-body strong {
    color: var(--erp-ink);
    display: block;
    font-size: 13px;
}

.erp-theme-choice-body small {
    color: var(--erp-muted);
    font-size: 11px;
}

.erp-theme-choice input:checked + .erp-theme-choice-body {
    background: color-mix(in srgb, var(--erp-primary) 8%, transparent);
    border-color: var(--erp-primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--erp-primary) 14%, transparent);
}

.erp-theme-choice input:checked + .erp-theme-choice-body i,
.erp-theme-choice input:checked + .erp-theme-choice-body strong {
    color: var(--erp-primary);
}

.erp-theme-choice input:focus-visible + .erp-theme-choice-body {
    outline: 2px solid var(--erp-primary);
    outline-offset: 2px;
}

/* Active browser sessions */
.erp-session {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    gap: 13px;
    padding: 13px 0;
}

.erp-session:last-child {
    border-bottom: none;
}

.erp-session-icon {
    align-items: center;
    background: var(--erp-surface-2);
    border-radius: 9px;
    color: var(--erp-muted);
    display: flex;
    flex: 0 0 38px;
    height: 38px;
    justify-content: center;
}

.erp-session.is-current .erp-session-icon {
    background: color-mix(in srgb, var(--erp-primary) 14%, transparent);
    color: var(--erp-primary);
}

.erp-session-body strong {
    color: var(--erp-ink);
    display: block;
    font-size: 13px;
    font-weight: 600;
}

.erp-session-body small {
    color: var(--erp-muted);
    font-size: 11.5px;
}

/* Three-way theme switch inside the user dropdown */
.erp-theme-switch {
    display: grid;
    gap: 4px;
    grid-template-columns: repeat(3, 1fr);
    padding: 4px 12px 8px;
}

.erp-theme-switch-btn {
    background: transparent;
    border: 1px solid var(--erp-line);
    border-radius: 7px;
    color: var(--erp-muted);
    display: block;
    font-size: 10.5px;
    padding: 7px 4px;
    text-align: center;
    transition: border-color .18s ease, background .18s ease, color .18s ease;
    width: 100%;
}

.erp-theme-switch-btn i {
    display: block;
    font-size: 14px;
    margin-bottom: 3px;
}

.erp-theme-switch-btn:hover {
    border-color: var(--erp-primary);
    color: var(--erp-ink);
}

.erp-theme-switch-btn.is-active {
    background: color-mix(in srgb, var(--erp-primary) 12%, transparent);
    border-color: var(--erp-primary);
    color: var(--erp-primary);
}

/* An uploaded avatar replaces the initials block, so it needs the same shape. */
img.erp-avatar {
    object-fit: cover;
}

/* ==========================================================================
   Roles & permissions
   ========================================================================== */

.erp-perm-group {
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    margin-bottom: 14px;
    overflow: hidden;
}

.erp-perm-group:last-child {
    margin-bottom: 0;
}

.erp-perm-group-head {
    align-items: center;
    background: var(--erp-surface-2);
    border-bottom: 1px solid var(--erp-line);
    color: var(--erp-ink);
    display: flex;
    font-size: 13px;
    font-weight: 600;
    justify-content: space-between;
    padding: 10px 14px;
}

.erp-perm-table {
    margin: 0;
}

.erp-perm-table th[scope="row"] {
    color: var(--erp-ink);
    font-size: 12.5px;
    font-weight: 600;
    vertical-align: middle;
    white-space: nowrap;
    width: 190px;
}

.erp-perm-abilities {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.erp-perm-ability {
    cursor: pointer;
    display: inline-block;
    margin: 0;
    position: relative;
}

.erp-perm-ability input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.erp-perm-ability span {
    background: var(--erp-surface);
    border: 1px solid var(--erp-line);
    border-radius: 20px;
    color: var(--erp-muted);
    display: block;
    font-size: 11.5px;
    font-weight: 500;
    padding: 4px 12px;
    transition: border-color .18s ease, background .18s ease, color .18s ease;
    white-space: nowrap;
}

.erp-perm-ability:hover span {
    border-color: var(--erp-primary);
    color: var(--erp-ink);
}

.erp-perm-ability input:checked + span {
    background: color-mix(in srgb, var(--erp-primary) 14%, transparent);
    border-color: var(--erp-primary);
    color: var(--erp-primary-dark);
}

.erp-perm-ability input:focus-visible + span {
    outline: 2px solid var(--erp-primary);
    outline-offset: 2px;
}

/* Read-only permission names on the user detail page */
.erp-perm-chip {
    background: var(--erp-surface-2);
    border: 1px solid var(--erp-line);
    border-radius: 5px;
    color: var(--erp-muted);
    font-size: 11px;
    padding: 3px 7px;
}

:root[data-theme="dark"] .erp-perm-ability input:checked + span {
    background: color-mix(in srgb, var(--erp-primary) 22%, transparent);
    color: var(--erp-primary);
}

:root[data-theme="dark"] .erp-perm-group-head,
:root[data-theme="dark"] .erp-perm-chip {
    background: var(--erp-surface-2);
    color: var(--erp-ink);
}

/* Per-company role grants on the user form */
.erp-company-roles {
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    margin-bottom: 12px;
    padding: 12px 14px;
}

.erp-company-roles:last-child {
    margin-bottom: 0;
}

.erp-company-roles-head {
    align-items: center;
    color: var(--erp-ink);
    display: flex;
    flex-wrap: wrap;
    font-size: 13px;
    font-weight: 600;
    gap: 8px;
    justify-content: space-between;
    margin-bottom: 10px;
}

.erp-company-roles-head small {
    font-weight: 400;
}

/* Super Admin overrides the grid below it. */
[data-company-roles].is-superseded {
    opacity: .45;
    pointer-events: none;
}

/* What a role grants, spelled out in the role list */
.erp-grant-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-width: 420px;
}

.erp-grant {
    display: flex;
    font-size: 11.5px;
    gap: 6px;
    line-height: 1.45;
}

.erp-grant strong {
    color: var(--erp-ink);
    flex: 0 0 108px;
    font-weight: 600;
}

.erp-grant span {
    color: var(--erp-muted);
}

.erp-grant span.is-full {
    color: var(--erp-primary-dark);
    font-weight: 600;
}

.erp-grant-count {
    color: var(--erp-muted);
    display: inline-block;
    font-size: 10.5px;
    margin-top: 4px;
    text-transform: uppercase;
    letter-spacing: .03em;
}

:root[data-theme="dark"] .erp-grant span.is-full {
    color: var(--erp-primary);
}

/* ==========================================================================
   CRM
   ========================================================================== */

/* Counts strip above a list, each one a filter link */
.erp-statrow {
    display: grid;
    gap: 10px;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    margin-bottom: var(--erp-gutter);
}

/* A deep two-stop gradient, lit from the top-left, with everything inside it
   turned light. The depth comes from three things stacked in one rule and not
   from any one of them: a soft highlight over the gradient so the surface is not
   flat, a hairline of white along the top edge, and a shadow tinted toward the
   tile's own hue rather than grey.

   The hue comes from the shared tone palette: a tile wears .is-tone-<name>, or
   takes the rotation further down, or falls back to deep slate. The DEEP pair is
   a separate pair of properties from the light one because a wash that works
   behind 10px text is not a background that works behind white 18px figures --
   the same hue at two very different weights.

   .erp-stat covers both shapes in the system: icon + body, and the label/value
   pair with no icon. flex-wrap plus full-width label and value is what lets the
   second one stack without needing :has(). */
.erp-stat {
    align-items: center;
    background-color: var(--erp-tone-deep, #334155);
    background-image:
        linear-gradient(160deg, rgba(255, 255, 255, .14) 0%, rgba(255, 255, 255, 0) 46%),
        linear-gradient(135deg, var(--erp-tone-deep, #334155), var(--erp-tone-deeper, #1e293b));
    border: 1px solid transparent;
    border-radius: var(--erp-radius);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .16),
        0 4px 14px -8px color-mix(in srgb, var(--erp-tone-deeper, #1e293b) 70%, transparent);
    color: #fff;
    display: flex;
    flex-wrap: wrap;
    gap: 11px;
    padding: 12px 14px;
    text-decoration: none;
    transition: box-shadow .18s ease, transform .18s ease, filter .18s ease;
}

/* Lifts rather than changing colour. Most of these tiles are links, and a hover
   that recolours a deep tile reads as a state change rather than a pointer. */
.erp-stat:hover {
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .2),
        0 10px 22px -10px color-mix(in srgb, var(--erp-tone-deeper, #1e293b) 85%, transparent);
    color: #fff;
    transform: translateY(-1px);
}

/* THE SELECTED FILTER.

   These tiles are filter buttons on twelve screens, and .is-active used to say
   so by being the only coloured one in the row. Now that they are all coloured
   it says so with a white ring and a lift instead -- a signal that survives
   whatever hue the tile happens to be wearing. */
.erp-stat.is-active {
    background-image:
        linear-gradient(160deg, rgba(255, 255, 255, .24) 0%, rgba(255, 255, 255, .04) 52%),
        linear-gradient(135deg, var(--erp-tone-deep, #334155), var(--erp-tone-deeper, #1e293b));
    box-shadow:
        inset 0 0 0 2px rgba(255, 255, 255, .9),
        0 10px 24px -10px color-mix(in srgb, var(--erp-tone-deeper, #1e293b) 90%, transparent);
}

/* ...and once a rail HAS a selection, the rest step back. Without this the
   selected tile competes with five equally saturated neighbours and the ring has
   to do all the work on its own. Browsers without :has() simply skip it, which
   leaves the ring — a graceful floor, not a broken layout. */
.erp-statrow:has(.erp-stat.is-active) .erp-stat:not(.is-active) {
    filter: saturate(.72);
    opacity: .74;
}

.erp-statrow:has(.erp-stat.is-active) .erp-stat:not(.is-active):hover {
    filter: none;
    opacity: 1;
}

.erp-stat-icon {
    align-items: center;
    /* Frosted rather than a second colour: one hue per tile, at two depths. */
    background: rgba(255, 255, 255, .17);
    border-radius: 9px;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .14);
    color: #fff;
    display: flex;
    flex: 0 0 36px;
    height: 36px;
    justify-content: center;
}

.erp-stat.is-active .erp-stat-icon {
    background: rgba(255, 255, 255, .26);
    color: #fff;
}

.erp-stat-body {
    display: block;
    min-width: 0;
}

.erp-stat-body strong {
    color: #fff;
    display: block;
    font-size: 18px;
    font-weight: 700;
    letter-spacing: -.2px;
    line-height: 1.1;
}

.erp-stat-body small {
    /* 93%, not the 80% this started at. Measured against all fifteen deep
       tones: below 93% the caption drops under WCAG AA on thirteen of them, and
       these captions are the content -- "down right now", "papers expiring" --
       rather than decoration. */
    color: rgba(255, 255, 255, .93);
    font-size: 11.5px;
}

/* The second shape: a caption above a figure, no icon. Used 85 times across the
   views and never defined until now -- on a flex row the two were sitting side
   by side instead of stacking, which is what the full width here fixes. */
.erp-stat-label {
    color: rgba(255, 255, 255, .93);
    display: block;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .05em;
    text-transform: uppercase;
    width: 100%;
}

.erp-stat-value {
    color: #fff;
    display: block;
    font-size: 19px;
    font-weight: 700;
    letter-spacing: -.3px;
    line-height: 1.15;
    width: 100%;
}

/* Equal heights on a grid ---------------------------------------------------- */

/* Bootstrap stretches the COLUMNS to match each other, but not the tile inside
   one, so a row where one tile carries a progress bar and another does not comes
   out with ragged bottoms.

   :only-child rather than a blanket rule: a tile that shares its column with
   other content must not fill the column, because it would stretch straight over
   its siblings. Every one of the 47 stat columns in the views holds nothing else,
   so this covers all of them and cannot catch the case it would break. */
[class*="col-"] > .erp-stat:only-child {
    height: 100%;
}

/* Tiles inside .erp-statrow need nothing: it is a grid, and grid items stretch
   to their row on their own. */

/* And now that the boxes match, the contents have to line up inside them.

   .erp-stat wraps, and a wrapping flex container with no align-content stretches
   its LINES to fill the height -- so a two-line tile gives each line half the
   card and a three-line tile gives each a third. The captions and the figures
   then sit at different heights from one card to the next, which is the ragged
   look that survives equalising the boxes. Packing the lines to the top puts
   every caption on one baseline and every figure on another, and leaves the
   slack at the bottom where the extras hang.

   Scoped to the caption/figure shape, because the icon + body shape is a single
   line that should stay vertically centred. Without :has() support the tiles are
   still equal height -- the ask -- and the contents merely spread as before. */
.erp-stat:has(> .erp-stat-label) {
    align-content: flex-start;
    /* The 11px column gap is right between an icon and its body, and far too
       much between a caption and the figure under it. */
    row-gap: 2px;
}

/* Anything after the figure -- a progress bar, an overdue note -- is secondary,
   so it takes the slack rather than the figure drifting down the card. */
.erp-stat > .erp-stat-value ~ * {
    margin-top: 4px;
    width: 100%;
}


/* Anything inside a tile that was coloured for a white card.

   Bootstrap's text utilities carry !important, so overriding them needs it too.
   These are the four that actually appear inside stat tiles in this codebase;
   without them a .text-muted caption is grey-on-deep-navy and unreadable. */
/* Each of these is the lightest tint of its own hue that still clears AA on the
   deepest tone it can land on. The first guesses -- #fde68a, #a7f3d0 -- came out
   at 4.03 and 4.28, which looks fine and is not readable enough. */
.erp-stat .text-muted { color: rgba(255, 255, 255, .93) !important; }
.erp-stat .text-danger { color: #ffe4e6 !important; }
.erp-stat .text-warning { color: #fffbeb !important; }
.erp-stat .text-success { color: #d1fae5 !important; }

/* A progress track inside a tile has to read against the tile, not against a
   card. */
.erp-stat .erp-progress {
    background: rgba(255, 255, 255, .2);
}

/* A link inside a tile -- a drill-through on a figure -- keeps the tile's ink
   rather than reverting to the page's link colour. */
.erp-stat .erp-plain-link,
.erp-stat a {
    color: #fff;
}

/* Hue rotation for a row of tiles.

   32 screens use these and almost none of them name a tone, so a default alone
   would give the whole system rows of identical navy. The rotation gives each
   position its own hue and costs no blade edits.

   Guarded so that anything MEANING something keeps its meaning: an explicit
   tone, or one of the alert tones below, is never rotated over. Rose and the
   alert hues stay out of the rotation itself, because a red tile has to mean
   red. */
.erp-statrow > .erp-stat:not([class*="is-tone-"]):not(.is-danger):not(.is-warning):not(.is-info):not(.is-success):nth-child(6n + 1) { --erp-tone-deep: #0369a1; --erp-tone-deeper: #075985; }
.erp-statrow > .erp-stat:not([class*="is-tone-"]):not(.is-danger):not(.is-warning):not(.is-info):not(.is-success):nth-child(6n + 2) { --erp-tone-deep: #4f46e5; --erp-tone-deeper: #3730a3; }
.erp-statrow > .erp-stat:not([class*="is-tone-"]):not(.is-danger):not(.is-warning):not(.is-info):not(.is-success):nth-child(6n + 3) { --erp-tone-deep: #0f766e; --erp-tone-deeper: #115e59; }
.erp-statrow > .erp-stat:not([class*="is-tone-"]):not(.is-danger):not(.is-warning):not(.is-info):not(.is-success):nth-child(6n + 4) { --erp-tone-deep: #7e22ce; --erp-tone-deeper: #581c87; }
.erp-statrow > .erp-stat:not([class*="is-tone-"]):not(.is-danger):not(.is-warning):not(.is-info):not(.is-success):nth-child(6n + 5) { --erp-tone-deep: #0e7490; --erp-tone-deeper: #155e75; }
.erp-statrow > .erp-stat:not([class*="is-tone-"]):not(.is-danger):not(.is-warning):not(.is-info):not(.is-success):nth-child(6n + 6) { --erp-tone-deep: #334155; --erp-tone-deeper: #1e293b; }

/* The same rotation for tiles laid out on a grid rather than in a statrow.

   Two container shapes exist in the views -- .erp-statrow > .erp-stat, and
   .row > .col-* > .erp-stat -- and covering only the first left the grid version
   as a run of identical slate tiles next to a six-hue strip. Same component, two
   looks, decided by nothing more than its wrapper.

   :has() keeps this off every other Bootstrap row in the application. Where it
   is unsupported the tiles keep the deep slate default: duller, never broken. */
.row:has(> [class*="col-"] > .erp-stat) > [class*="col-"]:nth-child(6n + 1) > .erp-stat:not([class*="is-tone-"]):not(.is-danger):not(.is-warning):not(.is-info):not(.is-success) { --erp-tone-deep: #0369a1; --erp-tone-deeper: #075985; }
.row:has(> [class*="col-"] > .erp-stat) > [class*="col-"]:nth-child(6n + 2) > .erp-stat:not([class*="is-tone-"]):not(.is-danger):not(.is-warning):not(.is-info):not(.is-success) { --erp-tone-deep: #4f46e5; --erp-tone-deeper: #3730a3; }
.row:has(> [class*="col-"] > .erp-stat) > [class*="col-"]:nth-child(6n + 3) > .erp-stat:not([class*="is-tone-"]):not(.is-danger):not(.is-warning):not(.is-info):not(.is-success) { --erp-tone-deep: #0f766e; --erp-tone-deeper: #115e59; }
.row:has(> [class*="col-"] > .erp-stat) > [class*="col-"]:nth-child(6n + 4) > .erp-stat:not([class*="is-tone-"]):not(.is-danger):not(.is-warning):not(.is-info):not(.is-success) { --erp-tone-deep: #7e22ce; --erp-tone-deeper: #581c87; }
.row:has(> [class*="col-"] > .erp-stat) > [class*="col-"]:nth-child(6n + 5) > .erp-stat:not([class*="is-tone-"]):not(.is-danger):not(.is-warning):not(.is-info):not(.is-success) { --erp-tone-deep: #0e7490; --erp-tone-deeper: #155e75; }
.row:has(> [class*="col-"] > .erp-stat) > [class*="col-"]:nth-child(6n + 6) > .erp-stat:not([class*="is-tone-"]):not(.is-danger):not(.is-warning):not(.is-info):not(.is-success) { --erp-tone-deep: #334155; --erp-tone-deeper: #1e293b; }

/* Tags */
.erp-tag-picker {
    display: flex;
    flex-wrap: wrap;
    gap: 7px;
}

.erp-tag-choice {
    cursor: pointer;
    display: inline-block;
    margin: 0;
    position: relative;
}

.erp-tag-choice input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.erp-tag-choice span {
    background: var(--erp-surface);
    border: 1px solid var(--erp-line);
    border-radius: 20px;
    color: var(--erp-muted);
    display: block;
    font-size: 11.5px;
    font-weight: 500;
    padding: 4px 13px;
    transition: border-color .18s ease, background .18s ease, color .18s ease;
    white-space: nowrap;
}

.erp-tag-choice:hover span {
    border-color: var(--tag, var(--erp-primary));
    color: var(--erp-ink);
}

.erp-tag-choice input:checked + span {
    background: color-mix(in srgb, var(--tag, var(--erp-primary)) 16%, transparent);
    border-color: var(--tag, var(--erp-primary));
    color: var(--erp-ink);
}

.erp-tag-choice input:focus-visible + span {
    outline: 2px solid var(--tag, var(--erp-primary));
    outline-offset: 2px;
}

.erp-tag-row {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    margin-top: 6px;
}

.erp-tag-chip {
    background: color-mix(in srgb, var(--tag, var(--erp-muted)) 16%, transparent);
    border-left: 3px solid var(--tag, var(--erp-muted));
    border-radius: 4px;
    color: var(--erp-ink);
    display: inline-block;
    font-size: 10.5px;
    font-weight: 500;
    padding: 2px 7px;
}

/* Contact rows on the customer detail page */
.erp-contact-row {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    gap: 13px;
    padding: 13px var(--erp-gutter);
}

.erp-contact-row:last-child {
    border-bottom: none;
}

.erp-contact-body {
    flex: 1 1 auto;
    min-width: 0;
}

.erp-contact-body strong {
    color: var(--erp-ink);
    display: block;
    font-size: 13.5px;
    font-weight: 600;
}

.erp-contact-body small {
    color: var(--erp-muted);
    display: block;
    font-size: 11.5px;
}

.erp-contact-actions {
    flex: 0 0 auto;
}

/* One-line action forms on the lead detail page */
.erp-inline-action {
    align-items: center;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    justify-content: space-between;
    margin-bottom: 10px;
    padding: 12px 14px;
}

.erp-inline-action:last-child {
    margin-bottom: 0;
}

.erp-inline-action.is-primary {
    background: color-mix(in srgb, var(--erp-primary) 7%, transparent);
    border-color: color-mix(in srgb, var(--erp-primary) 35%, transparent);
}

.erp-inline-action strong {
    color: var(--erp-ink);
    display: block;
    font-size: 13px;
    font-weight: 600;
}

.erp-inline-action small {
    color: var(--erp-muted);
    display: block;
    font-size: 11.5px;
}

.erp-inline-action .form-control,
.erp-inline-action .form-select {
    max-width: 190px;
}

/* Table cell links that should not look like links until hovered */
.erp-plain-link {
    color: inherit;
    text-decoration: none;
}

.erp-plain-link:hover {
    color: var(--erp-primary);
    text-decoration: underline;
}

:root[data-theme="dark"] .erp-tag-choice span,
:root[data-theme="dark"] .erp-inline-action {
    background: var(--erp-surface);
    border-color: var(--erp-line);
}

/* .erp-stat is deliberately NOT repainted here. Its gradient is already dark and
   its text is already white, so it needs nothing from the dark theme -- and the
   rule that used to be here set `background`, the shorthand, which resets
   background-image and would have dropped the gradient on the floor. The tile
   only loses a little of its lift, because a shadow tinted toward the hue does
   less work on a dark card. */
:root[data-theme="dark"] .erp-stat {
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .12),
        0 4px 14px -10px rgba(0, 0, 0, .8);
}

/* Lead score bar on the detail page */
.erp-scorebar {
    background: var(--erp-surface-2);
    border-radius: 20px;
    display: block;
    height: 8px;
    overflow: hidden;
    width: 100%;
}

.erp-scorebar span {
    border-radius: 20px;
    display: block;
    height: 100%;
    transition: width .25s ease;
}

.erp-scorebar-legend small {
    color: var(--erp-muted);
    font-size: 11px;
    margin-top: 5px;
}

/* Label/value grid — one card instead of several for a record's fields */
.erp-factgrid {
    display: grid;
    gap: 0;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}

.erp-fact {
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    gap: 12px;
    justify-content: space-between;
    padding: 9px var(--erp-gutter);
}

.erp-fact-label {
    color: var(--erp-muted);
    flex: 0 0 auto;
    font-size: 12px;
}

.erp-fact-value {
    color: var(--erp-ink);
    font-size: 12.5px;
    font-weight: 600;
    text-align: right;
    word-break: break-word;
}

.erp-fact-value.is-empty {
    color: var(--erp-muted);
    font-weight: 400;
}

.erp-fact-value a {
    color: inherit;
    text-decoration: none;
}

.erp-fact-value a:hover {
    color: var(--erp-primary);
    text-decoration: underline;
}

.erp-fact-notes {
    border-top: 1px solid var(--erp-line);
    padding: 12px var(--erp-gutter);
}

.erp-fact-notes p {
    color: var(--erp-ink);
    font-size: 13px;
    margin: 4px 0 0;
    white-space: pre-line;
}

/* ==========================================================================
   Follow-ups
   ========================================================================== */

.erp-attachments {
    display: grid;
    gap: 8px;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
}

.erp-attachment {
    align-items: center;
    background: var(--erp-surface);
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    color: inherit;
    cursor: pointer;
    display: flex;
    gap: 10px;
    margin: 0;
    padding: 9px 11px;
    text-decoration: none;
    transition: border-color .18s ease;
}

.erp-attachment:hover {
    border-color: var(--erp-primary);
    color: inherit;
}

.erp-attachment-icon {
    align-items: center;
    background: var(--erp-surface-2);
    border-radius: 7px;
    color: var(--erp-muted);
    display: flex;
    flex: 0 0 32px;
    height: 32px;
    justify-content: center;
}

.erp-attachment-body {
    flex: 1 1 auto;
    min-width: 0;
}

.erp-attachment-body strong {
    color: var(--erp-ink);
    display: block;
    font-size: 12.5px;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-attachment-body small {
    color: var(--erp-muted);
    font-size: 11px;
}

.erp-attachment-remove {
    align-items: center;
    color: var(--erp-muted);
    display: flex;
    flex: 0 0 auto;
    font-size: 11px;
    gap: 4px;
}

/* ==========================================================================
   Select2 — matched to the form controls
   ========================================================================== */

.select2-container--default .select2-selection--single {
    background: var(--erp-surface);
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    height: 38px;
}

.select2-container--default .select2-selection--single .select2-selection__rendered {
    color: var(--erp-ink);
    font-size: 13px;
    line-height: 36px;
    padding-left: 12px;
}

.select2-container--default .select2-selection--single .select2-selection__arrow {
    height: 36px;
}

.select2-container--default .select2-selection--single .select2-selection__placeholder {
    color: var(--erp-muted);
}

.select2-container--default.select2-container--focus .select2-selection--single,
.select2-container--default.select2-container--open .select2-selection--single {
    border-color: var(--erp-primary);
}

.select2-dropdown {
    background: var(--erp-surface);
    border-color: var(--erp-line);
    border-radius: var(--erp-radius);
}

.select2-container--default .select2-search--dropdown .select2-search__field {
    background: var(--erp-surface-2);
    border: 1px solid var(--erp-line);
    border-radius: 7px;
    color: var(--erp-ink);
    font-size: 13px;
    padding: 6px 9px;
}

.select2-container--default .select2-results__option {
    color: var(--erp-ink);
    font-size: 13px;
    padding: 7px 12px;
}

.select2-container--default .select2-results__option--highlighted[aria-selected] {
    background: var(--erp-primary);
    color: #fff;
}

.select2-container--default .select2-results__option[aria-selected="true"] {
    background: var(--erp-surface-2);
    color: var(--erp-ink);
}

/* Name on the left, phone on the right, code underneath */
.erp-s2-row {
    align-items: baseline;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.erp-s2-name {
    font-weight: 500;
}

.erp-s2-phone {
    color: var(--erp-muted);
    font-size: 11.5px;
    margin-left: auto;
}

/* Follows the row's own text colour rather than assuming a dark highlight:
   a highlighted row is white-on-primary in a single-select but primary-on-white
   in the multi-select checkbox list, and a fixed colour vanishes on one of them. */
.select2-container--default .select2-results__option--highlighted .erp-s2-phone,
.select2-container--default .select2-results__option--highlighted .erp-s2-meta {
    color: inherit;
    opacity: .75;
}

/* Multi-select — selected values render as tidy badges, not a plain list */
.select2-container--default .select2-selection--multiple {
    background: var(--erp-surface);
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    min-height: 38px;
    padding: 2px 4px;
}

.select2-container--default.select2-container--focus .select2-selection--multiple,
.select2-container--default.select2-container--open .select2-selection--multiple {
    border-color: var(--erp-primary);
}

.select2-container--default .select2-selection--multiple .select2-selection__rendered {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    padding: 3px 2px;
}

.select2-container--default .select2-selection--multiple .select2-selection__choice {
    align-items: center;
    background: var(--erp-primary-soft, rgba(0, 194, 146, .13));
    border: 1px solid var(--erp-primary, #00c292);
    border-radius: 999px;
    color: var(--erp-primary-ink, var(--erp-primary, #00c292));
    display: inline-flex;
    flex-direction: row-reverse;
    font-size: 12px;
    font-weight: 500;
    gap: 6px;
    line-height: 1.4;
    margin: 0;
    padding: 3px 10px;
}

.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
    border: 0;
    color: var(--erp-primary, #00c292);
    font-size: 15px;
    font-weight: 700;
    margin: 0;
    opacity: .75;
    padding: 0;
}

.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
    background: transparent;
    color: var(--erp-danger, #e91e63);
    opacity: 1;
}

.select2-container--default .select2-selection--multiple .select2-search--inline .select2-search__field {
    color: var(--erp-ink);
    font-size: 13px;
    margin-top: 4px;
}

.select2-container--default .select2-selection--multiple .select2-selection__placeholder {
    color: var(--erp-muted);
    line-height: 30px;
    margin-left: 6px;
}

/* Checkbox-list dropdown for multi-selects (dropdownCssClass: erp-s2-checkable).
   Each option gets a tick box on the left, filled when the option is selected. */
.erp-s2-checkable .select2-results__option {
    align-items: center;
    display: flex;
    gap: 9px;
}

.erp-s2-checkable .select2-results__option::before {
    border: 1.5px solid var(--erp-line);
    border-radius: 4px;
    background: var(--erp-surface);
    color: transparent;
    content: "\2713"; /* check mark, hidden until selected */
    flex: 0 0 auto;
    font-size: 11px;
    font-weight: 700;
    height: 16px;
    line-height: 14px;
    text-align: center;
    width: 16px;
}

/* A row counts as ticked when Select2 marks it --selected. aria-selected is
   ALSO true for the row merely under the cursor, so keying the tick off it
   made a hovered row look chosen when it was not. */
.erp-s2-checkable .select2-results__option--selected {
    background: var(--erp-surface);
    color: var(--erp-ink);
    font-weight: 500;
}

.erp-s2-checkable .select2-results__option--selected::before {
    background: var(--erp-primary, #00c292);
    border-color: var(--erp-primary, #00c292);
    color: #fff;
}

/* A highlighted row sits on the primary colour, so its box needs a white edge
   to show at all — and a ticked one has to invert completely, or it is a green
   box on a green row. */
.erp-s2-checkable .select2-results__option--highlighted::before {
    border-color: #fff;
}

.erp-s2-checkable .select2-results__option--highlighted.select2-results__option--selected::before {
    background: #fff;
    border-color: #fff;
    color: var(--erp-primary, #00c292);
}

.erp-s2-meta {
    color: var(--erp-muted);
    font-size: 10.5px;
}

/* Colour picker on the follow-up type form */
.erp-pill-choices {
    display: flex;
    flex-wrap: wrap;
    gap: 7px;
}

.erp-pill-choice {
    cursor: pointer;
    display: inline-block;
    margin: 0;
    position: relative;
}

.erp-pill-choice input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.erp-pill-choice .erp-pill {
    border: 2px solid transparent;
    display: inline-block;
}

.erp-pill-choice input:checked + .erp-pill {
    border-color: var(--erp-ink);
}

.erp-pill-choice input:focus-visible + .erp-pill {
    outline: 2px solid var(--erp-primary);
    outline-offset: 2px;
}

/* ==========================================================================
   Record detail — one card, sectioned
   ==========================================================================
   Replaces the old multi-card / label-left-value-right layout. Labels sit
   above their values in a responsive grid, which packs far tighter than a
   two-column list and avoids the ragged gap between a long label and a short
   value. */

.erp-rec-head {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    gap: 14px;
    padding: 16px var(--erp-gutter);
}

.erp-rec-avatar {
    align-items: center;
    background: linear-gradient(135deg, var(--erp-primary), var(--erp-primary-dark));
    border-radius: 12px;
    color: #fff;
    display: flex;
    flex: 0 0 46px;
    font-size: 16px;
    font-weight: 600;
    height: 46px;
    justify-content: center;
    letter-spacing: .3px;
    overflow: hidden;
}

.erp-rec-avatar img {
    height: 100%;
    object-fit: cover;
    width: 100%;
}

.erp-rec-id {
    flex: 1 1 auto;
    min-width: 0;
}

.erp-rec-id h2 {
    color: var(--erp-ink);
    font-size: 16px;
    font-weight: 600;
    line-height: 1.25;
    margin: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-rec-id p {
    color: var(--erp-muted);
    font-size: 12px;
    margin: 2px 0 0;
}

.erp-rec-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    margin-top: 7px;
}

/* Sections stack inside the one card, split by hairlines instead of gaps. */
.erp-rec-section {
    border-bottom: 1px solid var(--erp-line);
    padding: 14px var(--erp-gutter);
}

.erp-rec-section:last-child {
    border-bottom: none;
}

.erp-rec-section-head {
    align-items: center;
    display: flex;
    gap: 10px;
    justify-content: space-between;
    margin-bottom: 11px;
}

.erp-rec-section-head h3 {
    color: var(--erp-muted);
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .07em;
    margin: 0;
    text-transform: uppercase;
}

.erp-deflist {
    display: grid;
    gap: 12px 20px;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}

.erp-def {
    min-width: 0;
}

.erp-def.is-wide {
    grid-column: 1 / -1;
}

.erp-def-label {
    color: var(--erp-muted);
    display: block;
    font-size: 10.5px;
    letter-spacing: .03em;
    margin-bottom: 2px;
    text-transform: uppercase;
}

.erp-def-value {
    color: var(--erp-ink);
    display: block;
    font-size: 13px;
    font-weight: 500;
    line-height: 1.4;
    word-break: break-word;
}

.erp-def-value.is-empty {
    color: var(--erp-muted);
    font-weight: 400;
}

.erp-def-value a {
    color: inherit;
    text-decoration: none;
}

.erp-def-value a:hover {
    color: var(--erp-primary);
    text-decoration: underline;
}

.erp-def-note {
    color: var(--erp-ink);
    font-size: 13px;
    line-height: 1.55;
    margin: 0;
    white-space: pre-line;
}

/* Compact people rows (contact persons) */
.erp-person {
    align-items: center;
    border-top: 1px solid var(--erp-line);
    display: flex;
    gap: 11px;
    padding: 9px 0;
}

.erp-person:first-of-type {
    border-top: none;
    padding-top: 0;
}

.erp-person-avatar {
    align-items: center;
    background: var(--erp-surface-2);
    border-radius: 8px;
    color: var(--erp-muted);
    display: flex;
    flex: 0 0 32px;
    font-size: 11px;
    font-weight: 600;
    height: 32px;
    justify-content: center;
}

.erp-person-body {
    flex: 1 1 auto;
    min-width: 0;
}

.erp-person-body strong {
    color: var(--erp-ink);
    display: block;
    font-size: 13px;
    font-weight: 600;
}

.erp-person-body small {
    color: var(--erp-muted);
    display: block;
    font-size: 11.5px;
}

.erp-person-actions {
    flex: 0 0 auto;
    white-space: nowrap;
}

/* Slim metric strip under the header */
.erp-rec-metrics {
    border-bottom: 1px solid var(--erp-line);
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
}

.erp-rec-metric {
    border-right: 1px solid var(--erp-line);
    padding: 11px var(--erp-gutter);
}

.erp-rec-metric:last-child {
    border-right: none;
}

.erp-rec-metric strong {
    color: var(--erp-ink);
    display: block;
    font-size: 15px;
    font-weight: 600;
    line-height: 1.2;
}

.erp-rec-metric small {
    color: var(--erp-muted);
    font-size: 10.5px;
    letter-spacing: .03em;
    text-transform: uppercase;
}

@media (max-width: 575.98px) {
    .erp-rec-metric {
        border-bottom: 1px solid var(--erp-line);
        border-right: none;
    }
}

/* Inline notes editor */
.erp-note-actions {
    display: flex;
    gap: 7px;
    justify-content: flex-end;
    margin-top: 9px;
}

.erp-note-hint {
    color: var(--erp-warning);
    display: inline-block;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .03em;
    text-transform: uppercase;
}

[data-note-form] textarea {
    font-size: 13px;
    line-height: 1.55;
    resize: vertical;
}

/* Communication log entries */
.erp-comm {
    border-top: 1px solid var(--erp-line);
    display: flex;
    gap: 11px;
    padding: 12px 0;
}

.erp-comm:first-of-type {
    border-top: none;
    padding-top: 0;
}

.erp-comm-icon {
    align-items: center;
    display: flex;
    flex: 0 0 30px;
    height: 30px;
    justify-content: center;
    padding: 0;
}

.erp-comm-body {
    flex: 1 1 auto;
    min-width: 0;
}

.erp-comm-body strong {
    color: var(--erp-ink);
    display: block;
    font-size: 13px;
    font-weight: 600;
}

.erp-comm-meta {
    color: var(--erp-muted);
    display: flex;
    flex-wrap: wrap;
    font-size: 11.5px;
    gap: 12px;
    margin-top: 3px;
}

.erp-comm-notes {
    color: var(--erp-ink);
    font-size: 12.5px;
    line-height: 1.55;
    margin: 6px 0 0;
    white-space: pre-line;
}

.erp-comm-audit {
    color: var(--erp-muted);
    display: block;
    font-size: 10.5px;
    margin-top: 5px;
}

.erp-comm-actions {
    flex: 0 0 auto;
    white-space: nowrap;
}


/* ==========================================================================
   Toasts — transient feedback for AJAX actions
   ==========================================================================
   Actions that never reload the page have no flash message to ride on, so
   window.erpToast() drops one of these in instead.

   Top-right, where the eye already is after clicking something in a toolbar or
   a dialog. It used to sit bottom-left — the far corner from every control
   that fires it, and the one most likely to be off-screen on a long list where
   the user is working near the top of the page.

   Offset by the real height of the sticky header rather than a guessed number,
   so it lands just under the module bar instead of over it; --erp-topbar-h is
   published by erp-app.js and already tracks the header being collapsed or the
   bar wrapping to a second row.
   -------------------------------------------------------------------------- */

.erp-toasts {
    position: fixed;
    right: 18px;
    top: calc(var(--erp-topbar-h, 96px) + 12px);
    z-index: 1085;
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-width: min(380px, calc(100vw - 36px));
    pointer-events: none;
}

/* Below lg the header is not sticky, so there is no bar to clear — pinning to
   its height would leave the toast hanging in the middle of a scrolled page. */
@media (max-width: 991.98px) {
    .erp-toasts {
        right: 12px;
        top: 12px;
        max-width: calc(100vw - 24px);
    }
}

.erp-toast {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 11px 14px;
    border-radius: 10px;
    border-left: 3px solid var(--erp-success);
    background: var(--erp-surface);
    color: var(--erp-text);
    box-shadow: 0 8px 26px rgba(15, 23, 42, 0.18);
    font-size: 12.5px;
    line-height: 1.45;
    cursor: pointer;
    pointer-events: auto;
    animation: erp-toast-in 0.22s ease-out;
}

.erp-toast.is-error {
    border-left-color: var(--erp-danger);
}

.erp-toast i {
    margin-top: 2px;
    color: var(--erp-success);
}

.erp-toast.is-error i {
    color: var(--erp-danger);
}

/* Both directions follow the anchor: a top-anchored toast that slid up from
   below read as rising off the bottom of a panel it was nowhere near. */
.erp-toast.is-leaving {
    opacity: 0;
    transform: translateY(-6px);
    transition: opacity 0.22s ease, transform 0.22s ease;
}

@keyframes erp-toast-in {
    from { opacity: 0; transform: translateY(-8px); }
    to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
    .erp-toast { animation: none; }
}

/* ==========================================================================
   Lead lifecycle state
   ==========================================================================
   Which actions make sense depends on whether the lead is open or written off.
   Both rules only ever hide, so nothing has to restore a display value the
   element never declared — the script just toggles one class on the wrapper.
   -------------------------------------------------------------------------- */

[data-lead-detail]:not(.is-lead-lost) [data-when-lost] {
    display: none !important;
}

[data-lead-detail].is-lead-lost [data-when-open] {
    display: none !important;
}

/* ==========================================================================
   HR — Employee CV, tabs, mini-cards and document cards
   ==========================================================================
   Reuses the record-detail primitives and adds the CV header, the tab strip,
   and the compact cards the satellite sections are built from.
   -------------------------------------------------------------------------- */

.erp-avatar-img {
    align-items: center;
    background: var(--erp-surface-2);
    border-radius: 10px;
    display: inline-flex;
    flex: 0 0 34px;
    height: 34px;
    overflow: hidden;
    width: 34px;
}

.erp-avatar-img img { height: 100%; object-fit: cover; width: 100%; }

.erp-cv-head {
    align-items: center;
    display: flex;
    gap: 16px;
    padding: var(--erp-gutter);
}

.erp-cv-photo {
    align-items: center;
    background: linear-gradient(135deg, var(--erp-primary), var(--erp-primary-dark));
    border-radius: 16px;
    color: #fff;
    display: flex;
    flex: 0 0 76px;
    font-size: 26px;
    font-weight: 600;
    height: 76px;
    justify-content: center;
    overflow: hidden;
}

.erp-cv-photo img { height: 100%; object-fit: cover; width: 100%; }

.erp-cv-id { flex: 1 1 auto; min-width: 0; }

.erp-cv-id h2 {
    color: var(--erp-ink);
    font-size: 19px;
    font-weight: 700;
    margin: 0 0 2px;
}

.erp-cv-id p { color: var(--erp-muted); font-size: 13px; margin: 0 0 8px; }

.erp-tabs {
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    gap: 2px;
    overflow-x: auto;
    padding: 0 var(--erp-gutter);
}

.erp-tab {
    align-items: center;
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    color: var(--erp-muted);
    cursor: pointer;
    display: inline-flex;
    font-size: 13px;
    font-weight: 500;
    gap: 7px;
    padding: 13px 14px;
    white-space: nowrap;
}

.erp-tab:hover { color: var(--erp-text); }

.erp-tab.is-active {
    border-bottom-color: var(--erp-primary);
    color: var(--erp-primary);
}

.erp-tab-count {
    background: var(--erp-surface-2);
    border-radius: 20px;
    font-size: 11px;
    font-weight: 600;
    min-width: 20px;
    padding: 1px 6px;
    text-align: center;
}

.erp-tab.is-active .erp-tab-count {
    background: color-mix(in srgb, var(--erp-primary) 16%, transparent);
    color: var(--erp-primary);
}

.erp-tab-panel { display: none; padding: var(--erp-gutter); }
.erp-tab-panel.is-active { display: block; }

.erp-cv-grid {
    display: grid;
    gap: 20px 28px;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}

.erp-cv-block-head {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    color: var(--erp-ink);
    display: flex;
    gap: 8px;
    margin-bottom: 10px;
    padding-bottom: 7px;
}

.erp-cv-block-head i { color: var(--erp-primary); font-size: 12px; }

.erp-cv-block-head h3 {
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .04em;
    margin: 0;
    text-transform: uppercase;
}

/* Bulk product entry grid */
.erp-bulk-table { counter-reset: bulkrow; font-size: 12.5px; }
.erp-bulk-table tbody tr { counter-increment: bulkrow; }
.erp-bulk-table .bulk-rownum::before { content: counter(bulkrow); }
.erp-bulk-table td { padding: 5px 6px; vertical-align: top; }
.erp-bulk-table th { white-space: nowrap; }
.erp-bulk-table .form-control-sm,
.erp-bulk-table .form-select-sm { font-size: 12.5px; }
.erp-bulk-table .bulk-err { color: var(--erp-danger, #e91e63); font-size: 10.5px; margin-top: 2px; }

.erp-bulk-table .bulk-flags {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 8px;
    padding-top: 8px;
}

.erp-bulk-table .bulk-check {
    align-items: center;
    display: inline-flex;
    font-size: 11px;
    gap: 3px;
    margin: 0;
}

.erp-bulk-table .bulk-check span { color: var(--erp-muted); }

/* Contextual "what to do next" callout inside a card */
.erp-nextstep {
    align-items: flex-start;
    background: var(--erp-primary-soft, rgba(0, 194, 146, .1));
    border: 1px solid var(--erp-primary, #00c292);
    border-radius: var(--erp-radius, 8px);
    color: var(--erp-ink);
    display: flex;
    font-size: 12.5px;
    gap: 10px;
    line-height: 1.5;
    padding: 10px 12px;
}

.erp-nextstep > i {
    color: var(--erp-primary, #00c292);
    font-size: 15px;
    margin-top: 1px;
}

.erp-nextstep strong { display: block; margin-bottom: 1px; }

.erp-nextstep .fa-pen,
.erp-nextstep .fa-trophy { color: var(--erp-primary, #00c292); }

.erp-nextstep-meta { color: var(--erp-muted); display: block; margin-top: 2px; }

/* Small pill showing a count next to a section heading */
.erp-count-chip {
    background: var(--erp-primary-soft, rgba(0, 194, 146, .13));
    border-radius: 999px;
    margin-left: auto;
    color: var(--erp-primary-ink, var(--erp-primary, #00c292));
    font-size: 11px;
    font-weight: 600;
    line-height: 1;
    min-width: 20px;
    padding: 4px 8px;
    text-align: center;
}

.erp-cv-section-head {
    align-items: center;
    display: flex;
    gap: 12px;
    justify-content: space-between;
    margin-bottom: 16px;
}

.erp-cv-section-head h3 {
    color: var(--erp-ink);
    font-size: 15px;
    font-weight: 600;
    margin: 0;
}

.erp-cv-subhead {
    color: var(--erp-muted);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .04em;
    margin: 16px 0 8px;
    text-transform: uppercase;
}

.erp-cv-empty { color: var(--erp-muted); font-size: 13px; padding: 8px 0; }

.erp-cv-hint {
    background: var(--erp-surface-2);
    border-radius: 9px;
    color: var(--erp-text);
    font-size: 12.5px;
    margin-bottom: 14px;
    padding: 9px 12px;
}

.erp-card-grid {
    display: grid;
    gap: 12px;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}

.erp-mini-card,
.erp-doc-card {
    align-items: flex-start;
    background: var(--erp-surface);
    border: 1px solid var(--erp-line);
    border-radius: 11px;
    display: flex;
    gap: 12px;
    padding: 13px 14px;
}

.erp-mini-card-body,
.erp-doc-body { flex: 1 1 auto; min-width: 0; }

.erp-mini-card-body strong,
.erp-doc-body strong {
    color: var(--erp-ink);
    display: block;
    font-size: 13.5px;
    font-weight: 600;
    margin-bottom: 4px;
}

.erp-mini-meta {
    color: var(--erp-muted);
    display: flex;
    flex-wrap: wrap;
    font-size: 12px;
    gap: 4px 12px;
}

.erp-mini-meta > span { align-items: center; display: inline-flex; }

.erp-mini-note {
    color: var(--erp-muted);
    display: block;
    font-size: 12px;
    margin-top: 5px;
}

.erp-mini-actions { display: flex; flex: 0 0 auto; gap: 4px; }

.erp-doc-icon {
    align-items: center;
    background: var(--erp-surface-2);
    border-radius: 10px;
    color: var(--erp-primary);
    display: flex;
    flex: 0 0 40px;
    font-size: 17px;
    height: 40px;
    justify-content: center;
}

.erp-timeline-item { display: flex; gap: 14px; padding-bottom: 18px; position: relative; }

.erp-timeline-item:not(:last-child)::before {
    background: var(--erp-line);
    bottom: 0;
    content: "";
    left: 15px;
    position: absolute;
    top: 32px;
    width: 2px;
}

.erp-timeline-dot {
    align-items: center;
    background: var(--erp-surface-2);
    border-radius: 50%;
    color: var(--erp-primary);
    display: flex;
    flex: 0 0 32px;
    font-size: 12px;
    height: 32px;
    justify-content: center;
    z-index: 1;
}

.erp-timeline-body { flex: 1 1 auto; min-width: 0; padding-top: 4px; }

.erp-timeline-head {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 4px;
}

.erp-timeline-head strong { color: var(--erp-ink); font-size: 13.5px; font-weight: 600; }

.erp-photo-field { align-items: center; display: flex; gap: 16px; margin-bottom: 18px; }

.erp-photo-preview {
    align-items: center;
    background: var(--erp-surface-2);
    border: 1px dashed var(--erp-line);
    border-radius: 14px;
    color: var(--erp-muted);
    display: flex;
    flex: 0 0 84px;
    font-size: 30px;
    height: 84px;
    justify-content: center;
    overflow: hidden;
}

.erp-photo-preview img { height: 100%; object-fit: cover; width: 100%; }

/* Passport-size variant: a taller dashed box that is itself the file picker. */
.erp-photo-preview.is-passport {
    flex: 0 0 104px;
    width: 104px;
    height: 132px;
    border-radius: 10px;
    font-size: 24px;
    cursor: pointer;
    transition: border-color .15s ease, color .15s ease;
}
.erp-photo-preview.is-passport:hover { border-color: var(--erp-primary); color: var(--erp-primary); }
.erp-photo-placeholder { display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; }
.erp-photo-placeholder small { font-size: 10px; line-height: 1.2; }

.erp-code-chip {
    background: var(--erp-surface-2);
    border-radius: 10px;
    flex: 0 0 auto;
    padding: 8px 14px;
    text-align: center;
}

.erp-code-chip small {
    color: var(--erp-muted);
    display: block;
    font-size: 10px;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.erp-code-chip strong { color: var(--erp-primary); font-size: 15px; font-weight: 700; }

.erp-fieldset-label {
    color: var(--erp-muted);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .04em;
    margin: 8px 0 10px;
    text-transform: uppercase;
}

.erp-nominee-row { align-items: center; display: flex; gap: 18px; }
.erp-nominee-row [data-nominee-share] { width: 130px; }

.erp-code-preview-box {
    background: var(--erp-surface-2);
    border-radius: 10px;
    padding: 8px 14px;
    width: 100%;
}

.erp-code-preview-box small {
    color: var(--erp-muted);
    display: block;
    font-size: 10px;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.erp-code-preview-box strong {
    color: var(--erp-primary);
    font-family: ui-monospace, monospace;
    font-size: 16px;
    font-weight: 700;
}

@media (max-width: 575.98px) {
    .erp-cv-head { flex-wrap: wrap; }
    .erp-mini-card, .erp-doc-card { flex-wrap: wrap; }
}

/* Multi-document upload rows */
.erp-doc-upload-row {
    background: var(--erp-surface-2);
    border-radius: 10px;
    margin-bottom: 10px;
    padding: 10px;
}

/* ==========================================================================
   Projects — progress bar and inline callout
   ========================================================================== */

.erp-progress {
    background: var(--erp-surface-2);
    border-radius: 20px;
    height: 8px;
    overflow: hidden;
    width: 100%;
}

.erp-progress-bar {
    background: var(--erp-primary);
    border-radius: 20px;
    display: block;
    height: 100%;
    transition: width 0.35s ease;
}

/* Tint the bar to match the status pill palette. */
.erp-progress-bar.is-available { background: var(--erp-success); }
.erp-progress-bar.is-hold { background: var(--erp-warning); }
.erp-progress-bar.is-booked { background: var(--erp-info); }
.erp-progress-bar.is-sold { background: var(--erp-purple); }
.erp-progress-bar.is-overdue { background: var(--erp-danger); }
.erp-progress-bar.is-neutral { background: var(--erp-muted); }

.erp-callout {
    align-items: center;
    background: var(--erp-surface-2);
    border-radius: 9px;
    color: var(--erp-text);
    display: flex;
    font-size: 12.5px;
    gap: 6px;
    margin-bottom: 12px;
    padding: 9px 12px;
}

/* Coloured initials tile can carry a status/type tint. */
.erp-initials.is-available { background: color-mix(in srgb, var(--erp-success) 20%, transparent); color: var(--erp-success); }
.erp-initials.is-hold { background: color-mix(in srgb, var(--erp-warning) 22%, transparent); color: var(--erp-warning); }
.erp-initials.is-booked { background: color-mix(in srgb, var(--erp-info) 20%, transparent); color: var(--erp-info); }
.erp-initials.is-sold { background: color-mix(in srgb, var(--erp-purple) 20%, transparent); color: var(--erp-purple); }
.erp-initials.is-overdue { background: color-mix(in srgb, var(--erp-danger) 20%, transparent); color: var(--erp-danger); }

/* ==========================================================================
   Project task board & team
   ========================================================================== */

.erp-team-grid {
    display: grid;
    gap: 10px;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}

.erp-team-chip {
    align-items: center;
    background: var(--erp-surface);
    border: 1px solid var(--erp-line);
    border-radius: 11px;
    display: flex;
    gap: 10px;
    padding: 9px 11px;
}

.erp-team-body { flex: 1 1 auto; min-width: 0; }
.erp-team-body strong { color: var(--erp-ink); display: block; font-size: 13px; font-weight: 600; }
.erp-team-body small { color: var(--erp-muted); font-size: 11.5px; }

/* Task list */
.erp-task-list { display: flex; flex-direction: column; gap: 8px; }

.erp-task {
    background: var(--erp-surface);
    border: 1px solid var(--erp-line);
    border-radius: 11px;
}

.erp-task.is-dragging { opacity: 0.55; }

.erp-task-main {
    align-items: center;
    display: flex;
    gap: 12px;
    padding: 10px 12px;
}

.erp-task-handle {
    color: var(--erp-muted);
    cursor: grab;
    flex: 0 0 auto;
    font-size: 13px;
}

.erp-task-handle:active { cursor: grabbing; }

.erp-task-check {
    cursor: pointer;
    flex: 0 0 auto;
    font-size: 15px;
    position: relative;
}

.erp-task-check input { position: absolute; opacity: 0; inset: 0; cursor: pointer; }
.erp-task-check i { color: var(--erp-muted); }
.erp-task-check input:checked + i { color: var(--erp-success); }

.erp-task-body { flex: 1 1 auto; min-width: 0; }
.erp-task-body strong { color: var(--erp-ink); display: block; font-size: 13.5px; font-weight: 600; }
.erp-task-body strong.is-done { color: var(--erp-muted); text-decoration: line-through; }

.erp-task-progress {
    align-items: center;
    display: flex;
    flex: 0 0 150px;
    flex-direction: column;
    gap: 3px;
}

.erp-task-progress .erp-progress { width: 100%; }
.erp-task-range { width: 100%; height: 14px; }

.erp-task-assignee { flex: 0 0 150px; }
.erp-task-assignee-name { color: var(--erp-muted); font-size: 12.5px; }

.erp-task-actions { display: flex; flex: 0 0 auto; gap: 3px; }

/* Subtasks nest under their parent with an indent rail. */
.erp-subtasks {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 0 12px 10px 40px;
    padding-top: 2px;
}

.erp-subtasks:empty { display: none; }

.erp-task.is-subtask { background: var(--erp-surface-2); }

@media (max-width: 767.98px) {
    .erp-task-main { flex-wrap: wrap; }
    .erp-task-progress, .erp-task-assignee { flex-basis: 100%; }
    .erp-subtasks { margin-left: 18px; }
}

/* Task cost cell + compact ledger table */
.erp-task-cost { flex: 0 0 110px; }

.erp-task-cost-btn {
    align-items: center;
    background: var(--erp-surface-2);
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    color: var(--erp-text);
    cursor: pointer;
    display: inline-flex;
    font-size: 12px;
    gap: 6px;
    padding: 5px 9px;
    width: 100%;
}

.erp-task-cost-btn:hover { border-color: var(--erp-primary); color: var(--erp-primary); }
.erp-task-cost-btn i { color: var(--erp-muted); }
.erp-task-cost-btn:hover i { color: var(--erp-primary); }

.erp-table-tight th,
.erp-table-tight td { padding-top: 7px; padding-bottom: 7px; font-size: 12.5px; }

@media (max-width: 767.98px) {
    .erp-task-cost { flex-basis: 100%; }
}

/* ==========================================================================
   Project Gantt / timeline
   ========================================================================== */

.erp-gantt {
    --gantt-row-h: 40px;
    --gantt-head-h: 52px;
    --gantt-names-w: 240px;
}

.erp-gantt-toolbar {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    justify-content: space-between;
    padding: 12px var(--erp-gutter);
}

.erp-gantt-modes { display: inline-flex; }

.erp-gantt-mode {
    background: var(--erp-surface);
    border: 1px solid var(--erp-line);
    color: var(--erp-muted);
    cursor: pointer;
    font-size: 12.5px;
    padding: 5px 12px;
}

.erp-gantt-mode:first-child { border-radius: 8px 0 0 8px; }
.erp-gantt-mode:last-child { border-radius: 0 8px 8px 0; }
.erp-gantt-mode:not(:first-child) { border-left: none; }
.erp-gantt-mode.is-active { background: var(--erp-primary); border-color: var(--erp-primary); color: #fff; }

.erp-gantt-legend {
    align-items: center;
    color: var(--erp-muted);
    display: flex;
    flex-wrap: wrap;
    font-size: 12px;
    gap: 14px;
}

.erp-gantt-legend span { align-items: center; display: inline-flex; gap: 5px; }

.erp-gantt-swatch {
    border-radius: 3px;
    display: inline-block;
    height: 11px;
    width: 16px;
}
.erp-gantt-swatch.is-available { background: var(--erp-success); }
.erp-gantt-swatch.is-overdue { background: var(--erp-danger); }
.erp-gantt-swatch.is-sold { background: var(--erp-purple); }

/* Body: fixed names panel + scrollable timeline */
.erp-gantt-body { display: flex; }

.erp-gantt-names {
    border-right: 1px solid var(--erp-line);
    display: flex;
    flex: 0 0 30%;
    flex-direction: column;
    min-width: 220px;
    max-width: 460px;
    width: 30%;
}

.erp-gantt-tree { flex: 1 1 auto; }

.erp-gantt-names-head {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    color: var(--erp-muted);
    display: flex;
    font-size: 11px;
    font-weight: 600;
    height: var(--gantt-head-h);
    justify-content: space-between;
    letter-spacing: .04em;
    padding: 0 12px;
    text-transform: uppercase;
}

.erp-gantt-names-hint { font-weight: 500; letter-spacing: 0; text-transform: none; opacity: 0.75; }

.erp-gantt-name {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    gap: 4px;
    height: var(--gantt-row-h);
    overflow: hidden;
    padding: 0 8px 0 calc(6px + var(--lvl, 0) * 16px);
}

.erp-gantt-name:hover { background: var(--erp-surface-2); }

.erp-gantt-toggle {
    align-items: center;
    background: none;
    border: none;
    color: var(--erp-muted);
    cursor: pointer;
    display: inline-flex;
    flex: 0 0 18px;
    font-size: 10px;
    height: 18px;
    justify-content: center;
    padding: 0;
}
.erp-gantt-toggle:hover { color: var(--erp-primary); }
.erp-gantt-toggle-spacer { flex: 0 0 18px; }

.erp-gantt-name-body { display: flex; flex: 1 1 auto; flex-direction: column; min-width: 0; }

.erp-gantt-name-text {
    color: var(--erp-ink);
    font-size: 12.5px;
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-gantt-name-meta {
    color: var(--erp-muted);
    font-size: 11px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Add-subtask button reveals on row hover. */
.erp-gantt-add-sub {
    background: none;
    border: 1px solid transparent;
    border-radius: 6px;
    color: var(--erp-muted);
    cursor: pointer;
    flex: 0 0 auto;
    font-size: 11px;
    opacity: 0;
    padding: 3px 7px;
}
.erp-gantt-name:hover .erp-gantt-add-sub { opacity: 1; }
.erp-gantt-add-sub:hover { border-color: var(--erp-primary); color: var(--erp-primary); }

/* Inline add composer rows. */
.erp-gantt-name.is-composer {
    background: color-mix(in srgb, var(--erp-primary) 5%, transparent);
    gap: 6px;
}
.erp-gantt-composer-icon { color: var(--erp-primary); font-size: 10px; flex: 0 0 auto; }
.erp-gantt-composer-input {
    background: var(--erp-surface);
    border: 1px solid var(--erp-line);
    border-radius: 7px;
    color: var(--erp-text);
    flex: 1 1 auto;
    font-size: 12.5px;
    min-width: 0;
    padding: 4px 8px;
}
.erp-gantt-composer-input:focus { border-color: var(--erp-primary); outline: none; }

.erp-gantt-empty { color: var(--erp-muted); font-size: 12.5px; padding: 14px; }

/* Composer / spacer timeline rows carry no bar. */
.erp-gantt-row.is-empty { background: transparent; }

.erp-gantt-timeline { flex: 1 1 auto; min-width: 0; overflow-x: auto; position: relative; }

.erp-gantt-track { position: relative; }

/* Header */
.erp-gantt-header {
    display: flex;
    flex-direction: column;
    height: var(--gantt-head-h);
    position: relative;
}

.erp-gantt-months, .erp-gantt-ticks { display: flex; height: 26px; }

.erp-gantt-month {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    border-left: 1px solid var(--erp-line);
    color: var(--erp-ink);
    display: flex;
    flex: 0 0 auto;
    font-size: 11.5px;
    font-weight: 600;
    overflow: hidden;
    padding: 0 8px;
    white-space: nowrap;
}

.erp-gantt-tick {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    border-left: 1px solid var(--erp-line);
    color: var(--erp-muted);
    display: flex;
    flex: 0 0 auto;
    font-size: 10px;
    justify-content: center;
    overflow: hidden;
}

.erp-gantt-tick.is-weekend { background: var(--erp-surface-2); }

/* Rows + bars */
.erp-gantt-row {
    border-bottom: 1px solid var(--erp-line);
    height: var(--gantt-row-h);
    position: relative;
}

.erp-gantt-bar {
    --bar: var(--erp-success);
    background: color-mix(in srgb, var(--bar) 26%, transparent);
    border: 1px solid var(--bar);
    border-radius: 6px;
    cursor: grab;
    height: calc(var(--gantt-row-h) - 16px);
    overflow: hidden;
    position: absolute;
    top: 8px;
    touch-action: none;
    user-select: none;
}

.erp-gantt-bar.is-dragging { cursor: grabbing; opacity: 0.85; }
.erp-gantt-bar.is-parent { border-style: solid; box-shadow: inset 0 0 0 1px var(--bar); }

.erp-gantt-bar.is-available { --bar: var(--erp-success); }
.erp-gantt-bar.is-overdue { --bar: var(--erp-danger); }
.erp-gantt-bar.is-sold { --bar: var(--erp-purple); }
.erp-gantt-bar.is-booked { --bar: var(--erp-info); }
.erp-gantt-bar.is-hold { --bar: var(--erp-warning); }
.erp-gantt-bar.is-neutral { --bar: var(--erp-muted); }

.erp-gantt-bar-fill {
    background: var(--bar);
    inset: 0 auto 0 0;
    opacity: 0.55;
    position: absolute;
}

.erp-gantt-bar-label {
    color: var(--erp-ink);
    font-size: 11px;
    font-weight: 600;
    left: 8px;
    overflow: hidden;
    position: absolute;
    right: 8px;
    top: 50%;
    transform: translateY(-50%);
    text-overflow: ellipsis;
    white-space: nowrap;
    pointer-events: none;
}

.erp-gantt-handle {
    bottom: 0;
    cursor: ew-resize;
    position: absolute;
    top: 0;
    width: 7px;
}
.erp-gantt-handle.is-left { left: 0; }
.erp-gantt-handle.is-right { right: 0; }

.erp-gantt-today {
    background: var(--erp-danger);
    bottom: 0;
    opacity: 0.6;
    position: absolute;
    top: var(--gantt-head-h);
    width: 2px;
    z-index: 2;
}

.erp-chip-row { display: flex; flex-wrap: wrap; gap: 6px; }

/* Badges ------------------------------------------------------------------- */

/* A small, dense label for a fact that belongs to a record: a trade, a rate, a
   count, an account code. Squarer and tighter than .erp-pill, which is for a
   STATUS -- one record has one status and a badge for each of six facts, so the
   two need to look different or the status stops standing out.

   Colour comes from the tone palette, so a badge says what it means rather than
   being decoration: amber on money held back, rose on the thing that is going
   to fail, green on the thing that is ready.

   Three weights, in order of how loud they are:
     (default)   soft wash, tone-coloured text -- the everyday badge
     .is-outline quieter still, for a fact that is merely present
     .is-solid   filled, for the one thing on a card that must be seen
*/
.erp-badge {
    align-items: center;
    background: var(--erp-tone-soft, rgba(138, 148, 166, .12));
    border: 1px solid transparent;
    border-radius: 6px;
    color: var(--erp-tone-ink, var(--erp-muted));
    display: inline-flex;
    font-size: 10.5px;
    font-weight: 700;
    gap: 4px;
    letter-spacing: .02em;
    line-height: 1.55;
    padding: 2px 7px;
    white-space: nowrap;
}

.erp-badge i { font-size: 9.5px; opacity: .85; }

/* A badge is often "label + figure". The figure takes the weight, because that
   is the part somebody is reading. */
.erp-badge strong { font-size: 11.5px; font-weight: 800; }

.erp-badge small { font-weight: 600; opacity: .8; }

.erp-badge.is-outline {
    background: transparent;
    border-color: var(--erp-tone-line, var(--erp-line));
    color: var(--erp-tone-ink, var(--erp-muted));
    font-weight: 600;
}

.erp-badge.is-solid {
    background: var(--erp-tone, var(--erp-muted));
    color: #fff;
}

:root[data-theme="dark"] .erp-badge.is-solid { color: #10202c; }

/* A row of them wraps rather than overflowing: a card can afford another line,
   and a badge cut in half is worse than one on the next row. */
.erp-badge-row {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    min-width: 0;
}



/* Gantt: unscheduled (dateless) tasks + footer hint */
.erp-gantt-bar.is-unscheduled {
    background: repeating-linear-gradient(45deg,
        color-mix(in srgb, var(--bar) 18%, transparent),
        color-mix(in srgb, var(--bar) 18%, transparent) 6px,
        transparent 6px, transparent 12px);
    border-style: dashed;
    opacity: 0.85;
}

.erp-gantt-swatch.is-dashed {
    background: transparent;
    border: 1px dashed var(--erp-muted);
}

.erp-gantt-foot {
    border-top: 1px solid var(--erp-line);
    color: var(--erp-muted);
    font-size: 12px;
    padding: 10px var(--erp-gutter);
}

/* ==========================================================================
   Project board — summary tiles, colour tags
   ========================================================================== */

.erp-psum-grid {
    display: grid;
    gap: 12px;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    margin-bottom: 16px;
}

.erp-psum {
    background: var(--erp-surface);
    border: 1px solid var(--erp-line);
    border-radius: 12px;
    padding: 14px 16px;
}

.erp-psum-head {
    align-items: center;
    color: var(--erp-muted);
    display: flex;
    font-size: 11px;
    font-weight: 600;
    gap: 6px;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.erp-psum-head i { color: var(--erp-primary); }

.erp-psum-edit {
    background: none;
    border: none;
    color: var(--erp-muted);
    cursor: pointer;
    font-size: 10px;
    margin-left: auto;
    padding: 2px 5px;
}
.erp-psum-edit:hover { color: var(--erp-primary); }

.erp-psum-value {
    color: var(--erp-ink);
    font-size: 22px;
    font-weight: 700;
    line-height: 1.15;
    margin: 6px 0;
}
.erp-psum-value small { color: var(--erp-muted); font-size: 12px; font-weight: 500; margin-left: 5px; }
.erp-psum-dates { font-size: 14px; }

.erp-psum-pills { display: flex; flex-wrap: wrap; gap: 5px; }

/* Task colour accent + swatches */
.erp-task.has-color { border-left: 3px solid var(--task-color); }

.erp-color-menu {
    display: grid !important;
    grid-template-columns: repeat(5, 1fr);
    gap: 6px;
    padding: 8px !important;
    min-width: 0;
    width: 172px;
}

.erp-color-choices { display: flex; flex-wrap: wrap; gap: 7px; }

.erp-color-swatch {
    background: var(--sw, transparent);
    border: 2px solid transparent;
    border-radius: 50%;
    cursor: pointer;
    height: 24px;
    padding: 0;
    width: 24px;
}
.erp-color-swatch.is-none {
    align-items: center;
    background: var(--erp-surface-2);
    color: var(--erp-muted);
    display: inline-flex;
    font-size: 10px;
    justify-content: center;
}
.erp-color-swatch.is-active { border-color: var(--erp-ink); box-shadow: 0 0 0 2px var(--erp-surface), 0 0 0 4px var(--erp-line); }
.erp-color-swatch:hover { transform: scale(1.08); }

/* ==========================================================================
   Projects — Kanban task board (status columns, cards, avatars, comments)
   ========================================================================== */

.erp-kanban {
    display: flex;
    gap: 14px;
    align-items: flex-start;
    overflow-x: auto;
    padding: 4px 2px 14px;
    scroll-snap-type: x proximity;
}

.erp-kanban-col {
    flex: 0 0 300px;
    max-width: 300px;
    background: var(--erp-bg);
    border: 1px solid var(--erp-line);
    border-radius: 12px;
    display: flex;
    flex-direction: column;
    scroll-snap-align: start;
    transition: box-shadow .15s ease, border-color .15s ease;
}

.erp-kanban-col.is-target {
    border-color: var(--erp-primary);
    box-shadow: 0 0 0 3px rgba(0, 194, 146, .14);
}

.erp-kanban-col-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 12px 14px 10px;
    border-bottom: 2px solid var(--erp-line);
}

/* A coloured underline keys each column to its status palette. */
.erp-kanban-col-head.is-available { border-bottom-color: var(--erp-success); }
.erp-kanban-col-head.is-hold { border-bottom-color: var(--erp-warning); }
.erp-kanban-col-head.is-booked { border-bottom-color: var(--erp-info); }
.erp-kanban-col-head.is-sold { border-bottom-color: var(--erp-purple); }
.erp-kanban-col-head.is-overdue { border-bottom-color: var(--erp-danger); }
.erp-kanban-col-head.is-neutral { border-bottom-color: var(--erp-muted); }
.erp-kanban-col-head .erp-kanban-col-title i { color: inherit; }

.erp-kanban-col-title {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--erp-ink);
    display: inline-flex;
    align-items: center;
    gap: 7px;
}

.erp-kanban-col-count {
    margin-left: auto;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--erp-muted);
    background: var(--erp-surface);
    border: 1px solid var(--erp-line);
    border-radius: 20px;
    min-width: 22px;
    text-align: center;
    padding: 1px 7px;
}

.erp-kanban-col-body {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 12px;
    min-height: 90px;
    flex: 1 1 auto;
}

.erp-kanban-empty {
    color: var(--erp-muted);
    font-size: 12px;
    text-align: center;
    padding: 18px 6px;
    border: 1px dashed var(--erp-line);
    border-radius: 9px;
}

/* Lead board — extra card bits not shared with the task card. */
a.erp-kcard-title { display: block; text-decoration: none; }
a.erp-kcard-title:hover { color: var(--erp-primary); }
.erp-kcard-note { font-size: 11.5px; color: var(--erp-muted); margin-top: 5px; line-height: 1.35; }
.erp-kanban-more {
    display: block;
    text-align: center;
    font-size: 11.5px;
    color: var(--erp-muted);
    padding: 4px;
    text-decoration: none;
}
.erp-kanban-more:hover { color: var(--erp-primary); }

/* --- Card ---------------------------------------------------------------- */

.erp-kcard {
    background: var(--erp-surface);
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    padding: 11px 12px 10px;
    box-shadow: 0 1px 2px rgba(16, 24, 40, .04);
    position: relative;
    transition: box-shadow .15s ease, transform .12s ease;
}

.erp-kcard[draggable="true"] { cursor: grab; }
.erp-kcard[draggable="true"]:active { cursor: grabbing; }

.erp-kcard:hover { box-shadow: 0 4px 14px rgba(16, 24, 40, .10); }

.erp-kcard.is-dragging { opacity: .55; transform: rotate(1deg); }

/* Colour accent stripe down the left edge. */
.erp-kcard.has-color::before {
    content: '';
    position: absolute;
    left: 0;
    top: 8px;
    bottom: 8px;
    width: 3px;
    border-radius: 3px;
    background: var(--task-color);
}

.erp-kcard-top {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    margin-bottom: 7px;
    min-height: 20px;
}

.erp-kcard-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    flex: 1 1 auto;
}

.erp-kcard-tags .erp-pill { font-size: 10.5px; padding: 2px 8px; }

.erp-kcard-parent {
    font-size: 10.5px;
    color: var(--erp-muted);
    background: var(--erp-bg);
    border: 1px solid var(--erp-line);
    border-radius: 20px;
    padding: 2px 8px;
}

.erp-kcard-menu,
.erp-kcard-view {
    border: none;
    background: transparent;
    color: var(--erp-muted);
    width: 24px;
    height: 24px;
    border-radius: 6px;
    line-height: 1;
    flex: 0 0 auto;
}
.erp-kcard-menu:hover,
.erp-kcard-view:hover { background: var(--erp-bg); color: var(--erp-ink); }
.erp-kcard-view:hover { color: var(--erp-primary); }

/* --- Team button (board header) + team modal ----------------------------- */

.erp-team-btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 6px 12px;
    background: var(--erp-surface);
    border: 1px solid var(--erp-line);
    border-radius: 24px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--erp-ink);
    flex: 0 0 auto;
    transition: border-color .15s ease, box-shadow .15s ease;
}
.erp-team-btn:hover { border-color: var(--erp-primary); box-shadow: 0 0 0 3px rgba(0, 194, 146, .12); }
.erp-team-btn > i { color: var(--erp-muted); }
.erp-team-btn-count {
    font-size: 11px;
    background: var(--erp-bg);
    border: 1px solid var(--erp-line);
    border-radius: 20px;
    padding: 0 7px;
    color: var(--erp-muted);
}
.erp-team-btn-avatars { display: inline-flex; align-items: center; padding-left: 2px; }
.erp-team-btn-avatars .erp-kavatar { width: 22px; height: 22px; font-size: 9.5px; }
.erp-team-btn-avatars .erp-kavatar + .erp-kavatar { margin-left: -8px; }

@media (max-width: 640px) {
    .erp-team-btn-label, .erp-team-btn-avatars { display: none; }
}

/* Team modal body */
.erp-team-add .erp-field .form-label { font-size: 11.5px; }

.erp-team-listhead { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }

.erp-team-list { display: flex; flex-direction: column; gap: 8px; }

.erp-team-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 9px 12px;
    background: var(--erp-bg);
    border: 1px solid var(--erp-line);
    border-radius: 10px;
}
.erp-team-row .erp-kavatar { width: 34px; height: 34px; font-size: 12px; flex: 0 0 auto; }
.erp-team-row-body { flex: 1 1 auto; min-width: 0; }
.erp-team-row-body strong { display: block; font-size: 13.5px; color: var(--erp-ink); }
.erp-team-row-body small { font-size: 11.5px; color: var(--erp-muted); }
.erp-team-row-actions { display: inline-flex; gap: 6px; flex: 0 0 auto; }
.erp-team-row-actions form { margin: 0; }

/* --- View / detail modal ------------------------------------------------- */

.erp-taskdetail-head { display: flex; align-items: flex-start; gap: 9px; }
.erp-taskdetail-head h4 { font-size: 17px; font-weight: 600; color: var(--erp-ink); margin: 0; line-height: 1.3; word-break: break-word; }
.erp-taskdetail-head h4.is-done { text-decoration: line-through; color: var(--erp-muted); }
.erp-taskdetail-dot { width: 12px; height: 12px; border-radius: 50%; margin-top: 5px; flex: 0 0 auto; }

.erp-taskdetail-tags { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 4px; }

.erp-taskdetail-progress { margin: 16px 0; }
.erp-taskdetail-label { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--erp-muted); }

.erp-taskdetail-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 14px 20px;
    margin: 16px 0;
    padding: 16px 0;
    border-top: 1px solid var(--erp-line);
    border-bottom: 1px solid var(--erp-line);
}
.erp-taskdetail-grid dt { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--erp-muted); margin-bottom: 3px; }
.erp-taskdetail-grid dd { font-size: 13.5px; color: var(--erp-ink); margin: 0; }

.erp-taskdetail-block { margin-top: 16px; }
.erp-taskdetail-block .erp-taskdetail-label { display: block; margin-bottom: 8px; }

.erp-taskdetail-people { display: flex; flex-wrap: wrap; gap: 8px 14px; }
.erp-taskdetail-person { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; color: var(--erp-ink); }

.erp-taskdetail-desc { font-size: 13.5px; color: var(--erp-ink); white-space: pre-wrap; word-break: break-word; margin: 0; }

.erp-taskdetail-subs { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.erp-taskdetail-subs li { display: flex; align-items: center; gap: 10px; padding: 8px 10px; background: var(--erp-bg); border-radius: 8px; }
.erp-taskdetail-substatus { flex: 0 0 auto; font-size: 10.5px; }
.erp-taskdetail-subtitle { flex: 1 1 auto; font-size: 13px; color: var(--erp-ink); word-break: break-word; }
.erp-taskdetail-subtitle.is-done { text-decoration: line-through; color: var(--erp-muted); }
.erp-taskdetail-subprog { flex: 0 0 auto; font-size: 12px; font-weight: 600; color: var(--erp-muted); }
.erp-taskdetail-subcost { flex: 0 0 auto; font-size: 11.5px; color: var(--erp-muted); }

.erp-kcard-colors { display: flex; flex-wrap: wrap; gap: 6px; max-width: 190px; }

.erp-kcard-title {
    font-size: 13px;
    font-weight: 600;
    color: var(--erp-ink);
    line-height: 1.35;
    word-break: break-word;
}
.erp-kcard-title.is-done { text-decoration: line-through; color: var(--erp-muted); }

.erp-kcard-progress { margin: 9px 0 8px; height: 6px; }

/* Lead quick-note: follow-up type as one-click chips. */
.lead-note-types { display: flex; flex-wrap: wrap; gap: 6px; }
.lead-note-type { cursor: pointer; margin: 0; }
.lead-note-type input { position: absolute; opacity: 0; pointer-events: none; }
.lead-note-type span {
    align-items: center;
    border: 1px solid var(--erp-line);
    border-radius: 999px;
    color: var(--erp-muted);
    display: inline-flex;
    font-size: 12px;
    padding: 4px 12px;
    transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.lead-note-type:hover span { border-color: var(--erp-primary); color: var(--erp-ink); }
.lead-note-type input:checked + span { background: var(--erp-primary); border-color: var(--erp-primary); color: #fff; }
.lead-note-type input:focus-visible + span { outline: 2px solid var(--erp-primary); outline-offset: 2px; }

/* Lead stage picker in the list. */
.lead-stage { min-width: 170px; }
.lead-stage.is-busy { opacity: .55; pointer-events: none; }
.lead-stage-btn { border: 0; cursor: pointer; }
.lead-stage-pct { font-weight: 700; margin-left: 4px; opacity: .8; }
.lead-stage-next {
    background: transparent;
    border: 1px solid var(--erp-line);
    border-radius: 999px;
    color: var(--erp-muted);
    cursor: pointer;
    font-size: 10px;
    margin-left: 4px;
    padding: 2px 7px;
    vertical-align: middle;
}
.lead-stage-next:hover { border-color: var(--erp-primary); color: var(--erp-primary); }
.lead-stage-bar { background: var(--erp-line); border-radius: 999px; height: 3px; margin-top: 5px; overflow: hidden; width: 140px; }
.lead-stage-bar span { background: linear-gradient(90deg, #6366f1, #16a34a); border-radius: 999px; display: block; height: 100%; }
.lead-stage-menu { font-size: 12.5px; min-width: 240px; }
.lead-stage-menu .dropdown-item { align-items: center; display: flex; }
.lead-stage-menu-pct { color: var(--erp-muted); font-size: 11px; margin-left: auto; padding-left: 12px; }
.lead-stage-menu .dropdown-item.active .lead-stage-menu-pct { color: inherit; }
tr.lead-row-leaving { opacity: 0; transition: opacity .4s ease; }
tr.lead-row-flash > td { animation: leadRowFlash 1.2s ease; }
@keyframes leadRowFlash { from { background: rgba(99, 102, 241, .14); } to { background: transparent; } }

/* Lead detail: the pipeline as a clickable stepper. */
.lead-stepper-card { margin-bottom: 14px; padding: 14px 16px; }
.lead-stepper-card.is-busy { opacity: .6; pointer-events: none; }
.lead-stepper-head { align-items: center; display: flex; flex-wrap: wrap; gap: 10px; justify-content: space-between; margin-bottom: 12px; }
.lead-stepper { display: flex; list-style: none; margin: 0; overflow-x: auto; padding: 0; }
.lead-stepper li { flex: 1 1 0; min-width: 100px; position: relative; }
.lead-stepper li + li::before {
    background: var(--erp-line);
    content: '';
    height: 3px;
    left: -50%;
    position: absolute;
    right: 50%;
    top: 17px;
    z-index: 0;
}
.lead-stepper li.is-done::before, .lead-stepper li.is-current::before { background: var(--erp-primary); }
.lead-stepper button {
    align-items: center;
    background: none;
    border: 0;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 0 4px;
    position: relative;
    width: 100%;
    z-index: 1;
}
.lead-stepper button:disabled { cursor: default; }
.lead-stepper-dot {
    align-items: center;
    background: #fff;
    border: 2px solid var(--erp-line);
    border-radius: 50%;
    color: var(--erp-muted);
    display: flex;
    font-size: 13px;
    height: 36px;
    justify-content: center;
    transition: transform .12s ease, border-color .12s ease;
    width: 36px;
}
.lead-stepper button:not(:disabled):hover .lead-stepper-dot { border-color: var(--erp-primary); transform: scale(1.08); }
.lead-stepper li.is-done .lead-stepper-dot { background: var(--erp-primary); border-color: var(--erp-primary); color: #fff; }
.lead-stepper li.is-current .lead-stepper-dot { background: #fff; border-color: var(--erp-primary); box-shadow: 0 0 0 4px rgba(99, 102, 241, .18); color: var(--erp-primary); }
.lead-stepper li:last-child.is-current .lead-stepper-dot { background: #16a34a; border-color: #16a34a; color: #fff; }
.lead-stepper-label { color: var(--erp-ink); font-size: 11.5px; font-weight: 600; text-align: center; }
.lead-stepper-pct { color: var(--erp-muted); font-size: 10.5px; }
.lead-stepper li.is-off { opacity: .55; }
.lead-stepper-closed { color: var(--erp-muted); font-size: 12px; margin: 10px 0 0; }

/* Lead detail: stage history. */
.lead-stage-history { list-style: none; margin: 0; padding: 0; }
.lead-stage-history li { border-left: 2px solid var(--erp-line); padding: 4px 0 8px 12px; position: relative; }
.lead-stage-history li::before {
    background: var(--erp-primary);
    border-radius: 50%;
    content: '';
    height: 8px;
    left: -5px;
    position: absolute;
    top: 9px;
    width: 8px;
}
.lead-stage-history-meta { color: var(--erp-muted); display: block; font-size: 11.5px; margin-top: 3px; }
.lead-stage-history-note { color: var(--erp-ink); display: block; font-size: 12px; margin-top: 2px; }

/* Lead distribution: list selection bar, assign / distribute dialogs. */
.lead-selection-bar {
    align-items: center;
    background: var(--erp-surface-2);
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    font-size: 12.5px;
    gap: 8px;
    padding: 8px 16px;
}
.lead-selection-bar[hidden] { display: none; }
.lead-dist-strategies { display: grid; gap: 8px; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
.lead-dist-strategy {
    align-items: flex-start;
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    cursor: pointer;
    display: flex;
    gap: 8px;
    padding: 10px 12px;
}
.lead-dist-strategy:has(input:checked) { border-color: var(--erp-primary); box-shadow: 0 0 0 3px rgba(99, 102, 241, .12); }
.lead-dist-strategy input { margin-top: 3px; }
.lead-dist-strategy strong { display: block; font-size: 13px; }
.lead-dist-strategy small { color: var(--erp-muted); display: block; font-size: 11.5px; }
.lead-dist-pool { border: 1px dashed var(--erp-line); border-radius: 10px; padding: 6px 12px; text-align: center; width: 100%; }
.lead-dist-pool strong { display: block; font-size: 18px; line-height: 1.2; }
.lead-dist-pool small { color: var(--erp-muted); font-size: 11px; }
.lead-dist-team { border: 1px solid var(--erp-line); border-radius: 10px; overflow: hidden; }
.lead-dist-team-head {
    align-items: center;
    background: var(--erp-surface-2);
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    font-size: 12.5px;
    font-weight: 600;
    padding: 8px 12px;
}
.lead-dist-team-head span { font-size: 11.5px; font-weight: 400; }
.lead-dist-team .erp-table td, .lead-dist-team .erp-table th { padding-bottom: 6px; padding-top: 6px; }

/* Lead quick-note: earlier notes, oldest first, newest at the bottom. */
.lead-note-history-wrap { border: 1px solid var(--erp-line); border-radius: 10px; margin-bottom: 14px; overflow: hidden; }
.lead-note-history-head {
    align-items: center;
    background: var(--erp-surface-2);
    border-bottom: 1px solid var(--erp-line);
    color: var(--erp-muted);
    display: flex;
    font-size: 11px;
    font-weight: 600;
    justify-content: space-between;
    letter-spacing: .04em;
    padding: 6px 12px;
    text-transform: uppercase;
}
.lead-note-history { display: flex; flex-direction: column; gap: 8px; max-height: 240px; overflow-y: auto; padding: 10px 12px; }
.lead-note-history-empty { color: var(--erp-muted); font-size: 12px; padding: 8px 0; text-align: center; }
.lead-note-item { background: var(--erp-surface-2); border-left: 3px solid var(--erp-primary); border-radius: 8px; padding: 7px 10px; }
.lead-note-item.is-scheduled { border-left-color: #f59e0b; }
.lead-note-item.is-cancelled, .lead-note-item.is-no_show { border-left-color: #94a3b8; opacity: .8; }
.lead-note-item-meta { align-items: center; color: var(--erp-muted); display: flex; font-size: 11px; gap: 8px; }
.lead-note-item-type { color: var(--erp-ink); font-weight: 600; }
.lead-note-item-status { background: var(--erp-line); border-radius: 999px; font-size: 10px; padding: 0 7px; }
.lead-note-item-text { color: var(--erp-ink); font-size: 12.5px; margin-top: 3px; white-space: pre-line; word-break: break-word; }
.lead-note-item-by { color: var(--erp-muted); font-size: 10.5px; margin-top: 2px; }
.lead-note-item-by:empty { display: none; }

/* Lead bulk grid: dense spreadsheet-like cells. */
.lead-bulk-shared { background: var(--erp-surface-2); border: 1px solid var(--erp-line); border-radius: 10px; padding: 10px 12px; }
.lead-bulk-shared .form-label { font-size: 11px; margin-bottom: 3px; }
.lead-bulk-grid { max-height: 46vh; overflow: auto; }
.lead-bulk-grid thead th { position: sticky; top: 0; z-index: 1; }
.lead-bulk-grid td { padding: 3px 4px !important; }
.lead-bulk-grid .form-control-sm { min-width: 110px; }

/* Subtask sliders inside the task modal, for setting a parent's progress. */
.erp-subtask-progress {
    background: var(--erp-surface-2);
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    margin-top: 10px;
    max-height: 260px;
    overflow-y: auto;
    padding: 6px 12px;
}

.erp-subtask-progress[hidden] { display: none; }

.erp-subtask-progress-row {
    align-items: center;
    display: grid;
    gap: 12px;
    grid-template-columns: minmax(0, 1fr) 160px 44px;
    padding: 5px 0;
}

.erp-subtask-progress-row + .erp-subtask-progress-row { border-top: 1px dashed var(--erp-line); }

.erp-subtask-progress-title { font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.erp-subtask-progress-val { font-size: 12px; text-align: right; }

/* A progress bar that can be dragged (or arrow-keyed) to set the value. */
.erp-progress.is-adjustable {
    cursor: ew-resize;
    overflow: visible;
    position: relative;
    touch-action: none;
    transition: height .12s ease;
    user-select: none;
}

.erp-progress.is-adjustable:hover,
.erp-progress.is-adjustable:focus-visible,
.erp-progress.is-adjustable.is-dragging { height: 10px; }

.erp-progress.is-adjustable:focus-visible { outline: 2px solid var(--erp-primary); outline-offset: 3px; }

.erp-progress.is-adjustable .erp-progress-bar { position: relative; min-width: 2px; }

.erp-progress.is-adjustable.is-dragging .erp-progress-bar { transition: none; }

/* The knob sits on the end of the fill, shown when the bar is in use. */
.erp-progress.is-adjustable .erp-progress-bar::after {
    background: #fff;
    border: 2px solid var(--erp-primary);
    border-radius: 50%;
    box-shadow: 0 1px 3px rgba(15, 23, 42, .25);
    content: '';
    height: 14px;
    opacity: 0;
    position: absolute;
    right: -7px;
    top: 50%;
    transform: translateY(-50%);
    transition: opacity .12s ease;
    width: 14px;
}

.erp-progress.is-adjustable:hover .erp-progress-bar::after,
.erp-progress.is-adjustable:focus-visible .erp-progress-bar::after,
.erp-progress.is-adjustable.is-dragging .erp-progress-bar::after { opacity: 1; }

.erp-progress.is-adjustable .erp-progress-out {
    color: var(--erp-muted);
    font-size: 10px;
    font-weight: 600;
    opacity: 0;
    pointer-events: none;
    position: absolute;
    right: 0;
    top: -16px;
    transition: opacity .12s ease;
}

.erp-progress.is-adjustable:hover .erp-progress-out,
.erp-progress.is-adjustable.is-dragging .erp-progress-out { opacity: 1; }

.erp-kcard-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

.erp-kcard-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 9px;
    font-size: 11px;
    color: var(--erp-muted);
}

/* Comment button — sits in the card's action row beside View, so it reads
   as an action rather than as one more figure in the metadata line. Carries
   its own count, and tints once a task has any discussion on it so a card
   with a thread is visible without opening anything. */
.erp-kcard-comments {
    position: relative;
    border: none;
    background: transparent;
    color: var(--erp-muted);
    width: 24px;
    height: 24px;
    border-radius: 6px;
    line-height: 1;
    flex: 0 0 auto;
}

.erp-kcard-comments:hover { background: var(--erp-bg); color: var(--erp-primary); }

.erp-kcard-comments.has-comments { color: var(--erp-primary); }

.erp-kcard-comment-count {
    position: absolute;
    top: -3px;
    right: -3px;
    min-width: 14px;
    height: 14px;
    padding: 0 3px;
    border-radius: 7px;
    background: var(--erp-primary);
    color: #fff;
    font-size: 9px;
    font-weight: 700;
    line-height: 14px;
    text-align: center;
}

/* --- Avatars ------------------------------------------------------------- */

.erp-kcard-people { display: inline-flex; align-items: center; }
.erp-kcard-people[role="button"] { cursor: pointer; }

.erp-kavatar {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: var(--erp-primary);
    color: #fff;
    font-size: 10px;
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 2px solid var(--erp-surface);
    text-transform: uppercase;
}
.erp-kcard-people .erp-kavatar + .erp-kavatar { margin-left: -8px; }
.erp-kavatar.is-empty { background: var(--erp-bg); color: var(--erp-muted); border: 1px dashed var(--erp-line); }
.erp-kavatar.is-more { background: var(--erp-muted); }

/* --- Comment thread ------------------------------------------------------ */

.erp-comments { display: flex; flex-direction: column; gap: 12px; max-height: 320px; overflow-y: auto; }

.erp-comment { display: flex; gap: 9px; }
.erp-comment .erp-kavatar { flex: 0 0 auto; width: 28px; height: 28px; font-size: 11px; }

.erp-comment-body { flex: 1 1 auto; }
.erp-comment-head { display: flex; align-items: center; gap: 8px; }
.erp-comment-head strong { font-size: 12.5px; color: var(--erp-ink); }
.erp-comment-head small { font-size: 11px; color: var(--erp-muted); }
.erp-comment-del {
    border: none;
    background: transparent;
    color: var(--erp-muted);
    margin-left: auto;
    line-height: 1;
    padding: 2px 4px;
    border-radius: 5px;
}
.erp-comment-del:hover { color: var(--erp-danger); background: var(--erp-bg); }
.erp-comment-body p { margin: 2px 0 0; font-size: 12.5px; color: var(--erp-ink); white-space: pre-wrap; word-break: break-word; }

/* ==========================================================================
   Accounting — Chart of Accounts (summary chips + account tree)
   ========================================================================== */

.erp-coa-summary {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 16px;
}

.erp-coa-stat {
    flex: 1 1 150px;
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 12px 14px;
    background: var(--erp-surface);
    border: 1px solid var(--erp-line);
    border-left: 3px solid var(--erp-muted);
    border-radius: 10px;
}
.erp-coa-stat.is-booked { border-left-color: var(--erp-info); }
.erp-coa-stat.is-overdue { border-left-color: var(--erp-danger); }
.erp-coa-stat.is-sold { border-left-color: var(--erp-purple); }
.erp-coa-stat.is-available { border-left-color: var(--erp-success); }
.erp-coa-stat.is-hold { border-left-color: var(--erp-warning); }
.erp-coa-stat.is-neutral { border-left-color: var(--erp-muted); }

.erp-coa-stat-icon {
    width: 34px;
    height: 34px;
    border-radius: 9px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--erp-bg);
    color: var(--erp-muted);
    flex: 0 0 auto;
}
.erp-coa-stat-body { display: flex; flex-direction: column; line-height: 1.2; }
.erp-coa-stat-body strong { font-size: 18px; color: var(--erp-ink); }
.erp-coa-stat-body small { font-size: 11px; color: var(--erp-muted); }

/* --- Tree table ---------------------------------------------------------- */

.erp-coa td { vertical-align: middle; }
.erp-coa-row.is-inactive { opacity: .62; }

.erp-coa-name { display: flex; align-items: center; gap: 8px; }

.erp-coa-caret {
    width: 22px;
    height: 22px;
    border: none;
    background: transparent;
    color: var(--erp-muted);
    border-radius: 6px;
    flex: 0 0 auto;
    font-size: 11px;
}
.erp-coa-caret:hover { background: var(--erp-bg); color: var(--erp-ink); }
.erp-coa-caret.is-leaf { visibility: hidden; }

.erp-coa-code {
    font-family: ui-monospace, "SFMono-Regular", "Consolas", monospace;
    font-size: 12px;
    color: var(--erp-muted);
    min-width: 46px;
}
.erp-coa-title { font-size: 13.5px; color: var(--erp-ink); }
.erp-coa-title.is-group { font-weight: 600; }
.erp-coa-badge { font-size: 10px; padding: 1px 7px; }
.erp-coa-locked { font-size: 11.5px; padding: 0 6px; }

/* --- Toggle switch ------------------------------------------------------- */

.erp-switch { position: relative; display: inline-block; width: 38px; height: 20px; cursor: pointer; margin: 0; }
.erp-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.erp-switch-track {
    position: absolute;
    inset: 0;
    background: var(--erp-line);
    border-radius: 20px;
    transition: background .15s ease;
}
.erp-switch-track::before {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    background: #fff;
    border-radius: 50%;
    transition: transform .15s ease;
    box-shadow: 0 1px 2px rgba(16, 24, 40, .2);
}
.erp-switch input:checked + .erp-switch-track { background: var(--erp-primary); }
.erp-switch input:checked + .erp-switch-track::before { transform: translateX(18px); }
.erp-switch input:focus-visible + .erp-switch-track { box-shadow: 0 0 0 3px rgba(0, 194, 146, .25); }

/* --- Inline checkbox ----------------------------------------------------- */

.erp-check { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--erp-ink); cursor: pointer; }
.erp-check input { width: 16px; height: 16px; accent-color: var(--erp-primary); }

/* ==========================================================================
   Suppliers — create wizard + manage-page section rows
   ========================================================================== */

.erp-wizard-steps {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    list-style: none;
    margin: 0;
    padding: 16px var(--erp-gutter);
    border-bottom: 1px solid var(--erp-line);
}
.erp-wizard-steps li {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--erp-muted);
    padding: 6px 14px;
    border-radius: 24px;
    cursor: pointer;
    white-space: nowrap;
}
.erp-wizard-steps li span {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--erp-line);
    color: var(--erp-muted);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
}
.erp-wizard-steps li.is-active { background: rgba(0, 194, 146, .12); color: var(--erp-primary); }
.erp-wizard-steps li.is-active span { background: var(--erp-primary); color: #fff; }
.erp-wizard-steps li.is-done { color: var(--erp-ink); }
.erp-wizard-steps li.is-done span { background: var(--erp-primary); color: #fff; }

.erp-wizard-nav { display: flex; align-items: center; gap: 8px; }

/* --- Manage-page rows ---------------------------------------------------- */

.erp-rows { display: flex; flex-direction: column; gap: 8px; }

.erp-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 11px 12px;
    background: var(--erp-bg);
    border: 1px solid var(--erp-line);
    border-radius: 10px;
}
.erp-row .erp-kavatar { width: 36px; height: 36px; font-size: 13px; flex: 0 0 auto; background: var(--erp-primary); }
.erp-row-body { flex: 1 1 auto; min-width: 0; }
.erp-row-body strong { display: block; font-size: 13.5px; color: var(--erp-ink); }
.erp-row-body small { font-size: 12px; color: var(--erp-muted); }
.erp-row-tag { font-size: 10px; padding: 1px 7px; margin-left: 6px; vertical-align: middle; }
.erp-row-meta { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 3px; font-size: 11.5px; color: var(--erp-muted); }
.erp-row-actions { display: inline-flex; gap: 4px; flex: 0 0 auto; }

/* Pipeline stage rows — a draggable variant of the manage-page row. */
.erp-stage-row { gap: 10px; }
.erp-stage-row[draggable="true"] { cursor: grab; }
.erp-stage-row[draggable="true"]:active { cursor: grabbing; }
.erp-stage-row.is-dragging { opacity: .55; }
.erp-stage-handle { color: var(--erp-muted); flex: 0 0 auto; cursor: grab; }
.erp-stage-dot { width: 12px; height: 12px; border-radius: 50%; flex: 0 0 auto; background: var(--dot, #94a3b8); }

/* ==========================================================================
   Suppliers — manage page (profile hero + tabbed detail card)
   ========================================================================== */

.erp-supplier-profile { padding: 0; overflow: hidden; }

.erp-supplier-hero {
    text-align: center;
    padding: 22px 18px 18px;
    border-bottom: 1px solid var(--erp-line);
    background: linear-gradient(180deg, rgba(3, 105, 161, .06), transparent);
}
.erp-supplier-avatar {
    width: 62px;
    height: 62px;
    border-radius: 16px;
    background: linear-gradient(135deg, #0369a1, #075985);
    color: #fff;
    font-size: 22px;
    font-weight: 700;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 10px;
}
.erp-supplier-hero h2 { font-size: 16px; font-weight: 600; color: var(--erp-ink); margin: 0; word-break: break-word; }
.erp-supplier-code { font-size: 12px; color: var(--erp-muted); font-family: ui-monospace, "Consolas", monospace; margin: 2px 0 0; }

.erp-supplier-quick {
    display: flex;
    gap: 8px;
    margin-top: 16px;
}
.erp-supplier-quick > div {
    flex: 1 1 0;
    background: var(--erp-surface);
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    padding: 8px 6px;
    display: flex;
    flex-direction: column;
    line-height: 1.25;
}
.erp-supplier-quick strong { font-size: 15px; color: var(--erp-ink); }
.erp-supplier-quick small { font-size: 10px; text-transform: uppercase; letter-spacing: .03em; color: var(--erp-muted); }

.erp-supplier-info { padding: 6px 18px 14px; }
.erp-supplier-info .erp-cv-block { margin-top: 14px; }

/* --- Tabbed detail card -------------------------------------------------- */

.erp-tabs {
    display: flex;
    flex-wrap: nowrap;
    gap: 2px;
    border-bottom: 1px solid var(--erp-line);
    padding: 0 10px;
    overflow-x: auto;
    scrollbar-width: thin;
}
.erp-tab {
    border: none;
    background: transparent;
    padding: 15px 14px;
    font-size: 13px;
    font-weight: 600;
    color: var(--erp-muted);
    border-bottom: 2px solid transparent;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    white-space: nowrap;
    cursor: pointer;
}
.erp-tab:hover { color: var(--erp-ink); }
.erp-tab.active { color: var(--erp-primary); border-bottom-color: var(--erp-primary); }
.erp-tab-count {
    font-size: 11px;
    min-width: 20px;
    text-align: center;
    padding: 0 6px;
    border-radius: 20px;
    background: var(--erp-bg);
    border: 1px solid var(--erp-line);
    color: var(--erp-muted);
}
.erp-tab.active .erp-tab-count { background: rgba(0, 194, 146, .12); color: var(--erp-primary); border-color: transparent; }

@media (max-width: 560px) {
    .erp-tab-label { display: none; }
}

/* --- Glass tabs ----------------------------------------------------------- */

/* An opt-in treatment: add `is-glass` to an .erp-tabs strip and each tab
   becomes a tinted glass button in its own colour. Opt-in rather than global
   because .erp-tabs is shared by a dozen screens, and a strip of two tabs does
   not want the same weight as a workflow of ten.

   Every tab carries its accent as `--tab`, three space-separated channels so
   one value can drive the plate, the border, the label and the badge at
   different alphas. The label is that accent mixed toward ink at a ratio
   measured — not guessed — to clear WCAG AA against its own plate in both
   themes: the worst case (amber, active) lands at 5.09:1. */

.erp-tabs.is-glass {
    --tab-plate: .12;
    --tab-ink-mix: 48%;

    border-bottom: 0;
    gap: 8px;
    padding: 12px 12px 4px;
    flex-wrap: wrap;
}

.erp-tabs.is-glass .erp-tab {
    --tab: 100 116 139;

    position: relative;
    border: 1px solid rgb(var(--tab) / .28);
    border-bottom: 1px solid rgb(var(--tab) / .28);
    border-radius: 12px;
    padding: 8px 12px;
    font-size: 12.5px;
    font-weight: 600;
    color: color-mix(in srgb, rgb(var(--tab)) var(--tab-ink-mix), #0e1726);
    background:
        linear-gradient(155deg, rgb(var(--tab) / var(--tab-plate)), rgb(var(--tab) / calc(var(--tab-plate) * .35)));
    -webkit-backdrop-filter: blur(10px) saturate(150%);
    backdrop-filter: blur(10px) saturate(150%);
    /* The top highlight is what reads as glass rather than as a flat chip. */
    box-shadow:
        inset 0 1px 0 rgb(255 255 255 / .55),
        0 1px 2px rgb(15 23 42 / .04);
    transition: transform .14s ease, box-shadow .14s ease, border-color .14s ease, background .14s ease;
}

/* A soft sheen across the upper half, clipped to the button's radius. */
.erp-tabs.is-glass .erp-tab::after {
    content: "";
    position: absolute;
    inset: 1px 1px auto;
    height: 45%;
    border-radius: 11px 11px 40% 40%;
    background: linear-gradient(180deg, rgb(255 255 255 / .35), rgb(255 255 255 / 0));
    pointer-events: none;
}

.erp-tabs.is-glass .erp-tab:hover {
    --tab-plate: .18;
    border-color: rgb(var(--tab) / .42);
    transform: translateY(-1px);
    box-shadow:
        inset 0 1px 0 rgb(255 255 255 / .6),
        0 4px 12px rgb(var(--tab) / .18);
}

.erp-tabs.is-glass .erp-tab.is-active,
.erp-tabs.is-glass .erp-tab.active {
    --tab-plate: .3;
    border-color: rgb(var(--tab) / .65);
    font-weight: 700;
    transform: translateY(-1px);
    box-shadow:
        inset 0 1px 0 rgb(255 255 255 / .65),
        0 0 0 3px rgb(var(--tab) / .18),
        0 6px 16px rgb(var(--tab) / .26);
}

/* A solid bar along the bottom of the selected tab. In a strip where every
   tab is already coloured, a deeper tint alone is a weak signal — this is the
   one mark only the active tab carries, and it survives greyscale. */
.erp-tabs.is-glass .erp-tab.is-active::before,
.erp-tabs.is-glass .erp-tab.active::before {
    content: "";
    position: absolute;
    left: 22%;
    right: 22%;
    bottom: -1px;
    height: 3px;
    border-radius: 3px 3px 0 0;
    background: rgb(var(--tab));
}

.erp-tabs.is-glass .erp-tab:focus-visible {
    outline: 2px solid rgb(var(--tab));
    outline-offset: 2px;
}

.erp-tabs.is-glass .erp-tab i { color: rgb(var(--tab) / .95); }

.erp-tabs.is-glass .erp-tab-count {
    background: rgb(var(--tab) / .18);
    border: 0;
    color: inherit;
    font-weight: 700;
}

.erp-tabs.is-glass .erp-tab.is-active .erp-tab-count,
.erp-tabs.is-glass .erp-tab.active .erp-tab-count {
    background: rgb(var(--tab) / .3);
    color: inherit;
}

/* The palette. Ordered as the work happens — describe the land, prove the
   title, price it, approve it, sign it, fund it, pay, register, file. */
.erp-tabs.is-glass .erp-tab[data-tab="dags"] { --tab: 14 165 233; }
.erp-tabs.is-glass .erp-tab[data-tab="owners"] { --tab: 139 92 246; }
.erp-tabs.is-glass .erp-tab[data-tab="verification"] { --tab: 245 158 11; }
.erp-tabs.is-glass .erp-tab[data-tab="valuation"] { --tab: 16 185 129; }
.erp-tabs.is-glass .erp-tab[data-tab="proposals"] { --tab: 249 115 22; }
.erp-tabs.is-glass .erp-tab[data-tab="agreements"] { --tab: 99 102 241; }
.erp-tabs.is-glass .erp-tab[data-tab="funding"] { --tab: 236 72 153; }
.erp-tabs.is-glass .erp-tab[data-tab="payments"] { --tab: 20 184 166; }
.erp-tabs.is-glass .erp-tab[data-tab="registration"] { --tab: 168 85 247; }
.erp-tabs.is-glass .erp-tab[data-tab="documents"] { --tab: 100 116 139; }

/* Without backdrop-filter the plate would sit over whatever is behind it with
   no blur and look washed out, so the tint is deepened to compensate. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .erp-tabs.is-glass { --tab-plate: .16; }
    .erp-tabs.is-glass .erp-tab.is-active,
    .erp-tabs.is-glass .erp-tab.active { --tab-plate: .36; }
}

@media (prefers-reduced-motion: reduce) {
    .erp-tabs.is-glass .erp-tab { transition: none; }
    .erp-tabs.is-glass .erp-tab:hover,
    .erp-tabs.is-glass .erp-tab.is-active,
    .erp-tabs.is-glass .erp-tab.active { transform: none; }
}

@media (max-width: 560px) {
    .erp-tabs.is-glass { gap: 6px; padding: 10px 10px 2px; }
    .erp-tabs.is-glass .erp-tab { padding: 7px 10px; font-size: 12px; }
}

@media print {
    .erp-tabs.is-glass .erp-tab {
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
        box-shadow: none;
        transform: none;
    }
    .erp-tabs.is-glass .erp-tab::after { display: none; }
}

.erp-tab-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 16px var(--erp-gutter) 4px;
}
.erp-tab-head > span { font-size: 14px; font-weight: 600; color: var(--erp-ink); }
.erp-tab-body { padding: 12px var(--erp-gutter) 18px; }

/* ==========================================================================
   Accounting — Journal voucher line editor + view
   ========================================================================== */

.jv-editor td, .jv-view td { vertical-align: middle; }
.jv-editor .jv-line-idx { color: var(--erp-muted); font-size: 12px; text-align: center; }
.jv-editor .form-select-sm, .jv-editor .form-control-sm { font-size: 12.5px; }
.jv-editor .select2-container { min-width: 100%; }

.jv-totals td { border-top: 2px solid var(--erp-line); font-size: 13.5px; }
.jv-balance-row td { border: none; padding-top: 4px; }

/* Keep the select2 dropdowns readable inside the compact editor rows. */
.jv-editor .select2-selection { min-height: 31px; }
.jv-editor .select2-selection__rendered { line-height: 29px !important; font-size: 12.5px; }

/* --- Compact filter bar (reports) ---------------------------------------- */

.erp-filterbar {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    flex: 1 1 auto;
}

.erp-search-sm { width: 210px; max-width: 100%; }

/* A filter bar with many controls, laid out as a grid instead of a wrapping
   flex. Flex-wrap leaves a ragged edge — the second line starts wherever the
   first happened to run out — whereas fixed columns keep every control
   aligned.

   The columns are declared explicitly rather than with auto-fit. auto-fit
   sizes by a single minimum, so the widest control sets the width of the
   narrowest, and ten equal columns need roughly 1350px before they stop
   wrapping. Weighting them by what they actually hold — a search box needs
   room, a bedroom count does not — brings the row's minimum to about 940px,
   which fits a 1366px laptop on one line. */
.erp-filter-grid {
    display: grid;
    grid-template-columns:
        minmax(150px, 1.6fr)    /* search */
        minmax(96px, 1.1fr)     /* project */
        minmax(96px, 1.1fr)     /* building */
        minmax(90px, 1fr)       /* status */
        minmax(90px, 1fr)       /* type */
        minmax(78px, .8fr)      /* bedrooms */
        minmax(76px, .8fr)      /* min price */
        minmax(76px, .8fr)      /* max price */
        auto;                   /* checkbox + buttons */
    align-items: center;
    gap: 8px;
    width: 100%;
    flex: 1 1 auto;
}

/* The controls stretch to their cell; the fixed widths they carry for the
   flex layout would otherwise fight the grid. */
.erp-filter-grid .erp-select,
.erp-filter-grid .erp-search,
.erp-filter-grid .erp-search-sm,
.erp-filter-grid .form-control,
.erp-filter-grid .select2-container {
    width: 100% !important;
    max-width: none;
    min-width: 0;
}

/* The checkbox and the buttons ride together at the end so the row finishes
   on an action rather than trailing off. */
.erp-filter-grid .erp-filter-actions {
    display: flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
}

.erp-filter-grid .erp-check { margin: 0; white-space: nowrap; }

/* Below the one-row width the count halves rather than dropping by one, so
   the bar never leaves a single control stranded on its own line. */
@media (max-width: 1399.98px) {
    .erp-filter-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
    /* Search keeps its extra width once the row wraps — it is still the
       control reached for first. */
    .erp-filter-grid .erp-filter-wide { grid-column: span 2; }
    .erp-filter-grid .erp-filter-actions { grid-column: 1 / -1; justify-content: flex-end; }
}

@media (max-width: 767.98px) {
    .erp-filter-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 479.98px) {
    .erp-filter-grid { grid-template-columns: 1fr; }
}

/* Native date input styled to match .erp-select / .erp-search exactly.
   NOTE: the Notika template (style.css) forces width:100%!important and
   height:45px!important on every input[type=date] (specificity 0,1,1), so this
   override must also use !important and match that specificity — hence the
   `input.erp-date` element+class selector rather than a bare `.erp-date`. */
input.erp-date {
    border: 1px solid #dde3ec !important;
    border-radius: 6px !important;
    background-color: #fff;
    color: #5b6675;
    font-size: 12.5px;
    min-height: 32px;
    height: 32px !important;
    padding: 4px 10px !important;
    width: 150px !important;
    max-width: 100%;
    position: static !important;
}
input.erp-date:focus { outline: none; border-color: var(--erp-primary) !important; }

:root[data-theme="dark"] input.erp-date {
    background: var(--erp-surface-2);
    border-color: var(--erp-line) !important;
    color: var(--erp-ink);
}
:root[data-theme="dark"] input.erp-date::-webkit-calendar-picker-indicator { filter: invert(1); }

/* Narrow, centered form card (e.g. income/expense entry). */
.erp-form-narrow { max-width: 720px; margin-inline: auto; }

/* --- Income/expense attachments preview ---------------------------------- */

.erp-doc-grid { display: flex; flex-wrap: wrap; gap: 12px; }

.erp-doc-tile {
    width: 150px;
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    overflow: hidden;
    background: var(--erp-surface);
    display: flex;
    flex-direction: column;
    position: relative;
}

/* Corner cross to pull a file straight off the tile. */
.erp-doc-remove {
    position: absolute;
    top: 5px;
    right: 5px;
    z-index: 2;
    display: grid;
    place-items: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(17, 24, 39, .55);
    color: #fff;
    font-size: 11px;
    line-height: 1;
    cursor: pointer;
    opacity: 0;
    transition: opacity .15s ease, background .15s ease, transform .1s ease;
}
.erp-doc-tile:hover .erp-doc-remove,
.erp-doc-remove:focus-visible { opacity: 1; }
.erp-doc-remove:hover { background: var(--erp-danger); transform: scale(1.08); }
.erp-doc-preview {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 104px;
    background: var(--erp-bg);
    overflow: hidden;
}
.erp-doc-preview img { width: 100%; height: 100%; object-fit: cover; }
.erp-doc-icon { font-size: 34px; color: var(--erp-muted); }
.erp-doc-tile .erp-doc-preview .fa-file-pdf { color: #d1373a; }

.erp-doc-meta { padding: 7px 9px 4px; }
.erp-doc-name { display: block; font-size: 12px; font-weight: 600; color: var(--erp-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.erp-doc-sub { font-size: 10.5px; color: var(--erp-muted); }

.erp-doc-actions { display: flex; align-items: center; justify-content: space-between; padding: 2px 6px 6px; }
.erp-doc-actions form { margin: 0; }

/* --- Income/expense entry — compact detail view -------------------------- */

.erp-ie-hero {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 16px 18px;
    background: linear-gradient(180deg, rgba(3, 105, 161, .05), transparent);
}
.erp-ie-hero-amount { display: flex; flex-direction: column; line-height: 1.2; }
.erp-ie-amount { font-size: 24px; font-weight: 700; color: var(--erp-ink); }
.erp-ie-date { font-size: 12px; color: var(--erp-muted); margin-top: 2px; }
.erp-ie-hero-pills { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }

.erp-ie-attach { padding: 10px 16px 16px; border-top: 1px solid var(--erp-line); }
.erp-ie-attach-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
.erp-ie-attach-form { display: flex; align-items: center; gap: 6px; }
.erp-ie-attach-form .form-control-sm { max-width: 200px; height: 32px; font-size: 12px; }

/* Slightly smaller tiles inside the compact card. */
.erp-ie-attach .erp-doc-tile { width: 118px; }
.erp-ie-attach .erp-doc-preview { height: 82px; }
.erp-ie-attach .erp-doc-icon { font-size: 28px; }

/* Form section subheading (e.g. building form). */
.erp-form-section {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--erp-muted);
    margin: 18px 0 10px;
    padding-bottom: 7px;
    border-bottom: 1px solid var(--erp-line);
}
.erp-form-section:first-child { margin-top: 0; }
.erp-form-section i { color: var(--erp-primary); }

/* --- Building floors & units --------------------------------------------- */

.erp-plan-summary { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 14px; }

.erp-floor {
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    overflow: hidden;
    margin-bottom: 12px;
}
.erp-floor-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 11px 14px;
    background: var(--erp-bg);
    border-bottom: 1px solid var(--erp-line);
}
.erp-floor-title { display: flex; align-items: center; gap: 11px; }
.erp-floor-icon {
    width: 34px; height: 34px; border-radius: 9px; flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--erp-surface); border: 1px solid var(--erp-line); color: var(--erp-muted);
}
.erp-floor-title strong { display: block; font-size: 13.5px; color: var(--erp-ink); }
.erp-floor-title small { font-size: 11.5px; color: var(--erp-muted); }
.erp-floor-actions { display: inline-flex; gap: 5px; flex: 0 0 auto; }

.erp-floor-empty { padding: 12px 14px; }

/* Apartments as cards.

   One card per flat, laid out as a responsive grid inside its floor. The card
   reads top to bottom the way a property listing does — number, what it is,
   the specs as icons, then the price — instead of asking the eye to travel
   across seven table columns.

   The status is carried by a colour accent down the left edge as well as by
   the pill, so a floor's availability is legible from the shape of the grid
   before any text is read. Colour is never the only signal: every card still
   names its status in the pill. */

.erp-unit-grid {
    display: grid;
    /* 158px, not the 232px the richer card needed. A floor plan is read by
       scanning, and the point of compacting the tile was to get a whole tower
       on one screen rather than four units per row. */
    grid-template-columns: repeat(auto-fill, minmax(158px, 1fr));
    gap: 8px;
    padding: 12px;
    /* Each card takes its own height. Stretching them to the tallest in the
       row leaves a sparse flat — no specs, no price yet — as a mostly empty
       box, which reads as broken rather than as tidy. */
    align-items: start;
}

.erp-unit-card {
    --unit-accent: var(--erp-muted);
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 13px 14px 12px 16px;
    border: 1px solid var(--erp-line);
    border-radius: 11px;
    background: var(--erp-surface, #fff);
    transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}

/* The accent stripe. A pseudo-element rather than a border-left so the card's
   own border stays a complete, unbroken rounded rectangle. */
.erp-unit-card::before {
    content: "";
    position: absolute;
    left: 0;
    top: 12px;
    bottom: 12px;
    width: 3px;
    border-radius: 0 3px 3px 0;
    background: var(--unit-accent);
}

.erp-unit-card:hover {
    border-color: var(--unit-accent);
    box-shadow: 0 4px 16px rgba(20, 35, 60, .09);
    transform: translateY(-1px);
}

/* Same hues as the status pills, so the two never disagree. */
.erp-unit-card.is-available { --unit-accent: #00c292; }
.erp-unit-card.is-booked { --unit-accent: #03a9f3; }
.erp-unit-card.is-sold { --unit-accent: #6c63ff; }
.erp-unit-card.is-hold { --unit-accent: #ff9800; }
.erp-unit-card.is-overdue { --unit-accent: #e91e63; }
.erp-unit-card.is-neutral { --unit-accent: #b3bcca; }

/* Gone, not available: settled back so the flats still on the market are what
   the eye lands on first. Restored in full on hover. */
.erp-unit-card.is-settled { opacity: .72; }
.erp-unit-card.is-settled:hover { opacity: 1; }

.erp-unit-card-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 8px;
}

/* Takes the space the pill does not, rather than a fixed width — a long flat
   name should use whatever the card actually has. */
.erp-unit-card-id {
    flex: 1 1 auto;
    min-width: 0;
}

.erp-unit-card-id strong {
    display: block;
    font-size: 15px;
    font-weight: 600;
    line-height: 1.2;
    color: var(--erp-ink);
}

.erp-unit-card-id span {
    display: block;
    font-size: 11px;
    color: var(--erp-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-unit-card-head .erp-pill { flex: 0 0 auto; font-size: 10.5px; padding: 2px 8px; }

.erp-unit-card-type {
    margin: 0;
    font-size: 11.5px;
    color: var(--erp-muted);
    line-height: 1.45;
}

.erp-unit-card-corner {
    color: var(--erp-ink);
    font-weight: 600;
}

/* The spec strip — beds, baths, balconies, parking, area. Icons carry the
   meaning at a glance; each item keeps a title for the exact wording. */
.erp-unit-card-specs {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.erp-unit-card-specs li {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--erp-ink);
    background: var(--erp-bg);
    border-radius: 7px;
    padding: 4px 8px;
}

.erp-unit-card-specs li i { font-size: 10.5px; color: var(--erp-muted); }
.erp-unit-card-specs li.is-area { color: var(--unit-accent); }
.erp-unit-card-specs li.is-area i { color: inherit; }

.erp-unit-card-price {
    display: flex;
    align-items: baseline;
    gap: 7px;
    flex-wrap: wrap;
    padding-top: 2px;
}

.erp-unit-card-price strong {
    font-size: 15px;
    font-weight: 700;
    color: var(--erp-ink);
    letter-spacing: -.2px;
}

.erp-unit-card-price strong.is-unpriced {
    font-size: 12px;
    font-weight: 500;
    color: var(--erp-muted);
    font-style: italic;
    letter-spacing: 0;
}

.erp-unit-card-price span { font-size: 11px; color: var(--erp-muted); }

.erp-unit-card-holder {
    margin: 0;
    font-size: 11px;
    color: var(--erp-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-unit-card-actions {
    display: flex;
    justify-content: flex-end;
    gap: 2px;
    margin: 2px -6px -4px 0;
    padding-top: 8px;
    border-top: 1px dashed var(--erp-line);
}

.erp-unit-card-actions .btn { padding: 4px 7px; font-size: 11.5px; }

/* Touch screens have no hover, so the actions must never be hover-revealed —
   they are always visible above. Only the lift is dropped. */
@media (hover: none) {
    .erp-unit-card:hover { transform: none; }
}

@media (max-width: 575.98px) {
    .erp-unit-grid { grid-template-columns: 1fr; padding: 12px; }
}

@media print {
    .erp-unit-grid { grid-template-columns: repeat(3, 1fr); }
    .erp-unit-card { break-inside: avoid; box-shadow: none; }
    .erp-unit-card-actions { display: none; }
}

/* --- Project team structure ----------------------------------------------- */

/* Teams and sub-teams. A sub-team is the same card nested inside its parent
   and tinted back, so the hierarchy is visible from the shape alone rather
   than from a label the eye has to read. */

.erp-teams { padding: 16px; }

.erp-teams-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 14px;
}

.erp-teams-sub { margin: 2px 0 0; font-size: 11.5px; color: var(--erp-muted); }

/* One horizontal track, never a second row. Teams read across as siblings,
   and a project with a dozen of them scrolls sideways instead of pushing the
   task board far down the page.

   The track's height is set by its tallest card, so nothing overflows
   vertically and only the horizontal scrollbar ever appears — which is why
   overflow-y can be left to resolve itself rather than hidden (hiding it would
   clip a tall card). */
.erp-teams-grid {
    display: flex;
    flex-wrap: nowrap;
    align-items: flex-start;
    gap: 12px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    padding-bottom: 8px;
    scroll-snap-type: x proximity;
    scrollbar-color: #cfd8e3 var(--erp-bg);
    scrollbar-width: thin;
}

/* Direct children only — sub-teams nest inside a card and must keep filling it. */
.erp-teams-grid > .erp-team-node {
    flex: 0 0 340px;
    scroll-snap-align: start;
    width: 340px;
}

.erp-teams-grid::-webkit-scrollbar { height: 8px; }

.erp-teams-grid::-webkit-scrollbar-track {
    background: var(--erp-bg);
    border-radius: 8px;
}

.erp-teams-grid::-webkit-scrollbar-thumb {
    background: #cfd8e3;
    border-radius: 8px;
}

.erp-teams-grid::-webkit-scrollbar-thumb:hover { background: #b6c2d2; }

.erp-teams-tools {
    align-items: center;
    display: flex;
    flex: none;
    gap: 8px;
}

.erp-teams-search { position: relative; }

.erp-teams-search i {
    color: var(--erp-muted);
    font-size: 11px;
    left: 10px;
    pointer-events: none;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
}

.erp-teams-search input {
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: 7px;
    color: var(--erp-ink);
    font-size: 12px;
    padding: 6px 10px 6px 28px;
    width: 220px;
}

.erp-teams-search input:focus {
    border-color: var(--erp-primary);
    box-shadow: 0 0 0 3px rgba(0, 194, 146, .12);
    outline: none;
}

.erp-teams-nomatch {
    color: var(--erp-muted);
    font-size: 12.5px;
    margin: 0;
    padding: 26px 4px;
    text-align: center;
}

.erp-team-node {
    border: 1px solid var(--erp-line);
    border-radius: 11px;
    background: var(--erp-surface, #fff);
    padding: 13px 14px;
}

/* Nested one level in, on the page background so it reads as contained by its
   parent rather than as a sibling that happens to sit nearby. */
.erp-team-node.is-sub {
    background: var(--erp-bg);
    border-style: dashed;
    padding: 11px 12px;
}

.erp-team-node-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 10px;
}

.erp-team-node-id { display: flex; align-items: center; gap: 10px; min-width: 0; }

.erp-team-node-icon {
    width: 32px; height: 32px; border-radius: 9px; flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--erp-bg); border: 1px solid var(--erp-line); color: var(--erp-primary);
}

.erp-team-node.is-sub .erp-team-node-icon { background: var(--erp-surface, #fff); }

.erp-team-node-id strong {
    display: block; font-size: 13.5px; color: var(--erp-ink);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.erp-team-node-id small { font-size: 11px; color: var(--erp-muted); }

.erp-team-node-actions { display: inline-flex; gap: 2px; flex: 0 0 auto; }
.erp-team-node-actions .btn { padding: 4px 7px; font-size: 11.5px; }

/* The lead, called out above the member list. They appear in the list too —
   a lead who is not on their own team would be a contradiction — but who is
   answerable should not have to be hunted for. */
.erp-team-lead {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 10px 0 0;
    padding: 8px 10px;
    border-radius: 9px;
    background: rgba(0, 194, 146, .07);
    border: 1px solid rgba(0, 194, 146, .18);
}

.erp-team-lead.is-vacant { background: var(--erp-bg); border-color: var(--erp-line); }

.erp-team-lead-tag {
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: .4px;
    text-transform: uppercase;
    color: var(--erp-muted);
    flex: 0 0 auto;
}

.erp-team-lead-name { min-width: 0; line-height: 1.25; }
.erp-team-lead-name a, .erp-team-lead-name { font-size: 12.5px; color: var(--erp-ink); text-decoration: none; font-weight: 600; }
.erp-team-lead-name a:hover { color: var(--erp-primary); }
.erp-team-lead-name small { display: block; font-size: 10.5px; color: var(--erp-muted); font-weight: 400; }

.erp-team-node-note {
    margin: 9px 0 0;
    font-size: 11.5px;
    color: var(--erp-muted);
    line-height: 1.5;
}

/* The team's function (Civil, MEP…), inline in the header sub-line. */
.erp-team-type {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-weight: 600;
    color: var(--erp-primary);
}
.erp-team-type i { font-size: 9px; }

.erp-team-node-empty {
    margin: 10px 0 0;
    font-size: 11.5px;
    color: var(--erp-muted);
    font-style: italic;
}

.erp-team-members { list-style: none; margin: 10px 0 0; padding: 0; }

.erp-team-member {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 6px 4px;
    border-top: 1px solid var(--erp-line);
}

.erp-team-member:first-child { border-top: 0; }
.erp-team-member-body { flex: 1 1 auto; min-width: 0; line-height: 1.25; }

.erp-team-member-body strong,
.erp-team-member-body a {
    font-size: 12.5px; font-weight: 600; color: var(--erp-ink); text-decoration: none;
    display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.erp-team-member-body a:hover { color: var(--erp-primary); }
.erp-team-member-body small { font-size: 10.5px; color: var(--erp-muted); }

.erp-team-member-flag { color: #f0a500; font-size: 10px; flex: 0 0 auto; }

.erp-team-member-actions { display: inline-flex; gap: 1px; flex: 0 0 auto; }
.erp-team-member-actions .btn { padding: 3px 6px; font-size: 11px; }

/* The actions stay out of the way until the row is approached, so a long
   member list reads as names rather than as rows of buttons. */
@media (hover: hover) {
    .erp-team-member-actions { opacity: 0; transition: opacity .12s ease; }
    .erp-team-member:hover .erp-team-member-actions,
    .erp-team-member:focus-within .erp-team-member-actions { opacity: 1; }
}

.erp-team-subs { display: grid; gap: 9px; margin-top: 11px; }

@media (max-width: 575.98px) {
    .erp-teams { padding: 12px; }

    /* The head stacks, but the track keeps scrolling sideways — one card at a
       time, so a phone shows a whole team rather than a sliver of two. */
    .erp-teams-head { align-items: stretch; flex-direction: column; }
    .erp-teams-tools { justify-content: space-between; }
    .erp-teams-search { flex: 1; }
    .erp-teams-search input { width: 100%; }
    .erp-teams-grid > .erp-team-node { flex-basis: 85vw; width: 85vw; }
}

/* Totals per measure, above the charts. Deliberately one card per measure and
   never a grand total: money and unit counts do not add up to anything. */
.erp-target-summary {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 10px;
    margin-bottom: 14px;
}

.erp-target-summary-card {
    display: flex;
    flex-direction: column;
    gap: 5px;
    padding: 11px 13px;
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    background: var(--erp-surface, #fff);
}

/* The one headline that is honest across every measure. */
.erp-target-summary-card.is-headline {
    background: var(--erp-bg);
    border-color: var(--erp-primary);
}

.erp-target-summary-label { font-size: 11px; color: var(--erp-muted); white-space: nowrap; }

.erp-target-summary-card strong {
    font-size: 17px;
    font-weight: 700;
    color: var(--erp-ink);
    line-height: 1.15;
    letter-spacing: -.2px;
}

.erp-target-summary-card strong small {
    font-size: 11.5px;
    font-weight: 500;
    color: var(--erp-muted);
    letter-spacing: 0;
}

/* --- Target charts -------------------------------------------------------- */

/* Two series — Target and Achieved — carried by categorical slots 1 and 2 of
   the house palette. The pair was checked with the palette validator against
   the actual card surfaces rather than eyeballed: every gate passes in both
   modes (worst CVD ΔE 24.7 light / 26.8 dark against a ≥8 target, contrast
   ≥3:1 on both surfaces).

   Identity is never colour alone — the legend names both series, every bar
   carries a <title>, and the table below the charts repeats every figure. */
.erp-chart-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 12px;
}

/* Scoped to the target grid. `.erp-chart` is also the dashboard's canvas
   wrapper, declared far earlier in this file, and an unscoped rule here won the
   cascade over it — so every dashboard chart was picking up a border, a
   background and 10px of padding meant for this page. The nesting is what keeps
   the two meanings of the class apart. */
.erp-chart-grid .erp-chart {
    --series-target: #2a78d6;
    --series-achieved: #eb6834;
    --chart-axis: #e3e8ef;

    margin: 0;
    padding: 10px 10px 4px;
    border: 1px solid var(--erp-line);
    border-radius: 11px;
    background: var(--erp-surface, #fff);
}

.erp-chart-title {
    font-size: 11.5px;
    font-weight: 700;
    color: var(--erp-ink);
    margin-bottom: 2px;
}

/* The caption carries the definition, so the chart does not have to. */
.erp-chart-title span {
    display: block;
    font-size: 10.5px;
    font-weight: 400;
    color: var(--erp-muted);
}

/* An SVG with a viewBox grows to whatever column it is given, and everything
   inside it — including the type — scales with it. The cap stops a wide screen
   from turning a summary panel into a poster; the chart simply centres in the
   space it does not need. */
.erp-chart-svg {
    display: block;
    width: 100%;
    height: auto;
    max-height: 132px;
    margin: 0 auto;
    overflow: visible;
}

/* Chrome, not data: hairline, solid, one step off the surface. The gridlines
   above the baseline sit back further still — they are a reading aid, not a
   frame. */
.erp-chart-axis { stroke: var(--chart-axis); stroke-width: 1; }
.erp-chart-axis.is-grid { opacity: .55; }

.erp-chart-tick {
    fill: var(--erp-muted);
    font-size: 7.5px;
    text-anchor: end;
}

/* Says "nothing" where a bar would otherwise just be absent. */
.erp-chart-zero {
    fill: var(--erp-muted);
    font-size: 8px;
    font-weight: 700;
    text-anchor: middle;
}

.erp-chart-bar.is-target { fill: var(--series-target); }
.erp-chart-bar.is-achieved { fill: var(--series-achieved); }

.erp-chart-band:hover .erp-chart-bar { filter: brightness(1.06); }

.erp-chart-xlabel {
    fill: var(--erp-muted);
    font-size: 8px;
    text-anchor: middle;
}

.erp-chart-xsub {
    fill: var(--erp-muted);
    font-size: 7.5px;
    text-anchor: middle;
    opacity: .85;
}

.erp-chart-legend {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 14px;
    margin-bottom: 12px;
    font-size: 11.5px;
    color: var(--erp-muted);
}

.erp-chart-legend span { display: inline-flex; align-items: center; gap: 6px; }

/* A swatch, not a coloured word: the label stays in ink. */
.erp-chart-swatch {
    width: 11px;
    height: 11px;
    border-radius: 3px;
    display: inline-block;
    flex: 0 0 auto;
}

.erp-chart-swatch.is-target { background: #2a78d6; }
.erp-chart-swatch.is-achieved { background: #eb6834; }

.erp-chart-note { margin-left: auto; font-size: 11px; }

/* The dark steps are the same two hues re-stepped for a dark surface and
   validated against it — not an automatic flip of the light values. */
:root[data-theme="dark"] .erp-chart {
    --series-target: #3987e5;
    --series-achieved: #d95926;
    --chart-axis: #2a3442;
}

:root[data-theme="dark"] .erp-chart-swatch.is-target { background: #3987e5; }
:root[data-theme="dark"] .erp-chart-swatch.is-achieved { background: #d95926; }

@media (max-width: 575.98px) {
    .erp-chart-grid { grid-template-columns: 1fr; }
    .erp-chart-note { margin-left: 0; }
}

@media print {
    .erp-chart { break-inside: avoid; }
}

/* --- Sales targets -------------------------------------------------------- */

/* Headline figures for the filtered period. Recomputed after every AJAX save,
   so the totals never drift from the rows beneath them. */
.erp-target-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: 10px;
}

.erp-target-stat {
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 11px 14px;
    border: 1px solid var(--erp-line);
    border-left: 3px solid var(--erp-line);
    border-radius: 10px;
    background: var(--erp-surface, #fff);
}

.erp-target-stat.is-money { border-left-color: #03a9f3; }
.erp-target-stat.is-units { border-left-color: #6c63ff; }

.erp-target-stat-label { font-size: 11px; color: var(--erp-muted); white-space: nowrap; }
.erp-target-stat strong {
    font-size: 19px;
    font-weight: 700;
    color: var(--erp-ink);
    line-height: 1.15;
    letter-spacing: -.3px;
}

/* The progress cell — the reason this table is worth reading. The bar is
   capped at 100% so the column stays aligned, while the figure beside it keeps
   telling the truth (140% still reads 140%). */
.erp-target-progress {
    display: flex;
    align-items: center;
    gap: 9px;
}

.erp-target-progress-track {
    flex: 1 1 auto;
    height: 7px;
    min-width: 90px;
    border-radius: 20px;
    background: var(--erp-bg);
    overflow: hidden;
}

.erp-target-progress-bar {
    display: block;
    height: 100%;
    border-radius: 20px;
    transition: width .3s ease;
}

.erp-target-progress-value {
    font-size: 12px;
    font-weight: 700;
    min-width: 46px;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* Three bands, not a gradient: "behind / on track / met" is the judgement a
   sales manager is making, and a band says it faster than a hue ramp. Each
   band pairs a bar colour with a matching figure so the two never disagree. */
.erp-target-progress.is-met .erp-target-progress-bar { background: linear-gradient(90deg, #00c292, #12b886); }
.erp-target-progress.is-met .erp-target-progress-value { color: #00926e; }

.erp-target-progress.is-close .erp-target-progress-bar { background: linear-gradient(90deg, #f0a500, #ff9800); }
.erp-target-progress.is-close .erp-target-progress-value { color: #b96f00; }

.erp-target-progress.is-behind .erp-target-progress-bar { background: linear-gradient(90deg, #f0616f, #e91e63); }
.erp-target-progress.is-behind .erp-target-progress-value { color: #c2185b; }

.erp-target-progress.is-none .erp-target-progress-value { color: var(--erp-muted); }

.erp-target-table td { vertical-align: middle; }

/* The measure picker: three cards rather than a dropdown, because the choice
   changes which figure the form then asks for and that is worth seeing. */
.erp-target-types {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px;
}

.erp-target-type { margin: 0; cursor: pointer; }
.erp-target-type input { position: absolute; opacity: 0; pointer-events: none; }

.erp-target-type-body {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    height: 100%;
    padding: 11px 8px;
    text-align: center;
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    background: var(--erp-surface, #fff);
    transition: border-color .14s ease, background .14s ease, box-shadow .14s ease;
}

.erp-target-type-body i { font-size: 15px; color: var(--erp-muted); }
.erp-target-type-body strong { font-size: 12px; color: var(--erp-ink); }
.erp-target-type-body small { font-size: 10px; color: var(--erp-muted); line-height: 1.3; }

.erp-target-type:hover .erp-target-type-body { border-color: var(--erp-primary); }

.erp-target-type input:checked + .erp-target-type-body {
    border-color: var(--erp-primary);
    background: rgb(0 194 146 / .07);
    box-shadow: 0 0 0 3px rgb(0 194 146 / .13);
}

.erp-target-type input:checked + .erp-target-type-body i { color: var(--erp-primary); }

/* Keyboard users must be able to see which card has focus. */
.erp-target-type input:focus-visible + .erp-target-type-body {
    outline: 2px solid var(--erp-primary);
    outline-offset: 2px;
}

/* The period range inside the modal. */
.erp-target-range { display: flex; align-items: center; gap: 8px; }
.erp-target-range .form-control { flex: 1 1 0; min-width: 0; }
.erp-target-range > span { color: var(--erp-muted); flex: 0 0 auto; }

/* Presets set the end from the start. They are shortcuts, not a separate
   choice, so they sit under the inputs rather than beside them. */
.erp-range-presets {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    margin-top: 8px;
}

.erp-range-presets button {
    border: 1px solid var(--erp-line);
    background: var(--erp-surface, #fff);
    border-radius: 20px;
    padding: 3px 11px;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--erp-muted);
    cursor: pointer;
    transition: border-color .14s ease, color .14s ease, background .14s ease;
}

.erp-range-presets button:hover { border-color: var(--erp-primary); color: var(--erp-primary); }

/* Lit when the dates currently picked happen to match this span, whether the
   preset was clicked or the dates were typed by hand. */
.erp-range-presets button.is-on {
    border-color: var(--erp-primary);
    background: rgb(0 194 146 / .1);
    color: #00926e;
}

/* What has actually been picked, in words. */
.erp-range-summary {
    display: inline-flex;
    align-items: baseline;
    gap: 4px;
    margin-top: 8px;
    padding: 4px 10px;
    border-radius: 8px;
    background: var(--erp-bg);
    font-size: 11.5px;
    color: var(--erp-muted);
}

.erp-range-summary strong { color: var(--erp-ink); font-size: 12.5px; }

.erp-range-summary.is-bad { background: rgb(233 30 99 / .1); }
.erp-range-summary.is-bad strong { color: #c2185b; }

/* Columns weighted by what each control holds, so the filter bar sits on one
   row from about a 1230px viewport upward. */
.erp-filter-grid.is-targets {
    grid-template-columns:
        minmax(150px, 1.6fr)   /* search */
        minmax(110px, 1.2fr)   /* person */
        minmax(100px, 1.1fr)   /* department */
        minmax(76px, .7fr)     /* year */
        minmax(92px, .9fr)     /* month */
        minmax(100px, 1fr)     /* measure */
        minmax(92px, .9fr)     /* status */
        auto;                  /* actions */
}

@media (max-width: 1279.98px) {
    .erp-filter-grid.is-targets { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (max-width: 767.98px) {
    .erp-filter-grid.is-targets { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .erp-target-types { grid-template-columns: 1fr; }
    .erp-target-type-body { flex-direction: row; align-items: center; text-align: left; gap: 8px; }
}

/* --- Unit inventory ------------------------------------------------------- */

/* The availability strip. Each figure is a link that filters the list, so the
   headline number and the way to act on it are the same control. */
.erp-unit-stats {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.erp-unit-stat {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 104px;
    padding: 9px 12px;
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    background: var(--erp-surface, #fff);
    text-decoration: none;
    transition: border-color .14s ease, box-shadow .14s ease, transform .14s ease;
}

.erp-unit-stat:hover {
    border-color: var(--erp-primary);
    transform: translateY(-1px);
    box-shadow: 0 3px 10px rgb(20 35 60 / .07);
}

.erp-unit-stat.is-on {
    border-color: var(--erp-primary);
    box-shadow: 0 0 0 3px rgb(0 194 146 / .14);
}

.erp-unit-stat-label { font-size: 11px; color: var(--erp-muted); white-space: nowrap; }
.erp-unit-stat strong { font-size: 17px; font-weight: 700; color: var(--erp-ink); line-height: 1.1; }

/* The status tint is carried by the same pill classes the rest of the app
   uses, so a status never means two different colours. */
.erp-unit-stat.is-available .erp-unit-stat-label { color: #00926e; }
.erp-unit-stat.is-booked .erp-unit-stat-label { color: #0281bb; }
.erp-unit-stat.is-sold .erp-unit-stat-label { color: #574fd6; }
.erp-unit-stat.is-hold .erp-unit-stat-label { color: #b96f00; }
.erp-unit-stat.is-overdue .erp-unit-stat-label { color: #c2185b; }

@media (max-width: 575.98px) {
    .erp-unit-stat { min-width: calc(50% - 4px); }
}

/* --- Investor form (land funding) ----------------------------------------- */

.erp-investor-head { display: flex; gap: 14px; align-items: flex-start; }

/* The portrait doubles as its own upload control — a separate "choose file"
   row would push the name field down for a field that is optional. */
.erp-investor-photo {
    position: relative;
    width: 84px; height: 84px; flex: 0 0 84px;
    border-radius: 12px;
    overflow: hidden;
    background: var(--erp-bg);
    border: 1px solid var(--erp-line);
    display: grid; place-items: center;
    color: var(--erp-muted); font-size: 26px;
}

.erp-investor-photo img { width: 100%; height: 100%; object-fit: cover; }

.erp-investor-photo-btn {
    position: absolute; right: 0; bottom: 0;
    width: 26px; height: 26px;
    display: grid; place-items: center;
    background: var(--erp-primary); color: #fff;
    border-radius: 8px 0 0 0;
    cursor: pointer; font-size: 11px;
}

.erp-investor-photo-btn:hover { filter: brightness(1.08); }
.erp-investor-head-fields { flex: 1 1 auto; min-width: 0; }

.erp-investor-section {
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--erp-line);
}

.erp-investor-section-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 10px; margin-bottom: 4px;
}

/* Title, then file, then remove — the title first because it is what the
   uploader is actually deciding. */
.erp-doc-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr) auto;
    gap: 6px;
    margin-bottom: 6px;
    align-items: center;
}

.erp-investor-account {
    margin-top: 10px;
    padding: 10px 12px;
    border-radius: 9px;
    background: var(--erp-bg);
    border: 1px solid var(--erp-line);
}

/* The investor list on the funding panel. */
.erp-investor-avatar {
    width: 30px; height: 30px; flex: 0 0 30px;
    border-radius: 50%; overflow: hidden;
    display: inline-grid; place-items: center;
    background: var(--erp-line); color: var(--erp-muted);
    font-size: 11px; font-weight: 600;
}

.erp-investor-avatar img { width: 100%; height: 100%; object-fit: cover; }

@media (max-width: 575.98px) {
    .erp-doc-row { grid-template-columns: 1fr auto; }
    .erp-investor-head { flex-direction: column; }
}

/* --- Central documents uploader ------------------------------------------ */
.erp-docs-upload { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
.erp-docs-upload .form-control-sm { max-width: 320px; height: auto; font-size: 12px; }

/* --- Global file input: dashed drop-zone look ---------------------------- *
 * Applies to every Bootstrap file field so uploads read as a drop area
 * rather than a plain text box. */
input[type="file"].form-control {
    border: 1.5px dashed var(--erp-line);
    border-radius: var(--erp-radius);
    background: var(--erp-bg);
    color: var(--erp-muted);
    cursor: pointer;
    transition: border-color .15s ease, background .15s ease, box-shadow .15s ease;
}

input[type="file"].form-control:hover {
    border-color: var(--erp-primary);
    background: color-mix(in srgb, var(--erp-primary) 6%, var(--erp-bg));
}

input[type="file"].form-control:focus {
    border-color: var(--erp-primary);
    background: var(--erp-bg);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--erp-primary) 18%, transparent);
}

input[type="file"].form-control::file-selector-button {
    margin-right: 12px;
    padding: .35rem .8rem;
    border: 0;
    border-radius: calc(var(--erp-radius) - 3px);
    background: var(--erp-primary);
    color: #fff;
    font-weight: 600;
    cursor: pointer;
    transition: background .15s ease, filter .15s ease;
}

input[type="file"].form-control:hover::file-selector-button,
input[type="file"].form-control:focus::file-selector-button {
    filter: brightness(.94);
}

input[type="file"].form-control-sm::file-selector-button {
    padding: .2rem .6rem;
    margin-right: 10px;
}

/* Standalone tab bar (navigation links styled as tabs). */
.erp-tabs-nav { margin-bottom: 14px; }
.erp-tabs a.erp-tab { text-decoration: none; }

/* Documents block tucked into the bottom of a detail card. */
.erp-doc-inline { margin-top: 8px; padding-top: 14px; border-top: 1px solid var(--erp-line); }

/* ============================================================ *
 * Point of Sale — sell screen
 * ============================================================ */
.erp-pos-results { max-height: 52vh; overflow: auto; display: flex; flex-direction: column; gap: 6px; }
.erp-pos-result {
    display: flex; justify-content: space-between; align-items: center; gap: 10px;
    width: 100%; text-align: left; border: 1px solid var(--erp-line); background: var(--erp-surface, #fff);
    border-radius: 10px; padding: 10px 12px; font-size: 13px; cursor: pointer; transition: border-color .12s, background .12s;
}
.erp-pos-result:hover { border-color: var(--erp-primary, #0f766e); background: var(--erp-tint, rgba(15,118,110,.05)); }
.erp-pos-result-price { font-weight: 700; white-space: nowrap; }
.erp-pos-qty { display: inline-flex; align-items: center; gap: 2px; }
.erp-pos-qty button {
    width: 26px; height: 28px; border: 1px solid var(--erp-line); background: transparent;
    border-radius: 7px; font-size: 15px; line-height: 1; cursor: pointer;
}
.erp-pos-qty input { width: 40px; height: 28px; text-align: center; border: 1px solid var(--erp-line); border-radius: 7px; font-size: 13px; }
.erp-pos-summary { border-top: 1px solid var(--erp-line); padding-top: 12px; display: flex; flex-direction: column; gap: 4px; }
.erp-pos-receipt { max-height: 60vh; overflow: auto; }

/* ============================================================ *
 * Point of Sale — full-screen till
 * ============================================================ */
.erp-pos { display: flex; flex-direction: column; gap: 12px; min-height: calc(100vh - 150px); }
.erp-pos.is-fullscreen { min-height: 100vh; height: 100vh; background: var(--erp-bg, #f5f6f8); padding: 12px; overflow: auto; }

.erp-pos-topbar { display: flex; align-items: center; gap: 12px; justify-content: space-between; }
.erp-pos-tabs-wrap { display: flex; align-items: center; gap: 6px; overflow-x: auto; flex: 1; }
.erp-pos-tabs { display: flex; align-items: center; gap: 6px; }
.erp-pos-newtab { flex: 0 0 auto; width: 34px; height: 34px; border: 1px dashed var(--erp-line); background: transparent; border-radius: 9px; cursor: pointer; color: var(--erp-muted); }
.erp-pos-newtab:hover { border-color: var(--erp-primary, #0f766e); color: var(--erp-primary, #0f766e); }

.erp-pos-tab { display: inline-flex; align-items: center; gap: 7px; padding: 7px 10px; border: 1px solid var(--erp-line); border-radius: 9px; background: var(--erp-surface, #fff); font-size: 12.5px; text-decoration: none; color: inherit; white-space: nowrap; }
.erp-pos-tab.is-current { border-color: var(--erp-primary, #0f766e); box-shadow: 0 0 0 2px var(--erp-tint, rgba(15,118,110,.12)); }
.erp-pos-tab-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--erp-muted); }
.erp-pos-tab-dot.is-available { background: #16a34a; } .erp-pos-tab-dot.is-hold { background: #d97706; } .erp-pos-tab-dot.is-booked { background: #2563eb; }
.erp-pos-tab-count { background: var(--erp-tint, rgba(15,118,110,.12)); border-radius: 20px; padding: 0 7px; font-weight: 600; }
.erp-pos-tab-status { color: var(--erp-muted); font-size: 11px; }
.erp-pos-tab-x { border: none; background: transparent; color: var(--erp-muted); cursor: pointer; padding: 0 2px; }
.erp-pos-tab-x:hover { color: #b91c1c; }

.erp-pos-topbar-right { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.erp-pos-session-chip { display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; border: 1px solid var(--erp-line); border-radius: 10px; background: var(--erp-surface, #fff); font-size: 12.5px; cursor: pointer; text-align: left; }

/* Two columns: the search card — which carries its own quick-access tiles
   under the search box — and the cart. 60/40: picking a product needs the
   room, and the cart still has enough for a long name, its quantity and its
   total on one line. */
.erp-pos-body { display: flex; gap: 12px; flex: 1; align-items: stretch; }
.erp-pos-left,
.erp-pos-right { background: var(--erp-surface, #fff); border: 1px solid var(--erp-line); border-radius: 14px; padding: 14px; display: flex; flex-direction: column; }
.erp-pos-left { flex: 1 1 60%; min-width: 0; }
.erp-pos-right { flex: 1 1 40%; min-width: 340px; }

/* The shared .erp-search caps itself at 340px, which is right in a filter bar
   and wrong here — this box is the till's primary input and fills its card. */
.erp-pos-left .erp-search { max-width: none; }
.erp-pos-results { flex: 1; overflow: auto; display: flex; flex-direction: column; gap: 6px; min-height: 120px; }

.erp-pos-customer { display: flex; align-items: center; gap: 6px; margin-bottom: 10px; }
.erp-pos-customer-btn { flex: 1; text-align: left; border: 1px solid var(--erp-line); border-radius: 9px; background: transparent; padding: 8px 10px; font-size: 12.5px; cursor: pointer; }
.erp-pos-customer-clear { border: 1px solid var(--erp-line); border-radius: 9px; background: transparent; width: 34px; height: 34px; cursor: pointer; color: var(--erp-muted); }

.erp-pos-cart-scroll { flex: 1; overflow: auto; min-height: 140px; }
.erp-pos-summary { border-top: 1px solid var(--erp-line); padding-top: 12px; margin-top: 8px; display: flex; flex-direction: column; gap: 4px; }
.erp-pos-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 12px; }
.erp-pos-actions .erp-pos-pay { grid-column: 1 / -1; padding: 13px; font-size: 15px; }

.erp-pos-tenders { display: flex; flex-direction: column; gap: 6px; }
.erp-pos-tender { display: grid; grid-template-columns: 1.1fr 1fr 1fr auto; gap: 6px; align-items: center; }
.erp-pos-paysum { border-top: 1px solid var(--erp-line); padding-top: 10px; display: flex; flex-direction: column; gap: 4px; }
.erp-pos-receipt { max-height: 60vh; overflow: auto; }

/* The tiles live inside the search card now, so there is no third column to
   drop: the two columns simply hold their 60/40 until the cart's 340px floor
   would squeeze the search side, at which point they stack. */
@media (max-width: 1100px) {
    .erp-pos-quick-grid { grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); max-height: 168px; }
}

@media (max-width: 900px) {
    .erp-pos-body { flex-direction: column; flex-wrap: nowrap; }
    .erp-pos-left,
    .erp-pos-right { flex: 1 1 auto; min-width: 0; }
}

/* --- Barcode labels -------------------------------------------------------- */

.erp-barcode-input { display: flex; gap: 6px; }
.erp-barcode-input .form-control { flex: 1 1 auto; min-width: 0; font-family: "Courier New", monospace; }
.erp-barcode-input .btn { flex: 0 0 auto; white-space: nowrap; }

.erp-barcode-preview {
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--erp-line);
}

.erp-barcode-preview-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 8px;
}

/* Scrolls rather than growing the modal: two hundred stickers is a legitimate
   run and the dialog must not become a mile long. */
.erp-barcode-preview-body {
    max-height: 260px;
    overflow: auto;
    background: var(--erp-bg);
    border-radius: 9px;
    padding: 4px;
}

.erp-pos-pin-count.is-warn { border-color: #f0a500; color: #b96f00; }
.erp-pos-pin-count.is-warn strong { color: #b96f00; }

/* The print area holds the sheet and is never seen on screen. */
#barcodePrintArea { display: none; }

@media print {
    /* Label mode: the sticker sheet is the only thing on the paper. Hiding the
       app rather than opening a new window keeps the operator where they were
       and avoids a popup blocker eating the job.

       This hides the print area's SIBLINGS, and products.js moves the print
       area to be a direct child of <body> before any of it runs. That order
       matters: the sheet is rendered deep inside the page (div > main > div >
       body), and hiding an ancestor cannot be undone by showing a descendant —
       display is not inherited, it is structural. Getting this wrong prints a
       blank page, which is exactly what it did. */
    body.is-printing-labels > *:not(#barcodePrintArea) { display: none !important; }

    body.is-printing-labels #barcodePrintArea {
        display: block !important;
        margin: 0;
        padding: 0;
    }

    /* One sticker after another, never side by side: each label is its own
       page, and a flex row would try to fit several across one. */
    body.is-printing-labels #barcodePrintArea .erp-label-sheet {
        display: block !important;
        padding: 0 !important;
        gap: 0 !important;
    }

    /* On paper the preview's dashed guides have no business existing. */
    body.is-printing-labels #barcodePrintArea .erp-label { border: 0 !important; }
}

/* --- POS quick access ------------------------------------------------------ */

/* The tiles sit in the catalog pane, above the search results, and stand down
   while a search is running so the results get the room. */
.erp-pos-quick-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 6px;
}

.erp-pos-quick-manage { font-size: 11px; color: var(--erp-muted); text-decoration: none; }
.erp-pos-quick-manage:hover { color: var(--erp-primary); }

/* The tiles now sit inside the search card as a band under the box, so the
   grid is auto-filled across the full width instead of two-up in a narrow
   column, and it is capped rather than flexible — the results list below is
   what should grow, not the tiles. */
.erp-pos-quick {
    flex: 0 0 auto;
    margin-bottom: 10px;
    padding-bottom: 10px;
    border-bottom: 1px dashed var(--erp-line);
    min-width: 0;
}

.erp-pos-quick-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(124px, 1fr));
    align-content: start;
    gap: 6px;
    max-height: 210px;
    overflow-y: auto;
    min-height: 0;
}

/* Big enough to hit without looking — this is a touch target at a counter,
   not a link in a document. */
.erp-pos-quick-tile {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: 4px;
    min-height: 58px;
    padding: 8px 9px;
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    background: var(--erp-surface, #fff);
    text-align: left;
    cursor: pointer;
    transition: border-color .12s ease, background .12s ease, transform .08s ease;
}

.erp-pos-quick-tile:hover { border-color: var(--erp-primary); }
.erp-pos-quick-tile:active { transform: scale(.97); }

/* A tap has to feel like it landed: the cart is on the other side of the
   screen and is easy to miss at a busy counter. */
.erp-pos-quick-tile.is-added {
    background: rgb(0 194 146 / .14);
    border-color: var(--erp-primary);
}

.erp-pos-quick-name {
    font-size: 11.5px;
    font-weight: 600;
    color: var(--erp-ink);
    line-height: 1.25;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.erp-pos-quick-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 6px;
}

.erp-pos-quick-price { font-size: 12px; font-weight: 700; color: var(--erp-primary); }

/* On-hand on the tile. Neutral while there is plenty. */
.erp-pos-quick-stock {
    flex: 0 0 auto;
    min-width: 20px;
    padding: 1px 6px;
    border-radius: 20px;
    background: var(--erp-surface-2);
    color: var(--erp-muted);
    font-size: 10.5px;
    font-weight: 700;
    text-align: center;
}

/* Running out. Warm red rather than the danger red used for destructive
   actions — this is "check the shelf", not "you broke something". */
.erp-pos-quick-tile.is-low {
    background: rgb(244 63 94 / .07);
    border-color: rgb(244 63 94 / .38);
}

.erp-pos-quick-tile.is-low .erp-pos-quick-name { color: #9f1239; }
.erp-pos-quick-tile.is-low .erp-pos-quick-price { color: #9f1239; }

.erp-pos-quick-tile.is-low .erp-pos-quick-stock {
    background: rgb(244 63 94 / .16);
    color: #b81d42;
}

.erp-pos-quick-tile.is-low:hover { border-color: rgb(244 63 94 / .7); }

/* The same figure beside a search hit. */
.erp-pos-result-right {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 3px;
    flex: 0 0 auto;
}

.erp-pos-stock {
    padding: 1px 7px;
    border-radius: 20px;
    background: var(--erp-surface-2);
    color: var(--erp-muted);
    font-size: 10.5px;
    font-weight: 700;
    white-space: nowrap;
}

.erp-pos-stock.is-low { background: rgb(244 63 94 / .14); color: #b81d42; }
.erp-pos-stock.is-out { background: rgb(244 63 94 / .9); color: #fff; }

/* The pin in the product list. */
.erp-pos-pin {
    border: 0;
    background: none;
    cursor: pointer;
    color: var(--erp-line);
    font-size: 14px;
    padding: 2px 6px;
    transition: color .12s ease, transform .08s ease;
}

.erp-pos-pin:hover { color: #f0a500; transform: scale(1.12); }
.erp-pos-pin.is-on { color: #f0a500; }
.erp-pos-pin:disabled { opacity: .5; cursor: default; }

.erp-pos-pin-count {
    display: inline-flex;
    align-items: center;
    font-size: 11.5px;
    color: var(--erp-muted);
    padding: 6px 10px;
    border: 1px solid var(--erp-line);
    border-radius: 8px;
}

.erp-pos-pin-count strong { color: var(--erp-ink); }

@media (max-width: 575.98px) {
    .erp-pos-quick-grid { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); max-height: 150px; }
}

/* ============================================================ *
 * Purchases — builder
 * ============================================================ */
.erp-purchase-sum { display: flex; flex-direction: column; gap: 7px; }
.erp-purchase-sum > div { display: flex; justify-content: space-between; align-items: baseline; font-size: 13px; }
.erp-purchase-sum > div span { color: var(--erp-muted); }
.erp-purchase-items input.form-control-sm { min-width: 70px; }
.erp-amt { max-width: 130px; text-align: right; }
.erp-discount-input { display: flex; gap: 4px; align-items: center; }
.erp-discount-input .form-select-sm { width: auto; min-width: 58px; flex: 0 0 auto; }
.erp-discount-input .erp-amt { max-width: 96px; }
.erp-save-state { min-width: 54px; text-align: right; }

/* Purchase order — requisition toggle & section divider. */
.erp-req-toggle { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; background: var(--erp-bg, #f7f9fc); border: 1px solid var(--erp-line); border-radius: 7px; padding: 6px 10px; }
.erp-req-toggle .form-check-label { font-size: 12.5px; font-weight: 500; cursor: pointer; }

/* What the typed requisition number resolves to, and the offer to pull its
   lines. Sits under the toggle so the answer appears where the question was
   asked, rather than as a toast that has gone by the time it is read. */
.erp-req-status {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 6px;
    padding: 6px 10px;
    border: 1px dashed var(--erp-line);
    border-radius: 7px;
    font-size: 12px;
    color: var(--erp-muted);
}

/* There is something to pull — say so in the app's own affirmative colour. */
.erp-req-status.is-ready {
    border-style: solid;
    border-color: color-mix(in srgb, var(--erp-primary) 40%, transparent);
    background: color-mix(in srgb, var(--erp-primary) 7%, transparent);
    color: var(--erp-ink);
}

.erp-req-status [data-requisition-load] { margin-left: auto; }

/* --- POS sale detail -------------------------------------------------------- */

/* The headline figures as one slim band. A detail screen is read, not
   monitored, so these earn a line rather than four dashboard tiles. */
.erp-sale-strip {
    /* The tiles supply the surface now, so the container is only a frame and a
       clip. The deep slate underneath shows through in the gap a wrapped row
       leaves, which is better than a strip of white appearing mid-band. */
    background: #1e293b;
    border: 1px solid transparent;
    border-radius: var(--erp-radius);
    box-shadow: 0 4px 16px -10px rgba(15, 23, 42, .5);
    display: flex;
    flex-wrap: wrap;
    gap: 0;
    margin-bottom: var(--erp-gutter);
    overflow: hidden;
}

/* Each figure gets a hue: a bar along its top edge, a wash fading out beneath
   it, and the number itself in a darkened version of the same colour.

   The colour is on the TOP EDGE rather than the left, because these tiles sit
   shoulder to shoulder in one band -- a left bar would read as a divider
   between two tiles rather than as a marker belonging to one.

   Where a figure MEANS something, the page says so with .is-tone-<name>: rose
   for a blacklist count, amber for a thing that will fail later. Where it does
   not, the rotation below gives it a hue anyway, so a strip of four figures
   reads as four figures instead of one grey ribbon. */
.erp-sale-stat {
    /* The same deep pair, highlight and top rule as .erp-stat, so the two
       components read as one thing however the page was built. */
    background-color: var(--erp-tone-deep, #334155);
    background-image:
        linear-gradient(170deg, rgba(255, 255, 255, .12) 0%, rgba(255, 255, 255, 0) 52%),
        linear-gradient(135deg, var(--erp-tone-deep, #334155), var(--erp-tone-deeper, #1e293b));
    /* Translucent white, not the grey line this used to be: a grey rule drawn
       between two deep panels reads as a crack rather than a division. */
    border-left: 1px solid rgba(255, 255, 255, .14);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .13);
    color: #fff;
    flex: 1 1 150px;
    min-width: 0;
    padding: 9px 13px 8px;
}

.erp-sale-stat:first-child { border-left: 0; }

/* Utilities that were written for a white card. Bootstrap's carry !important, so
   the override has to as well. The tints are the same ones .erp-stat uses, each
   measured to clear WCAG AA on the deepest tone it can land on. */
.erp-sale-stat .text-muted { color: rgba(255, 255, 255, .93) !important; }
.erp-sale-stat .text-danger { color: #ffe4e6 !important; }
.erp-sale-stat .text-warning { color: #fffbeb !important; }
.erp-sale-stat .text-success { color: #d1fae5 !important; }

.erp-sale-stat .erp-plain-link,
.erp-sale-stat a { color: #fff; }

/* A tile that names no tone still gets one, cycling through six hues that sit
   together without arguing. Guarded by :not() so an explicit tone always wins --
   this selector is far more specific than a bare palette class, and without the
   guard it would quietly override every meaning a page tried to express. */
.erp-sale-strip > .erp-sale-stat:not([class*="is-tone-"]):not(.is-danger):not(.is-warning):not(.is-info):not(.is-success):not(.is-warn):nth-child(6n + 1) { --erp-tone-deep: #0369a1; --erp-tone-deeper: #075985; }
.erp-sale-strip > .erp-sale-stat:not([class*="is-tone-"]):not(.is-danger):not(.is-warning):not(.is-info):not(.is-success):not(.is-warn):nth-child(6n + 2) { --erp-tone-deep: #4f46e5; --erp-tone-deeper: #3730a3; }
.erp-sale-strip > .erp-sale-stat:not([class*="is-tone-"]):not(.is-danger):not(.is-warning):not(.is-info):not(.is-success):not(.is-warn):nth-child(6n + 3) { --erp-tone-deep: #0f766e; --erp-tone-deeper: #115e59; }
.erp-sale-strip > .erp-sale-stat:not([class*="is-tone-"]):not(.is-danger):not(.is-warning):not(.is-info):not(.is-success):not(.is-warn):nth-child(6n + 4) { --erp-tone-deep: #b45309; --erp-tone-deeper: #78350f; }
.erp-sale-strip > .erp-sale-stat:not([class*="is-tone-"]):not(.is-danger):not(.is-warning):not(.is-info):not(.is-success):not(.is-warn):nth-child(6n + 5) { --erp-tone-deep: #7e22ce; --erp-tone-deeper: #581c87; }
.erp-sale-strip > .erp-sale-stat:not([class*="is-tone-"]):not(.is-danger):not(.is-warning):not(.is-info):not(.is-success):not(.is-warn):nth-child(6n + 6) { --erp-tone-deep: #0e7490; --erp-tone-deeper: #155e75; }

.erp-sale-stat-label {
    /* 93%, for the same reason as the .erp-stat caption: below that it drops
       under WCAG AA on most of the deep tones, and a caption is content. */
    color: rgba(255, 255, 255, .93);
    display: block;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .05em;
    text-transform: uppercase;
}

.erp-sale-stat strong {
    color: #fff;
    display: block;
    font-size: 17px;
    font-weight: 700;
    letter-spacing: -.4px;
    line-height: 1.25;
}

.erp-sale-stat strong small { font-size: 11px; font-weight: 600; color: rgba(255, 255, 255, .93); }

.erp-sale-stat-foot {
    color: rgba(255, 255, 255, .93);
    display: block;
    font-size: 11px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* These now tint the TILE rather than just its figure, which is what makes them
   visible on a deep band at all -- a dark red figure on deep slate is unreadable,
   and was the whole reason the pale version existed. */
.erp-sale-stat.is-danger  { --erp-tone-deep: #b91c1c; --erp-tone-deeper: #7f1d1d; }
.erp-sale-stat.is-warning { --erp-tone-deep: #b45309; --erp-tone-deeper: #78350f; }
.erp-sale-stat.is-info    { --erp-tone-deep: #1d4ed8; --erp-tone-deeper: #1e3a8a; }
.erp-sale-stat.is-success { --erp-tone-deep: #047857; --erp-tone-deeper: #065f46; }

/* A run-in heading inside the totals rail — cheaper vertically than another
   card head, and the rail is already one scrolling column. */
.erp-sale-section {
    border-top: 1px dashed var(--erp-line);
    color: var(--erp-muted);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .05em;
    margin: 12px 0 7px;
    padding-top: 10px;
    text-transform: uppercase;
}

.erp-sale-meta {
    display: grid;
    grid-template-columns: 88px 1fr;
    gap: 4px 10px;
    margin: 0;
}

.erp-sale-meta dt {
    color: var(--erp-muted);
    font-size: 11.5px;
    font-weight: 500;
}

.erp-sale-meta dd {
    color: var(--erp-ink);
    font-size: 12px;
    font-weight: 600;
    margin: 0;
    min-width: 0;
    overflow-wrap: anywhere;
}

/* Divider row introducing the returns inside the same line table. */
.erp-sale-subhead td {
    background: var(--erp-surface-2);
    color: var(--erp-muted);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
}

/* A table that scrolls inside a card keeps its header visible, or the columns
   stop meaning anything halfway down. */
.erp-sticky-head thead th {
    background-color: var(--erp-table-head-solid);
    position: sticky;
    top: 0;
    z-index: 1;
}

/* --- Stock list ------------------------------------------------------------ */

/* The count tiles double as status filters, so the clickable ones say so on
   hover and the active one stays visibly pressed. */
.erp-stock-tile {
    color: inherit;
    display: block;
    text-decoration: none;
}

.erp-stock-tile.is-static { cursor: default; }
.erp-stock-tile:not(.is-static):hover { border-color: color-mix(in srgb, var(--erp-primary) 45%, transparent); }
.erp-stock-tile:hover { color: inherit; }

.erp-stock-tile.is-on {
    border-color: var(--erp-primary);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--erp-primary) 18%, transparent);
}

/* --- Goods receipt (receive against a PO) ---------------------------------- */

/* Batch / manufacture / expiry sit under the item name rather than taking three
   columns of their own — the name is what the storeman reads first. */
/* Two columns, with the batch spanning both: the item cell gives roughly 280px
   and three across would squeeze each date under the ~105px Chrome needs to
   render "mm/dd/yyyy" plus its picker without clipping. Pairing the two dates
   on one row also reads correctly — they are the same kind of fact. */
.erp-grn-batch {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 6px;
    margin-top: 6px;
    max-width: 268px;
}

/* Each control carries its own caption: two unlabelled date boxes side by side
   are indistinguishable, and an expiry typed into the manufacture field is not
   an error anyone notices until the stock is wrong. */
.erp-grn-batch-field {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-bottom: 0;
}

.erp-grn-batch-field > span {
    color: var(--erp-muted);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .04em;
    line-height: 1.2;
    text-transform: uppercase;
}

/* The Notika template forces width:100%!important on every input[type=date]
   (see the input.erp-date note above), so these have to match its specificity
   and carry !important too, or the two dates each take the whole row. */
.erp-grn-batch-field.is-batch { grid-column: 1 / -1; }
.erp-grn-batch [data-cell="batch_number"] { width: 100%; }
.erp-grn-batch input[type="date"] { width: 100% !important; min-width: 0; }

/* Label / amount pairs down the totals rail. A grid rather than a flex row so
   every input lines up regardless of how long its label runs. */
.erp-grn-adjust {
    align-items: center;
    display: grid;
    grid-template-columns: 1fr 108px;
    gap: 6px 10px;
}

.erp-grn-adjust .form-label { font-size: 12px; color: var(--erp-muted); }

.erp-grn-due {
    align-items: center;
    border-top: 1px solid var(--erp-line);
    color: var(--erp-danger, #b91c1c);
    display: flex;
    font-weight: 600;
    justify-content: space-between;
    margin-top: 8px;
    padding-top: 8px;
}

/* The line table scrolls inside its card, so the header has to stay put or the
   columns stop meaning anything halfway down a long delivery. */
.erp-grn-table thead th {
    background: var(--erp-surface-2);
    position: sticky;
    top: 0;
    z-index: 1;
}
.erp-req-toggle .form-check-input { cursor: pointer; }
.erp-req-toggle .form-check-input:checked { background-color: var(--erp-primary, #2f6bff); border-color: var(--erp-primary, #2f6bff); }
.erp-req-toggle [data-requisition-ref] { min-width: 240px; }
.erp-divider { border: 0; border-top: 1px solid var(--erp-line); opacity: 1; }

/* Purchase order — compact header card. */
.erp-block-head-sm { padding-bottom: 2px; }
.erp-block-head-sm h3 { font-size: 12px; }
.erp-block-head-sm i { font-size: 12px; }
.erp-builder-head { padding: 10px 12px; }
.erp-builder-head .row { --bs-gutter-y: 6px; }
.erp-builder-head .form-label { font-size: 11px; margin-bottom: 1px; color: var(--erp-muted); }
.erp-builder-head .erp-field { margin-bottom: 0; }
.erp-builder-head .form-control-sm,
.erp-builder-head .form-select-sm { min-height: 30px; height: 30px; padding: 2px 8px; font-size: 12.5px; }
.erp-builder-head .select2-container .select2-selection--single { height: 30px; }
.erp-builder-head .select2-container--default .select2-selection--single .select2-selection__rendered { line-height: 28px; font-size: 12.5px; }
.erp-builder-head .select2-container--default .select2-selection--single .select2-selection__arrow { height: 28px; }
.erp-builder-head hr.erp-divider { margin-top: 8px; margin-bottom: 8px; }

/* Purchase builder — fits the viewport; only the item list scrolls. */
/* Height comes from --erp-fill-h, which erp-app.js measures from where this
   pane actually starts (see fillViewport). The fallback still has to guess,
   but it guesses from the measured top bar rather than one frozen number, so
   the worst case is a few pixels out for one frame instead of a second
   scrollbar on every wrapped-navbar or long-breadcrumb layout. */
.erp-builder {
    align-items: stretch;
    display: flex;
    gap: 12px;
    height: var(--erp-fill-h, calc(100dvh - var(--erp-topbar-h, 96px) - 118px));
    min-height: 460px;
}
.erp-builder-main { flex: 1 1 auto; display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.erp-builder-head { flex: 0 1 auto; overflow-y: auto; max-height: 52%; }
.erp-builder-items { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
.erp-builder-items .erp-table-wrap { flex: 1 1 auto; overflow: auto; min-height: 120px; }
.erp-builder-items thead th { position: sticky; top: 0; z-index: 1; background-color: var(--erp-table-head-solid); }
.erp-builder-side { flex: 0 0 360px; display: flex; flex-direction: column; overflow: hidden; }
.erp-builder-side-scroll { flex: 1 1 auto; overflow-y: auto; min-height: 0; }
.erp-builder-side-actions { flex: 0 0 auto; padding-top: 10px; margin-top: 2px; border-top: 1px solid var(--erp-line); }
.erp-builder-side .erp-card:last-child { margin-bottom: 0; }
/* 991.98, not 992: erp-app.js hands the sizing over at min-width 992, and an
   inclusive 992 here left both rules live on a viewport exactly that wide. */
@media (max-width: 991.98px) {
    .erp-builder { flex-direction: column; height: auto; min-height: 0; }
    .erp-builder-side { flex-basis: auto; overflow: visible; }
    .erp-builder-head { max-height: none; }
    .erp-builder-side-scroll { overflow: visible; }
    .erp-builder-items .erp-table-wrap { max-height: 46vh; }
}

/* Purchase index — compact date range at the front of the filter bar. */
.erp-daterange { display: inline-flex; align-items: center; gap: 4px; }
.erp-daterange .erp-select { width: auto; }
.erp-daterange span { color: var(--erp-muted); }
/* A button sharing the range — a clear control, usually. Flex items shrink by
   default, and a button with only an icon in it shrinks below its own padding
   and renders as a sliver. */
.erp-daterange .btn { flex: 0 0 auto; }

/* ============================================================
 * Financial statements — Balance Sheet & Cash Flow
 * ============================================================ */
.bs-report-head h2, .cf-report h2 { font-weight: 700; }
.bs-panel { border: 1px solid var(--erp-line); border-radius: 12px; overflow: hidden; }
.bs-panel-head { background: var(--erp-bg, #f7f9fc); padding: 10px 14px; font-weight: 700; font-size: 13px; border-bottom: 1px solid var(--erp-line); }
.bs-table td, .cf-table td { padding: 6px 12px; font-size: 13px; vertical-align: middle; }
.bs-table tbody tr:not(.bs-row-group):hover { background: var(--erp-bg, #f7f9fc); }
.bs-code { display: inline-block; min-width: 46px; font-size: 11.5px; }
.bs-row-group > td { background: rgba(47, 107, 255, 0.03); }
.bs-row-top > td { font-weight: 600; }
.bs-subtotal td { border-top: 1px solid var(--erp-line); font-weight: 600; }
.bs-total td { border-top: 2px solid var(--erp-ink, #1f2733); border-bottom: 2px solid var(--erp-ink, #1f2733); font-weight: 700; font-size: 14px; }

.cf-sheet { max-width: 640px; }
.cf-section td { background: var(--erp-bg, #f7f9fc); font-weight: 700; font-size: 13px; border-top: 1px solid var(--erp-line); }
.cf-subtotal td { border-top: 1px solid var(--erp-line); font-weight: 600; }
.cf-net td { border-top: 2px solid var(--erp-ink, #1f2733); font-weight: 700; font-size: 14px; }
.cf-close td { border-top: 1px solid var(--erp-line); border-bottom: 2px solid var(--erp-ink, #1f2733); font-weight: 700; }

/* Requisition — fulfilment progress bar */
.erp-progress { height: 7px; border-radius: 6px; background: var(--erp-line); overflow: hidden; min-width: 90px; }
.erp-progress-bar { height: 100%; border-radius: 6px; background: linear-gradient(90deg, #2f6bff, #16a34a); transition: width .3s ease; }

@media print {
    .erp-sidebar, .erp-topbar, .erp-submenubar, .erp-pagehead-actions, .erp-toolbar, .app-sidebar, nav.breadcrumb, .breadcrumb { display: none !important; }
    .erp-card { border: none !important; box-shadow: none !important; }
    .bs-panel { break-inside: avoid; }
}


/* ============================================================ *
 * RFQ / Quotations
 * ============================================================ */
.erp-steps { display: flex; flex-wrap: wrap; gap: 8px; }
.erp-step { display: inline-flex; align-items: center; gap: 8px; padding: 7px 14px; border: 1px solid var(--erp-line); border-radius: 999px; font-size: 12.5px; color: var(--erp-muted); background: var(--erp-surface, #fff); }
.erp-step-dot { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; border-radius: 50%; background: var(--erp-line); color: #fff; font-size: 11px; font-weight: 700; }
.erp-step.is-done { color: var(--erp-text, #111); border-color: var(--erp-primary, #0f766e); }
.erp-step.is-done .erp-step-dot { background: var(--erp-primary, #0f766e); }
.erp-compare th, .erp-compare td { white-space: nowrap; }
.erp-compare .erp-best { background: rgba(22, 163, 74, .12); font-weight: 700; color: #15803d; }
.erp-best-key { display: inline-block; width: 12px; height: 12px; border-radius: 3px; background: rgba(22, 163, 74, .3); vertical-align: middle; margin-right: 4px; }
.erp-row-off { opacity: .5; }

/* ============================================================ *
 * Invoice template designer
 * ============================================================ */
.erp-code { width: 100%; font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; font-size: 12.5px; line-height: 1.5; border: 1px solid var(--erp-line); border-radius: 10px; padding: 12px; background: #0f172a; color: #e2e8f0; resize: vertical; tab-size: 2; }
.erp-code:focus { outline: none; border-color: var(--erp-primary, #0f766e); box-shadow: 0 0 0 2px var(--erp-tint, rgba(15,118,110,.15)); }
.erp-token-group { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; margin-bottom: 6px; }
.erp-token-group-label { font-size: 11px; font-weight: 700; color: var(--erp-muted); text-transform: uppercase; letter-spacing: .03em; min-width: 88px; }
.erp-token { border: 1px solid var(--erp-line); background: var(--erp-surface, #fff); border-radius: 7px; padding: 3px 8px; font-size: 11.5px; font-family: monospace; cursor: pointer; color: var(--erp-primary, #0f766e); }
.erp-token:hover { background: var(--erp-tint, rgba(15,118,110,.08)); border-color: var(--erp-primary, #0f766e); }
.erp-preview-frame { width: 100%; height: 62vh; border: 1px solid var(--erp-line); border-radius: 10px; background: #fff; }

/* Invoice designer — drag & drop block builder */
.erp-palette { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.erp-palette-item { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--erp-line); background: var(--erp-surface, #fff); border-radius: 8px; padding: 6px 10px; font-size: 12.5px; cursor: grab; }
.erp-palette-item:hover { border-color: var(--erp-primary, #0f766e); color: var(--erp-primary, #0f766e); }
.erp-palette-item i { color: var(--erp-muted); }
.erp-canvas { display: flex; flex-direction: column; gap: 8px; min-height: 120px; border: 1px dashed var(--erp-line); border-radius: 10px; padding: 8px; }
.erp-canvas-empty { color: var(--erp-muted); font-size: 13px; text-align: center; padding: 32px 8px; }
.erp-block-card { border: 1px solid var(--erp-line); border-radius: 10px; background: var(--erp-surface, #fff); }
.erp-block-card.is-open { border-color: var(--erp-primary, #0f766e); box-shadow: 0 0 0 2px var(--erp-tint, rgba(15,118,110,.1)); }
.erp-block-card.is-dragging { opacity: .5; }
.erp-block-head { display: flex; align-items: center; gap: 8px; padding: 9px 10px; cursor: pointer; font-size: 13px; }
.erp-block-grip { color: var(--erp-muted); cursor: grab; }
.erp-block-title { font-weight: 600; }
.erp-block-summary { color: var(--erp-muted); font-size: 12px; margin-left: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.erp-block-tools { display: inline-flex; gap: 2px; }
.erp-block-tools button { border: none; background: transparent; color: var(--erp-muted); cursor: pointer; padding: 2px 5px; border-radius: 6px; }
.erp-block-tools button:hover { background: var(--erp-tint, rgba(15,118,110,.1)); }
.erp-block-editor { border-top: 1px solid var(--erp-line); padding: 10px; display: flex; flex-direction: column; gap: 6px; }
.erp-row-edit { display: flex; align-items: center; gap: 6px; }
.erp-row-edit-label { font-size: 12px; color: var(--erp-muted); min-width: 62px; }
.erp-row-edit .form-control, .erp-row-edit .form-select { flex: 1; }

/* ==========================================================================
   Land Acquisition
   ========================================================================== */

/* The pipeline strip on the parcel list — a stage counter that is also a
   filter link, so the list can be driven from the shape of the pipeline. */
/* The acquisition pipeline, as a colour progression rather than ten identical
   grey boxes.

   The hue comes from LandStage::tone() and runs cold at the scouting end, warms
   through the money stages and lands on green once the title is ours. That
   ordering is the point: somebody glancing at this strip should know roughly
   where the portfolio is sitting before they have read a label.

   Each step also carries a --erp-tone pair rather than hard-coded colours, so
   the whole rail restyles from the palette block and a new stage needs one line
   in the enum and nothing here. */
.erp-stage-rail {
    display: flex;
    gap: 8px;
    overflow-x: auto;
    padding-bottom: 4px;
}

.erp-stage-step {
    display: flex;
    align-items: center;
    gap: 9px;
    flex: 0 0 auto;
    padding: 8px 13px 8px 10px;
    border: 1px solid var(--erp-tone-line, var(--erp-line));
    border-radius: 10px;
    text-decoration: none;
    color: var(--erp-ink);
    background: var(--erp-surface, #fff);
    /* A hairline of the tone down the leading edge. Inset rather than a real
       border so switching tone never moves the text by a pixel. */
    box-shadow: inset 3px 0 0 var(--erp-tone, var(--erp-muted));
    transition: border-color .15s ease, background .15s ease, box-shadow .15s ease;
}

.erp-stage-step:hover {
    background: var(--erp-tone-soft, rgba(138, 148, 166, .12));
    border-color: var(--erp-tone, var(--erp-muted));
    color: var(--erp-ink);
}

.erp-stage-step.is-active {
    background: var(--erp-tone-soft, rgba(138, 148, 166, .12));
    border-color: var(--erp-tone, var(--erp-muted));
    box-shadow: inset 3px 0 0 var(--erp-tone, var(--erp-muted)), 0 1px 3px rgba(16, 30, 54, .08);
}

/* The icon sits in a tinted chip, which is what carries most of the colour.
   Doing it on the glyph alone is not enough to read at a glance across a strip
   that scrolls. */
.erp-stage-icon {
    align-items: center;
    background: var(--erp-tone-soft, rgba(138, 148, 166, .12));
    border-radius: 8px;
    color: var(--erp-tone, var(--erp-muted));
    display: inline-flex;
    flex: 0 0 auto;
    font-size: 13px;
    height: 30px;
    justify-content: center;
    width: 30px;
}

.erp-stage-step.is-active .erp-stage-icon {
    background: var(--erp-tone, var(--erp-muted));
    color: #fff;
}

.erp-stage-body { display: flex; flex-direction: column; line-height: 1.2; }

/* The count is the number somebody came here to read, so it takes the tone. */
.erp-stage-body strong {
    color: var(--erp-tone, var(--erp-muted));
    font-size: 16px;
    font-weight: 700;
}

.erp-stage-body small { font-size: 11px; color: var(--erp-muted); white-space: nowrap; }

/* A stage with nothing in it keeps its hue but stops competing.

   Draining an empty step to grey would be easy, and on a pipeline where most
   stages are empty -- the normal state of a land portfolio -- it turns the whole
   strip grey and throws away the one thing the strip is for: the shape of the
   pipeline at a glance, including the stages nothing has reached yet. So the
   colour stays and only the EMPHASIS drops: no leading bar, and the count in
   grey because a zero is not a figure anybody needs drawn to. A stage holding
   parcels still reads louder than one that does not. */
.erp-stage-step.is-empty {
    box-shadow: none;
    opacity: .78;
}

.erp-stage-step.is-empty .erp-stage-body strong { color: var(--erp-muted); font-weight: 600; }

.erp-stage-step.is-empty:hover { opacity: 1; }

/* The hues come from the shared tone palette near the top of this file. The
   rail simply wears .is-tone-<name>, which LandStage::tone() supplies. */
:root[data-theme="dark"] .erp-stage-step { background: var(--erp-surface, #1b2430); }
:root[data-theme="dark"] .erp-stage-step.is-active .erp-stage-icon { color: #10202c; }

/* The legal due-diligence checklist. */
.erp-checklist { display: flex; flex-direction: column; gap: 10px; }
.erp-checklist-item {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 12px;
    border: 1px solid var(--erp-line);
    border-radius: 10px;
}
.erp-checklist-icon {
    flex: 0 0 34px;
    height: 34px;
    display: grid;
    place-items: center;
    border-radius: 8px;
    background: var(--erp-line);
    color: var(--erp-muted);
}
.erp-checklist-body { flex: 1 1 auto; min-width: 0; }
.erp-checklist-body p { margin-bottom: 4px; }
.erp-checklist-meta { display: flex; flex-wrap: wrap; gap: 10px; color: var(--erp-muted); }
.erp-checklist-action { flex: 0 0 auto; }

@media (max-width: 640px) {
    .erp-checklist-item { flex-wrap: wrap; }
    .erp-checklist-action { width: 100%; }
}

/* A sub-heading inside a card body, for a second table under the first. */
.erp-subhead {
    font-size: 13px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--erp-muted);
    margin-bottom: 10px;
}

/* Marks the row that is currently authoritative — the final valuation. */
.erp-row-accent { background: rgba(0, 194, 146, .06); }

/* --------------------------------------------------------------------------
   12b. Sign-in screen — brand palette
   --------------------------------------------------------------------------
   The shipped theme paints this screen in the stock green. These tokens are
   sampled from the company's own logo so the sign-in page and the mark on it
   belong to each other.

   Contrast was checked rather than eyeballed — white body text needs 4.5:1:
     deep   #1c41ad  8.72:1
     core   #2459d2  6.11:1
     bright #2d6ee0  4.75:1
   The logo's lightest tone, sky #49a4ed, only reaches 2.69:1, so it never
   carries text: it appears as a corner glow and as the plate's ring.

   Scoped to .erp-auth so the rest of the application keeps its own accent.
   -------------------------------------------------------------------------- */

.erp-auth {
    --auth-deep: #1c41ad;
    --auth-core: #2459d2;
    --auth-bright: #2d6ee0;
    --auth-sky: #49a4ed;

    /* Re-pointed for this screen only, so anything reading the app accent —
       including inline var(--erp-primary) — follows the brand here too. */
    --erp-primary: var(--auth-core);
    --erp-primary-dark: var(--auth-deep);
}

.erp-auth .nk-auth-brand {
    background:
        radial-gradient(circle at 100% 0%, rgba(73, 164, 237, .45) 0%, transparent 42%),
        linear-gradient(150deg, var(--auth-deep) 0%, var(--auth-core) 55%, var(--auth-bright) 100%);
}

/* Form side ---------------------------------------------------------------- */

.erp-auth .nk-auth-btn {
    background: var(--auth-core);
    border-color: var(--auth-core);
}

.erp-auth .nk-auth-btn:focus,
.erp-auth .nk-auth-btn:hover {
    background: var(--auth-deep);
    border-color: var(--auth-deep);
    box-shadow: 0 4px 12px rgba(36, 89, 210, .35);
}

.erp-auth .nk-auth-btn:active {
    background: #17368f;
    border-color: #17368f;
}

.erp-auth .nk-auth-link,
.erp-auth .nk-auth-footer a {
    color: var(--auth-core);
}

.erp-auth .nk-auth-link:hover,
.erp-auth .nk-auth-footer a:hover {
    color: var(--auth-deep);
}

.erp-auth .nk-auth-form .form-control:focus {
    border-color: var(--auth-core);
    box-shadow: 0 0 0 .2rem rgba(36, 89, 210, .15);
}

.erp-auth .nk-auth-form .input-group:focus-within .input-group-text {
    border-color: var(--auth-core);
    color: var(--auth-core);
}

.erp-auth .nk-auth-form .form-check-input:checked {
    background-color: var(--auth-core);
    border-color: var(--auth-core);
}

.erp-auth .nk-auth-form .form-check-input:focus {
    border-color: var(--auth-core);
    box-shadow: 0 0 0 .2rem rgba(36, 89, 210, .15);
}

.erp-auth .nk-toggle-password:hover {
    color: var(--auth-core);
}

/* ==========================================================================
   Apartment Sales & Money Receipts
   ========================================================================== */

/* The customer's position, shown the moment they are chosen on the payment
   screen — the three figures a collections clerk actually acts on. */
.erp-customer-snapshot {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 14px;
    padding: 12px 14px;
    background: var(--erp-line);
    border-radius: 10px;
}
.erp-snap-item { flex: 1 1 140px; }
.erp-snap-label {
    display: block;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--erp-muted);
}
.erp-snap-value { font-size: 17px; font-weight: 600; }

/* One sale's outstanding lines inside the dues list. */
.erp-due-group {
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    margin-bottom: 12px;
    overflow: hidden;
}
.erp-due-group:last-child { margin-bottom: 0; }
.erp-due-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    background: var(--erp-line);
}
.erp-due-head strong { font-size: 13.5px; }

/* A single tender on a receipt. */
.erp-tender {
    border-bottom: 1px solid var(--erp-line);
    padding: 10px var(--erp-gutter);
}
.erp-tender:last-child { border-bottom: 0; }
.erp-tender-head { display: flex; gap: 8px; align-items: center; }
.erp-tender-head .form-control { flex: 1; }
.erp-tender-body {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 8px;
    margin-top: 8px;
}

/* The running receipt arithmetic. */
.erp-receipt-totals {
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    overflow: hidden;
}
.erp-receipt-line {
    display: flex;
    justify-content: space-between;
    padding: 8px 12px;
    font-size: 13px;
    border-bottom: 1px solid var(--erp-line);
}
.erp-receipt-line:last-child { border-bottom: 0; }
.erp-receipt-line span { color: var(--erp-muted); }
.erp-receipt-line.is-total {
    background: var(--erp-line);
    font-size: 15px;
}
.erp-receipt-line.is-total strong { font-weight: 700; }

/* Keeps the tender panel in view while the dues list is scrolled. */
@media (min-width: 992px) {
    .erp-sticky-panel { position: sticky; top: 90px; }
}

/* --------------------------------------------------------------------------
   Building unit mix — status bar, legend and per-floor chips on the
   building overview. Colours reuse the shared status palette so a unit
   reads the same here as it does in the floors table.
   -------------------------------------------------------------------------- */

.erp-unit-mix-bar {
    background: var(--erp-line);
    border-radius: 20px;
    display: flex;
    gap: 2px;
    height: 10px;
    margin-bottom: 12px;
    overflow: hidden;
    width: 100%;
}

.erp-unit-mix-bar .erp-progress-bar {
    border-radius: 0;
    min-width: 3px;
}

.erp-unit-mix-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 7px;
}

.erp-unit-mix-legend .erp-pill {
    text-decoration: none;
    transition: transform .15s ease, filter .15s ease;
}

.erp-unit-mix-legend .erp-pill:hover {
    filter: brightness(.96);
    transform: translateY(-1px);
}

.erp-unit-mix-legend .erp-pill strong {
    font-weight: 700;
}

.erp-floor-chips {
    border-top: 1px dashed var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 12px;
    padding-top: 12px;
}

.erp-floor-chip {
    align-items: center;
    background: var(--erp-surface-2);
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    color: var(--erp-ink);
    display: inline-flex;
    font-size: 11.5px;
    font-weight: 600;
    gap: 6px;
    padding: 5px 9px;
    text-decoration: none;
    transition: border-color .15s ease, transform .15s ease;
}

.erp-floor-chip:hover {
    border-color: var(--erp-primary);
    color: var(--erp-ink);
    transform: translateY(-1px);
}

.erp-floor-chip i {
    color: var(--erp-muted);
    font-size: 11px;
}

.erp-floor-chip strong {
    background: var(--erp-primary);
    border-radius: 20px;
    color: #fff;
    font-size: 10.5px;
    line-height: 1.5;
    min-width: 20px;
    padding: 0 6px;
    text-align: center;
}

/* A floor with nothing on it yet should not wear a filled badge. */
.erp-floor-chip strong:empty,
.erp-floor-chip.is-empty strong {
    background: var(--erp-line);
    color: var(--erp-muted);
}

/* Compact icon+number badge for count columns in list tables. */
.erp-count-badge {
    align-items: center;
    border-radius: 20px;
    display: inline-flex;
    font-size: 11.5px;
    font-weight: 700;
    gap: 5px;
    justify-content: center;
    min-width: 52px;
    padding: 4px 10px;
}

.erp-count-badge i { font-size: 10.5px; opacity: .85; }

.erp-count-badge.is-info { background: rgba(3, 169, 243, .12); color: #0281bb; }
.erp-count-badge.is-purple { background: rgba(108, 99, 255, .12); color: #574fd6; }
.erp-count-badge.is-zero { background: var(--erp-surface-2); color: var(--erp-muted); }

/* --------------------------------------------------------------------------
   Workspace layout — a full-height, two-column working screen for the
   till-style forms (money receipts). The page itself does not scroll: each
   column owns its own scrolling so the running totals and the submit button
   stay put while a long dues list is worked through.

   Desktop only. Below lg there is not enough height to be worth pinning, and
   a phone user expects to scroll, so the whole thing falls back to normal
   document flow.
   -------------------------------------------------------------------------- */

.erp-ws {
    display: flex;
    flex-direction: column;
    gap: var(--erp-gutter);
}

.erp-ws-col {
    display: flex;
    flex-direction: column;
    gap: var(--erp-gutter);
    /* Without this a flex child refuses to shrink below its content, and the
       inner overflow-y never engages. */
    min-height: 0;
}

.erp-ws-card {
    display: flex;
    flex-direction: column;
    /* .erp-card sets height:100%, which would stretch every card in the
       column and defeat the flex sizing below. */
    height: auto;
    margin-bottom: 0;
    min-height: 0;
    overflow: hidden;
}

.erp-ws-card.is-fill { flex: 1 1 auto; }
.erp-ws-card.is-static { flex: 0 0 auto; }

/* Compact card header: one line, no descriptive paragraph. On a working
   screen the label and the controls earn their space; prose does not. */
.erp-ws-head {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    flex: 0 0 auto;
    gap: 10px;
    padding: 10px 14px;
}

.erp-ws-head h2 {
    font-size: 14px;
    font-weight: 600;
    letter-spacing: -.2px;
    margin: 0;
}

.erp-ws-head-icon {
    align-items: center;
    background: rgba(0, 194, 146, .12);
    border-radius: 9px;
    color: var(--erp-primary);
    display: flex;
    flex: 0 0 auto;
    font-size: 13px;
    height: 30px;
    justify-content: center;
    width: 30px;
}

.erp-ws-head-hint {
    color: var(--erp-muted);
    font-size: 11.5px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-ws-head-actions {
    display: flex;
    flex: 0 0 auto;
    gap: 6px;
    margin-left: auto;
}

/* The scrolling region inside a workspace card. */
.erp-ws-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 14px;
}

.erp-ws-scroll.is-flush { padding: 0; }

.erp-ws-foot {
    border-top: 1px solid var(--erp-line);
    flex: 0 0 auto;
    padding: 12px 14px;
}

/* A row of fields that stays on one line on desktop. */
.erp-ws-row {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    padding: 14px 14px 0;
}

.erp-ws-foot .erp-ws-row { padding: 0; }

.erp-ws-row .erp-field { margin-bottom: 14px; }
.erp-ws-foot .erp-ws-row .erp-field { margin-bottom: 10px; }

.erp-ws-grow { flex: 1 1 180px; min-width: 0; }
.erp-ws-fixed { flex: 0 1 150px; min-width: 0; }

/* Snapshot sits flush inside the compact customer card. */
.erp-ws-card .erp-customer-snapshot {
    border-radius: 0;
    margin-top: 0;
    padding: 10px 14px;
}

.erp-ws-card .erp-snap-value { font-size: 15px; }

@media (min-width: 992px) {
    .erp-ws {
        flex-direction: row;
        /* Fills what is left of the viewport under the page chrome. The offset
           is measured at runtime because the module bar wraps to two lines on
           narrower desktops; the fallback covers a stylesheet loaded without
           the script. */
        height: calc(100dvh - var(--erp-ws-offset, 230px));
        /* A floor so the panels stay usable; on a viewport too short to give
           even this, letting the page scroll beats crushing the form. */
        min-height: 360px;
    }

    /* Read screens have no inputs to squeeze, so they tolerate a shorter panel
       than a data-entry desk and can still fit a 720px-tall laptop. */
    .erp-ws.is-compact {
        min-height: 280px;
    }

    .erp-ws-main { flex: 1 1 58%; min-width: 0; }
    .erp-ws-side { flex: 0 0 40%; max-width: 460px; min-width: 340px; }
}

/* Tenders sit tighter inside the pinned panel than they did in a long page. */
.erp-ws .erp-tender { padding: 9px 14px; }
.erp-ws .erp-receipt-line { padding: 6px 12px; }
.erp-ws .erp-due-group { margin-bottom: 10px; }
.erp-ws .erp-due-head { padding: 8px 12px; }

/* ==========================================================================
   Organisation chart
   ==========================================================================
   Two layouts over ONE set of nested ul/li markup, so the template never has
   to know which is on screen:

     .is-crow  the crow's-foot chart — parent on top, a stem dropping to a
               horizontal rail, and a riser up into each child
     .is-list  a compact indented tree, which stays readable when the org is
               too wide or too deep to chart comfortably

   The connectors are pseudo-elements rather than SVG: they reflow for free
   when a branch is collapsed, and they print.
   ========================================================================== */

.erp-tree,
.erp-tree-children {
    list-style: none;
    margin: 0;
    padding: 0;
}

.erp-tree-children.is-collapsed { display: none; }

/* The pannable, zoomable surface the chart is drawn on. */
.erp-chart-viewport {
    overflow: auto;
    padding: 8px 0 24px;
    position: relative;
}

.erp-chart-viewport.is-pannable { cursor: grab; }
.erp-chart-viewport.is-grabbing { cursor: grabbing; user-select: none; }

.erp-chart-canvas {
    transform: scale(var(--chart-zoom, 1));
    transform-origin: top center;
    transition: transform .12s ease-out;
    width: max-content;
    min-width: 100%;
}

/* --------------------------------------------------------------------------
   Crow's foot
   -------------------------------------------------------------------------- */

.erp-tree.is-crow,
.erp-tree.is-crow .erp-tree-children {
    display: flex;
    justify-content: center;
    position: relative;
}

.erp-tree.is-crow .erp-tree-children { padding-top: 26px; }

.erp-tree.is-crow .erp-tree-item {
    position: relative;
    padding: 26px 10px 0;
    text-align: center;
}

.erp-tree.is-crow > .erp-tree-item { padding-top: 0; }

/* The rail across a row of siblings: each child draws half of it, left and
   right, so the rail always spans exactly the children present. */
.erp-tree.is-crow .erp-tree-item::before,
.erp-tree.is-crow .erp-tree-item::after {
    content: "";
    position: absolute;
    top: 0;
    width: 50%;
    height: 26px;
    border-top: 2px solid var(--erp-line);
}

.erp-tree.is-crow .erp-tree-item::before { right: 50%; }
.erp-tree.is-crow .erp-tree-item::after { left: 50%; border-left: 2px solid var(--erp-line); }

/* Outer corners round off, and the rail must not overshoot the end children —
   otherwise it hangs in the air past the last box. */
.erp-tree.is-crow .erp-tree-item:first-child::before { border: 0 none; }
.erp-tree.is-crow .erp-tree-item:last-child::after { border: 0 none; }

.erp-tree.is-crow .erp-tree-item:last-child::before {
    border-right: 2px solid var(--erp-line);
    border-radius: 0 6px 0 0;
}

.erp-tree.is-crow .erp-tree-item:first-child::after { border-radius: 6px 0 0 0; }

/* An only child needs no rail — just the stem straight down. */
.erp-tree.is-crow .erp-tree-item:only-child::before,
.erp-tree.is-crow .erp-tree-item:only-child::after { display: none; }
.erp-tree.is-crow .erp-tree-item:only-child { padding-top: 26px; }

/* The stem leaving the bottom of a parent. */
.erp-tree.is-crow .erp-tree-children::before {
    content: "";
    position: absolute;
    top: 0;
    left: 50%;
    width: 0;
    height: 26px;
    border-left: 2px solid var(--erp-line);
}

/* Roots sit side by side; there is nothing above them to connect to. */
.erp-tree.is-crow > .erp-tree-item::before,
.erp-tree.is-crow > .erp-tree-item::after { display: none; }

.erp-tree.is-crow .erp-tree-card {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    width: 186px;
    padding: 14px 12px 16px;
    gap: 6px;
}

.erp-tree.is-crow .erp-tree-avatar { width: 46px; height: 46px; flex: 0 0 46px; font-size: 15px; }
.erp-tree.is-crow .erp-tree-body { width: 100%; }
.erp-tree.is-crow .erp-tree-tags { justify-content: center; margin-top: 2px; }

/* The toggle hangs off the bottom edge, exactly where the stem leaves. */
.erp-tree.is-crow .erp-tree-toggle {
    position: absolute;
    bottom: -11px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 2;
}

/* A collapsed parent says how many people are folded away beneath it. */
.erp-tree.is-crow .erp-tree-item.is-closed > .erp-tree-card::after {
    content: attr(data-hidden);
    position: absolute;
    bottom: -30px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 10.5px;
    color: var(--erp-muted);
    white-space: nowrap;
}

/* --------------------------------------------------------------------------
   Indented list
   -------------------------------------------------------------------------- */

.erp-tree.is-list .erp-tree-children {
    margin-left: 22px;
    padding-left: 18px;
    position: relative;
}

.erp-tree.is-list .erp-tree-children::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    bottom: 22px;
    width: 1px;
    background: var(--erp-line);
}

.erp-tree.is-list .erp-tree-item { position: relative; }

.erp-tree.is-list .erp-tree-children > .erp-tree-item::before {
    content: "";
    position: absolute;
    left: -18px;
    top: 22px;
    width: 18px;
    height: 1px;
    background: var(--erp-line);
}

.erp-tree.is-list .erp-tree-card {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
}

.erp-tree.is-list .erp-tree-body { flex: 1 1 auto; min-width: 0; text-align: left; }
.erp-tree.is-list .erp-tree-role,
.erp-tree.is-list .erp-tree-dept { display: inline; }
.erp-tree.is-list .erp-tree-dept::before { content: " · "; }
.erp-tree.is-list .erp-tree-toggle { position: static; transform: none; order: -1; }

/* --------------------------------------------------------------------------
   The card itself — shared by both layouts
   -------------------------------------------------------------------------- */

.erp-tree-card {
    position: relative;
    margin: 4px 0;
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    background: var(--erp-surface, #fff);
    transition: border-color .15s ease, box-shadow .15s ease;
}

.erp-tree-card:hover {
    border-color: var(--erp-primary);
    box-shadow: 0 2px 12px rgba(20, 35, 60, .1);
}

.erp-tree-card.is-focus {
    border-color: var(--erp-primary);
    box-shadow: 0 0 0 3px rgba(0, 194, 146, .18);
}

.erp-tree-card.is-note { border-style: dashed; }

.erp-tree-item.is-dimmed > .erp-tree-card { opacity: .55; }

.erp-tree-item.is-match > .erp-tree-card {
    border-color: var(--erp-primary);
    background: rgba(0, 194, 146, .07);
}

.erp-tree-avatar {
    flex: 0 0 34px;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    overflow: hidden;
    display: grid;
    place-items: center;
    background: var(--erp-line);
    color: var(--erp-muted);
    font-size: 12px;
    font-weight: 600;
}

.erp-tree-avatar img { width: 100%; height: 100%; object-fit: cover; }

.erp-tree-body { min-width: 0; }

.erp-tree-name {
    display: block;
    font-size: 13px;
    font-weight: 600;
    color: var(--erp-ink);
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.erp-tree-name:hover { color: var(--erp-primary); }

.erp-tree-role,
.erp-tree-dept {
    display: block;
    font-size: 11px;
    color: var(--erp-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.erp-tree-tags {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 0 0 auto;
}

.erp-tree-count {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    color: var(--erp-muted);
    background: var(--erp-line);
    border-radius: 20px;
    padding: 2px 8px;
}

.erp-tree-flag { font-size: 11px; }
.erp-tree-flag.is-warn { color: #b45309; }
.erp-tree-flag.is-bad { color: #b91c1c; }

.erp-tree-toggle {
    width: 22px;
    height: 22px;
    display: grid;
    place-items: center;
    border: 1px solid var(--erp-line);
    border-radius: 50%;
    background: var(--erp-surface, #fff);
    color: var(--erp-muted);
    cursor: pointer;
    font-size: 9px;
    padding: 0;
}

.erp-tree-toggle:hover { border-color: var(--erp-primary); color: var(--erp-primary); }

.erp-tree-focus {
    color: var(--erp-muted);
    text-decoration: none;
    padding: 2px 5px;
    border-radius: 6px;
    font-size: 11px;
}

.erp-tree-focus:hover { color: var(--erp-primary); background: var(--erp-line); }

/* Zoom control in the toolbar. */
.erp-chart-zoom {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    overflow: hidden;
}

.erp-chart-zoom button {
    border: 0;
    background: none;
    color: var(--erp-muted);
    padding: 6px 10px;
    cursor: pointer;
    font-size: 12px;
}

.erp-chart-zoom button:hover { background: var(--erp-line); color: var(--erp-ink); }
.erp-chart-zoom span { font-size: 12px; color: var(--erp-muted); min-width: 46px; text-align: center; }

@media (max-width: 640px) {
    .erp-tree.is-list .erp-tree-children { margin-left: 10px; padding-left: 12px; }
    .erp-tree.is-list .erp-tree-children > .erp-tree-item::before { left: -12px; width: 12px; }
}

@media print {
    .erp-tree-children.is-collapsed { display: block; }   /* print the whole org */
    .erp-tree-toggle,
    .erp-tree-focus { display: none; }
    .erp-tree-card { break-inside: avoid; }
    .erp-chart-canvas { transform: none; }
    .erp-chart-viewport { overflow: visible; }
}

/* Glass tabs on a dark surface: the label is lightened toward white rather
   than darkened toward ink, at a ratio measured the same way (worst case
   5.18:1). The white sheen is dropped — on dark glass it reads as haze. */
:root[data-theme="dark"] .erp-tabs.is-glass .erp-tab {
    color: color-mix(in srgb, #ffffff 45%, rgb(var(--tab)));
    border-color: rgb(var(--tab) / .38);
    box-shadow: inset 0 1px 0 rgb(255 255 255 / .08);
}

:root[data-theme="dark"] .erp-tabs.is-glass .erp-tab::after {
    background: linear-gradient(180deg, rgb(255 255 255 / .07), rgb(255 255 255 / 0));
}

:root[data-theme="dark"] .erp-tabs.is-glass .erp-tab.is-active,
:root[data-theme="dark"] .erp-tabs.is-glass .erp-tab.active {
    border-color: rgb(var(--tab) / .6);
    box-shadow:
        inset 0 1px 0 rgb(255 255 255 / .12),
        0 0 0 3px rgb(var(--tab) / .16),
        0 6px 18px rgb(0 0 0 / .35);
}

/* ============================================================================
   POS — discount policies
   ========================================================================= */

/* Field-level validation messages. The form puts each message against its own
   input rather than stacking them all in one alert, because "which box is
   wrong" is the only question an error is asked. */
.erp-error {
    color: var(--erp-danger);
    display: block;
    font-size: 11px;
    line-height: 1.45;
    margin-top: 4px;
    min-height: 0;
}

.erp-error:empty { display: none; }

/* A hint that has turned into a caution — a date window that runs backwards,
   say. Same slot, different weight, so nothing jumps as it changes. */
.erp-help.is-warning {
    color: var(--erp-danger);
    font-weight: 600;
}

/* A unit that belongs to the input rather than beside it: "10 %" and
   "10 BDT" are different rules, and the suffix is what says which. */
.erp-input-suffix {
    align-items: center;
    display: flex;
    gap: 0;
    position: relative;
}

.erp-input-suffix .form-control {
    border-bottom-right-radius: 0;
    border-right: 0;
    border-top-right-radius: 0;
}

.erp-input-suffix > span {
    background: #f7f9fc;
    border: 1px solid var(--erp-line);
    border-bottom-right-radius: 9px;
    border-top-right-radius: 9px;
    color: var(--erp-muted);
    flex: 0 0 auto;
    font-size: 11.5px;
    font-weight: 600;
    padding: 8px 10px;
    white-space: nowrap;
}

/* Seven controls, weighted by what each one holds, so the bar sits on a single
   row from about a 900px viewport upward. */
.erp-filter-grid.is-policies {
    grid-template-columns:
        minmax(150px, 1.6fr)   /* search */
        minmax(110px, 1.1fr)   /* applies */
        minmax(110px, 1.1fr)   /* scope */
        minmax(100px, 1fr)     /* measure */
        minmax(92px, .9fr)     /* status */
        auto                   /* live now */
        auto;                  /* actions */
}

.erp-filter-grid .erp-filter-check {
    align-items: center;
    display: flex;
    gap: 6px;
    margin: 0;
    white-space: nowrap;
}

@media (max-width: 1199.98px) {
    .erp-filter-grid.is-policies { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 767.98px) {
    .erp-filter-grid.is-policies { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Priority leads the row because it is the first thing resolution looks at.
   A monospace-ish tile so 9 and 100 line up as magnitudes, not as words. */
.erp-policy-priority {
    align-items: center;
    background: #f1f5fb;
    border: 1px solid var(--erp-line);
    border-radius: 7px;
    color: var(--erp-ink);
    display: inline-flex;
    flex: 0 0 auto;
    font-size: 11.5px;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    height: 26px;
    justify-content: center;
    min-width: 30px;
    padding: 0 6px;
}

/* The ceilings that quietly shrink the headline rate. Stacked, because a
   policy can carry both and they bind independently. */
.erp-policy-limit {
    align-items: center;
    color: var(--erp-muted);
    display: flex;
    font-size: 11px;
    gap: 5px;
    justify-content: flex-end;
    line-height: 1.5;
    white-space: nowrap;
}

.erp-policy-limit i { opacity: .7; }

/* A policy that is not biting is still worth reading — it is usually about to
   be switched on — so it recedes rather than disappearing. */
.erp-policy-table tr.is-dormant { background: #fcfdfe; }
.erp-policy-table tr.is-dormant .erp-cell-strong { color: var(--erp-muted); }

/* ---- Till: the discount control ----------------------------------------- */

/* The cart rail is 330-460px wide. A fifth column for the discount leaves the
   product name with nothing at every width in that range (measured, not
   guessed), so the control rides under the item name instead, on the line that
   already carries the unit price. */
.erp-pos-cart-scroll .erp-table th,
.erp-pos-cart-scroll .erp-table td {
    padding-left: 6px;
    padding-right: 6px;
}

/* Wraps rather than overflowing. At the rail's 330px minimum the item cell is
   about 90px and the control needs ~130px, so the ceiling drops below the
   input and the row simply gets taller — which a cart can afford, and a
   clipped discount box cannot. */
.erp-pos-disc-line {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 2px 6px;
    margin-top: 3px;
}

.erp-pos-disc {
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: 7px;
    flex: 0 1 66px;
    font-size: 12px;
    font-variant-numeric: tabular-nums;
    min-width: 56px;
    padding: 3px 6px;
    text-align: right;
    width: 66px;
}

.erp-pos-disc:focus {
    border-color: #0f766e;
    outline: 0;
}

/* The ceiling doubles as the button that takes all of it — the common case is
   "give them the whole discount", and at a queue that should be one tap. */
.erp-pos-disc-max {
    background: none;
    border: 0;
    color: var(--erp-muted);
    cursor: pointer;
    font-size: 10.5px;
    line-height: 1.4;
    padding: 0;
    text-align: left;
    white-space: nowrap;
}

.erp-pos-disc-max:hover { color: #0f766e; text-decoration: underline; }

/* An automatic discount is stated, not offered: no input, because it is not
   the cashier's to change. */
.erp-pos-disc-line.is-auto {
    color: #0f766e;
    font-size: 11.5px;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
}

.erp-pos-disc-line.is-auto em {
    color: var(--erp-muted);
    font-size: 10.5px;
    font-style: normal;
    font-weight: 400;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Why a line is offering nothing — said quietly, but said. */
.erp-pos-disc-line.is-none {
    color: var(--erp-muted);
    font-size: 10.5px;
}

/* The struck-through list price above the charged one. The saving is the point
   of a discount, and showing only the final figure hides it. */
.erp-pos-was {
    color: var(--erp-muted);
    font-size: 11px;
    font-weight: 400;
}

.erp-pos-discount-total {
    color: #0f766e;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

/* --- Reports ---------------------------------------------------------------- */

/* Tiles on the reports index: a link that reads as a card. */
.erp-report-tile {
    align-items: flex-start;
    background: var(--erp-surface-2);
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    color: inherit;
    display: flex;
    gap: 11px;
    height: 100%;
    padding: 13px 14px;
    text-decoration: none;
    transition: border-color .15s ease, transform .15s ease, box-shadow .15s ease;
}

.erp-report-tile:hover {
    border-color: var(--erp-primary);
    box-shadow: var(--erp-shadow);
    color: inherit;
    transform: translateY(-1px);
}

.erp-report-tile-icon {
    align-items: center;
    background: rgba(126, 34, 206, .12);
    border-radius: 9px;
    color: #7e22ce;
    display: flex;
    flex: 0 0 auto;
    font-size: 14px;
    height: 34px;
    justify-content: center;
    width: 34px;
}

.erp-report-tile strong { display: block; font-size: 13px; font-weight: 600; }
.erp-report-tile small { color: var(--erp-muted); display: block; font-size: 11.5px; line-height: 1.4; margin-top: 2px; }

/* A grouped column header spanning several metric columns. */
.erp-report-span {
    background: var(--erp-surface-2);
    border-bottom: 1px solid var(--erp-line);
    font-size: 11px;
    letter-spacing: .05em;
}

/* The figure the whole report exists to produce, so it is stated rather than
   left as the last row of the body. */
.erp-report-total td {
    background-color: var(--erp-table-foot);
    border-top: 2px solid var(--erp-table-line);
    color: var(--erp-ink);
    font-weight: 700;
}

.erp-report-table tbody tr:hover > td { background-color: var(--erp-table-hover); }

/* Print: drop the chrome so a report prints as a report. */
@media print {
    .erp-topbar-stack, .erp-submenubar, .erp-pagehead-actions, .erp-filterbar, .erp-card-foot { display: none !important; }
    .erp-card { border: 0; box-shadow: none; }
}

/* A row that is present for completeness but has nothing to report. */
.erp-row-quiet td { color: var(--erp-muted); }
.erp-row-quiet .erp-cell-strong { font-weight: 600; }

/* --- Building design register ----------------------------------------------- */

.erp-design-set {
    border: 1px solid var(--erp-line);
    border-radius: 12px;
    margin-bottom: 12px;
    padding: 14px;
}

.erp-design-set:last-child { margin-bottom: 0; }

.erp-design-head { align-items: flex-start; display: flex; gap: 11px; }

.erp-design-avatar {
    align-items: center;
    background: linear-gradient(135deg, #4f46e5, #3730a3);
    border-radius: 11px;
    color: #fff;
    display: flex;
    flex: 0 0 auto;
    font-size: 15px;
    height: 38px;
    justify-content: center;
    width: 38px;
}

.erp-design-id { flex: 1 1 auto; min-width: 0; }
.erp-design-id h3 { font-size: 14px; font-weight: 600; margin: 0 0 2px; }
.erp-design-id p { color: var(--erp-muted); font-size: 12px; margin: 0; }
.erp-design-actions { display: flex; flex: 0 0 auto; gap: 4px; }

/* The characteristics, as a wrapping row of label/value pairs. Only the
   recorded ones are rendered, so this never becomes a wall of dashes. */
.erp-design-facts {
    display: grid;
    gap: 8px;
    grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
    margin-top: 12px;
}

.erp-design-fact {
    background: var(--erp-surface-2);
    border-radius: 8px;
    padding: 7px 10px;
}

.erp-design-fact span {
    color: var(--erp-muted);
    display: block;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.erp-design-fact strong { font-size: 13px; font-weight: 700; }

.erp-design-components { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 10px; }

.erp-design-files {
    align-items: center;
    border-top: 1px dashed var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    gap: 7px;
    margin-top: 12px;
    padding-top: 10px;
}

.erp-design-file {
    align-items: center;
    background: var(--erp-surface-2);
    border: 1px solid var(--erp-line);
    border-radius: 7px;
    display: inline-flex;
    font-size: 11.5px;
    gap: 4px;
    padding: 3px 4px 3px 8px;
}

.erp-design-file a { color: var(--erp-ink); text-decoration: none; }
.erp-design-file a:hover { color: var(--erp-primary); }

.erp-design-file-x {
    background: none;
    border: 0;
    color: var(--erp-muted);
    cursor: pointer;
    font-size: 11px;
    line-height: 1;
    padding: 2px 4px;
}

.erp-design-file-x:hover { color: var(--erp-danger, #b91c1c); }

/* The components checklist in the modal. */
.erp-component-grid {
    display: grid;
    gap: 6px 12px;
    grid-template-columns: repeat(auto-fill, minmax(178px, 1fr));
}

/* One repeatable file + title row. */
.erp-file-row {
    align-items: center;
    display: flex;
    gap: 6px;
    margin-bottom: 6px;
}

.erp-file-row input[type="file"] { flex: 1 1 55%; min-width: 0; }
.erp-file-row input[type="text"] { flex: 1 1 45%; min-width: 0; }

/* --- Modal stacking ---------------------------------------------------------
   The sticky header stack sits at z-index 1090 (see section 2), which is above
   Bootstrap's modal (1055) and its backdrop (1050). A short centred dialog
   never reached that high so the clash went unnoticed; a tall one slid under
   the navbar and the header stayed undimmed behind the backdrop.

   Both layers are lifted over the header rather than dropping the header to
   static, which would break the sticky module bar. Select2 already parents its
   dropdown to the modal (see erp-select2.js), so nothing inside is orphaned.
   -------------------------------------------------------------------------- */

.modal-backdrop { z-index: 1100; }
.modal { z-index: 1110; }

/* Toasts stay above an open modal: they are the confirmation that the save
   worked, and are fired a beat before the dialog finishes closing. */
.erp-toasts { z-index: 1120; }

/* Tall dialogs get real breathing room instead of Bootstrap's 1.75rem, which
   pressed a scrollable modal against the top and bottom of the viewport. */
@media (min-width: 576px) {
    .modal-dialog-scrollable,
    .modal-lg,
    .modal-xl { --bs-modal-margin: 2.5rem; }
}

/* Below that the screen is too short to spend 2.5rem on chrome. */
@media (max-width: 575.98px) {
    .modal-dialog-scrollable,
    .modal-lg,
    .modal-xl { --bs-modal-margin: 1rem; }
}

/* --- Floor-plan apartment schedule -------------------------------------------- */

/* Column headings for the repeatable apartment grid in the modal. */
.erp-unit-grid-head,
.erp-unit-row {
    display: grid;
    gap: 5px;
    grid-template-columns: 1.1fr 1.2fr .85fr .85fr .55fr .55fr .55fr .9fr 32px;
}

.erp-unit-grid-head {
    color: var(--erp-muted);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .04em;
    margin-bottom: 4px;
    text-transform: uppercase;
}

.erp-unit-row { align-items: center; margin-bottom: 5px; }
.erp-unit-row input { min-width: 0; }

/* Below tablet the grid stops being a grid — eight columns on a phone is a
   row of slivers nobody can type into. */
@media (max-width: 991.98px) {
    .erp-unit-grid-head { display: none; }
    .erp-unit-row {
        border: 1px solid var(--erp-line);
        border-radius: 8px;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        padding: 8px;
    }
}

/* The schedule as it reads under its drawing in the register. */
.erp-unit-schedule td {
    background: var(--erp-surface-2);
    border-top: 0;
    padding-top: 0;
}

.erp-unit-chips { display: flex; flex-wrap: wrap; gap: 6px; }

.erp-unit-chip {
    align-items: baseline;
    background: var(--erp-surface, #fff);
    border: 1px solid var(--erp-line);
    border-radius: 7px;
    display: inline-flex;
    flex-wrap: wrap;
    font-size: 11px;
    gap: 7px;
    padding: 4px 9px;
}

.erp-unit-chip strong { font-size: 12px; font-weight: 700; }
.erp-unit-chip span { color: var(--erp-muted); }
.erp-unit-chip em { font-style: normal; opacity: .8; }

/* The read-only drawing detail inside its view modal. */
.erp-plan-detail-head { align-items: flex-start; display: flex; gap: 11px; }
.erp-plan-detail .erp-sale-section:first-of-type { margin-top: 14px; }

/* ------------------------------------------------------------------ */
/* Engineering module                                                  */
/* ------------------------------------------------------------------ */

/* Workflow strip: where engineering has got to, and what is wrong. */
.erp-eng-progress {
    margin: 0 var(--erp-gutter) 14px;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    background: #fff;
    box-shadow: var(--erp-shadow);
    overflow: hidden;
}
.erp-eng-steps {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    list-style: none;
    margin: 0;
    padding: 0;
}
.erp-eng-step + .erp-eng-step { border-left: 1px solid var(--erp-line); }
.erp-eng-step > a {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 11px 14px;
    text-decoration: none;
    color: inherit;
    transition: background .14s ease;
}
.erp-eng-step > a:hover { background: color-mix(in srgb, var(--erp-line) 35%, transparent); }
.erp-eng-step-mark {
    display: grid;
    place-items: center;
    width: 26px;
    height: 26px;
    flex: 0 0 26px;
    border-radius: 50%;
    font-size: 13px;
}
.erp-eng-step-text { display: flex; flex-direction: column; min-width: 0; }
.erp-eng-step-text strong {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--erp-ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.erp-eng-step-text span {
    font-size: 11.5px;
    color: var(--erp-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.erp-eng-step.is-done .erp-eng-step-mark { color: var(--erp-primary-dark); background: color-mix(in srgb, var(--erp-primary) 14%, transparent); }
.erp-eng-step.is-partial .erp-eng-step-mark { color: #b26a00; background: color-mix(in srgb, var(--erp-warning) 18%, transparent); }
.erp-eng-step.is-pending .erp-eng-step-mark { color: var(--erp-muted); background: color-mix(in srgb, var(--erp-line) 60%, transparent); }

.erp-eng-flags {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    padding: 9px 14px;
    border-top: 1px solid var(--erp-line);
    background: #fbfcfe;
}
.erp-eng-flag {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    border-radius: 999px;
    border: 1px solid transparent;
    font-size: 11.5px;
    font-weight: 600;
    text-decoration: none;
}
.erp-eng-flag i { font-size: 11px; }
.erp-eng-flag.is-danger  { color: #b3123f; background: color-mix(in srgb, var(--erp-danger) 12%, #fff); border-color: color-mix(in srgb, var(--erp-danger) 28%, transparent); }
.erp-eng-flag.is-warning { color: #a35c00; background: color-mix(in srgb, var(--erp-warning) 14%, #fff); border-color: color-mix(in srgb, var(--erp-warning) 30%, transparent); }
.erp-eng-flag.is-info    { color: #04688f; background: color-mix(in srgb, var(--erp-info) 12%, #fff); border-color: color-mix(in srgb, var(--erp-info) 26%, transparent); }
.erp-eng-flag.is-clear   { color: var(--erp-primary-dark); background: color-mix(in srgb, var(--erp-primary) 10%, #fff); border-color: color-mix(in srgb, var(--erp-primary) 24%, transparent); }
.erp-eng-flag[href]:hover { filter: brightness(.96); }

/* Grid rows used by the borehole log, the strata and the checklist builder.
   Same shape as .erp-unit-row in the floor-plan schedule, so the two registers
   read as one system rather than two people's work. */
.erp-bh-row,
.erp-layer-row,
.erp-check-row {
    display: grid;
    gap: 6px;
    align-items: start;
    margin-bottom: 6px;
}
.erp-bh-row { grid-template-columns: 1.1fr 0.7fr 1.4fr 1.6fr 34px; }
.erp-layer-row { grid-template-columns: 0.7fr 0.7fr 1.5fr 0.7fr 1.5fr 34px; }
.erp-check-row { grid-template-columns: 1fr 34px; }
.erp-grid-head {
    display: grid;
    gap: 6px;
    margin-bottom: 4px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--erp-muted);
}
.erp-grid-head.for-bh { grid-template-columns: 1.1fr 0.7fr 1.4fr 1.6fr 34px; }
.erp-grid-head.for-layer { grid-template-columns: 0.7fr 0.7fr 1.5fr 0.7fr 1.5fr 34px; }

/* One borehole and its strata, boxed so a log of eight holes stays readable. */
.erp-bh-block {
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    padding: 10px 12px;
    margin-bottom: 10px;
    background: #fcfdff;
}
.erp-bh-block-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 8px;
}
.erp-bh-block-head strong { font-size: 12px; font-weight: 700; color: var(--erp-ink); }

/* Drawing register: a sheet with its revision history stacked underneath. */
.erp-dwg {
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    margin-bottom: 8px;
    background: #fff;
    overflow: hidden;
}
.erp-dwg-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    background: #fbfcfe;
    border-bottom: 1px solid var(--erp-line);
}
.erp-dwg-no { font-size: 12.5px; font-weight: 700; color: var(--erp-ink); }
.erp-dwg-title { font-size: 12px; color: var(--erp-muted); }
.erp-dwg-body { padding: 4px 0; }
.erp-rev {
    display: grid;
    grid-template-columns: 62px 1fr auto auto;
    gap: 10px;
    align-items: center;
    padding: 6px 12px;
    font-size: 12px;
    border-bottom: 1px dashed var(--erp-line);
}
.erp-rev:last-child { border-bottom: 0; }
.erp-rev.is-current { background: color-mix(in srgb, var(--erp-primary) 6%, transparent); }
.erp-rev-no { font-weight: 700; color: var(--erp-ink); }
.erp-rev-why { color: var(--erp-muted); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Photo wall. */
.erp-photo-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
    gap: 8px;
}
.erp-photo {
    position: relative;
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    overflow: hidden;
    background: #f6f8fb;
}
.erp-photo img { display: block; width: 100%; height: 92px; object-fit: cover; }
.erp-photo-cap {
    display: block;
    padding: 5px 7px;
    font-size: 11px;
    line-height: 1.3;
    color: var(--erp-ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.erp-photo-file { display: grid; place-items: center; height: 92px; font-size: 26px; color: var(--erp-muted); }

/* Checklist results, tapped through on site. */
.erp-check-line {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 10px;
    align-items: center;
    padding: 6px 0;
    border-bottom: 1px dashed var(--erp-line);
    font-size: 12.5px;
}
.erp-check-line:last-child { border-bottom: 0; }
.erp-check-opts { display: inline-flex; gap: 3px; }
.erp-check-opt {
    border: 1px solid var(--erp-line);
    background: #fff;
    border-radius: 6px;
    padding: 2px 9px;
    font-size: 11px;
    font-weight: 600;
    color: var(--erp-muted);
    cursor: pointer;
    transition: all .12s ease;
}
.erp-check-opt:hover { border-color: var(--erp-muted); color: var(--erp-ink); }
.erp-check-opt.is-on[data-result="pass"] { background: var(--erp-primary); border-color: var(--erp-primary); color: #fff; }
.erp-check-opt.is-on[data-result="fail"] { background: var(--erp-danger); border-color: var(--erp-danger); color: #fff; }
.erp-check-opt.is-on[data-result="observation"] { background: var(--erp-warning); border-color: var(--erp-warning); color: #fff; }
.erp-check-opt.is-on[data-result="na"] { background: var(--erp-muted); border-color: var(--erp-muted); color: #fff; }

:root[data-theme="dark"] .erp-eng-progress,
:root[data-theme="dark"] .erp-dwg,
:root[data-theme="dark"] .erp-photo { background: var(--erp-card, #1b2431); }
:root[data-theme="dark"] .erp-eng-flags,
:root[data-theme="dark"] .erp-dwg-head,
:root[data-theme="dark"] .erp-bh-block { background: rgba(255,255,255,.03); }

/* Project navigation, in the same glass buttons as the land-parcel tabs.
   Each destination owns a colour so people navigate by hue and position
   rather than by re-reading fifteen labels every time.

   Every hue below is measured, not chosen by eye. Two checks, both run over
   the whole row in scripts/../tabhues (see the commit notes):

   1. CONTRAST. The label is mixed from its own hue — 48% hue into #0e1726 on
      light, 45% white into the hue on dark — and sits on a plate of that same
      hue at five different alphas (.12 rest, .18 hover, .30 active, and the
      .16/.36 no-backdrop-filter fallbacks), over either the page background or
      a card. Every combination of those clears WCAG AA; the worst in the row
      is 4.57:1.

   2. SEPARATION. Neighbours must be tellable apart, including by people who
      cannot see the difference the hue is carrying. Each adjacent pair is
      compared in OKLab under normal vision and simulated deuteranopia,
      protanopia and tritanopia, and scored on its WEAKEST view. Every pair
      that could be chosen scores 12.7 or better against a bar of 8.

   Reds are deliberately absent: they are the reserved status colours, and a
   navigation tab wearing one reads as an alert about the section rather than
   as the section's identity.

   Two pre-existing neighbours remain below the bar and are left alone because
   they already ship with their own written rationale: Civil/Structural beside
   Supervision (3.7) and Cost Breakdown beside Financial Records (6.2). Both
   are weak under TRITANOPIA only — under normal vision they are 26 and 28,
   plainly different — and every tab carries an icon and a word as well as a
   colour, so identity is never on the hue alone.

   Changing any value here means re-running both checks. The two themes compute
   the label from different formulas, so passing one says nothing about the
   other. */

/* Planning, in the order the work is actually done:
   WBS → ESTIMATION → BOQ → BUDGET. */
.erp-tabs.is-glass.is-project .erp-tab[data-tab="overview"] { --tab: 194 65 12; }
.erp-tabs.is-glass.is-project .erp-tab[data-tab="work-breakdown"] { --tab: 2 132 199; }
.erp-tabs.is-glass.is-project .erp-tab[data-tab="estimates"] { --tab: 22 163 74; }
.erp-tabs.is-glass.is-project .erp-tab[data-tab="boq"] { --tab: 162 28 175; }
.erp-tabs.is-glass.is-project .erp-tab[data-tab="budget"] { --tab: 22 101 52; }

/* Architecture */
.erp-tabs.is-glass.is-project .erp-tab[data-tab="architecture"] { --tab: 14 165 233; }
.erp-tabs.is-glass.is-project .erp-tab[data-tab="building-design"] { --tab: 139 92 246; }
.erp-tabs.is-glass.is-project .erp-tab[data-tab="floor-plan"] { --tab: 14 165 233; }
.erp-tabs.is-glass.is-project .erp-tab[data-tab="approval-drawings"] { --tab: 16 185 129; }

/* Civil / structural */
.erp-tabs.is-glass.is-project .erp-tab[data-tab="materials"] { --tab: 217 119 6; }
.erp-tabs.is-glass.is-project .erp-tab[data-tab="engineering"] { --tab: 245 158 11; }
.erp-tabs.is-glass.is-project .erp-tab[data-tab="engineers"] { --tab: 99 102 241; }
.erp-tabs.is-glass.is-project .erp-tab[data-tab="assessments"] { --tab: 180 83 9; }
.erp-tabs.is-glass.is-project .erp-tab[data-tab="structural-design"] { --tab: 20 184 166; }
.erp-tabs.is-glass.is-project .erp-tab[data-tab="supervision"] { --tab: 236 72 153; }

/* Site and records. Check-ins was slate and Documents and Teams had no hue at
   all, so a third of the row fell back to the default grey — which is what the
   colouring exists to avoid in the first place. */
.erp-tabs.is-glass.is-project .erp-tab[data-tab="check-ins"] { --tab: 21 94 117; }

/* Activity. Bronze, and chosen from what was left rather than from what would
   have been ideal — this is the sixteenth tab, and sixteen is past the point
   where hue alone can separate a row. Measured like the rest: 6.43:1 light and
   4.79:1 dark, and 6.9 or better against every tab within three of it. Its
   closest match anywhere is Budget, six tabs away. Every tab carries an icon
   and a word as well, which is what the row leans on from here. */
.erp-tabs.is-glass.is-project .erp-tab[data-tab="activity"] { --tab: 133 77 14; }
.erp-tabs.is-glass.is-project .erp-tab[data-tab="documents"] { --tab: 192 38 211; }
.erp-tabs.is-glass.is-project .erp-tab[data-tab="teams"] { --tab: 161 98 7; }

/* Violet, and picked rather than liked: it is the one strong hue left unused
   in this row. Its neighbours are pink (supervision), amber (engineering),
   orange (materials) and sky (architecture), so a warm or a blue would have
   read as one of them at a glance — which is the whole point of colouring tabs
   in the first place.

   Measured against its own plate the same way the hues above were, across all
   five plate alphas (.12 rest / .18 hover / .30 active, and the .16 / .36
   no-backdrop-filter fallbacks): worst case 5.96:1 light, 4.67:1 dark. Both
   clear WCAG AA. Substituting a different violet means re-measuring — the two
   themes compute the ink from different formulas, so passing one says nothing
   about the other. */
.erp-tabs.is-glass.is-project .erp-tab[data-tab="material-batches"] { --tab: 124 58 237; }

/* Teal, the last strong hue unused in this row. Measured the same way as the
   others across all five plate alphas: worst case 5.33:1 light, 4.78:1 dark —
   both clear WCAG AA. */
.erp-tabs.is-glass.is-project .erp-tab[data-tab="financials"] { --tab: 13 148 136; }

/* Cost Breakdown. Deep indigo — the last hue in this row far enough from the
   teal beside it and the violet two along. Measured across all five plate
   alphas the same way as its neighbours: worst case 6.41:1 light, 4.57:1 dark,
   both clear of WCAG AA. */
.erp-tabs.is-glass.is-project .erp-tab[data-tab="cbs"] { --tab: 67 56 202; }

/* The second row: the same buttons one size down, so the hierarchy reads
   without the two rows competing. */
.erp-tabs.is-glass.is-sub {
    /* Enough air to read as a second level, not as the first row wrapping. */
    padding: 6px 12px 8px;
    gap: 6px;
}
.erp-tabs.is-glass.is-sub .erp-tab {
    padding: 6px 11px;
    font-size: 12px;
    border-radius: 10px;
}
.erp-tabs.is-glass.is-sub .erp-tab i { font-size: 11px; }
.erp-tabs.is-glass.is-sub .erp-tab::after { border-radius: 9px 9px 40% 40%; }

/* An anchor carries the browser's link colour and underline into the button,
   which the base .erp-tab rules never had to deal with. */
.erp-tabs.is-glass a.erp-tab,
.erp-tabs.is-glass a.erp-tab:hover,
.erp-tabs.is-glass a.erp-tab:focus {
    text-decoration: none;
}

:root[data-theme="dark"] .erp-tabs.is-glass .erp-tab {
    --tab-ink-mix: 78%;
    box-shadow: inset 0 1px 0 rgb(255 255 255 / .08), 0 1px 2px rgb(0 0 0 / .2);
}
:root[data-theme="dark"] .erp-tabs.is-glass .erp-tab::after {
    background: linear-gradient(180deg, rgb(255 255 255 / .1), rgb(255 255 255 / 0));
}

/* The assessment form's step rail, sharing the same glass buttons. */
.erp-tabs.is-glass .erp-tab[data-tab="step-basics"] { --tab: 100 116 139; }
.erp-tabs.is-glass .erp-tab[data-tab="step-site"] { --tab: 14 165 233; }
.erp-tabs.is-glass .erp-tab[data-tab="step-findings"] { --tab: 16 185 129; }
.erp-tabs.is-glass .erp-tab[data-tab="step-boreholes"] { --tab: 180 83 9; }
.erp-tabs.is-glass .erp-tab[data-tab="step-files"] { --tab: 139 92 246; }

/* ==========================================================================
   Sign-in screen — premium treatment
   ==========================================================================
   Appended after the brand-colour block above so these rules win at equal
   specificity. The palette and its contrast ratios are unchanged; this is
   about depth and material, not new colours.

   The left panel is built from four stacked layers — aurora glow, blueprint
   grid, vignette, gradient — because a single flat gradient is what makes a
   sign-in screen look like a template. The grid is a deliberate nod to what
   the product is for: it reads as drafting paper at a glance and as texture
   from a distance.
   ========================================================================== */

.erp-auth {
    --auth-ink: #0b1c46;
    /* An even split. The base theme gives the brand side a fixed 520px, which
       leaves it looking like a sidebar next to a wide form panel. */
    --auth-panel: 50%;
}

/* Left: the brand panel ---------------------------------------------------- */

.erp-auth .nk-auth-brand {
    width: var(--auth-panel);
    padding: 3.25rem 3rem;
    background:
        /* aurora */
        radial-gradient(ellipse 70% 55% at 88% 6%, rgba(73, 164, 237, .5), transparent 60%),
        radial-gradient(ellipse 60% 50% at 6% 96%, rgba(45, 110, 224, .45), transparent 58%),
        /* base */
        linear-gradient(155deg, var(--auth-deep) 0%, var(--auth-core) 52%, var(--auth-bright) 100%);
}

/* The blueprint grid. Two gradients rather than an image so it costs nothing
   to load and stays crisp at any density. */
.erp-auth .nk-auth-brand::before {
    content: "";
    position: absolute;
    inset: 0;
    right: auto;
    top: 0;
    width: 100%;
    height: 100%;
    background-image:
        linear-gradient(rgba(255, 255, 255, .055) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, .055) 1px, transparent 1px);
    background-size: 46px 46px;
    /* Faded at the edges so the grid never fights the text in the middle. */
    -webkit-mask-image: radial-gradient(ellipse 85% 75% at 50% 45%, #000 35%, transparent 100%);
    mask-image: radial-gradient(ellipse 85% 75% at 50% 45%, #000 35%, transparent 100%);
    opacity: 1;
    pointer-events: none;
}

/* A vignette that seats the panel against the form side. */
.erp-auth .nk-auth-brand::after {
    content: "";
    position: absolute;
    inset: 0;
    left: auto;
    bottom: 0;
    width: 100%;
    height: 100%;
    background:
        linear-gradient(to right, rgba(6, 20, 58, .34) 0%, transparent 28%, transparent 72%, rgba(6, 20, 58, .3) 100%),
        linear-gradient(to top, rgba(6, 20, 58, .28), transparent 45%);
    pointer-events: none;
}

.erp-auth .nk-auth-brand-inner {
    max-width: 460px;
}

.erp-auth .nk-auth-brand h1 {
    font-size: 2.05rem;
    line-height: 1.22;
    letter-spacing: -.02em;
    font-weight: 700;
    margin-bottom: .9rem;
    text-wrap: balance;
    text-shadow: 0 1px 18px rgba(6, 20, 58, .35);
}

.erp-auth .nk-auth-brand p {
    font-size: .95rem;
    line-height: 1.65;
    opacity: .88;
    margin-bottom: 2rem;
    max-width: 36ch;
}

/* The four capability lines, as glass cards. Same material as the app's
   glass tabs, so the sign-in screen already looks like the product. */
.erp-auth .nk-auth-features {
    gap: .55rem;
    margin-bottom: 1.75rem;
}

.erp-auth .nk-auth-feature {
    gap: .8rem;
    padding: .6rem .85rem;
    border: 1px solid rgba(255, 255, 255, .16);
    border-radius: 12px;
    background: linear-gradient(150deg, rgba(255, 255, 255, .15), rgba(255, 255, 255, .05));
    -webkit-backdrop-filter: blur(8px) saturate(140%);
    backdrop-filter: blur(8px) saturate(140%);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .22);
    font-size: .875rem;
    font-weight: 500;
    opacity: 1;
    transition: transform .16s ease, border-color .16s ease, background .16s ease;
}

.erp-auth .nk-auth-feature:hover {
    transform: translateX(3px);
    border-color: rgba(255, 255, 255, .3);
    background: linear-gradient(150deg, rgba(255, 255, 255, .22), rgba(255, 255, 255, .08));
}

/* The icon gets its own tile, which is what stops four rows of icons reading
   as a bulleted list. */
.erp-auth .nk-auth-feature i {
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    flex: 0 0 30px;
    border-radius: 9px;
    background: rgba(255, 255, 255, .18);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25);
    font-size: .875rem;
    opacity: 1;
}

.erp-auth-modules {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    padding-top: 1.15rem;
    border-top: 1px solid rgba(255, 255, 255, .16);
}

.erp-auth-modules span {
    padding: 4px 11px;
    border: 1px solid rgba(255, 255, 255, .22);
    border-radius: 999px;
    background: rgba(255, 255, 255, .1);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, .92);
}

/* Right: the form panel ---------------------------------------------------- */

.erp-auth .nk-auth-form-panel {
    /* Matched to the brand panel so neither side can win on content width. */
    flex: 0 0 50%;
    max-width: 50%;
    padding: 2.5rem 2rem;
    /* A brand wash that deepens gradually from right to left, so the form side
       reads as part of the product instead of a blank sheet. */
    background:
        radial-gradient(ellipse 70% 60% at 0% 50%, rgba(28, 78, 196, .26), transparent 64%),
        radial-gradient(ellipse 55% 45% at 4% 108%, rgba(45, 110, 224, .22), transparent 66%),
        linear-gradient(to left, #eaf0fb 0%, rgba(45, 110, 224, .12) 48%, rgba(28, 78, 196, .24) 100%);
}

.erp-auth .nk-auth-card {
    max-width: 424px;
    padding: 2.35rem 2.25rem;
    border: 1px solid rgba(15, 32, 78, .07);
    border-radius: 18px;
    /* Three layers: a hairline, a close contact shadow, and a wide soft one.
       A single box-shadow is what makes a card look pasted on. */
    box-shadow:
        0 1px 0 rgba(255, 255, 255, .9) inset,
        0 2px 6px rgba(12, 30, 82, .05),
        0 18px 46px -12px rgba(12, 30, 82, .18);
}

.erp-auth-mark {
    margin-bottom: 1.1rem;
}

.erp-auth .nk-auth-card-header {
    margin-bottom: 1.5rem;
}

.erp-auth .nk-auth-card-header h2 {
    font-size: 1.55rem;
    font-weight: 700;
    letter-spacing: -.02em;
    color: var(--auth-ink);
}

.erp-auth .nk-auth-card-header p {
    font-size: .875rem;
    color: #6b7794;
}

/* A quiet reassurance line above the form. This is a private ERP, and saying
   so is worth more here than another decorative flourish. */
.erp-auth-secure {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    margin-bottom: 1rem;
    padding: 4px 11px 4px 8px;
    border: 1px solid rgba(36, 89, 210, .18);
    border-radius: 999px;
    background: linear-gradient(150deg, rgba(36, 89, 210, .1), rgba(36, 89, 210, .04));
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: .01em;
    color: var(--auth-deep);
}

.erp-auth-secure i { font-size: 11px; }

/* Fields -------------------------------------------------------------------- */

.erp-auth .nk-auth-form .form-label {
    font-size: 12.5px;
    font-weight: 600;
    letter-spacing: .01em;
    color: #45526f;
    margin-bottom: .38rem;
}

.erp-auth .nk-auth-form .input-group {
    /* One border on the group, none on the children. Bootstrap otherwise gives
       each child its own border, so the icon/input/toggle met in double 2px
       seams that read as a broken outline. */
    border: 1px solid #dfe5f0;
    border-radius: 11px;
    overflow: hidden;
    background: #fff;
    box-shadow: 0 1px 2px rgba(12, 30, 82, .04);
    transition: box-shadow .16s ease, border-color .16s ease;
}

.erp-auth .nk-auth-form .input-group:focus-within {
    border-color: var(--auth-core);
    box-shadow: 0 0 0 3.5px rgba(36, 89, 210, .13), 0 2px 8px rgba(36, 89, 210, .12);
}

.erp-auth .nk-auth-form .input-group-text {
    background: #f4f7fc;
    border: 0;
    /* A single hairline divider between the icon tile and the field. */
    border-right: 1px solid #dfe5f0;
    color: #8a95ad;
    border-radius: 0;
    width: 42px;
    justify-content: center;
    transition: color .16s ease, background .16s ease, border-color .16s ease;
}

.erp-auth .nk-auth-form .form-control {
    border: 0;
    padding: .62rem .85rem;
    font-size: .9375rem;
    color: var(--auth-ink);
}

.erp-auth .nk-auth-form .form-control::placeholder { color: #a7b0c4; }

/* The focus ring lives on the group, so the control itself must not draw a
   second one on top of it. */
.erp-auth .nk-auth-form .form-control:focus {
    border: 0;
    box-shadow: none;
}

.erp-auth .nk-auth-form .input-group:focus-within .input-group-text {
    background: rgba(36, 89, 210, .08);
    border-right-color: var(--auth-core);
    color: var(--auth-core);
}

.erp-auth .nk-toggle-password {
    border-radius: 0;
    border: 0;
    /* Divider between the field and the reveal button. */
    border-left: 1px solid #dfe5f0;
    background: #f4f7fc;
    color: #8a95ad;
    cursor: pointer;
    transition: color .16s ease, background .16s ease;
}

.erp-auth .nk-toggle-password:hover {
    background: rgba(36, 89, 210, .08);
    color: var(--auth-core);
}

.erp-auth .nk-auth-form .form-check-label {
    font-size: 13px;
    color: #55617d;
}

/* The button ---------------------------------------------------------------- */

.erp-auth .nk-auth-btn {
    position: relative;
    overflow: hidden;
    padding: .72rem 1rem;
    border: 0;
    border-radius: 11px;
    font-size: .9375rem;
    font-weight: 600;
    letter-spacing: .01em;
    background: linear-gradient(140deg, var(--auth-bright), var(--auth-core) 55%, var(--auth-deep));
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .22),
        0 6px 18px -6px rgba(28, 65, 173, .55);
    transition: transform .16s ease, box-shadow .16s ease, filter .16s ease;
}

/* A sheen that crosses on hover. Pure decoration, and the one place on this
   screen where a little motion is worth the bytes. */
.erp-auth .nk-auth-btn::after {
    content: "";
    position: absolute;
    inset: 0 auto 0 -60%;
    width: 45%;
    background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .3), transparent);
    transform: skewX(-18deg);
    transition: left .5s ease;
    pointer-events: none;
}

.erp-auth .nk-auth-btn:hover::after { left: 115%; }

.erp-auth .nk-auth-btn:focus,
.erp-auth .nk-auth-btn:hover {
    background: linear-gradient(140deg, var(--auth-bright), var(--auth-core) 55%, var(--auth-deep));
    transform: translateY(-1px);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .26),
        0 10px 26px -8px rgba(28, 65, 173, .6);
}

.erp-auth .nk-auth-btn:active {
    transform: translateY(0);
    filter: brightness(.95);
    box-shadow: inset 0 2px 6px rgba(9, 24, 70, .3);
}

.erp-auth .nk-auth-btn:focus-visible {
    outline: 2px solid var(--auth-core);
    outline-offset: 3px;
}

/* Footnotes ----------------------------------------------------------------- */

.erp-auth-note {
    margin-top: 1.4rem;
    padding: .8rem .95rem;
    border: 1px solid rgba(36, 89, 210, .14);
    border-left: 3px solid var(--auth-core);
    border-radius: 10px;
    background: linear-gradient(150deg, rgba(36, 89, 210, .06), rgba(36, 89, 210, .02));
    font-size: 12.5px;
    line-height: 1.55;
    color: #55617d;
}

.erp-auth-note strong {
    display: block;
    margin-bottom: 2px;
    color: var(--auth-ink);
}

.erp-auth .nk-auth-footer {
    margin-top: 1.15rem;
    font-size: 13px;
}

.erp-auth .alert {
    border-radius: 10px;
    font-size: 13px;
}

/* Dark mode ----------------------------------------------------------------- */

:root[data-theme="dark"] .erp-auth .nk-auth-form-panel {
    background:
        radial-gradient(ellipse 60% 45% at 50% -8%, rgba(45, 110, 224, .16), transparent 70%),
        #0d1626;
}

:root[data-theme="dark"] .erp-auth .nk-auth-card {
    background: #151f33;
    border-color: rgba(255, 255, 255, .07);
    box-shadow:
        0 1px 0 rgba(255, 255, 255, .05) inset,
        0 20px 50px -14px rgba(0, 0, 0, .6);
}

:root[data-theme="dark"] .erp-auth {
    --auth-ink: #e8edf7;
}

:root[data-theme="dark"] .erp-auth .nk-auth-card-header p,
:root[data-theme="dark"] .erp-auth .nk-auth-form .form-check-label,
:root[data-theme="dark"] .erp-auth-note { color: #97a3bd; }

:root[data-theme="dark"] .erp-auth .nk-auth-form .form-label { color: #b3bed6; }

:root[data-theme="dark"] .erp-auth .nk-auth-form .form-control,
:root[data-theme="dark"] .erp-auth .nk-auth-form .input-group-text,
:root[data-theme="dark"] .erp-auth .nk-toggle-password {
    background: #1c283f;
    border-color: rgba(255, 255, 255, .1);
    color: #e8edf7;
}

:root[data-theme="dark"] .erp-auth .nk-auth-form .form-control::placeholder { color: #6b7794; }

:root[data-theme="dark"] .erp-auth-secure,
:root[data-theme="dark"] .erp-auth-note {
    background: rgba(45, 110, 224, .12);
    border-color: rgba(73, 164, 237, .22);
}

:root[data-theme="dark"] .erp-auth-secure { color: #8ab7f5; }

/* Responsive ---------------------------------------------------------------- */

@media (max-width: 991.98px) {
    .erp-auth .nk-auth-brand {
        width: 100%;
        padding: 2.25rem 1.5rem;
    }

    /* Stacked now, so the 50% split is released on both sides. */
    .erp-auth .nk-auth-form-panel {
        flex: 1 1 auto;
        max-width: none;
    }

    .erp-auth .nk-auth-brand h1 { font-size: 1.55rem; }
    .erp-auth .nk-auth-brand p { margin-bottom: 1.25rem; }
    .erp-auth .nk-auth-brand-inner { max-width: none; }

    /* The module chips carry the same message as the feature cards in a
       fraction of the height, so they are what survives on a phone. */
    .erp-auth .nk-auth-features { display: none; }
    .erp-auth-modules { padding-top: .9rem; }

    .erp-auth .nk-auth-form-panel { padding: 1.75rem 1.15rem; }
    .erp-auth .nk-auth-card { padding: 1.75rem 1.4rem; }
}

@media (prefers-reduced-motion: reduce) {
    .erp-auth .nk-auth-feature,
    .erp-auth .nk-auth-btn,
    .erp-auth .nk-auth-btn::after {
        transition: none;
    }

    .erp-auth .nk-auth-feature:hover,
    .erp-auth .nk-auth-btn:hover { transform: none; }
    .erp-auth .nk-auth-btn:hover::after { left: -60%; }
    .erp-auth .nk-auth-card.toggled { animation: none; }
}

/* Without backdrop-filter the feature cards would sit unblurred over the
   gradient and look washed out, so their tint is deepened to compensate. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .erp-auth .nk-auth-feature {
        background: rgba(255, 255, 255, .16);
    }
}

/* ------------------------------------------------------------------ */
/* Warehouse location map                                              */
/* ------------------------------------------------------------------ */

/* The indentation IS the information here — a bin only means something once
   you can see which rack it sits in — so the depth is driven by a custom
   property the node sets, and the connecting rail is drawn from it. */
.erp-loc-tree { padding: 6px 0; }

.erp-loc-node { --loc-indent: calc(var(--loc-depth, 0) * 26px); }

.erp-loc-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px var(--erp-gutter) 5px calc(var(--erp-gutter) + var(--loc-indent));
    border-bottom: 1px solid var(--erp-line);
    position: relative;
    transition: background .12s ease;
}
.erp-loc-row:hover { background: color-mix(in srgb, var(--erp-line) 32%, transparent); }

/* A short elbow into each nested row, so the eye can follow a rack back to its
   aisle without counting pixels. Absent at the top level, where there is
   nothing to connect to. */
.erp-loc-rail {
    position: absolute;
    left: calc(var(--erp-gutter) + var(--loc-indent) - 14px);
    width: 10px;
    height: 1px;
    background: var(--erp-line);
}
.erp-loc-node[style*="--loc-depth: 0"] .erp-loc-rail { display: none; }

.erp-loc-icon {
    display: grid;
    place-items: center;
    width: 24px;
    height: 24px;
    flex: 0 0 24px;
    border-radius: 7px;
    font-size: 11px;
    color: var(--erp-primary-dark);
    background: color-mix(in srgb, var(--erp-primary) 12%, transparent);
}
/* Containers read as quieter than the places that actually hold stock. */
.erp-loc-icon.is-container {
    color: var(--erp-muted);
    background: color-mix(in srgb, var(--erp-line) 70%, transparent);
}

.erp-loc-id { display: flex; flex-direction: column; min-width: 0; margin-right: auto; }
.erp-loc-id strong { font-size: 12.5px; font-weight: 700; color: var(--erp-ink); }
.erp-loc-id span {
    font-size: 11.5px;
    color: var(--erp-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.erp-loc-count {
    min-width: 22px;
    padding: 1px 7px;
    border-radius: 20px;
    background: var(--erp-surface-2, #eef1f6);
    font-size: 11px;
    font-weight: 700;
    color: var(--erp-muted);
    text-align: center;
}

.erp-loc-note {
    max-width: 200px;
    font-size: 11.5px;
    color: var(--erp-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Actions stay out of the way until the row is hovered, so a map of two
   hundred racks is not a wall of buttons. They remain reachable by keyboard. */
.erp-loc-actions {
    display: flex;
    gap: 2px;
    flex: 0 0 auto;
    opacity: 0;
    transition: opacity .12s ease;
}
.erp-loc-row:hover .erp-loc-actions,
.erp-loc-actions:focus-within { opacity: 1; }

.erp-loc-node.is-off > .erp-loc-row { opacity: .55; }

@media (max-width: 767.98px) {
    /* Deep indentation eats the whole width on a phone. */
    .erp-loc-node { --loc-indent: calc(var(--loc-depth, 0) * 12px); }
    .erp-loc-note { display: none; }
    .erp-loc-actions { opacity: 1; }
}

:root[data-theme="dark"] .erp-loc-row:hover { background: rgba(255, 255, 255, .04); }
:root[data-theme="dark"] .erp-loc-count { background: rgba(255, 255, 255, .07); }

/* ============================================================================
   Procurement — material requirements
   ========================================================================= */

/* The chain, so somebody landing on a stage knows where it sits. Scrolls
   rather than wrapping: eleven steps in two ragged rows reads as a list, and
   the point of drawing it is that it is a sequence. */
.erp-chain {
    align-items: center;
    display: flex;
    gap: 4px;
    margin-bottom: 14px;
    overflow-x: auto;
    padding-bottom: 4px;
    scrollbar-width: thin;
}

.erp-chain-step {
    align-items: center;
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: 999px;
    color: var(--erp-muted);
    display: inline-flex;
    flex: 0 0 auto;
    font-size: 11px;
    gap: 5px;
    padding: 5px 11px;
    white-space: nowrap;
}

.erp-chain-step i { font-size: 10px; opacity: .75; }

/* Separators drawn with a pseudo-element rather than markup, so the steps stay
   one element each and the list is still readable to a screen reader. */
.erp-chain-step + .erp-chain-step::before {
    color: var(--erp-line);
    content: '\f105';
    font-family: 'Font Awesome 6 Free', 'Font Awesome 7 Free';
    font-weight: 900;
    margin-left: -4px;
    margin-right: 3px;
}

.erp-chain-step.is-here {
    background: #eef2ff;
    border-color: #c7d2fe;
    color: #3730a3;
    font-weight: 600;
}

.erp-chain-step.is-here i { opacity: 1; }

/* One card per plan. The unit somebody acts on is the plan, not the line. */
.erp-plan {
    border-bottom: 1px solid var(--erp-line);
    padding: 14px 16px;
}

.erp-plan:last-child { border-bottom: 0; }

/* A left edge rather than a tinted row: the urgency belongs to the plan, and
   colouring the whole card makes the figures inside it harder to read. */
.erp-plan.is-urgent { box-shadow: inset 3px 0 0 var(--erp-danger); }

.erp-plan-head {
    align-items: flex-start;
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: space-between;
    margin-bottom: 10px;
}

.erp-plan-title { align-items: center; display: flex; flex-wrap: wrap; gap: 8px; }

.erp-plan-no {
    color: var(--erp-ink);
    font-size: 13.5px;
    font-weight: 700;
    text-decoration: none;
}

.erp-plan-no:hover { text-decoration: underline; }

.erp-plan-meta { color: var(--erp-muted); font-size: 11.5px; }

.erp-plan-when { align-items: center; display: flex; gap: 14px; }

.erp-plan-date {
    align-items: center;
    color: var(--erp-muted);
    display: inline-flex;
    font-size: 11.5px;
    font-weight: 600;
    gap: 5px;
}

.erp-plan-date.is-urgent { color: var(--erp-danger); }

.erp-plan-coverage {
    align-items: center;
    color: var(--erp-muted);
    display: inline-flex;
    font-size: 11px;
    gap: 6px;
}

.erp-plan-coverage-track {
    background: var(--erp-line);
    border-radius: 999px;
    display: inline-block;
    height: 5px;
    overflow: hidden;
    width: 70px;
}

.erp-plan-coverage-bar {
    background: #0f766e;
    border-radius: 999px;
    display: block;
    height: 100%;
}

/* The netting table. Figures are tabular so the columns line up as magnitudes,
   which is the whole reason for putting them side by side. */
.erp-plan-table { font-size: 12.5px; margin-bottom: 0; }
.erp-plan-table th { font-size: 10.5px; white-space: nowrap; }

/* The six figures are pinned narrow so they stay a group the eye can run down.
   Left to stretch, a wide screen spreads them across the card and comparing
   Required against Short becomes a saccade instead of a glance. The material
   name takes whatever is left. */
.erp-plan-table th:not(:first-child),
.erp-plan-table td:not(:first-child) { width: 104px; }
.erp-plan-table th:first-child { width: auto; min-width: 180px; }
.erp-plan-table td { padding-bottom: 7px; padding-top: 7px; }

.erp-num { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* A covered line recedes. It is still worth showing — a planner wants to see
   that the material was considered — but it is not what they are here for. */
.erp-plan-table tr.is-covered { color: var(--erp-muted); }
.erp-plan-table tr.is-covered .erp-cell-strong { color: var(--erp-muted); font-weight: 500; }

.erp-plan-short { color: var(--erp-danger); }
.erp-plan-ok { color: #0f766e; }

.erp-plan-foot {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: space-between;
    margin-top: 10px;
}

.erp-plan-total { color: var(--erp-muted); font-size: 12px; }
.erp-plan-total strong { color: var(--erp-ink); }

.erp-plan-actions { display: flex; flex-wrap: wrap; gap: 6px; }

/* Five controls, weighted by what each holds. */
.erp-filter-grid.is-requirements {
    grid-template-columns:
        minmax(160px, 2fr)     /* search */
        minmax(120px, 1.2fr)   /* project */
        minmax(110px, 1fr)     /* status */
        auto                   /* due soon */
        auto;                  /* actions */
}

@media (max-width: 991.98px) {
    .erp-filter-grid.is-requirements { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .erp-plan-head { flex-direction: column; }
    .erp-plan-when { width: 100%; }
}

/* Select2 inside a filter bar.
   erp-select2.js initialises every instance at width:100%, which is right in a
   form column but wrong in a flex filter bar: each dropdown then claims a full
   row and the controls stack instead of sitting side by side. Sized like the
   plain inputs beside them instead. */
.erp-filterbar .select2-container {
    width: auto !important;
    flex: 0 1 190px;
    min-width: 150px;
}
.erp-filterbar .select2-container .select2-selection--single {
    min-height: 32px;
}
@media (max-width: 767.98px) {
    /* One per line is the right answer on a phone; the point of the rule above
       is only to stop it happening on a desktop. */
    .erp-filterbar .select2-container { flex: 1 1 100%; }
}

/* The trailing end of a filter bar: a date range and the control that clears
   it. Kept as one flex item so the button cannot be orphaned onto a line of
   its own underneath the dates — as separate items it was simply the last
   thing on the row, and the first thing to wrap. */
.erp-filter-tail {
    align-items: center;
    display: inline-flex;
    flex: 0 0 auto;
    gap: 8px;
}

/* Inside a range the boxes sit next to each other, so they do not each need
   the breathing room a lone date input gets. 140px twice made this group the
   widest thing in the bar and pushed everything after it onto a second row;
   dd/mm/yyyy needs nothing like that much. */
.erp-filterbar .erp-daterange input[type="date"].erp-select {
    min-width: 0;
    width: 124px;
}

/* A line of context under a card head — which bill a report is measured
   against, what a figure is scoped to. Deliberately NOT a child of
   .erp-card-head: that is a space-between flex row, so anything sharing it
   takes width away from the filter bar and is what pushes trailing controls
   onto a second line. */
.erp-card-note {
    border-bottom: 1px solid var(--erp-line);
    color: var(--erp-muted);
    font-size: 12px;
    padding: 7px 18px;
}

/* Staff tick-list in the team form ---------------------------------------
   A bordered, scrolling box of checkboxes: each row is one person with their
   code and designation, so someone can be identified without leaving the
   dialog. Rows are <label>s, which makes the whole row a hit target. */

.erp-picklist-head {
    align-items: baseline;
    display: flex;
    justify-content: space-between;
    margin-bottom: 6px;
}

.erp-picklist-count {
    color: var(--erp-muted);
    font-size: 11px;
    font-weight: 600;
}

.erp-picklist {
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    overflow: hidden;
}

.erp-picklist-tools {
    align-items: center;
    background: #fafbfd;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    gap: 6px;
    padding: 7px 8px;
}

.erp-picklist-tools .form-control { flex: 1; font-size: 12px; }
.erp-picklist-tools .btn { flex: none; font-size: 11px; padding: 3px 9px; }

.erp-picklist-body {
    max-height: 250px;
    min-height: 96px;
    overflow-y: auto;
}

/* Dimmed rather than emptied while reloading, so the box does not collapse
   and jump the dialog around as departments are switched. */
.erp-picklist-body.is-busy { opacity: .55; pointer-events: none; }

.erp-picklist-empty {
    color: var(--erp-muted);
    font-size: 12px;
    margin: 0;
    padding: 28px 14px;
    text-align: center;
}

.erp-pick {
    align-items: center;
    border-bottom: 1px solid #f2f5f9;
    cursor: pointer;
    display: flex;
    gap: 10px;
    margin: 0;
    padding: 7px 11px;
    transition: background .12s ease;
}

.erp-pick:last-child { border-bottom: 0; }
.erp-pick:hover { background: #f7fbff; }

.erp-pick input[type="checkbox"] {
    accent-color: var(--erp-primary);
    flex: none;
    height: 15px;
    margin: 0;
    width: 15px;
}

.erp-pick-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; }

.erp-pick-name {
    color: var(--erp-ink);
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1.3;
}

.erp-pick-meta {
    align-items: center;
    color: var(--erp-muted);
    display: flex;
    flex-wrap: wrap;
    font-size: 11px;
    gap: 6px;
    line-height: 1.3;
}

.erp-pick-code {
    background: #eef2f7;
    border-radius: 4px;
    color: #55637a;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .2px;
    padding: 1px 5px;
}

.erp-pick-desig.is-muted { font-style: italic; opacity: .75; }

/* The lead's row: ticked, badged, and not a free choice. */
.erp-pick.is-lead { background: rgba(0, 194, 146, .07); }

.erp-pick-badge {
    background: var(--erp-primary);
    border-radius: 4px;
    color: #fff;
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: .4px;
    margin-left: auto;
    padding: 2px 6px;
    text-transform: uppercase;
}

@media (max-width: 575.98px) {
    .erp-picklist-tools { flex-wrap: wrap; }
    .erp-picklist-tools .form-control { flex: 1 0 100%; }
    .erp-picklist-body { max-height: 200px; }
}

/* "New" flag on a value typed into a tags-enabled Select2 (see erp-select2.js).
   Marks the entry that creates a value rather than picking an existing one. */
.erp-s2-new {
    background: var(--erp-primary);
    border-radius: 4px;
    color: #fff;
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: .4px;
    margin-left: auto;
    padding: 1px 5px;
    text-transform: uppercase;
}

.select2-container--default .select2-results__option--highlighted .erp-s2-new {
    background: #fff;
    color: var(--erp-primary);
}

/* ------------------------------------------------------------------ */
/* Project progress bands                                              */
/* ------------------------------------------------------------------ */

/* The bar shows how far along a project is, cool at the start and green at
   the end, so a glance down the register reads as a gradient of maturity.
   Two states override the band because the percentage stops meaning momentum
   on them: a halted project is stopped, not "more than halfway".

   Each band is a two-stop gradient rather than a flat fill — at 7px tall a
   flat colour reads as a printed rule, and the slight depth is what makes it
   read as a measure. */
.erp-progress-bar.is-p-none {
    background: var(--erp-line);
}
.erp-progress-bar.is-p-low {
    background: linear-gradient(90deg, #7dd3fc, var(--erp-info));
}
.erp-progress-bar.is-p-mid {
    background: linear-gradient(90deg, #818cf8, var(--erp-purple));
}
.erp-progress-bar.is-p-high {
    background: linear-gradient(90deg, #5eead4, var(--erp-primary));
}
.erp-progress-bar.is-p-done {
    background: linear-gradient(90deg, var(--erp-primary), var(--erp-success-dark));
}
.erp-progress-bar.is-p-hold {
    background: linear-gradient(90deg, #fcd34d, var(--erp-warning));
}

/* Cancelled is deliberately not a colour with momentum in it: hatched and
   grey, so it never reads as progress at a glance. */
.erp-progress-bar.is-p-cancelled {
    background: repeating-linear-gradient(
        45deg,
        var(--erp-muted) 0 4px,
        color-mix(in srgb, var(--erp-muted) 55%, #fff) 4px 8px
    );
}

/* A bar at 0% has nothing to paint, so the track alone has to say "not
   started" — without this the row looks like a rendering failure. */
.erp-progress:has(.erp-progress-bar[style*="width: 0%"]) {
    background: repeating-linear-gradient(
        90deg,
        var(--erp-line) 0 6px,
        transparent 6px 10px
    );
}

:root[data-theme="dark"] .erp-progress-bar.is-p-none { background: rgba(255, 255, 255, .12); }


/* ---------------------------------------------------------------------------
   Material flow strip (projects → Materials)

   Six documents in the order material actually moves through them. Laid out
   as a wrapping row of equal cards rather than a stepper with connectors:
   the steps are links people jump into out of order, and drawn arrows would
   claim a strictness the workflow does not have.
--------------------------------------------------------------------------- */
.erp-matflow {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 10px;
}

.erp-matflow-step {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 13px;
    border: 1px solid var(--erp-border);
    border-radius: 12px;
    background: var(--erp-surface);
    text-decoration: none;
    color: inherit;
    transition: border-color .15s ease, transform .15s ease, box-shadow .15s ease;
}

.erp-matflow-step:hover {
    border-color: color-mix(in srgb, var(--erp-primary) 45%, transparent);
    box-shadow: 0 6px 18px rgba(15, 23, 42, .07);
    transform: translateY(-1px);
    color: inherit;
}

.erp-matflow-num {
    flex: 0 0 auto;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    font-size: 11px;
    font-weight: 700;
    color: var(--erp-primary-dark);
    background: color-mix(in srgb, var(--erp-primary) 12%, #fff);
    border: 1px solid color-mix(in srgb, var(--erp-primary) 26%, transparent);
}

.erp-matflow-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

.erp-matflow-stage {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--erp-muted);
}

.erp-matflow-body strong { font-size: 13px; font-weight: 600; }

.erp-matflow-why { font-size: 11.5px; color: var(--erp-muted); line-height: 1.35; }

:root[data-theme="dark"] .erp-matflow-step {
    background: var(--erp-surface);
    border-color: var(--erp-border);
}

:root[data-theme="dark"] .erp-matflow-step:hover {
    box-shadow: 0 6px 18px rgba(0, 0, 0, .35);
}

/* Roles workbench ---------------------------------------------------------
   Two panes: the roles on the left, what the selected one may do on the right.
   Ticking saves on the spot, so there is no form chrome and no save button —
   the header carries a live status instead. */

.erp-roles {
    align-items: start;
    display: grid;
    gap: 14px;
    grid-template-columns: 300px minmax(0, 1fr);
}

/* Left pane ---------------------------------------------------------------- */

.erp-roles-list {
    background: var(--erp-surface, #fff);
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    box-shadow: var(--erp-shadow);
    overflow: hidden;
    position: sticky;
    top: 14px;
}

.erp-roles-list-head {
    border-bottom: 1px solid var(--erp-line);
    padding: 13px 14px 11px;
}

.erp-roles-list-title {
    color: var(--erp-ink);
    display: block;
    font-size: 13px;
    font-weight: 600;
}

.erp-roles-list-note {
    color: var(--erp-muted);
    font-size: 11px;
}

.erp-roles-search {
    border-bottom: 1px solid var(--erp-line);
    padding: 9px 10px;
    position: relative;
}

.erp-roles-search.is-inline {
    border-bottom: 0;
    flex: 1;
    padding: 0;
}

.erp-roles-search i {
    color: var(--erp-muted);
    font-size: 11px;
    left: 20px;
    pointer-events: none;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
}

.erp-roles-search.is-inline i { left: 10px; }

.erp-roles-search input {
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: 7px;
    color: var(--erp-ink);
    font-size: 12px;
    padding: 6px 10px 6px 28px;
    width: 100%;
}

.erp-roles-search input:focus {
    border-color: var(--erp-primary);
    box-shadow: 0 0 0 3px rgba(0, 194, 146, .12);
    outline: none;
}

.erp-roles-items {
    max-height: calc(100vh - 220px);
    overflow-y: auto;
    padding: 7px;
}

.erp-role-item {
    align-items: center;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 9px;
    cursor: pointer;
    display: flex;
    gap: 10px;
    margin-bottom: 3px;
    padding: 9px 10px;
    text-align: left;
    transition: background .12s ease, border-color .12s ease;
    width: 100%;
}

.erp-role-item:hover { background: var(--erp-bg); }

.erp-role-item.is-active {
    background: color-mix(in srgb, var(--erp-primary) 10%, transparent);
    border-color: var(--erp-primary);
}

.erp-role-item-icon {
    align-items: center;
    background: var(--erp-bg);
    border-radius: 8px;
    color: var(--erp-muted);
    display: flex;
    flex: none;
    font-size: 12px;
    height: 30px;
    justify-content: center;
    width: 30px;
}

.erp-role-item.is-active .erp-role-item-icon {
    background: var(--erp-primary);
    color: #fff;
}

.erp-role-item-body { flex: 1; min-width: 0; }

.erp-role-item-name {
    color: var(--erp-ink);
    display: block;
    font-size: 12.5px;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-role-item-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 2px;
}

.erp-role-chip {
    align-items: center;
    color: var(--erp-muted);
    display: inline-flex;
    font-size: 10.5px;
    gap: 3px;
}

.erp-role-item-count {
    background: var(--erp-bg);
    border-radius: 20px;
    color: #55637a;
    flex: none;
    font-size: 10.5px;
    font-weight: 700;
    padding: 2px 8px;
}

.erp-role-item.is-active .erp-role-item-count {
    background: var(--erp-primary);
    color: #fff;
}

.erp-roles-nomatch,
.erp-perm-nomatch {
    color: var(--erp-muted);
    font-size: 12px;
    margin: 0;
    padding: 18px 8px;
    text-align: center;
}

/* Right pane --------------------------------------------------------------- */

.erp-roles-detail {
    background: var(--erp-surface, #fff);
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    box-shadow: var(--erp-shadow);
    min-width: 0;
    overflow: hidden;
}

.erp-roles-detail-head {
    align-items: flex-start;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    justify-content: space-between;
    padding: 14px 16px;
}

.erp-roles-detail-id h2 {
    font-size: 16px;
    font-weight: 600;
    margin: 0;
}

.erp-roles-detail-sub {
    color: var(--erp-muted);
    font-size: 11.5px;
    margin: 2px 0 0;
}

.erp-roles-detail-actions {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

/* Live save state. This replaces a save button, so it has to be readable at a
   glance rather than decorative. */
.erp-roles-status {
    border-radius: 20px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .3px;
    padding: 3px 9px;
    text-transform: uppercase;
}

.erp-roles-status.is-ok {
    background: color-mix(in srgb, var(--erp-primary) 15%, transparent);
    color: var(--erp-primary-dark);
}

.erp-roles-status.is-busy { background: #fff4e0; color: #b45309; }
.erp-roles-status.is-error { background: #fde8e8; color: #b91c1c; }
.erp-roles-status.is-muted { background: var(--erp-bg); color: var(--erp-muted); }

.erp-roles-detail-tools {
    align-items: center;
    background: #fafbfd;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    gap: 12px;
    padding: 9px 16px;
}

.erp-roles-onlygranted {
    align-items: center;
    color: var(--erp-muted);
    cursor: pointer;
    display: flex;
    flex: none;
    font-size: 11.5px;
    gap: 6px;
    margin: 0;
}

.erp-roles-onlygranted input { accent-color: var(--erp-primary); margin: 0; }

.erp-roles-detail-body { padding: 14px 16px; }

/* Matrix ------------------------------------------------------------------- */

.erp-perms.is-busy { opacity: .5; pointer-events: none; }

.erp-perm-mod {
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    margin-bottom: 10px;
    overflow: hidden;
}

.erp-perm-mod:last-of-type { margin-bottom: 0; }

.erp-perm-mod-head {
    align-items: center;
    background: var(--erp-surface-2, #fafbfd);
    display: flex;
    gap: 8px;
    padding: 9px 12px;
}

.erp-perm-mod:not(.is-collapsed) .erp-perm-mod-head {
    border-bottom: 1px solid var(--erp-line);
}

.erp-perm-mod-toggle {
    background: none;
    border: 0;
    color: var(--erp-muted);
    cursor: pointer;
    font-size: 11px;
    padding: 2px 4px;
    transition: transform .15s ease;
}

.erp-perm-mod.is-collapsed .erp-perm-mod-toggle { transform: rotate(-90deg); }
.erp-perm-mod.is-collapsed .erp-perm-rows { display: none; }

.erp-perm-mod-check {
    align-items: center;
    cursor: pointer;
    display: flex;
    flex: 1;
    gap: 8px;
    margin: 0;
    min-width: 0;
}

.erp-perm-mod-check input {
    accent-color: var(--erp-primary);
    flex: none;
    height: 15px;
    margin: 0;
    width: 15px;
}

.erp-perm-mod-name {
    align-items: center;
    color: var(--erp-ink);
    display: flex;
    font-size: 12.5px;
    font-weight: 600;
    gap: 7px;
}

.erp-perm-mod-name i { color: var(--erp-muted); font-size: 11px; }

.erp-perm-mod-count {
    background: var(--erp-bg);
    border-radius: 20px;
    color: #55637a;
    flex: none;
    font-size: 10.5px;
    font-weight: 700;
    padding: 2px 8px;
}

.erp-perm-row {
    align-items: baseline;
    border-bottom: 1px solid #f2f5f9;
    display: grid;
    gap: 10px;
    grid-template-columns: 190px minmax(0, 1fr);
    padding: 8px 12px;
}

.erp-perm-row:last-child { border-bottom: 0; }
.erp-perm-row:hover { background: #f9fbfe; }

.erp-perm-subject {
    align-items: center;
    color: var(--erp-ink);
    cursor: pointer;
    display: flex;
    font-size: 12px;
    font-weight: 600;
    gap: 8px;
    margin: 0;
}

.erp-perm-subject input {
    accent-color: var(--erp-primary);
    flex: none;
    height: 14px;
    margin: 0;
    width: 14px;
}

/* Locked / idle panes ------------------------------------------------------ */

.erp-perm-locked,
.erp-perm-idle {
    padding: 46px 20px;
    text-align: center;
}

.erp-perm-locked-icon,
.erp-perm-idle-icon {
    align-items: center;
    background: var(--erp-bg);
    border-radius: 50%;
    color: var(--erp-muted);
    display: inline-flex;
    font-size: 20px;
    height: 54px;
    justify-content: center;
    margin-bottom: 12px;
    width: 54px;
}

.erp-perm-locked h3,
.erp-perm-idle h3 { font-size: 15px; font-weight: 600; margin: 0 0 6px; }

.erp-perm-locked p,
.erp-perm-idle p {
    color: var(--erp-muted);
    font-size: 12.5px;
    margin: 0 auto;
    max-width: 430px;
}

.btn.is-disabled { opacity: .45; pointer-events: none; }

@media (max-width: 991.98px) {
    .erp-roles { grid-template-columns: 1fr; }
    .erp-roles-list { position: static; }
    .erp-roles-items { max-height: 260px; }
    .erp-perm-row { grid-template-columns: 1fr; }
}

@media (max-width: 575.98px) {
    .erp-roles-detail-tools { flex-wrap: wrap; }
    .erp-roles-detail-body { padding: 12px; }
}

/* Effective permissions on the user record -------------------------------
   A read-only tick-list of the same catalogue the role screen edits. Boxes are
   disabled on purpose — permissions come from roles, so this reports rather
   than edits — which means the granted state has to be legible without relying
   on the browser's washed-out disabled styling. */

.erp-panel-lead {
    color: var(--erp-muted);
    font-size: 12.5px;
    margin: 0 0 14px;
}

.erp-note {
    align-items: flex-start;
    background: var(--erp-bg);
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    display: flex;
    font-size: 12.5px;
    gap: 11px;
    line-height: 1.5;
    padding: 13px 15px;
}

.erp-note > i { font-size: 15px; margin-top: 1px; }

.erp-note.is-warning {
    background: #fff8ec;
    border-color: #f5e0bd;
    color: #7c5310;
}

.erp-note.is-warning > i { color: var(--erp-warning); }

.erp-eperm-tools {
    align-items: center;
    background: #fafbfd;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 12px;
    padding: 9px 12px;
}

.erp-eperm-search { flex: 1; min-width: 200px; position: relative; }

.erp-eperm-search i {
    color: var(--erp-muted);
    font-size: 11px;
    left: 10px;
    pointer-events: none;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
}

.erp-eperm-search input {
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: 7px;
    color: var(--erp-ink);
    font-size: 12px;
    padding: 6px 10px 6px 28px;
    width: 100%;
}

.erp-eperm-search input:focus {
    border-color: var(--erp-primary);
    box-shadow: 0 0 0 3px rgba(0, 194, 146, .12);
    outline: none;
}

.erp-eperm-toggle {
    align-items: center;
    color: var(--erp-muted);
    cursor: pointer;
    display: flex;
    flex: none;
    font-size: 11.5px;
    gap: 6px;
    margin: 0;
}

.erp-eperm-toggle input { accent-color: var(--erp-primary); margin: 0; }

.erp-eperm-total {
    color: var(--erp-muted);
    flex: none;
    font-size: 11.5px;
}

.erp-eperm-total strong { color: var(--erp-ink); font-size: 13px; }

.erp-eperm-mod {
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    margin-bottom: 10px;
    overflow: hidden;
}

.erp-eperm-mod:last-of-type { margin-bottom: 0; }

.erp-eperm-mod-head {
    align-items: center;
    background: var(--erp-surface-2, #fafbfd);
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    gap: 8px;
    justify-content: space-between;
    padding: 9px 12px;
}

.erp-eperm-mod-name {
    align-items: center;
    color: var(--erp-ink);
    display: flex;
    font-size: 12.5px;
    font-weight: 600;
    gap: 8px;
}

.erp-eperm-mod-name i { color: var(--erp-muted); font-size: 11px; }

.erp-eperm-mod-count {
    background: var(--erp-bg);
    border-radius: 20px;
    color: #55637a;
    flex: none;
    font-size: 10.5px;
    font-weight: 700;
    padding: 2px 9px;
}

/* Everything in the module granted — worth spotting at a glance. */
.erp-eperm-mod-count.is-full {
    background: color-mix(in srgb, var(--erp-primary) 16%, transparent);
    color: var(--erp-primary-dark);
}

.erp-eperm-row {
    align-items: baseline;
    border-bottom: 1px solid #f2f5f9;
    display: grid;
    gap: 10px;
    grid-template-columns: 180px minmax(0, 1fr);
    padding: 8px 12px;
}

.erp-eperm-row:last-child { border-bottom: 0; }

.erp-eperm-subject {
    color: var(--erp-ink);
    font-size: 12px;
    font-weight: 600;
}

.erp-eperm-abilities {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

/* The ability itself. Uppercase here rather than in the template, so the text
   stays a normal word for search, screen readers and copy-paste. */
.erp-eperm-ability {
    align-items: center;
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: 20px;
    color: #97a1b2;
    cursor: default;
    display: inline-flex;
    font-size: 10px;
    font-weight: 700;
    gap: 6px;
    letter-spacing: .5px;
    margin: 0;
    padding: 3px 11px 3px 8px;
    text-transform: uppercase;
}

.erp-eperm-ability input {
    accent-color: var(--erp-primary);
    height: 12px;
    margin: 0;
    pointer-events: none;
    width: 12px;
}

/* Disabled inputs are dimmed by the browser; granted ones must still read as
   clearly on, so the full opacity is restored deliberately. */
.erp-eperm-ability.is-on {
    background: color-mix(in srgb, var(--erp-primary) 12%, transparent);
    border-color: color-mix(in srgb, var(--erp-primary) 45%, transparent);
    color: var(--erp-primary-dark);
}

.erp-eperm-ability.is-on input { opacity: 1; }
.erp-eperm-ability:not(.is-on) input { opacity: .45; }

.erp-eperm-nomatch {
    color: var(--erp-muted);
    font-size: 12px;
    margin: 0;
    padding: 22px 8px;
    text-align: center;
}

@media (max-width: 767.98px) {
    .erp-eperm-row { grid-template-columns: 1fr; }
    .erp-eperm-tools { gap: 9px; }
    .erp-eperm-total { width: 100%; }
}

/* Site-visit photos: picker, preview tiles, and the gallery ---------------- */

.erp-dropzone {
    align-items: center;
    background: #fafbfd;
    border: 1.5px dashed var(--erp-line);
    border-radius: var(--erp-radius);
    cursor: pointer;
    display: flex;
    flex-direction: column;
    gap: 3px;
    margin: 0;
    padding: 18px 14px;
    text-align: center;
    transition: border-color .15s ease, background .15s ease;
}

.erp-dropzone:hover,
.erp-dropzone.is-over {
    background: color-mix(in srgb, var(--erp-primary) 6%, #fafbfd);
    border-color: var(--erp-primary);
}

.erp-dropzone > i { color: var(--erp-primary); font-size: 17px; }

.erp-dropzone-lead {
    color: var(--erp-ink);
    font-size: 12.5px;
    font-weight: 600;
}

.erp-dropzone-sub {
    color: var(--erp-muted);
    font-size: 11px;
    line-height: 1.45;
    max-width: 420px;
}

/* Chosen-but-not-yet-uploaded tiles. */
.erp-shots {
    display: grid;
    gap: 9px;
    grid-template-columns: repeat(auto-fill, minmax(122px, 1fr));
    margin-top: 10px;
}

.erp-shot {
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: 9px;
    overflow: hidden;
    position: relative;
}

.erp-shot img {
    aspect-ratio: 4 / 3;
    display: block;
    object-fit: cover;
    width: 100%;
}

.erp-shot-file {
    align-items: center;
    aspect-ratio: 4 / 3;
    background: var(--erp-bg);
    color: var(--erp-danger);
    display: flex;
    font-size: 24px;
    justify-content: center;
}

.erp-shot-cap {
    border: 0;
    border-top: 1px solid var(--erp-line);
    display: block;
    font-size: 11px;
    outline: none;
    padding: 6px 8px;
    width: 100%;
}

.erp-shot-cap:focus { background: color-mix(in srgb, var(--erp-primary) 7%, transparent); }

.erp-shot-x {
    align-items: center;
    background: rgba(16, 24, 40, .62);
    border: 0;
    border-radius: 50%;
    color: #fff;
    cursor: pointer;
    display: flex;
    font-size: 10px;
    height: 22px;
    justify-content: center;
    position: absolute;
    right: 5px;
    top: 5px;
    width: 22px;
}

.erp-shot-x:hover { background: var(--erp-danger); }

/* Gallery overlay ---------------------------------------------------------
   z-index clears Bootstrap's modal (1055) because photos are opened from
   inside the visit drawer, which is itself a modal. */

body.is-lightboxed { overflow: hidden; }

.erp-lightbox {
    align-items: center;
    background: rgba(9, 14, 24, .93);
    display: grid;
    gap: 10px;
    grid-template-areas:
        "prev stage next"
        "strip strip strip";
    grid-template-columns: 64px minmax(0, 1fr) 64px;
    grid-template-rows: minmax(0, 1fr) auto;
    inset: 0;
    padding: 18px;
    position: fixed;
    z-index: 2000;
}

.erp-lightbox[hidden] { display: none; }

.erp-lightbox-stage {
    align-items: center;
    display: flex;
    flex-direction: column;
    gap: 10px;
    grid-area: stage;
    justify-content: center;
    margin: 0;
    min-height: 0;
}

.erp-lightbox-stage img {
    border-radius: 8px;
    box-shadow: 0 18px 60px rgba(0, 0, 0, .5);
    max-height: calc(100vh - 190px);
    max-width: 100%;
    object-fit: contain;
}

.erp-lightbox-stage figcaption {
    color: #e8ecf3;
    display: flex;
    flex-direction: column;
    font-size: 12.5px;
    gap: 2px;
    text-align: center;
}

.erp-lightbox-stage figcaption small { color: #99a3b4; font-size: 11px; }

.erp-lightbox-nav {
    align-items: center;
    background: rgba(255, 255, 255, .1);
    border: 0;
    border-radius: 50%;
    color: #fff;
    cursor: pointer;
    display: flex;
    font-size: 15px;
    height: 44px;
    justify-content: center;
    justify-self: center;
    transition: background .15s ease;
    width: 44px;
}

.erp-lightbox-nav:hover { background: rgba(255, 255, 255, .22); }
.erp-lightbox-nav.is-prev { grid-area: prev; }
.erp-lightbox-nav.is-next { grid-area: next; }

/* A single photo has nothing to page through. */
.erp-lightbox.is-single .erp-lightbox-nav,
.erp-lightbox.is-single .erp-lightbox-strip { display: none; }

.erp-lightbox-close {
    background: rgba(255, 255, 255, .1);
    border: 0;
    border-radius: 50%;
    color: #fff;
    cursor: pointer;
    font-size: 15px;
    height: 38px;
    position: absolute;
    right: 16px;
    top: 16px;
    width: 38px;
    z-index: 1;
}

.erp-lightbox-close:hover { background: var(--erp-danger); }

.erp-lightbox-strip {
    display: flex;
    gap: 7px;
    grid-area: strip;
    justify-content: flex-start;
    overflow-x: auto;
    padding: 4px 2px;
    scrollbar-width: thin;
}

.erp-lightbox-thumb {
    background: none;
    border: 2px solid transparent;
    border-radius: 6px;
    cursor: pointer;
    flex: none;
    height: 54px;
    opacity: .55;
    overflow: hidden;
    padding: 0;
    transition: opacity .15s ease, border-color .15s ease;
    width: 72px;
}

.erp-lightbox-thumb img { height: 100%; object-fit: cover; width: 100%; }
.erp-lightbox-thumb:hover { opacity: .85; }

.erp-lightbox-thumb.is-active {
    border-color: var(--erp-primary);
    opacity: 1;
}

/* The grid thumbnails now open the gallery rather than a new tab. */
.erp-photo a[data-gallery-item] { cursor: zoom-in; }

@media (max-width: 575.98px) {
    .erp-lightbox {
        grid-template-areas:
            "stage stage stage"
            "prev strip next";
        grid-template-columns: 46px minmax(0, 1fr) 46px;
        padding: 12px;
    }

    .erp-lightbox-nav { height: 38px; width: 38px; }
    .erp-lightbox-stage img { max-height: calc(100vh - 210px); }
}

/* Site-visit card toolbar: date range and search left, count and PDF right. */

.erp-visit-tools {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: space-between;
    width: 100%;
}

.erp-visit-filter {
    align-items: flex-end;
    display: flex;
    flex-wrap: wrap;
    gap: 7px;
}

.erp-visit-dates {
    display: flex;
    gap: 7px;
}

.erp-visit-dates label {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin: 0;
}

.erp-visit-dates label span {
    color: var(--erp-muted);
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: .4px;
    padding-left: 2px;
    text-transform: uppercase;
}

.erp-visit-dates input {
    width: 148px;
}

.erp-visit-tools-end {
    align-items: center;
    display: flex;
    flex: none;
    gap: 9px;
}

/* Says what the register is currently narrowed to, and that the PDF follows
   the same range — otherwise a filtered print looks like lost records. */
.erp-visit-range {
    align-items: center;
    background: color-mix(in srgb, var(--erp-info) 8%, transparent);
    border-bottom: 1px solid var(--erp-line);
    color: #33506b;
    display: flex;
    font-size: 11.5px;
    gap: 7px;
    padding: 7px 16px;
}

.erp-visit-range i { color: var(--erp-info); font-size: 10px; }

@media (max-width: 767.98px) {
    .erp-visit-tools { align-items: stretch; flex-direction: column; }
    .erp-visit-filter { align-items: flex-end; }
    .erp-visit-dates { flex: 1; }
    .erp-visit-dates label { flex: 1; }
    .erp-visit-dates input { width: 100%; }
    .erp-visit-tools-end { justify-content: space-between; }
}

/* The project's client: a label that opens into a picker ------------------- */

.erp-client-slot {
    align-items: center;
    display: inline-flex;
    gap: 6px;
}

.erp-client-slot.is-busy { opacity: .6; pointer-events: none; }

.erp-client-view {
    align-items: center;
    display: inline-flex;
    gap: 6px;
}

/* Looks like the pills beside it, but behaves like a control: the pencil only
   appears on approach, so the resting state stays a plain readable name. */
.erp-client-pill[role="button"] {
    cursor: pointer;
    transition: box-shadow .12s ease, border-color .12s ease;
}

.erp-client-pill[role="button"]:hover,
.erp-client-pill[role="button"]:focus-visible {
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--erp-primary) 30%, transparent);
    outline: none;
}

.erp-client-pill.is-empty { font-style: italic; }

.erp-client-pen {
    font-size: 8.5px;
    margin-left: 5px;
    opacity: 0;
    transition: opacity .12s ease;
}

.erp-client-pill:hover .erp-client-pen,
.erp-client-pill:focus-visible .erp-client-pen { opacity: .75; }

.erp-client-open {
    color: var(--erp-muted);
    font-size: 10px;
    line-height: 1;
    text-decoration: none;
}

.erp-client-open:hover { color: var(--erp-primary); }

.erp-client-more { font-size: 10px; }

.erp-client-edit {
    align-items: center;
    display: inline-flex;
    gap: 5px;
    /* Wide enough to read a client name; Select2 resolves width:100% against
       this rather than against the whole pill row. */
    min-width: 260px;
}

.erp-client-edit .form-select { font-size: 12px; }

.erp-client-none {
    color: var(--erp-muted);
    font-size: 10.5px;
    white-space: nowrap;
}

@media (max-width: 575.98px) {
    .erp-client-edit { min-width: 200px; }
}

/* Customer portal ---------------------------------------------------------
   A calmer shell than the ERP: no sidebar, no company switcher, three things
   in the top bar. Customers visit occasionally and are not trained on it, so
   everything is bigger and there is far less of it. */

.erp-portal-body {
    background: var(--erp-bg);
    color: var(--erp-ink);
    display: flex;
    flex-direction: column;
    font-family: 'Segoe UI', system-ui, -apple-system, Arial, sans-serif;
    margin: 0;
    min-height: 100vh;
}

.erp-portal-top {
    background: #fff;
    border-bottom: 1px solid var(--erp-line);
    box-shadow: 0 1px 3px rgba(16, 30, 54, .04);
    position: sticky;
    top: 0;
    z-index: 30;
}

.erp-portal-top-inner {
    align-items: center;
    display: flex;
    gap: 22px;
    margin: 0 auto;
    max-width: 1120px;
    padding: 11px 18px;
}

.erp-portal-brand {
    align-items: center;
    color: var(--erp-ink);
    display: flex;
    flex: none;
    font-size: 14px;
    font-weight: 700;
    gap: 9px;
    text-decoration: none;
}

.erp-portal-brand img { height: 30px; width: auto; }

.erp-portal-nav {
    display: flex;
    flex: 1;
    gap: 4px;
}

.erp-portal-nav a {
    align-items: center;
    border-radius: 8px;
    color: var(--erp-muted);
    display: inline-flex;
    font-size: 13px;
    font-weight: 600;
    gap: 8px;
    padding: 9px 14px;
    text-decoration: none;
    transition: background .12s ease, color .12s ease;
}

.erp-portal-nav a:hover { background: var(--erp-bg); color: var(--erp-ink); }

.erp-portal-nav a.is-active {
    background: color-mix(in srgb, var(--erp-primary) 12%, transparent);
    color: var(--erp-primary-dark);
}

.erp-portal-account-btn {
    align-items: center;
    background: none;
    border: 0;
    cursor: pointer;
    display: flex;
    gap: 9px;
    padding: 4px;
}

.erp-portal-initials {
    align-items: center;
    background: var(--erp-primary);
    border-radius: 50%;
    color: #fff;
    display: flex;
    flex: none;
    font-size: 12px;
    font-weight: 700;
    height: 34px;
    justify-content: center;
    width: 34px;
}

.erp-portal-who {
    display: flex;
    flex-direction: column;
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1.25;
    text-align: left;
}

.erp-portal-who small { color: var(--erp-muted); font-size: 10.5px; font-weight: 500; }

.erp-portal-main {
    flex: 1;
    margin: 0 auto;
    max-width: 1120px;
    padding: 22px 18px 40px;
    width: 100%;
}

.erp-portal-foot {
    border-top: 1px solid var(--erp-line);
    color: var(--erp-muted);
    font-size: 11.5px;
    padding: 16px;
    text-align: center;
}

.erp-portal-hello { margin-bottom: 18px; }
.erp-portal-hello h1 { font-size: 22px; font-weight: 700; margin: 0; }
.erp-portal-hello p { color: var(--erp-muted); font-size: 13px; margin: 3px 0 0; }

.erp-portal-back {
    color: var(--erp-muted);
    display: inline-block;
    font-size: 12.5px;
    margin-bottom: 12px;
    text-decoration: none;
}

.erp-portal-back:hover { color: var(--erp-primary); }

/* The three headline figures. */
.erp-portal-tiles {
    display: grid;
    gap: 14px;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    margin-bottom: 18px;
}

.erp-portal-tile {
    background: #fff;
    border: 1px solid var(--erp-line);
    border-left: 4px solid var(--erp-info);
    border-radius: var(--erp-radius);
    box-shadow: var(--erp-shadow);
    padding: 16px 18px;
}

.erp-portal-tile.is-paid { border-left-color: var(--erp-primary); }
.erp-portal-tile.is-due { border-left-color: var(--erp-warning); }
.erp-portal-tile.is-clear { border-left-color: var(--erp-primary); }

.erp-portal-tile-label {
    align-items: center;
    color: var(--erp-muted);
    display: flex;
    font-size: 11px;
    font-weight: 700;
    gap: 7px;
    letter-spacing: .4px;
    text-transform: uppercase;
}

.erp-portal-tile strong {
    display: block;
    font-size: 25px;
    font-weight: 700;
    line-height: 1.2;
    margin: 7px 0 5px;
}

.erp-portal-tile small { color: var(--erp-muted); font-size: 11.5px; }
.erp-portal-tile .erp-progress { margin-bottom: 6px; }

.erp-portal-cols {
    display: grid;
    gap: 14px;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
}

.erp-portal-card {
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    box-shadow: var(--erp-shadow);
    margin-bottom: 14px;
    overflow: hidden;
}

.erp-portal-card-head {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    gap: 10px;
    justify-content: space-between;
    padding: 13px 16px;
}

.erp-portal-card-head h2 { font-size: 14px; font-weight: 600; margin: 0; }
.erp-portal-card-head a { color: var(--erp-primary); font-size: 12px; text-decoration: none; }

.erp-portal-row {
    align-items: center;
    border-bottom: 1px solid #f2f5f9;
    color: inherit;
    display: flex;
    gap: 14px;
    padding: 12px 16px;
    text-decoration: none;
    transition: background .12s ease;
}

.erp-portal-row:last-child { border-bottom: 0; }
.erp-portal-row:not(.is-static):hover { background: #f8fbff; }

.erp-portal-row-main { flex: 1; min-width: 0; }
.erp-portal-row-main strong { display: block; font-size: 13px; font-weight: 600; }
.erp-portal-row-main small { color: var(--erp-muted); font-size: 11.5px; }

.erp-portal-row-progress { flex: none; width: 160px; }
.erp-portal-row-progress small { color: var(--erp-muted); font-size: 11px; }

.erp-portal-ref { color: var(--erp-muted); font-size: 11.5px; }

.erp-portal-empty {
    color: var(--erp-muted);
    font-size: 12.5px;
    margin: 0;
    padding: 30px 16px;
    text-align: center;
}

/* Project cards on My Projects. */
.erp-portal-project {
    align-items: center;
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    box-shadow: var(--erp-shadow);
    color: inherit;
    display: flex;
    gap: 16px;
    margin-bottom: 12px;
    padding: 16px 18px;
    text-decoration: none;
    transition: border-color .12s ease, transform .12s ease;
}

.erp-portal-project:hover {
    border-color: var(--erp-primary);
    transform: translateY(-1px);
}

.erp-portal-project-icon {
    align-items: center;
    background: color-mix(in srgb, var(--erp-primary) 12%, transparent);
    border-radius: 12px;
    color: var(--erp-primary-dark);
    display: flex;
    flex: none;
    font-size: 18px;
    height: 48px;
    justify-content: center;
    width: 48px;
}

.erp-portal-project-body { flex: 1; min-width: 0; }
.erp-portal-project-body strong { display: block; font-size: 15px; font-weight: 600; }
.erp-portal-project-body small { color: var(--erp-muted); font-size: 11.5px; }

.erp-portal-project-end {
    display: flex;
    flex: none;
    flex-direction: column;
    gap: 5px;
    text-align: right;
}

.erp-portal-project-end small { color: var(--erp-muted); font-size: 11px; }

.erp-portal-facts {
    display: grid;
    gap: 0 28px;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    margin: 0;
    padding: 14px 16px;
}

.erp-portal-facts > div { border-bottom: 1px solid #f4f6fa; padding: 8px 0; }
.erp-portal-facts dt {
    color: var(--erp-muted);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .4px;
    text-transform: uppercase;
}
.erp-portal-facts dd { font-size: 13px; margin: 2px 0 0; word-break: break-word; }

.erp-portal-note {
    align-items: flex-start;
    background: #fafbfd;
    border-top: 1px solid var(--erp-line);
    color: var(--erp-muted);
    display: flex;
    font-size: 11.5px;
    gap: 8px;
    margin: 0;
    padding: 11px 16px;
}

.erp-portal-owners {
    border-top: 1px solid var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    padding: 12px 16px;
}

.erp-portal-owners-label {
    color: var(--erp-muted);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .4px;
    text-transform: uppercase;
    width: 100%;
}

.erp-portal-bigbar {
    align-items: center;
    display: flex;
    gap: 14px;
    padding: 16px 16px 4px;
}

.erp-portal-bigbar .erp-progress { flex: 1; height: 12px; }
.erp-portal-bigbar strong { font-size: 20px; font-weight: 700; }

.erp-portal-link {
    color: var(--erp-primary);
    display: inline-block;
    font-size: 12.5px;
    padding: 0 16px 14px;
    text-decoration: none;
}

.erp-portal-tablewrap { overflow-x: auto; }

.erp-portal-table {
    border-collapse: collapse;
    width: 100%;
}

.erp-portal-table th,
.erp-portal-table td {
    border-bottom: 1px solid #f2f5f9;
    font-size: 12.5px;
    padding: 10px 16px;
    text-align: left;
    white-space: nowrap;
}

.erp-portal-table thead th {
    background-color: var(--erp-table-head-solid);
    color: var(--erp-table-head-ink);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .4px;
    text-transform: uppercase;
}

.erp-portal-table thead th + th {
    box-shadow: inset 1px 0 0 var(--erp-table-head-rule);
}

/* The portal carries the same striping as the back office. A buyer reading a
   payment schedule on a phone needs the row tracking more than staff do, not
   less. */
.erp-portal-table tbody tr:nth-child(even) {
    background-color: var(--erp-table-stripe);
}

.erp-portal-table .num { text-align: right; }
.erp-portal-table tfoot th { background: var(--erp-table-foot); font-weight: 700; }

@media (max-width: 767.98px) {
    .erp-portal-top-inner { flex-wrap: wrap; gap: 10px; }
    .erp-portal-nav { order: 3; width: 100%; }
    .erp-portal-nav a { flex: 1; justify-content: center; }
    .erp-portal-who { display: none; }
    .erp-portal-row { flex-wrap: wrap; }
    .erp-portal-row-progress { width: 100%; }
    .erp-portal-project { flex-wrap: wrap; }
    .erp-portal-project-end { flex-direction: row; text-align: left; width: 100%; }
}

/* Portal-access status block on the client screen. */
.erp-portal-status {
    align-items: flex-start;
    background: var(--erp-bg);
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    display: flex;
    font-size: 12.5px;
    gap: 11px;
    padding: 13px 15px;
}

.erp-portal-status > i { color: var(--erp-muted); font-size: 15px; margin-top: 1px; }
.erp-portal-status strong { display: block; font-size: 13px; }
.erp-portal-status span { color: var(--erp-muted); }
.erp-portal-status code { background: #fff; border-radius: 4px; padding: 1px 5px; }

.erp-portal-status.is-on {
    background: color-mix(in srgb, var(--erp-primary) 8%, transparent);
    border-color: color-mix(in srgb, var(--erp-primary) 35%, transparent);
}

.erp-portal-status.is-on > i { color: var(--erp-primary); }

/* Portal forms — larger controls than the ERP: customers use this rarely and
   are not trained on it. */
.erp-portal-form { padding: 14px 16px 18px; }

.erp-portal-field { margin-bottom: 13px; max-width: 380px; }

.erp-portal-field label {
    color: var(--erp-ink);
    display: block;
    font-size: 12px;
    font-weight: 600;
    margin-bottom: 4px;
}

.erp-portal-field input {
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    color: var(--erp-ink);
    font-size: 13.5px;
    padding: 9px 12px;
    width: 100%;
}

.erp-portal-field input:focus {
    border-color: var(--erp-primary);
    box-shadow: 0 0 0 3px rgba(0, 194, 146, .12);
    outline: none;
}

.erp-portal-field small { color: var(--erp-muted); display: block; font-size: 11px; margin-top: 3px; }

.erp-portal-btn {
    background: var(--erp-primary);
    border: 0;
    border-radius: 8px;
    color: #fff;
    cursor: pointer;
    font-size: 13px;
    font-weight: 600;
    padding: 10px 20px;
}

.erp-portal-btn:hover { background: var(--erp-primary-dark); }

.erp-portal-alert {
    align-items: flex-start;
    border-radius: 8px;
    display: flex;
    font-size: 12.5px;
    gap: 9px;
    margin-bottom: 14px;
    max-width: 380px;
    padding: 10px 13px;
}

.erp-portal-alert.is-error { background: #fde8e8; color: #b91c1c; }

/* Portal statement — agreement, terms, instalments, amendments, payments. */
.erp-portal-agreement .erp-portal-card-head h2 small {
    color: var(--erp-muted);
    display: block;
    font-size: 11px;
    font-weight: 500;
}

.erp-portal-alert-wide { max-width: none; }

.erp-portal-was {
    color: var(--erp-muted);
    font-size: 11px;
    margin-left: 6px;
    text-decoration: line-through;
}

.erp-portal-facts.is-tight > div { border-bottom: 0; padding: 5px 0; }

/* A titled block inside an agreement card. */
.erp-portal-sub { border-top: 1px solid var(--erp-line); }

.erp-portal-sub h3 {
    align-items: baseline;
    color: var(--erp-ink);
    display: flex;
    font-size: 11px;
    font-weight: 700;
    gap: 8px;
    letter-spacing: .5px;
    margin: 0;
    padding: 12px 16px 8px;
    text-transform: uppercase;
}

.erp-portal-sub-count {
    color: var(--erp-muted);
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0;
    text-transform: none;
}

.erp-portal-table td small {
    color: var(--erp-muted);
    display: block;
    font-size: 10.5px;
}

.erp-portal-table td.wrap { max-width: 320px; white-space: normal; }

/* An instalment past its date, marked on the row rather than only in the
   status column — it should be visible while scanning, not only while reading. */
.erp-portal-table tr.is-overdue td { background: #fff6f6; }
.erp-portal-table tr.is-overdue td:first-child { box-shadow: inset 3px 0 0 var(--erp-danger); }

@media (max-width: 767.98px) {
    .erp-portal-table td.wrap { max-width: 200px; }
}

/* The agreement strip on a project card — the money half of "how is my
   development doing", sitting on the project it belongs to. */
.erp-portal-project-card {
    background: #fff;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    box-shadow: var(--erp-shadow);
    margin-bottom: 12px;
    overflow: hidden;
    transition: border-color .12s ease;
}

.erp-portal-project-card:hover { border-color: var(--erp-primary); }

/* The card owns the frame now, so the link inside gives its own up. */
.erp-portal-project-card .erp-portal-project {
    border: 0;
    border-radius: 0;
    box-shadow: none;
    margin: 0;
}

.erp-portal-project-card .erp-portal-project:hover { transform: none; }

.erp-portal-agree-strip {
    background: #fafbfd;
    border-top: 1px solid var(--erp-line);
    padding: 12px 18px 14px;
}

.erp-portal-agree-strip.is-none {
    align-items: center;
    color: var(--erp-muted);
    display: flex;
    font-size: 12px;
    gap: 8px;
    padding: 11px 18px;
}

.erp-portal-agree-head {
    align-items: baseline;
    display: flex;
    gap: 10px;
    justify-content: space-between;
    margin-bottom: 9px;
}

.erp-portal-agree-head > span {
    color: var(--erp-ink);
    font-size: 12px;
    font-weight: 600;
}

.erp-portal-agree-head i { color: var(--erp-muted); margin-right: 6px; }
.erp-portal-agree-head small { color: var(--erp-muted); font-weight: 500; margin-left: 5px; }

.erp-portal-agree-head a {
    color: var(--erp-primary);
    font-size: 11.5px;
    text-decoration: none;
    white-space: nowrap;
}

.erp-portal-agree-figures {
    display: grid;
    gap: 10px;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
    margin-bottom: 9px;
}

.erp-portal-agree-figures small {
    color: var(--erp-muted);
    display: block;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .4px;
    text-transform: uppercase;
}

.erp-portal-agree-figures strong { font-size: 14px; font-weight: 700; }
.erp-portal-agree-figures strong.is-paid { color: var(--erp-primary-dark); }
.erp-portal-agree-figures strong.is-due { color: var(--erp-warning); }

.erp-portal-agree-bar {
    align-items: center;
    display: flex;
    gap: 10px;
}

.erp-portal-agree-bar .erp-progress { flex: 1; }
.erp-portal-agree-bar small { color: var(--erp-muted); font-size: 11px; white-space: nowrap; }

.erp-portal-agree-next {
    align-items: center;
    color: var(--erp-muted);
    display: flex;
    font-size: 11.5px;
    gap: 7px;
    margin: 9px 0 0;
}

.erp-portal-agree-next.is-overdue { color: #b91c1c; font-weight: 600; }
.erp-portal-agree-next.is-clear { color: var(--erp-primary-dark); }

/* Portal dashboard charts. Each chart sits beside its own numbers — the fills
   are below 3:1 on a white card, so the written values are the accessible
   reading of the chart, not an optional extra. */
.erp-portal-charts {
    display: grid;
    gap: 14px;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    margin-bottom: 18px;
}

.erp-portal-chart { margin-bottom: 0; }

.erp-portal-chart-note { color: var(--erp-muted); font-size: 11px; }

.erp-portal-chart-body {
    align-items: center;
    display: flex;
    gap: 18px;
    padding: 16px;
}

.erp-portal-chart-canvas {
    flex: none;
    height: 150px;
    position: relative;
    width: 150px;
}

.erp-portal-chart-canvas canvas { height: 100% !important; width: 100% !important; }

/* The doughnut's hole carries the headline; the pie's sits over its centre,
   which is why the text is centred rather than inset. */
.erp-portal-chart-centre {
    align-items: center;
    display: flex;
    flex-direction: column;
    inset: 0;
    justify-content: center;
    pointer-events: none;
    position: absolute;
}

.erp-portal-chart-centre strong {
    color: var(--erp-ink);
    font-size: 21px;
    font-weight: 700;
    line-height: 1;
}

.erp-portal-chart-centre small {
    color: var(--erp-muted);
    font-size: 10px;
    letter-spacing: .4px;
    text-transform: uppercase;
}

/* Legend and value list in one — a swatch carries identity, the text stays ink. */
.erp-portal-chart-key {
    flex: 1;
    list-style: none;
    margin: 0;
    min-width: 0;
    padding: 0;
}

.erp-portal-chart-key li {
    align-items: baseline;
    color: var(--erp-ink);
    display: flex;
    font-size: 12px;
    gap: 8px;
    padding: 4px 0;
}

.erp-portal-chart-key li strong {
    font-size: 12.5px;
    font-weight: 700;
    margin-left: auto;
    white-space: nowrap;
}

.erp-portal-chart-key li strong.is-overdue { color: #b91c1c; }

.erp-portal-chart-key li.is-sub {
    color: var(--erp-muted);
    font-size: 11.5px;
    padding-left: 18px;
}

.erp-portal-chart-key-split {
    border-top: 1px solid var(--erp-line);
    color: var(--erp-muted);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .4px;
    margin-top: 5px;
    padding-top: 7px !important;
    text-transform: uppercase;
}

.erp-portal-swatch {
    border-radius: 3px;
    flex: none;
    height: 10px;
    position: relative;
    top: 1px;
    width: 10px;
}

@media (max-width: 575.98px) {
    .erp-portal-chart-body { flex-direction: column; }
    .erp-portal-chart-key { width: 100%; }
}

/* Material stock per item, on the project overview card. */
.erp-mstock .erp-rec-section-head {
    align-items: baseline;
    display: flex;
    justify-content: space-between;
}

.erp-mstock-link {
    color: var(--erp-primary);
    font-size: 11.5px;
    text-decoration: none;
    white-space: nowrap;
}

.erp-mstock-alert-bar {
    align-items: flex-start;
    background: #fdf1f1;
    border: 1px solid #f2cfcf;
    border-radius: 8px;
    color: #8c2b2b;
    display: flex;
    font-size: 11.5px;
    gap: 8px;
    margin: 0 0 12px;
    padding: 9px 12px;
}

.erp-mstock-alert-bar > i { color: #d03b3b; position: relative; top: 1px; }

/* Small multiples. auto-fill rather than a fixed count so ten materials wrap
   into tidy rows on a wide card and stack cleanly on a phone, with no
   breakpoint per column count. */
.erp-mstock-grid {
    display: grid;
    gap: 10px;
    grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
}

.erp-mstock-card {
    background: var(--erp-surface, #fff);
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    display: flex;
    flex-direction: column;
    gap: 9px;
    padding: 11px 11px 10px;
}

.erp-mstock-card.is-out {
    background: #fefafa;
    border-color: #f0d3d3;
}

.erp-mstock-head {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

.erp-mstock-head strong {
    color: var(--erp-ink);
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1.25;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-mstock-head span {
    color: var(--erp-muted);
    font-size: 10px;
    letter-spacing: .3px;
}

.erp-mstock-ring {
    align-self: center;
    height: 96px;
    position: relative;
    width: 96px;
}

.erp-mstock-ring canvas { height: 100% !important; width: 100% !important; }

.erp-mstock-centre {
    align-items: center;
    display: flex;
    flex-direction: column;
    inset: 0;
    justify-content: center;
    pointer-events: none;
    position: absolute;
    text-align: center;
}

.erp-mstock-centre strong {
    color: var(--erp-ink);
    font-size: 17px;
    font-weight: 700;
    line-height: 1;
    max-width: 64px;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Stepped down by digit count rather than truncated: a stock figure that
   reads "1,210,00…" is worse than one set a few pixels smaller. */
.erp-mstock-centre.is-long strong { font-size: 14px; }
.erp-mstock-centre.is-xlong strong { font-size: 10px; }

.erp-mstock-centre small {
    color: var(--erp-muted);
    font-size: 9px;
    letter-spacing: .4px;
    margin-top: 3px;
    text-transform: uppercase;
}

/* Takes the ring's place, so an empty shelf occupies the same footprint and
   the grid stays on its rows. */
.erp-mstock-out {
    align-items: center;
    align-self: center;
    display: flex;
    flex-direction: column;
    height: 96px;
    justify-content: center;
    text-align: center;
    width: 96px;
}

.erp-mstock-out > i {
    color: #d03b3b;
    font-size: 21px;
    margin-bottom: 7px;
}

.erp-mstock-out strong {
    color: #8c2b2b;
    font-size: 12.5px;
    font-weight: 700;
}

.erp-mstock-out small {
    color: var(--erp-muted);
    font-size: 9.5px;
    line-height: 1.35;
    margin-top: 2px;
}

.erp-mstock-key {
    border-top: 1px solid var(--erp-line);
    list-style: none;
    margin: 0;
    padding: 8px 0 0;
}

.erp-mstock-key li {
    align-items: baseline;
    color: var(--erp-ink);
    display: flex;
    font-size: 11px;
    gap: 6px;
    padding: 2px 0;
}

.erp-mstock-key li strong {
    font-size: 11.5px;
    font-weight: 700;
    margin-left: auto;
    white-space: nowrap;
}

.erp-mstock-key li.is-sub {
    color: var(--erp-muted);
    font-size: 10px;
    padding-top: 4px;
}

.erp-mstock-key li.is-sub i { font-size: 9px; }

.erp-mstock-swatch {
    border-radius: 3px;
    flex: none;
    height: 9px;
    position: relative;
    top: 1px;
    width: 9px;
}

.erp-mstock-foot {
    color: var(--erp-muted);
    font-size: 10.5px;
    margin: 11px 0 0;
}

@media (max-width: 575.98px) {
    .erp-mstock-grid { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); }
}

/* Quality-control state on the goods receipts list ------------------------- */

.erp-qc {
    align-items: center;
    border: 1px solid transparent;
    border-radius: 999px;
    display: inline-flex;
    font-size: 11px;
    font-weight: 600;
    gap: 5px;
    padding: 3px 9px;
    text-decoration: none;
    white-space: nowrap;
}

a.erp-qc:hover { filter: brightness(0.94); }

.erp-qc > i { font-size: 10px; }

.erp-qc.is-open {
    animation: erp-qc-blink 1.4s ease-in-out infinite;
    background: #fdecec;
    border-color: #f1c4c4;
    color: #a52121;
}

.erp-qc.is-wait {
    background: #fff6e8;
    border-color: #f3ddb8;
    color: #7c5310;
}

.erp-qc.is-clear {
    background: #e9f8f2;
    border-color: #c2e8da;
    color: #1c7a5c;
}

.erp-qc.is-none {
    color: var(--erp-muted);
    font-weight: 500;
}

/* The blink. Roughly 0.7Hz — far below the three-per-second threshold that
   makes flashing a seizure risk — and it never fades to nothing, so the badge
   stays readable at every point in the cycle. */
@keyframes erp-qc-blink {
    0%, 100% { box-shadow: 0 0 0 0 rgba(208, 59, 59, 0); }
    50% { box-shadow: 0 0 0 3px rgba(208, 59, 59, 0.22); }
}

@keyframes erp-qc-edge {
    0%, 100% { box-shadow: inset 3px 0 0 0 rgba(208, 59, 59, 0.45); }
    50% { box-shadow: inset 3px 0 0 0 rgba(208, 59, 59, 1); }
}

/* An edge stripe rather than a filled row: the figures in the row have to stay
   as readable as any other, and twenty tinted rows would say nothing at all.
   The box-shadow is the still fallback the animation then takes over. */
.erp-table tbody tr.is-qc-open > td:first-child {
    animation: erp-qc-edge 1.4s ease-in-out infinite;
    box-shadow: inset 3px 0 0 0 #d03b3b;
}

/* Motion is a preference the browser already knows about. The state still has
   to be visible, so the colour, icon and wording stay and only the movement
   goes — the badge simply sits still and red. */
@media (prefers-reduced-motion: reduce) {
    .erp-qc.is-open,
    .erp-table tbody tr.is-qc-open > td:first-child {
        animation: none;
    }

    .erp-qc.is-open { box-shadow: 0 0 0 2px rgba(208, 59, 59, 0.25); }
    .erp-table tbody tr.is-qc-open > td:first-child { box-shadow: inset 3px 0 0 0 #d03b3b; }
}

/* Task list — status > task > subtask, on the project dashboard ------------- */

.erp-tasklist { display: flex; flex-direction: column; gap: 6px; }

/* Group -------------------------------------------------------------------- */

.erp-tgroup {
    border: 1px solid var(--erp-line);
    border-radius: 9px;
    overflow: hidden;
}

.erp-tgroup.is-target { border-color: var(--erp-primary); box-shadow: 0 0 0 2px rgba(42, 120, 214, .14); }

.erp-tgroup-head {
    align-items: center;
    background: var(--erp-subtle, #f7f8fa);
    border: 0;
    display: flex;
    gap: 9px;
    padding: 8px 12px;
    text-align: left;
    width: 100%;
}

.erp-tgroup-head:hover { background: #eef1f5; }

.erp-tgroup-caret {
    color: var(--erp-muted);
    font-size: 9px;
    transition: transform .18s ease;
}

.erp-tgroup.is-open .erp-tgroup-caret { transform: rotate(90deg); }

/* The status colour, borrowed from the pill classes so the list and the board
   never disagree about what "On Hold" looks like. */
.erp-tgroup-dot { border-radius: 3px; flex: none; height: 11px; width: 11px; }
.erp-tgroup-dot.is-available { background: #00c292; }
.erp-tgroup-dot.is-booked { background: #2a78d6; }
.erp-tgroup-dot.is-hold { background: #94a3b8; }
.erp-tgroup-dot.is-pending { background: #f0a202; }
.erp-tgroup-dot.is-overdue { background: #d03b3b; }
.erp-tgroup-dot.is-neutral { background: #b9c2cc; }
.erp-tgroup-dot.is-sold { background: #8b5cf6; }

.erp-tgroup-name {
    color: var(--erp-ink);
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .3px;
    text-transform: uppercase;
}

.erp-tgroup-count {
    background: var(--erp-line);
    border-radius: 999px;
    color: var(--erp-muted);
    font-size: 10.5px;
    font-weight: 700;
    padding: 1px 7px;
}

.erp-tgroup-subs { color: var(--erp-muted); font-size: 10px; }

/* 0fr -> 1fr is the one transition that animates to a height the content
   decides. A max-height guess would clip a long group or leave a short one
   easing against nothing. */
.erp-tgroup-wrap {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows .22s ease;
}

.erp-tgroup.is-open > .erp-tgroup-wrap { grid-template-rows: 1fr; }

/* The inner element is what actually clips; the grid row cannot. */
.erp-tgroup-inner { min-height: 0; overflow: hidden; }

/* ...except while a row's three-dot menu is open.

   Two ancestors clip a menu opened inside a row: .erp-tgroup, for its rounded
   corners, and .erp-tgroup-inner, which is what the 0fr -> 1fr collapse needs
   something to clip against. The menu is a child of the row, so both cut it
   off at the group's edge and it reads as sliding under the rows below.

   Unclipping while the menu is open is safe because the group is necessarily
   expanded at that moment — the row had to be visible for anyone to click its
   button — so there is no collapse in flight for the clip to be doing a job
   for. The class comes off as soon as the menu closes and the clip is back
   before any group can animate again.

   Clipping is the whole bug, so clipping is all this touches. The row is NOT
   given a z-index: .erp-trow-line is position:relative but z-index:auto, so it
   creates no stacking context, and the menu's own z-index (1000, Bootstrap's)
   already paints it above every later row. Lifting the row would have made the
   row a stacking context and trapped that 1000 inside it — putting the menu
   UNDER the sticky bars on this page, which sit at 5 and 1090. */
.erp-tgroup.has-menu-open,
.erp-tgroup.has-menu-open .erp-tgroup-inner { overflow: visible; }

/* A subtask's menu is clipped a third time, by the wrapper its parent row uses
   to fold it away. Scoped to the row that actually owns the open menu, and not
   to every row in the group: a sibling row with its subtasks COLLAPSED is
   relying on this same clip to hide them, and unclipping the lot would spill
   them all over the list. The owning row's subtasks are necessarily expanded —
   the menu was clicked on one of them. */
.erp-trow.has-menu-open > .erp-trow-subs > .erp-trow-subs-inner { overflow: visible; }

.erp-tgroup-none {
    color: var(--erp-muted);
    font-size: 11.5px;
    margin: 0;
    padding: 10px 14px 12px 34px;
}

/* Row ---------------------------------------------------------------------- */

.erp-trow { border-top: 1px solid var(--erp-line); }
.erp-tgroup-inner > .erp-trow:first-child { border-top: 0; }

.erp-trow-line {
    align-items: center;
    background: var(--erp-surface, #fff);
    display: flex;
    gap: 9px;
    padding: 7px 12px;
    position: relative;
}

.erp-trow-line:hover { background: #fafbfc; }

/* Row tints -----------------------------------------------------------------

   Two jobs, two treatments.

   A MAIN task row gets one common sober tone, the same on every row whatever
   colour it carries. These are the structural rows — the things the project is
   actually made of — so they should read as one rank rather than as eight
   competing labels.

   A SUBTASK row is tinted from the colour chosen in the picker, washed down to
   about a tenth so it tells you which branch you are in without shouting. The
   custom property inherits: subtask rows are nested inside their parent's
   element, so colouring a parent tints everything under it, and a subtask that
   carries its own colour overrides just itself. That is why this needs no Blade
   change — the cascade already models "belongs to".

   The default below is what makes the mix safe: without it an uncoloured task
   would hand color-mix() nothing to work with. It is also deliberately a cool
   grey, so an uncoloured subtask still reads as a step in from its parent. */

.erp-tasklist { --task-color: #94a3b8; }

/* Main rows — one tone, and it is not white: the subtask wash below is lighter,
   so the nesting reads as going inward rather than darker. */
.erp-tasklist .erp-trow > .erp-trow-line { background: #f3f5f8; }
.erp-tasklist .erp-trow > .erp-trow-line:hover { background: #eceff4; }

/* Subtask rows — the picker colour, at a tenth. The flat colour is declared
   first as the fallback any browser without color-mix() will keep. */
.erp-tasklist .erp-trow.is-sub > .erp-trow-line {
    background: #fbfcfd;
    background: color-mix(in srgb, var(--task-color) 9%, #fff);
}

.erp-tasklist .erp-trow.is-sub > .erp-trow-line:hover {
    background: #f3f6f9;
    background: color-mix(in srgb, var(--task-color) 16%, #fff);
}

/* The edge stripe stays opt-in — only a row that actually carries a colour gets
   one, so the default above never paints a stripe on everything. */
.erp-tasklist .erp-trow.is-sub > .erp-trow-line::before { width: 2px; }

/* A finished row recedes whatever its colour: done work is reference, not
   something to keep drawing the eye. */
.erp-tasklist .erp-trow.is-done > .erp-trow-line,
.erp-tasklist .erp-trow.is-done.is-sub > .erp-trow-line { background: #f7f8fa; }

.erp-trow.is-dragging > .erp-trow-line { opacity: .45; }

/* The colour tag, as an edge stripe rather than a filled row: the figures in
   the row have to stay as readable as any other. */
.erp-trow.has-color > .erp-trow-line::before {
    background: var(--task-color);
    bottom: 0;
    content: "";
    left: 0;
    position: absolute;
    top: 0;
    width: 3px;
}

.erp-trow-twisty {
    background: none;
    border: 0;
    color: var(--erp-muted);
    flex: none;
    font-size: 9px;
    height: 18px;
    padding: 0;
    width: 18px;
}

.erp-trow-twisty i { transition: transform .18s ease; }
.erp-trow-twisty:not(.is-blank):hover { color: var(--erp-primary); }
.erp-trow:not(.subs-closed) > .erp-trow-line > .erp-trow-twisty i { transform: rotate(90deg); }

/* Selection mode is a class on the board host, so it survives the swaps this
   markup does not. */
.erp-trow-pick { display: none; flex: none; margin: 0; }
[data-task-board].is-selecting .erp-trow-pick { display: inline-flex; }

.erp-trow-check { cursor: pointer; flex: none; margin: 0; position: relative; }
.erp-trow-check input { height: 0; opacity: 0; position: absolute; width: 0; }

.erp-trow-check i {
    align-items: center;
    border: 1.5px solid var(--erp-line);
    border-radius: 50%;
    color: transparent;
    display: flex;
    font-size: 8px;
    height: 17px;
    justify-content: center;
    width: 17px;
}

.erp-trow-check:hover i { border-color: var(--erp-primary); color: #c9d8ea; }
.erp-trow-check input:checked + i { background: #00c292; border-color: #00c292; color: #fff; }
.erp-trow-check input:disabled + i { cursor: default; opacity: .55; }

.erp-trow-title {
    align-items: baseline;
    display: flex;
    flex: 1 1 auto;
    gap: 7px;
    min-width: 0;
}

.erp-trow-name {
    color: var(--erp-ink);
    font-size: 12.5px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-trow.is-done .erp-trow-name { color: var(--erp-muted); text-decoration: line-through; }

.erp-trow-cat {
    background: var(--erp-line);
    border-radius: 4px;
    color: var(--erp-muted);
    flex: none;
    font-size: 9.5px;
    padding: 1px 6px;
}

.erp-trow-kids { color: var(--erp-muted); flex: none; font-size: 10px; }
.erp-trow-kids i { margin-right: 3px; }

.erp-trow-status { flex: none; }

/* The status pill as a control.

   It is a <button> now, so the browser's own button styling has to be undone
   before .erp-pill's colours show through — background and border especially,
   which would otherwise paint a grey chip over the status tint. */
.erp-trow-status-pick { flex: none; }

button.erp-trow-status.is-pick {
    align-items: center;
    background-color: transparent;   /* .erp-pill's own tint supplies the fill */
    border: 0;
    cursor: pointer;
    display: inline-flex;
    font: inherit;
    font-size: 11.5px;
    font-weight: 600;
    gap: 2px;
    letter-spacing: .2px;
    padding: 3px 9px 3px 11px;
}

/* The caret earns its place: without it the pill looks like the label it used
   to be, and nobody clicks a label. Dimmed so it reads as an affordance rather
   than as part of the status. */
.erp-trow-status-caret {
    font-size: 9px;
    margin-left: 1px;
    opacity: .55;
    transition: opacity .14s ease;
}

button.erp-trow-status.is-pick:hover .erp-trow-status-caret,
button.erp-trow-status.is-pick[aria-expanded="true"] .erp-trow-status-caret { opacity: 1; }

button.erp-trow-status.is-pick:hover { filter: brightness(.96); }

button.erp-trow-status.is-pick:focus-visible {
    outline: 2px solid var(--erp-primary);
    outline-offset: 2px;
}

.erp-status-menu { font-size: 12.5px; min-width: 170px; }
.erp-status-menu .dropdown-header { font-size: 10.5px; letter-spacing: .3px; text-transform: uppercase; }
.erp-status-menu .dropdown-item { align-items: center; display: flex; gap: 8px; }

/* The same dot the pill wears, so the menu row and the result match. The pill
   classes carry a background AND a colour; here only the colour is wanted. */
.erp-status-menu-icon {
    background: none !important;
    font-size: 11px;
    width: 14px;
}

.erp-trow-when { color: var(--erp-muted); flex: none; font-size: 11px; min-width: 84px; }
.erp-trow-when i { margin-right: 4px; }
.erp-trow-when .is-late { color: #d03b3b; font-weight: 600; }

.erp-trow-cost {
    color: var(--erp-muted);
    display: flex;
    flex: none;
    font-size: 11px;
    gap: 8px;
    justify-content: flex-end;
    min-width: 96px;
}

.erp-trow-cost .is-consumed { color: #7c5310; }
.erp-trow-cost .is-consumed i { margin-right: 3px; }

.erp-trow-progress { align-items: center; display: flex; flex: none; gap: 6px; width: 96px; }
.erp-trow-progress .erp-progress { flex: 1 1 auto; }
.erp-trow-progress small { color: var(--erp-muted); font-size: 10px; min-width: 26px; text-align: right; }

.erp-trow-people { display: flex; flex: none; gap: 2px; }

.erp-trow-acts { align-items: center; display: flex; flex: none; gap: 1px; }

.erp-trow-btn {
    align-items: center;
    background: none;
    border: 0;
    border-radius: 5px;
    color: var(--erp-muted);
    display: inline-flex;
    font-size: 11px;
    gap: 3px;
    height: 24px;
    justify-content: center;
    min-width: 24px;
    padding: 0 5px;
}

.erp-trow-btn:hover { background: var(--erp-line); color: var(--erp-ink); }
.erp-trow-btn.has-count { color: var(--erp-primary); }
.erp-trow-btn span { font-size: 9.5px; font-weight: 700; }

/* Subtasks ----------------------------------------------------------------- */

.erp-trow-subs {
    display: grid;
    grid-template-rows: 1fr;
    transition: grid-template-rows .2s ease;
}

.erp-trow.subs-closed > .erp-trow-subs { grid-template-rows: 0fr; }
.erp-trow-subs-inner { min-height: 0; overflow: hidden; position: relative; }

/* The indent, and a guide line down to the last child so a long subtask list
   still reads as belonging to the row above it. */
.erp-trow.is-sub > .erp-trow-line { padding-left: 40px; }

.erp-trow.is-sub > .erp-trow-line::after {
    background: var(--erp-line);
    content: "";
    height: 1px;
    left: 26px;
    position: absolute;
    top: 50%;
    width: 8px;
}

.erp-trow-subs-inner::before {
    background: var(--erp-line);
    bottom: 18px;
    content: "";
    left: 26px;
    position: absolute;
    top: 0;
    width: 1px;
}

.erp-tasklist-foot { color: var(--erp-muted); font-size: 11px; margin: 2px 0 0; text-align: right; }

/* Narrow screens: the figures fold away before the title does. */
@media (max-width: 991.98px) {
    .erp-trow-cost, .erp-trow-progress { display: none; }
}

@media (max-width: 767.98px) {
    .erp-trow-when, .erp-trow-status { display: none; }
}

/* Motion is a preference the browser already knows about. The fold still
   works — it just arrives rather than eases. */
@media (prefers-reduced-motion: reduce) {
    .erp-tgroup-wrap,
    .erp-trow-subs,
    .erp-tgroup-caret,
    .erp-trow-twisty i { transition: none; }
}

/* The visit summary, carried under its own row on the supervision register. */
.erp-visit-summary > td {
    background: var(--erp-subtle, #f7f8fa);
    border-top: 0 !important;
    color: var(--erp-ink);
    font-size: 12px;
    line-height: 1.5;
    padding: 4px 14px 10px 14px !important;
}

.erp-visit-summary > td > i {
    color: var(--erp-muted);
    font-size: 9px;
    margin-right: 6px;
    opacity: .7;
    position: relative;
    top: -1px;
}

/* Long entries wrap rather than stretching the table; line breaks an engineer
   typed are kept, because a list of findings is usually written as one. */
.erp-visit-summary > td > span {
    display: inline;
    overflow-wrap: anywhere;
    white-space: pre-wrap;
}

/* Income/expense detail — compact -------------------------------------------- */

/* Amount, state and progress in one strip. These three questions were answered
   in three separate places down the page, which is most of why it scrolled. */
.erp-iehead {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 18px 28px;
    justify-content: space-between;
    margin-bottom: 12px;
    padding: 13px 18px;
}

.erp-iehead-money {
    align-items: baseline;
    display: flex;
    flex-wrap: wrap;
    gap: 6px 14px;
}

.erp-iehead-amount {
    color: var(--erp-ink);
    font-size: 21px;
    font-weight: 700;
    letter-spacing: -.2px;
    line-height: 1;
}

.erp-iehead-meta { color: var(--erp-muted); font-size: 12px; }
.erp-iehead-pills { display: inline-flex; flex-wrap: wrap; gap: 5px; }

/* The decision trail, laid along the strip instead of down a card. Four steps
   stacked vertically with "Not yet" under each was the tallest empty block on
   the page. */
.erp-iesteps {
    display: flex;
    flex-wrap: wrap;
    gap: 0;
    list-style: none;
    margin: 0;
    padding: 0;
}

.erp-iestep {
    align-items: center;
    display: flex;
    gap: 7px;
    padding-right: 16px;
    position: relative;
}

.erp-iestep:not(:last-child) { margin-right: 16px; }

/* The connector, drawn between dots rather than around the whole item. */
.erp-iestep:not(:last-child)::after {
    background: var(--erp-line);
    content: "";
    height: 1px;
    position: absolute;
    right: 0;
    top: 13px;
    width: 16px;
}

.erp-iestep-dot {
    align-items: center;
    background: var(--erp-bg);
    border: 1px solid var(--erp-line);
    border-radius: 50%;
    color: var(--erp-muted);
    display: flex;
    flex: none;
    font-size: 10px;
    height: 26px;
    justify-content: center;
    width: 26px;
}

.erp-iestep.is-done .erp-iestep-dot {
    background: #e9f8f2;
    border-color: #a6dcc8;
    color: #1c7a5c;
}

.erp-iestep.is-bad.is-done .erp-iestep-dot {
    background: #fdecec;
    border-color: #f1c4c4;
    color: #a52121;
}

.erp-iestep-body { display: flex; flex-direction: column; line-height: 1.25; }
.erp-iestep-body strong { color: var(--erp-ink); font-size: 11.5px; font-weight: 600; }
.erp-iestep-body small { color: var(--erp-muted); font-size: 10px; }

/* Notes that used to sit inside cards; as one-line strips they cost a fraction
   of the height and read first. */
.erp-ienote {
    align-items: flex-start;
    background: #eef4fb;
    border: 1px solid #cfe0f2;
    border-radius: 8px;
    color: #1f4a76;
    display: flex;
    font-size: 12px;
    gap: 8px;
    margin: 0 0 12px;
    padding: 8px 12px;
}

.erp-ienote > i { position: relative; top: 2px; }
.erp-ienote.is-bad { background: #fdecec; border-color: #f1c4c4; color: #8c2b2b; }
.erp-ienote.is-warn { background: #fff8ec; border-color: #f5e0bd; color: #7c5310; }

/* Cards -------------------------------------------------------------------- */

.erp-iecard { margin-bottom: 12px; overflow: hidden; padding: 0; }

.erp-iecard-head {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    gap: 8px;
    padding: 9px 14px;
}

.erp-iecard-head > i { color: var(--erp-muted); font-size: 11px; }

.erp-iecard-head h3 {
    color: var(--erp-ink);
    font-size: 12.5px;
    font-weight: 600;
    margin: 0;
}

.erp-iecard-count {
    background: var(--erp-line);
    border-radius: 999px;
    color: var(--erp-muted);
    font-size: 10px;
    font-weight: 700;
    padding: 1px 7px;
}

.erp-iecard-upload { margin-left: auto; }

.erp-iecard-file {
    align-items: center;
    border: 1px dashed var(--erp-line);
    border-radius: 6px;
    color: var(--erp-muted);
    cursor: pointer;
    display: inline-flex;
    font-size: 11px;
    gap: 5px;
    margin: 0;
    padding: 3px 9px;
}

.erp-iecard-file:hover { border-color: var(--erp-primary); color: var(--erp-primary); }
.erp-iecard-file input[type="file"] { height: 0; opacity: 0; position: absolute; width: 0; }

/* Facts — denser than the shared task grid, which was built for a wider
   column and left this one with three items to a row and 14px gutters. */
.erp-iefacts {
    display: grid;
    gap: 9px 16px;
    grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
    margin: 0;
    padding: 12px 14px;
}

.erp-iefacts dt {
    color: var(--erp-muted);
    font-size: 9.5px;
    font-weight: 600;
    letter-spacing: .04em;
    margin: 0 0 1px;
    text-transform: uppercase;
}

.erp-iefacts dd {
    color: var(--erp-ink);
    font-size: 12.5px;
    margin: 0;
    overflow-wrap: anywhere;
}

.erp-iedesc {
    border-top: 1px solid var(--erp-line);
    color: var(--erp-ink);
    font-size: 12.5px;
    margin: 0;
    padding: 10px 14px;
    white-space: pre-wrap;
}

/* Voucher ------------------------------------------------------------------ */

.erp-ievoucher th, .erp-ievoucher td { font-size: 12px; padding: 7px 12px; }
.erp-ievoucher .erp-identity-meta { font-size: 10.5px; }

.erp-ievoucher-none {
    align-items: center;
    color: var(--erp-muted);
    display: flex;
    font-size: 12px;
    gap: 9px;
    margin: 0;
    padding: 16px 14px;
}

.erp-ievoucher-none > i { font-size: 15px; opacity: .6; }

/* Attachment tiles, sized for a side column rather than a full page. */
.erp-iecard .erp-doc-grid { gap: 8px; padding: 12px 14px; }
.erp-iecard .erp-doc-tile { width: 104px; }
.erp-iecard .erp-doc-preview { height: 70px; }
.erp-iecard .erp-doc-icon { font-size: 24px; }
.erp-iecard .erp-cv-empty { margin: 0; padding: 14px; }

@media (max-width: 767.98px) {
    .erp-iehead { gap: 12px; }
    .erp-iesteps { gap: 10px 0; }
    .erp-iestep:not(:last-child)::after { display: none; }
    .erp-iestep:not(:last-child) { margin-right: 12px; }
}

.erp-help.is-attached { color: #1c7a5c; font-weight: 600; }

/* Bulk income/expense entry grid -------------------------------------------- */

.erp-bulk-fill {
    align-items: center;
    background: var(--erp-subtle, #f7f8fa);
    border: 1px solid var(--erp-line);
    border-radius: 9px;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 12px;
    padding: 9px 12px;
}

.erp-bulk-fill-label {
    color: var(--erp-muted);
    font-size: 11.5px;
    font-weight: 600;
    white-space: nowrap;
}

.erp-bulk-fill-label i { color: var(--erp-primary); }
.erp-bulk-fill input[type="date"] { max-width: 160px; }
.erp-bulk-fill select { max-width: 230px; }

/* The grid scrolls sideways on a narrow screen rather than crushing eight
   columns of inputs into an unusable width. */
.erp-bulk-wrap { max-height: 46vh; overflow: auto; }

.erp-bulk-table th {
    background: var(--erp-surface, #fff);
    position: sticky;
    top: 0;
    z-index: 2;
}

.erp-bulk-table td { padding: 5px 6px; vertical-align: middle; }
.erp-bulk-table .form-control, .erp-bulk-table .form-select { font-size: 12px; }

.erp-bulk-num {
    color: var(--erp-muted);
    font-size: 11px;
    font-weight: 600;
    text-align: center;
}

/* The native file input is unstylable and reads "No file chosen" forever; the
   label replaces it with a count that changes as files are picked. */
.erp-bulk-file {
    align-items: center;
    border: 1px dashed var(--erp-line);
    border-radius: 7px;
    cursor: pointer;
    display: flex;
    font-size: 11px;
    gap: 5px;
    justify-content: center;
    margin: 0;
    padding: 6px 8px;
    white-space: nowrap;
}

.erp-bulk-file input[type="file"] {
    height: 0;
    opacity: 0;
    position: absolute;
    width: 0;
}

.erp-bulk-file:hover { border-color: var(--erp-primary); color: var(--erp-primary); }

.erp-bulk-file.has-files {
    background: #e9f8f2;
    border-color: #c2e8da;
    border-style: solid;
    color: #1c7a5c;
    font-weight: 600;
}

.erp-bulk-foot {
    align-items: center;
    display: flex;
    gap: 12px;
    margin-top: 12px;
}

.erp-bulk-count {
    color: var(--erp-muted);
    font-size: 11.5px;
    margin-left: auto;
}

/* Income vs expense ring on the income/expense index ----------------------- */

.erp-iesplit { margin-bottom: 14px; }

.erp-iesplit-body {
    align-items: center;
    display: flex;
    gap: 26px;
}

.erp-iesplit-ring {
    flex: none;
    height: 150px;
    position: relative;
    width: 150px;
}

.erp-iesplit-ring canvas { height: 100% !important; width: 100% !important; }

.erp-iesplit-centre {
    align-items: center;
    display: flex;
    flex-direction: column;
    inset: 0;
    justify-content: center;
    pointer-events: none;
    position: absolute;
    text-align: center;
}

.erp-iesplit-centre strong {
    color: var(--erp-primary);
    font-size: 19px;
    font-weight: 700;
    line-height: 1;
    max-width: 92px;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Spending more than came in is worth seeing without reading the words. */
.erp-iesplit-centre.is-negative strong { color: #d03b3b; }

.erp-iesplit-centre small {
    color: var(--erp-muted);
    font-size: 9.5px;
    letter-spacing: .4px;
    margin-top: 4px;
    text-transform: uppercase;
}

.erp-iesplit-key {
    flex: 1;
    list-style: none;
    margin: 0;
    max-width: 340px;
    min-width: 0;
    padding: 0;
}

.erp-iesplit-key li {
    align-items: baseline;
    color: var(--erp-ink);
    display: flex;
    font-size: 12.5px;
    gap: 8px;
    padding: 5px 0;
}

.erp-iesplit-key li strong {
    font-size: 13px;
    font-weight: 700;
    margin-left: auto;
    white-space: nowrap;
}

.erp-iesplit-key li.is-sub {
    color: var(--erp-muted);
    font-size: 11px;
    padding-top: 4px;
}

.erp-iesplit-key li.is-sub i { font-size: 9.5px; }

.erp-iesplit-split {
    border-top: 1px solid var(--erp-line);
    color: var(--erp-muted);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .4px;
    margin-top: 6px;
    padding-top: 8px !important;
    text-transform: uppercase;
}

.erp-iesplit-swatch {
    border-radius: 3px;
    flex: none;
    height: 10px;
    position: relative;
    top: 1px;
    width: 10px;
}

@media (max-width: 575.98px) {
    .erp-iesplit-body { flex-direction: column; gap: 16px; }
    .erp-iesplit-key { max-width: none; width: 100%; }
}

/* Bank on an agreement collection ------------------------------------------ */

.erp-bank-empty {
    align-items: center;
    background: #fff8ec;
    border: 1px solid #f5e0bd;
    border-radius: 8px;
    color: #7c5310;
    display: flex;
    font-size: 11.5px;
    gap: 10px;
    padding: 10px 12px;
}

.erp-bank-empty > i { color: var(--erp-warning); font-size: 15px; }
.erp-bank-empty strong { display: block; font-size: 12.5px; }
.erp-bank-empty span { display: block; }
.erp-bank-empty a { flex: none; margin-left: auto; white-space: nowrap; }

/* The chosen bank's own record, shown read-only. Not inputs: these are facts
   about the bank, not fields of the payment, and editing them here would be
   editing a copy nobody else would ever see. */
.erp-bank-card {
    background: var(--erp-subtle, #f7f8fa);
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    margin-top: 8px;
    padding: 10px 12px;
}

.erp-bank-card dl {
    display: grid;
    gap: 6px 16px;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    margin: 0;
}

.erp-bank-card dt {
    color: var(--erp-muted);
    font-size: 9.5px;
    font-weight: 600;
    letter-spacing: .4px;
    margin: 0;
    text-transform: uppercase;
}

.erp-bank-card dd {
    color: var(--erp-ink);
    font-size: 12.5px;
    font-weight: 600;
    margin: 1px 0 0;
    overflow-wrap: anywhere;
}

/* What an installment has already taken, inside the Receive modal. */
.erp-paid-so-far {
    border-top: 1px solid var(--erp-line);
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-top: 14px;
    padding-top: 11px;
}

.erp-paid-so-far span {
    align-items: baseline;
    color: var(--erp-muted);
    display: flex;
    font-size: 11.5px;
    gap: 6px;
}

.erp-paid-so-far b { color: var(--erp-ink); font-weight: 600; }
.erp-paid-so-far em { font-style: normal; font-weight: 700; margin-left: auto; }

/* Expander for one installment's own receipts. */
.erp-pay-toggle {
    background: none;
    border: 0;
    color: var(--erp-primary);
    display: inline-flex;
    align-items: center;
    font-size: 10.5px;
    font-weight: 600;
    gap: 4px;
    margin-left: 8px;
    padding: 1px 5px;
}

.erp-pay-toggle i { font-size: 8px; transition: transform .15s ease; }
.erp-pay-toggle.is-open i { transform: rotate(90deg); }

.erp-pay-detail > td { background: var(--erp-subtle, #f7f8fa); padding: 0 12px 10px !important; }

.erp-pay-table { border-collapse: collapse; width: 100%; }

.erp-pay-table th {
    color: var(--erp-muted);
    font-size: 9.5px;
    font-weight: 600;
    letter-spacing: .4px;
    padding: 7px 8px 5px;
    text-align: left;
    text-transform: uppercase;
}

.erp-pay-table td {
    border-top: 1px solid var(--erp-line);
    font-size: 11.5px;
    padding: 6px 8px;
}

.erp-pay-table .text-end { text-align: right; }

.erp-pay-split {
    background: var(--erp-line);
    border-radius: 999px;
    color: var(--erp-muted);
    font-size: 9px;
    margin-left: 6px;
    padding: 1px 6px;
    white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
    .erp-pay-toggle i { transition: none; }
}

/* Stock transfers ---------------------------------------------------------- */

.erp-transit-strip {
    align-items: center;
    background: #fff8ec;
    border: 1px solid #f5e0bd;
    border-radius: var(--erp-radius);
    color: #7c5310;
    display: flex;
    font-size: 12.5px;
    gap: 10px;
    margin-bottom: 14px;
    padding: 11px 15px;
}

.erp-transit-strip > i { color: var(--erp-warning); }
.erp-transit-strip strong { font-size: 14px; }
.erp-transit-strip a { color: #7c5310; font-weight: 600; margin-left: auto; white-space: nowrap; }

.erp-route {
    align-items: center;
    display: inline-flex;
    font-size: 12.5px;
    gap: 7px;
}

.erp-route i { color: var(--erp-muted); font-size: 10px; }

.erp-route-arrow { color: var(--erp-muted); font-size: 16px; }

.erp-inline-date { display: flex; flex-direction: column; gap: 2px; margin: 0; }

.erp-inline-date span {
    color: var(--erp-muted);
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: .4px;
    padding-left: 2px;
    text-transform: uppercase;
}

.erp-inline-date input { width: 140px; }

/* Material that left one shelf and reached no other. */
.erp-transfer-loss {
    align-items: flex-start;
    background: #fde8e8;
    border-top: 1px solid #f5c6c6;
    color: #b91c1c;
    display: flex;
    font-size: 12.5px;
    gap: 11px;
    padding: 13px 18px;
}

.erp-transfer-loss strong { display: block; }
.erp-transfer-loss span { color: #8a3232; }

.erp-line-table td { vertical-align: middle; }
.erp-line-empty {
    color: var(--erp-muted);
    font-size: 12.5px;
    margin: 0;
    padding: 22px 16px;
    text-align: center;
}

.erp-table tr.is-warning-row td { background: #fffcf3; }

/* The document's own history. */
.erp-timeline { list-style: none; margin: 0; padding: 0; }

.erp-timeline li {
    display: flex;
    gap: 11px;
    padding-bottom: 14px;
    position: relative;
}

.erp-timeline li:not(:last-child)::before {
    background: var(--erp-line);
    bottom: 0;
    content: '';
    left: 13px;
    position: absolute;
    top: 28px;
    width: 2px;
}

.erp-timeline-dot {
    align-items: center;
    background: color-mix(in srgb, var(--erp-primary) 12%, transparent);
    border-radius: 50%;
    color: var(--erp-primary-dark);
    display: flex;
    flex: none;
    font-size: 11px;
    height: 28px;
    justify-content: center;
    width: 28px;
    z-index: 1;
}

.erp-timeline strong { display: block; font-size: 12.5px; }
.erp-timeline small { color: var(--erp-muted); font-size: 11px; }

/* Live shelf check on the stock-transfer line editor. The red state is the
   point of it: a material the source store simply does not have. */
.erp-stock-flag {
    align-items: center;
    border-radius: 20px;
    display: inline-flex;
    font-size: 11px;
    font-weight: 600;
    gap: 6px;
    line-height: 1.3;
    padding: 4px 10px;
    white-space: nowrap;
}

.erp-stock-flag > i { font-size: 10px; }

.erp-stock-flag.is-idle {
    background: var(--erp-bg);
    color: var(--erp-muted);
    font-weight: 500;
}

.erp-stock-flag.is-ok {
    background: color-mix(in srgb, var(--erp-primary) 13%, transparent);
    color: var(--erp-primary-dark);
}

/* Some, but not as much as is being asked for. */
.erp-stock-flag.is-short {
    background: #fff4e0;
    color: #9a5b00;
}

/* Nothing on that shelf at all. */
.erp-stock-flag.is-none {
    background: #fde8e8;
    color: #b91c1c;
}

.erp-stock-flag.is-unknown {
    background: var(--erp-bg);
    color: #8a5a00;
}

/* The row itself picks up the alarm, so a long line list can be scanned
   rather than read cell by cell. */
.erp-line-table tr:has(.erp-stock-flag.is-none) td { background: #fffafa; }
.erp-line-table tr:has(.erp-stock-flag.is-short) td { background: #fffdf7; }

/* Stock transfer document — one card, not seven.
   Everything here is tighter than the app default on purpose: a transfer is a
   short document that gets scanned, not a record that gets studied. */

.erp-tr-head {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    justify-content: space-between;
    padding: 20px 22px;
}

.erp-tr-route {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 22px;
}

.erp-tr-store {
    align-items: center;
    display: inline-flex;
    gap: 14px;
}

/* Big enough to read at a glance from across a desk — this is the one thing
   on the page somebody checks before anything else. */
.erp-tr-store > i {
    align-items: center;
    border-radius: 13px;
    display: flex;
    flex: none;
    font-size: 19px;
    height: 52px;
    justify-content: center;
    width: 52px;
}

/* Leaving and arriving are different events, so they do not look alike. */
.erp-tr-store.is-from > i {
    background: color-mix(in srgb, var(--erp-warning) 15%, transparent);
    color: #9a5b00;
}

.erp-tr-store.is-to > i {
    background: color-mix(in srgb, var(--erp-primary) 14%, transparent);
    color: var(--erp-primary-dark);
}

.erp-tr-store-body {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}

.erp-tr-store-body small {
    color: var(--erp-muted);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .7px;
    line-height: 1;
    text-transform: uppercase;
}

.erp-tr-store-body strong {
    color: var(--erp-ink);
    font-size: 18px;
    font-weight: 700;
    line-height: 1.2;
}

.erp-tr-store-body em {
    align-items: center;
    color: var(--erp-muted);
    display: flex;
    font-size: 12.5px;
    font-style: normal;
    gap: 6px;
    line-height: 1.2;
}

.erp-tr-store-body em > i { font-size: 10px; opacity: .8; }

.erp-tr-arrow {
    color: var(--erp-muted);
    font-size: 20px;
    opacity: .55;
}

.erp-tr-status { align-self: center; font-size: 12px; padding: 5px 14px; }

/* Facts on one strip rather than a grid of metric tiles. */
.erp-tr-facts {
    background: #fafbfd;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    gap: 4px 26px;
    padding: 9px 16px;
}

.erp-tr-facts > span {
    color: var(--erp-ink);
    font-size: 12.5px;
    font-weight: 600;
}

.erp-tr-facts > span small {
    color: var(--erp-muted);
    display: block;
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: .4px;
    text-transform: uppercase;
}

.erp-tr-facts > span.is-warning { color: #9a5b00; }

.erp-tr-alert {
    align-items: flex-start;
    display: flex;
    font-size: 12px;
    gap: 9px;
    padding: 10px 16px;
}

.erp-tr-alert.is-loss {
    background: #fde8e8;
    border-bottom: 1px solid #f5c6c6;
    color: #8a3232;
}

.erp-tr-alert.is-loss > i { color: #b91c1c; }

.erp-tr-lines td, .erp-tr-lines th { padding-bottom: 7px; padding-top: 7px; }
.erp-tr-lines td { vertical-align: middle; }

.erp-tr-sub {
    color: var(--erp-muted);
    display: block;
    font-size: 10.5px;
}

/* The action bar under the lines — replaces a whole card per action. */
.erp-tr-act {
    align-items: center;
    background: #fafbfd;
    border-top: 1px solid var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: space-between;
    padding: 10px 16px;
}

.erp-tr-act > span {
    color: var(--erp-muted);
    flex: 1;
    font-size: 11.5px;
    min-width: 220px;
}

.erp-tr-act.is-short { background: #fff8ec; }
.erp-tr-act.is-short > span { color: #7c5310; }
.erp-tr-act.is-short i { color: var(--erp-warning); }

.erp-tr-notes {
    border-top: 1px solid var(--erp-line);
    color: var(--erp-muted);
    font-size: 12px;
    padding: 10px 16px;
}

.erp-tr-notes strong { color: var(--erp-ink); margin-right: 6px; }

/* History and ledger, side by side and small. */
.erp-tr-meta {
    display: grid;
    gap: 14px;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    margin-top: 14px;
}

.erp-tr-meta .erp-card { margin-bottom: 0; }

.erp-tr-metahead {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    color: var(--erp-ink);
    display: flex;
    font-size: 11px;
    font-weight: 700;
    gap: 8px;
    letter-spacing: .5px;
    padding: 10px 14px;
    text-transform: uppercase;
}

.erp-tr-metahead > i { color: var(--erp-muted); font-size: 11px; }

.erp-tr-metahead > span {
    background: var(--erp-bg);
    border-radius: 20px;
    color: #55637a;
    font-size: 10px;
    margin-left: auto;
    padding: 1px 8px;
}

.erp-tr-trail, .erp-tr-ledger { list-style: none; margin: 0; padding: 6px 14px 10px; }

.erp-tr-trail li {
    align-items: baseline;
    display: flex;
    font-size: 11.5px;
    gap: 9px;
    padding: 4px 0;
}

.erp-tr-trail li > i { color: var(--erp-primary); font-size: 10px; width: 13px; }
.erp-tr-trail strong { color: var(--erp-ink); margin-right: 5px; }
.erp-tr-trail span { color: var(--erp-muted); }

.erp-tr-ledger li {
    align-items: baseline;
    border-bottom: 1px solid #f4f6fa;
    display: flex;
    font-size: 11.5px;
    gap: 9px;
    padding: 5px 0;
}

.erp-tr-ledger li:last-child { border-bottom: 0; }
.erp-tr-ledger li > span { color: var(--erp-ink); font-weight: 600; }
.erp-tr-ledger li > small { color: var(--erp-muted); font-size: 10.5px; }
.erp-tr-ledger li > strong { margin-left: auto; }

.erp-tr-empty {
    color: var(--erp-muted);
    font-size: 11.5px;
    margin: 0;
    padding: 16px 14px;
    text-align: center;
}

@media (max-width: 575.98px) {
    .erp-tr-facts { gap: 4px 16px; }
    .erp-tr-act { flex-direction: column; align-items: stretch; }
}

/* ---------------------------------------------------------------------------
   Warehouse composition (stock screen)

   Four pies to a row on a wide screen, dropping to two and then one. Each
   card is a fixed-height canvas over a scrollable legend, so a store holding
   two items and one holding nine still line up in the grid — ragged card
   heights in a four-column grid read as broken rather than as data.
--------------------------------------------------------------------------- */

/* Segmented control — a two-way view switch, not two buttons. */
.erp-seg {
    display: inline-flex;
    padding: 3px;
    gap: 2px;
    border: 1px solid var(--erp-border);
    border-radius: 10px;
    background: var(--erp-surface-2);
}

.erp-seg-btn {
    border: none;
    background: transparent;
    color: var(--erp-muted);
    font-size: 12px;
    font-weight: 600;
    padding: 5px 11px;
    border-radius: 7px;
    line-height: 1.2;
    white-space: nowrap;
}

.erp-seg-btn:hover { color: var(--erp-ink); }

/* The switch is sometimes two links rather than two buttons — a view that has
   its own URL should be reachable by middle-click and bookmarkable, which a
   button cannot be. Same pill, so the two spellings look identical. */
a.erp-seg-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    text-decoration: none;
}

a.erp-seg-btn:hover { text-decoration: none; }

.erp-seg-btn.is-on {
    background: var(--erp-surface);
    color: var(--erp-primary-dark);
    box-shadow: 0 1px 3px rgba(15, 23, 42, .12);
}

.erp-whchart-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 12px;
}

@media (max-width: 1399px) { .erp-whchart-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 991px)  { .erp-whchart-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 575px)  { .erp-whchart-grid { grid-template-columns: 1fr; } }

.erp-whchart {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--erp-border);
    border-radius: 12px;
    background: var(--erp-surface);
    padding: 12px;
    min-width: 0;
}

.erp-whchart.is-empty { background: var(--erp-surface-2); }

.erp-whchart-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 10px;
}

.erp-whchart-name { min-width: 0; }

.erp-whchart-name strong {
    display: block;
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.erp-whchart-name span {
    display: block;
    font-size: 11px;
    color: var(--erp-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Fixed height: a responsive canvas with no height collapses to nothing. */
/* Taller than the pie it replaced: horizontal bars need a row each, and a
   square box that suited a circle leaves them crushed together. */
.erp-whchart-canvas {
    height: 186px;
    position: relative;
}

.erp-whchart-total {
    text-align: center;
    font-size: 13px;
    font-weight: 700;
    margin-top: 8px;
}

.erp-whchart-legend {
    list-style: none;
    margin: 8px 0 0;
    padding: 0;
    font-size: 11.5px;
    max-height: 116px;
    overflow-y: auto;
}

.erp-whchart-legend li {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 2px 0;
    min-width: 0;
}

/* No swatch: colour stopped encoding identity when the pie became a single-hue
   bar chart, and a dot that means nothing is a key to nothing. The item names
   are on the axis and repeated here with their figures. */
.erp-whchart-legend li + li { border-top: 1px dotted var(--erp-line); }

.erp-whchart-item {
    flex: 1 1 auto;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.erp-whchart-amt {
    flex: 0 0 auto;
    color: var(--erp-muted);
    font-variant-numeric: tabular-nums;
}

.erp-whchart-share {
    flex: 0 0 auto;
    width: 30px;
    text-align: right;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.erp-whchart-none {
    margin: 0;
    padding: 28px 0;
    text-align: center;
    font-size: 12px;
    color: var(--erp-muted);
}

.erp-whchart-none i { display: block; margin-bottom: 6px; opacity: .6; }

:root[data-theme="dark"] .erp-seg-btn.is-on { box-shadow: 0 1px 3px rgba(0, 0, 0, .4); }

/* ---------------------------------------------------------------------------
   General ledger — account movement chart and period totals
--------------------------------------------------------------------------- */

/* Height is set inline per render, scaled to the bar count. A responsive
   canvas with no height on its parent collapses to nothing. */
.erp-glchart {
    position: relative;
    width: 100%;
}

/* Requisition demand chart — a pie of which items account for the requesting,
   with the ranking carried as text in the legend beside it.

   The palette lives here rather than in the script so one file owns the
   colours and the theme swap is a variable change. Both sets are validated
   against their own surface (adjacent-pair CVD ΔE 9.1 light / 8.4 dark, OKLab
   ×100; normal-vision 19.6 / 19.3) — the dark column is the same six hues
   re-stepped for the dark ground, not an automatic lightening. "Other" is a
   remainder rather than an entity, so it takes a neutral and does not spend a
   categorical hue. */
.erp-reqdemand {
    --reqdemand-1: #2a78d6;
    --reqdemand-2: #eb6834;
    --reqdemand-3: #1baf7a;
    --reqdemand-4: #eda100;
    --reqdemand-5: #e87ba4;
    --reqdemand-6: #008300;
    --reqdemand-other: #b4bcc8;
    /* The ring drawn between wedges, so adjacent slices never fuse. */
    --reqdemand-surface: #fff;

    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 18px;
}

/* Chart.js measures its canvas against the nearest positioned ancestor, so the
   wrapper owns the height and the canvas fills it — sizing the canvas directly
   makes it grow on every resize. */
.erp-reqdemand-chart {
    flex: 0 0 auto;
    height: 236px;
    position: relative;
    width: 236px;
}

/* The legend IS the ranking: ordered, with the count and share as text, so
   neither identity nor magnitude depends on telling two hues apart. That is
   also what discharges the palette's light-mode contrast warning. */
.erp-reqdemand-legend {
    display: flex;
    flex: 1 1 260px;
    flex-direction: column;
    gap: 1px;
    list-style: none;
    margin: 0;
    min-width: 0;
    padding: 0;
}

.erp-reqdemand-legend li {
    align-items: center;
    border-radius: 6px;
    display: grid;
    gap: 9px;
    grid-template-columns: 10px minmax(0, 1fr) auto auto;
    padding: 5px 7px;
}

.erp-reqdemand-legend li:hover {
    background: var(--erp-bg);
}

.erp-reqdemand-legend li.is-other .erp-reqdemand-name {
    color: var(--erp-muted);
}

.erp-reqdemand-swatch {
    border-radius: 3px;
    display: block;
    height: 10px;
    width: 10px;
}

.erp-reqdemand-name {
    color: var(--erp-ink);
    font-size: 12.5px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-reqdemand-name small {
    color: var(--erp-muted);
    font-size: 11px;
    margin-left: 5px;
}

/* An item people keep typing by hand is usually one the catalogue is missing. */
.erp-reqdemand-freetext {
    color: var(--erp-warning);
    font-size: 9.5px;
    margin-left: 4px;
}

/* Tabular figures so the counts line up as a column rather than drifting with
   their digit widths. */
.erp-reqdemand-value {
    color: var(--erp-ink);
    font-size: 12.5px;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    text-align: right;
}

.erp-reqdemand-share {
    color: var(--erp-muted);
    font-size: 11.5px;
    font-variant-numeric: tabular-nums;
    min-width: 34px;
    text-align: right;
}

:root[data-theme="dark"] .erp-reqdemand {
    --reqdemand-1: #3987e5;
    --reqdemand-2: #d95926;
    --reqdemand-3: #199e70;
    --reqdemand-4: #c98500;
    --reqdemand-5: #d55181;
    --reqdemand-6: #008300;
    --reqdemand-other: #64748b;
    --reqdemand-surface: #1b2430;
}

@media (max-width: 575.98px) {
    .erp-reqdemand {
        justify-content: center;
    }

    .erp-reqdemand-chart {
        height: 200px;
        width: 200px;
    }
}

.erp-glsum {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.erp-glsum-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 7px 0;
    border-bottom: 1px dashed var(--erp-line);
    font-size: 12.5px;
}

.erp-glsum-row:last-child { border-bottom: none; }

.erp-glsum-row span { color: var(--erp-muted); }

.erp-glsum-row strong {
    font-size: 13.5px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    text-align: right;
}

.erp-glsum-row.is-total {
    border-bottom: none;
    border-top: 1px solid var(--erp-border);
    margin-top: 4px;
    padding-top: 10px;
}

.erp-glsum-ok { color: var(--erp-success); }
.erp-glsum-off { color: var(--erp-danger); }

/* ---------------------------------------------------------------------------
   Running-task progress (project overview card, right of the identity)

   Deliberately shaped like .erp-mstock further down the same card: a small
   ring with the figure in its hole and the numbers printed beneath. Two ring
   groups on one card have to read as one idea.
--------------------------------------------------------------------------- */
.erp-tprog {
    flex: 0 1 auto;
    margin-left: auto;
    min-width: 0;
    max-width: 560px;
}

.erp-tprog-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 8px;
}

.erp-tprog-head h3 {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--erp-muted);
    margin: 0;
}

.erp-tprog-head span { font-size: 11px; color: var(--erp-muted); }

.erp-tprog-none {
    margin: 0;
    font-size: 12px;
    color: var(--erp-muted);
    max-width: 280px;
}

/* The status row: one chip per task status, with its count.

   Chips rather than a select, because the counts ARE the information — how
   much is on Hold is worth seeing without opening anything. A status with
   nothing in it stays in the row, greyed and unclickable: a row that changes
   shape between projects is harder to scan than one that does not. */
.erp-tprog-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    margin-bottom: 10px;
}

.erp-tprog-tab {
    align-items: center;
    background: var(--erp-tint, #f7f8fb);
    border: 1px solid transparent;
    border-radius: 999px;
    color: var(--erp-muted);
    cursor: pointer;
    display: inline-flex;
    font-size: 11.5px;
    gap: 5px;
    line-height: 1;
    padding: 5px 10px;
    transition: background .12s ease, color .12s ease, border-color .12s ease;
}

.erp-tprog-tab i { font-size: 10px; opacity: .8; }

.erp-tprog-tab strong {
    color: var(--erp-ink);
    font-size: 11.5px;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
}

.erp-tprog-tab:hover:not(:disabled) {
    background: var(--erp-line);
    color: var(--erp-ink);
}

/* The selected chip. Marked by weight, border and background together, not by
   colour alone — the chips are otherwise identical, so colour would be the
   only thing saying which panel is open. */
.erp-tprog-tab.is-active {
    background: #ffffff;
    border-color: var(--erp-ink);
    color: var(--erp-ink);
    font-weight: 600;
}

.erp-tprog-tab.is-active i { opacity: 1; }

/* Nothing in this status. Left in place and stepped back rather than removed. */
.erp-tprog-tab.is-empty {
    cursor: default;
    opacity: .45;
}

.erp-tprog-tab:focus-visible {
    outline: 2px solid var(--erp-ink);
    outline-offset: 1px;
}

.erp-tprog-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.erp-tprog-card {
    flex: 0 0 auto;
    width: 118px;
    border: 1px solid var(--erp-border);
    border-radius: 11px;
    background: var(--erp-surface);
    padding: 9px 8px 8px;
    text-align: center;
}

/* Due today or tomorrow. Colour is a hint only — the day count under the
   ring says it in words for anyone who cannot see the tint. */
.erp-tprog-card.is-urgent {
    border-color: color-mix(in srgb, var(--erp-warning) 45%, transparent);
    background: color-mix(in srgb, var(--erp-warning) 7%, var(--erp-surface));
}

.erp-tprog-name {
    display: block;
    font-size: 11.5px;
    font-weight: 600;
    line-height: 1.25;
    /* Two lines then ellipsis: task titles are sentences, and letting them
       run would make one card twice the height of its neighbours. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    min-height: 29px;
    margin-bottom: 6px;
}

.erp-tprog-ring {
    position: relative;
    height: 62px;
    margin: 0 auto 6px;
}

.erp-tprog-centre {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}

.erp-tprog-centre strong { font-size: 15px; font-weight: 700; line-height: 1; }
.erp-tprog-centre small { font-size: 9px; font-weight: 600; opacity: .7; }

.erp-tprog-key {
    list-style: none;
    margin: 0 0 5px;
    padding: 0;
    font-size: 10.5px;
    color: var(--erp-muted);
}

.erp-tprog-key li {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 1px 0;
}

.erp-tprog-key strong {
    margin-left: auto;
    color: var(--erp-ink);
    font-variant-numeric: tabular-nums;
}

.erp-tprog-swatch {
    width: 7px;
    height: 7px;
    border-radius: 2px;
    flex: 0 0 auto;
}

.erp-tprog-due {
    display: block;
    font-size: 10px;
    color: var(--erp-muted);
    border-top: 1px dashed var(--erp-line);
    padding-top: 5px;
}

.erp-tprog-due i { margin-right: 3px; opacity: .7; }

.erp-tprog-card.is-urgent .erp-tprog-due { color: #a35c00; font-weight: 600; }

/* The status chips in dark mode. The selected one flips to a light-on-dark
   plate rather than keeping a white background, which would glare. */
:root[data-theme="dark"] .erp-tprog-tab { background: rgba(255, 255, 255, .05); }
:root[data-theme="dark"] .erp-tprog-tab:hover:not(:disabled) { background: rgba(255, 255, 255, .10); }
:root[data-theme="dark"] .erp-tprog-tab.is-active {
    background: rgba(255, 255, 255, .12);
    border-color: rgba(255, 255, 255, .45);
}

.erp-tprog-more {
    display: inline-block;
    margin-top: 8px;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--erp-primary-dark);
    text-decoration: none;
}

.erp-tprog-more i { font-size: 9px; margin-left: 2px; }

/* Below the identity rather than beside it once the row can no longer hold
   both — .erp-cv-head already wraps at this width. */
@media (max-width: 1199px) {
    .erp-tprog { margin-left: 0; max-width: 100%; width: 100%; }
}

:root[data-theme="dark"] .erp-tprog-card { background: var(--erp-surface-2); }

/* ---------------------------------------------------------------------------
   Material cost layers (project → Materials → row modal)
--------------------------------------------------------------------------- */

/* A whole table row that opens something. The hint icon only appears on
   hover, so it does not compete with the figures at rest. */
.erp-table tr.erp-row-click { cursor: pointer; }
.erp-table tr.erp-row-click:hover { background-color: var(--erp-table-hover); }

.erp-row-click-hint {
    margin-left: 6px;
    font-size: 10px;
    opacity: 0;
    color: var(--erp-primary);
    transition: opacity .12s ease;
}

.erp-table tr.erp-row-click:hover .erp-row-click-hint,
.erp-table tr.erp-row-click:focus-visible .erp-row-click-hint { opacity: 1; }

.erp-layers-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 14px;
}

.erp-layers-head h3 { font-size: 15px; font-weight: 700; margin: 0; }
.erp-layers-head span { font-size: 12px; color: var(--erp-muted); }

.erp-layers-strip {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
    gap: 1px;
    background: var(--erp-line);
    border: 1px solid var(--erp-border);
    border-radius: 10px;
    overflow: hidden;
    margin-bottom: 14px;
}

.erp-layers-strip > div {
    background: var(--erp-surface);
    padding: 9px 11px;
}

.erp-layers-strip span {
    display: block;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--erp-muted);
    margin-bottom: 2px;
}

.erp-layers-strip strong { font-size: 13.5px; font-weight: 700; font-variant-numeric: tabular-nums; }

.erp-layers-warn,
.erp-layers-note,
.erp-layers-basis {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 12px 0 0;
    padding: 9px 12px;
    border-radius: 9px;
    font-size: 12px;
    line-height: 1.45;
}

.erp-layers-warn {
    color: #a35c00;
    background: color-mix(in srgb, var(--erp-warning) 12%, #fff);
    border: 1px solid color-mix(in srgb, var(--erp-warning) 28%, transparent);
}

.erp-layers-note {
    color: var(--erp-text);
    background: var(--erp-surface-2);
    border: 1px solid var(--erp-border);
}

/* The basis line is quieter than the data it qualifies, but never hidden —
   a derived split presented as a record is worse than no split at all. */
.erp-layers-basis {
    color: var(--erp-muted);
    background: transparent;
    border: 1px dashed var(--erp-line);
    font-size: 11.5px;
}

.erp-layers-warn i,
.erp-layers-note i,
.erp-layers-basis i { margin-top: 2px; flex: 0 0 auto; }

/* ---------------------------------------------------------------------------
   Consumption vs Purchase report — text left, pies right
--------------------------------------------------------------------------- */
.erp-cvp-split {
    display: flex;
    gap: 18px;
    align-items: flex-start;
}

.erp-cvp-text { flex: 1 1 60%; min-width: 0; }

.erp-cvp-charts {
    flex: 0 0 340px;
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.erp-cvp-charts figure {
    margin: 0;
    border: 1px solid var(--erp-border);
    border-radius: 11px;
    background: var(--erp-surface);
    padding: 10px;
}

.erp-cvp-charts figcaption {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--erp-muted);
    text-align: center;
    margin-bottom: 6px;
}

/* Fixed height: a responsive canvas whose parent has none collapses. */
.erp-cvp-canvas {
    position: relative;
    height: 230px;
}

.erp-cvp-none {
    margin: 0;
    padding: 80px 0;
    text-align: center;
    font-size: 11.5px;
    color: var(--erp-muted);
}

/* The pies drop under the table rather than squeezing to a third of a
   narrow screen, where a nine-slice legend is unreadable anyway. */
@media (max-width: 1199px) {
    .erp-cvp-split { flex-direction: column; }
    .erp-cvp-charts { flex: 1 1 auto; width: 100%; flex-direction: row; flex-wrap: wrap; }
    .erp-cvp-charts figure { flex: 1 1 260px; }
}

/* ---------------------------------------------------------------------------
   Agreement payment schedule — collection progress

   Ring plus the figures it stands for. The per-installment detail is the
   table directly below, so nothing here repeats it.
--------------------------------------------------------------------------- */
.erp-sched-split {
    display: flex;
    align-items: center;
    gap: 24px;
    /* Held back from the full card width: four rows of figures stretched
       across a desktop put the amount so far from its label that the pair
       stops reading as one line. */
    max-width: 560px;
}

.erp-sched-ring {
    position: relative;
    flex: 0 0 150px;
    height: 150px;
}

.erp-sched-centre {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    line-height: 1.1;
}

.erp-sched-centre strong { font-size: 24px; font-weight: 700; }
.erp-sched-centre strong small { font-size: 13px; font-weight: 600; opacity: .7; }
.erp-sched-centre > small { font-size: 10px; color: var(--erp-muted); text-transform: uppercase; letter-spacing: .05em; }

.erp-sched-key {
    list-style: none;
    margin: 0;
    padding: 0;
    flex: 1 1 auto;
    min-width: 0;
    font-size: 12.5px;
}

.erp-sched-key li {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 0;
    border-bottom: 1px dashed var(--erp-line);
    color: var(--erp-muted);
}

.erp-sched-key li:last-child { border-bottom: none; }

.erp-sched-key strong {
    margin-left: auto;
    color: var(--erp-ink);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* Money past its date is the one line on this card worth a colour. */
.erp-sched-key li.is-alert,
.erp-sched-key li.is-alert strong { color: var(--erp-danger); }

.erp-sched-key li.is-total {
    border-top: 1px solid var(--erp-border);
    border-bottom: none;
    margin-top: 3px;
    padding-top: 9px;
    font-weight: 600;
}

.erp-sched-key li.is-total span { width: 9px; }

.erp-sched-swatch {
    width: 9px;
    height: 9px;
    border-radius: 2px;
    flex: 0 0 auto;
}

@media (max-width: 575px) {
    .erp-sched-split { flex-direction: column; align-items: stretch; gap: 14px; }
    .erp-sched-ring { flex: 0 0 130px; height: 130px; }
}

/* ---------------------------------------------------------------------------
   Installment collection — the allocation preview inside the modal
--------------------------------------------------------------------------- */
.erp-alloc {
    margin-top: 16px;
    border: 1px solid var(--erp-border);
    border-radius: 10px;
    overflow: hidden;
}

.erp-alloc-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    padding: 8px 12px;
    background: var(--erp-surface-2);
    border-bottom: 1px solid var(--erp-border);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--erp-muted);
}

.erp-alloc-head strong { text-transform: none; letter-spacing: 0; color: var(--erp-ink); font-size: 12px; }

.erp-alloc ul { list-style: none; margin: 0; padding: 0; }

.erp-alloc li {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    border-bottom: 1px dashed var(--erp-line);
    font-size: 12.5px;
}

.erp-alloc li:last-child { border-bottom: none; }

.erp-alloc-label { flex: 1 1 auto; min-width: 0; }
.erp-alloc-label small { display: block; font-size: 10.5px; color: var(--erp-muted); }

.erp-alloc-amt {
    flex: 0 0 auto;
    text-align: right;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.erp-alloc-amt small { display: block; font-size: 10.5px; font-weight: 400; color: var(--erp-muted); }

/* Over the outstanding balance — the one hint that is a refusal, not advice. */
.erp-help.is-warning { color: var(--erp-danger); font-weight: 600; }

/* --------------------------------------------------------------------------
   Income & expense approval workflow
   The queue tiles on the index, the bulk action bar, and the decision trail
   on an entry's own page.
   -------------------------------------------------------------------------- */

/* A queue tile is a link that filters the list; it must not look like one. */
a.erp-coa-stat {
    text-decoration: none;
    transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}

a.erp-coa-stat:hover {
    box-shadow: var(--erp-shadow);
    transform: translateY(-1px);
}

/* The tile whose filter is currently applied. */
.erp-coa-stat.is-current {
    box-shadow: inset 0 0 0 1px var(--erp-primary);
}

/* Selection column ---------------------------------------------------------- */

.erp-table .erp-select-col {
    width: 34px;
    padding-right: 0;
    text-align: center;
}

.erp-table tr.is-selectable:hover {
    background-color: var(--erp-table-hover);
}

/* Bulk action bar ----------------------------------------------------------- */

/* Docked to the viewport so it stays reachable however far down a long
   listing the user has scrolled to make their selection. */
.erp-bulkbar {
    bottom: 16px;
    left: 0;
    padding: 0 var(--erp-gutter);
    pointer-events: none;   /* only the panel itself is clickable */
    position: fixed;
    right: 0;
    z-index: 1040;          /* above the page, below Bootstrap's modals */
}

.erp-bulkbar-inner {
    align-items: center;
    background: var(--erp-ink);
    border-radius: 12px;
    box-shadow: 0 8px 28px rgba(16, 30, 54, .28);
    color: #fff;
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    justify-content: space-between;
    margin: 0 auto;
    max-width: 940px;
    padding: 10px 14px;
    pointer-events: auto;
}

.erp-bulkbar.is-busy .erp-bulkbar-inner {
    opacity: .65;
}

.erp-bulkbar-count {
    align-items: center;
    display: flex;
    font-size: 12.5px;
    gap: 8px;
    white-space: nowrap;
}

.erp-bulkbar-count strong {
    font-size: 15px;
}

.erp-bulkbar-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

/* The bar paints its own dark ground, so the ghost buttons on it need to be
   legible against that rather than against the page. */
.erp-bulkbar .btn-erp-ghost {
    background: rgba(255, 255, 255, .08);
    border-color: rgba(255, 255, 255, .18);
    color: #fff;
}

.erp-bulkbar .btn-erp-ghost:hover:not(:disabled) {
    background: rgba(255, 255, 255, .16);
    border-color: rgba(255, 255, 255, .3);
    color: #fff;
}

.erp-bulkbar .btn-erp-ghost.text-danger {
    color: #fda4af !important;
}

.erp-bulkbar .btn:disabled {
    opacity: .4;
}

/* How many of the selected rows this particular action would actually touch. */
.erp-bulkbar-badge {
    background: rgba(255, 255, 255, .22);
    border-radius: 9px;
    display: inline-block;
    font-size: 10.5px;
    font-weight: 600;
    line-height: 1;
    margin-left: 5px;
    min-width: 17px;
    padding: 3px 5px;
    text-align: center;
}

.erp-bulkbar .btn-erp .erp-bulkbar-badge {
    background: rgba(0, 0, 0, .22);
}

@media (max-width: 767.98px) {
    .erp-bulkbar-inner {
        justify-content: center;
    }

    .erp-bulkbar-actions {
        justify-content: center;
        width: 100%;
    }
}

:root[data-theme="dark"] .erp-bulkbar-inner {
    background: var(--erp-surface-2);
    border: 1px solid var(--erp-line);
    box-shadow: 0 8px 28px rgba(0, 0, 0, .5);
}

/* The bar is a control, not content: it has no place on a printed page. */
@media print {
    .erp-bulkbar,
    .erp-table .erp-select-col { display: none !important; }
}

/* ---------------------------------------------------------------------------
   Quick "add to BOQ" from an out-of-stock material card
--------------------------------------------------------------------------- */
.erp-mstock-onbill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-top: 8px;
    font-size: 10.5px;
    color: var(--erp-muted);
}

.erp-boqq-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    border: 1px solid var(--erp-border);
    border-radius: 10px;
    background: var(--erp-surface-2);
    margin-bottom: 12px;
}

.erp-boqq-icon {
    width: 32px;
    height: 32px;
    border-radius: 9px;
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    color: var(--erp-primary-dark);
    background: color-mix(in srgb, var(--erp-primary) 12%, #fff);
}

.erp-boqq-item strong { display: block; font-size: 13.5px; }
.erp-boqq-item span { font-size: 11.5px; color: var(--erp-muted); }

/* Why this is a draft and not the live bill. Quiet, but never omitted —
   somebody who thinks they just changed the budget will not go and
   activate it. */
.erp-boqq-note {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 0 0 14px;
    padding: 9px 12px;
    border: 1px dashed var(--erp-line);
    border-radius: 9px;
    font-size: 11.5px;
    line-height: 1.45;
    color: var(--erp-muted);
}

.erp-boqq-note i { margin-top: 2px; flex: 0 0 auto; }

.erp-boqq-total {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid var(--erp-border);
    font-size: 12px;
    color: var(--erp-muted);
}

.erp-boqq-total strong { font-size: 16px; color: var(--erp-ink); font-variant-numeric: tabular-nums; }

.erp-boqq-activate {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    margin-top: 12px;
    padding: 10px 12px;
    border: 1px solid var(--erp-border);
    border-radius: 9px;
    font-size: 12.5px;
    cursor: pointer;
}

.erp-boqq-activate input { margin-top: 2px; flex: 0 0 auto; }
.erp-boqq-activate small { display: block; font-size: 11px; color: var(--erp-muted); margin-top: 2px; }

/* Already-budgeted warning inside the quick BOQ modal. */
.erp-boqq-note.is-topup {
    border-style: solid;
    border-color: color-mix(in srgb, var(--erp-warning) 30%, transparent);
    background: color-mix(in srgb, var(--erp-warning) 8%, transparent);
    color: #a35c00;
}

/* ---------------------------------------------------------------------------
   Procurement journey strip (BOQ → … → GRN)

   Named erp-journey, NOT erp-chain: /procurement/requirements already owns
   .erp-chain for its static pill breadcrumb, and reusing the name put both
   rulesets on the same markup — the pills' borders and connectors drawn over
   this grid is what made the strip look broken.

   A horizontal chain on a desktop, a vertical one once the steps can no
   longer hold their captions side by side. The connector is drawn on the
   marker rather than between cards so it cannot drift out of line when a
   step's body is taller than its neighbour's.
--------------------------------------------------------------------------- */
.erp-journey {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr));
    gap: 0;
}

.erp-journey-step {
    position: relative;
    padding: 0 12px 0 0;
    min-width: 0;
}

/* The line to the next step, drawn from this one's marker. */
.erp-journey-step::before {
    content: "";
    position: absolute;
    top: 13px;
    left: 26px;
    right: 6px;
    height: 2px;
    background: var(--erp-line);
}

.erp-journey-step:last-child::before { display: none; }
.erp-journey-step.is-done::before { background: color-mix(in srgb, var(--erp-success) 55%, transparent); }

.erp-journey-mark {
    position: relative;
    z-index: 1;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    font-size: 12px;
    font-weight: 700;
    border: 2px solid var(--erp-line);
    background: var(--erp-surface);
    color: var(--erp-muted);
    margin-bottom: 9px;
}

.erp-journey-step.is-done .erp-journey-mark {
    border-color: var(--erp-success);
    background: var(--erp-success);
    color: #fff;
}

.erp-journey-step.is-current .erp-journey-mark {
    border-color: var(--erp-primary);
    color: var(--erp-primary-dark);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--erp-primary) 14%, transparent);
}

.erp-journey-step.is-skipped .erp-journey-mark {
    border-style: dashed;
    color: var(--erp-muted);
}

.erp-journey-body { min-width: 0; padding-right: 8px; }

.erp-journey-label {
    display: block;
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1.3;
}

.erp-journey-label i { margin-right: 5px; opacity: .65; font-size: 11px; }
.erp-journey-label a { color: inherit; text-decoration: none; }
.erp-journey-label a:hover { color: var(--erp-primary-dark); }

.erp-journey-caption {
    display: block;
    font-size: 11px;
    color: var(--erp-muted);
    margin-top: 2px;
}

.erp-journey-step.is-pending .erp-journey-label,
.erp-journey-step.is-pending .erp-journey-caption { opacity: .6; }

.erp-journey-docs {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin-top: 5px;
}

.erp-journey-docs a {
    font-size: 10px;
    padding: 1px 6px;
    border-radius: 999px;
    background: var(--erp-surface-2);
    border: 1px solid var(--erp-border);
    color: var(--erp-muted);
    text-decoration: none;
}

.erp-journey-docs a:hover { color: var(--erp-primary-dark); }

.erp-journey-act { display: inline-block; margin-top: 8px; }

.erp-journey-hint {
    display: block;
    font-size: 10.5px;
    line-height: 1.4;
    color: var(--erp-muted);
    margin-top: 5px;
}

.erp-journey-next {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--erp-primary-dark);
    text-decoration: none;
    white-space: nowrap;
}

.erp-journey-next i { font-size: 10px; margin-left: 2px; }

.erp-journey-modal-note {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 0 0 14px;
    padding: 9px 12px;
    border: 1px dashed var(--erp-line);
    border-radius: 9px;
    font-size: 11.5px;
    line-height: 1.45;
    color: var(--erp-muted);
}

.erp-journey-modal-note i { margin-top: 2px; flex: 0 0 auto; }

@media (max-width: 1199px) {
    .erp-journey { grid-template-columns: repeat(3, minmax(0, 1fr)); row-gap: 18px; }
    /* The connector only makes sense along a row. */
    .erp-journey-step:nth-child(3n)::before { display: none; }
}

@media (max-width: 767px) {
    .erp-journey { grid-template-columns: 1fr; row-gap: 14px; }
    .erp-journey-step::before { display: none; }
    .erp-journey-step { display: flex; gap: 10px; }
    .erp-journey-mark { flex: 0 0 28px; margin-bottom: 0; }
}

/* ---------------------------------------------------------------------------
   Project site check-ins
--------------------------------------------------------------------------- */
.erp-checkin-done {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    align-self: center;
    padding: 6px 12px;
    border-radius: 9px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--erp-success-dark);
    background: color-mix(in srgb, var(--erp-success) 12%, #fff);
    border: 1px solid color-mix(in srgb, var(--erp-success) 28%, transparent);
}

.erp-checkin-day + .erp-checkin-day { margin-top: 18px; }

.erp-checkin-date {
    display: flex;
    align-items: baseline;
    gap: 10px;
    padding-bottom: 6px;
    margin-bottom: 6px;
    border-bottom: 1px solid var(--erp-border);
}

.erp-checkin-date strong { font-size: 12.5px; }
.erp-checkin-date span { font-size: 11px; color: var(--erp-muted); }

.erp-checkin-list { list-style: none; margin: 0; padding: 0; }

.erp-checkin-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 9px 0;
    border-bottom: 1px dashed var(--erp-line);
}

.erp-checkin-row:last-child { border-bottom: none; }

/* Withdrawn stays readable rather than hidden — see the view. */
.erp-checkin-row.is-withdrawn { opacity: .55; }
.erp-checkin-row.is-withdrawn .erp-checkin-who strong { text-decoration: line-through; }

.erp-checkin-who { flex: 1 1 auto; min-width: 0; }
.erp-checkin-who strong { display: block; font-size: 13px; }
.erp-checkin-who > span { display: block; font-size: 11px; color: var(--erp-muted); }

.erp-checkin-remark {
    display: block;
    margin-top: 3px;
    font-size: 11.5px;
    color: var(--erp-text);
    font-style: italic;
}

.erp-checkin-remark i { font-size: 8px; margin-right: 4px; opacity: .5; vertical-align: middle; }

.erp-checkin-time {
    flex: 0 0 auto;
    text-align: right;
    line-height: 1.2;
}

.erp-checkin-time strong { display: block; font-size: 13px; font-variant-numeric: tabular-nums; }
.erp-checkin-time small { font-size: 10.5px; color: var(--erp-muted); }

.erp-checkin-note {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 0 0 14px;
    padding: 9px 12px;
    border: 1px dashed var(--erp-line);
    border-radius: 9px;
    font-size: 11.5px;
    line-height: 1.45;
    color: var(--erp-muted);
}

.erp-checkin-note i { margin-top: 2px; flex: 0 0 auto; }

@media (max-width: 575px) {
    .erp-checkin-row { flex-wrap: wrap; }
    .erp-checkin-time { text-align: left; }
}

/* ---------------------------------------------------------------------------
   Employee login (personnel file)
--------------------------------------------------------------------------- */
.erp-emp-login {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    align-self: center;
    padding: 6px 12px;
    border-radius: 9px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--erp-success-dark);
    background: color-mix(in srgb, var(--erp-success) 12%, #fff);
    border: 1px solid color-mix(in srgb, var(--erp-success) 28%, transparent);
}

.erp-emp-cred {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1px;
    background: var(--erp-line);
    border: 1px solid var(--erp-border);
    border-radius: 10px;
    overflow: hidden;
}

.erp-emp-cred > div { background: var(--erp-surface); padding: 9px 12px; }

.erp-emp-cred span {
    display: block;
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--erp-muted);
}

/* Monospace: these are credentials somebody reads aloud or copies down. */
.erp-emp-cred strong { font-size: 14px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

.erp-emp-note {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 14px 0 0;
    padding: 9px 12px;
    border-radius: 9px;
    font-size: 11.5px;
    line-height: 1.45;
    color: #a35c00;
    background: color-mix(in srgb, var(--erp-warning) 10%, #fff);
    border: 1px solid color-mix(in srgb, var(--erp-warning) 26%, transparent);
}

.erp-emp-note i { margin-top: 2px; flex: 0 0 auto; }

/* A login that cannot be created yet: the button stays, visibly out of
   action, so the feature is still discoverable. */
.btn.is-blocked,
.btn.is-blocked:hover {
    opacity: .55;
    cursor: not-allowed;
    border-style: dashed;
}

/* The reason, for anyone who cannot hover a tooltip. */
.erp-emp-blocked-why {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 0 0 12px;
    padding: 9px 12px;
    border-radius: 9px;
    font-size: 12px;
    line-height: 1.45;
    color: var(--erp-muted);
    background: var(--erp-surface-2);
    border: 1px dashed var(--erp-line);
}

.erp-emp-blocked-why i { margin-top: 2px; flex: 0 0 auto; }
.erp-emp-blocked-why strong { color: var(--erp-ink); }


/* --------------------------------------------------------------------------
   Team member transfers
   People lent between projects: the muted "away" rows on the lending team,
   the "on loan" marker on a borrowed member, and the history log.
   -------------------------------------------------------------------------- */

/* Someone who is away is present on the card but not available. Muted and
   dashed rather than hidden: a name vanishing from a team is how a project
   loses track of its own people, but it must not read as working headcount. */
.erp-team-member.is-away {
    opacity: .72;
    border-style: dashed;
}

.erp-team-member.is-away .erp-kavatar {
    filter: grayscale(1);
}

/* A borrowed member IS working here, so the row stays at full strength; only
   the accent edge says they are on loan and will be leaving. */
.erp-team-member.is-borrowed {
    border-left: 2px solid var(--erp-info);
}

.erp-team-loan {
    display: block;
    margin-top: 2px;
    font-size: 11px;
    color: var(--erp-info);
}

.erp-team-loan i {
    margin-right: 4px;
}

/* Overdue is the one state that must interrupt. Colour alone would not carry
   it, so the row also says "Overdue by N days" in text. */
.erp-team-loan.is-overdue,
.erp-team-member.is-overdue .erp-team-loan {
    color: var(--erp-danger);
}

.erp-team-member.is-away.is-overdue {
    border-color: var(--erp-danger);
    opacity: 1;
}

.erp-team-away {
    color: var(--erp-info);
    font-weight: 600;
}

.erp-team-loans {
    margin-top: 6px;
}

.erp-team-histcount {
    margin-left: 4px;
    font-size: 10.5px;
    font-weight: 600;
    opacity: .75;
}

/* ---- the history log ---- */

.erp-transfer-log {
    max-height: 60vh;
    overflow-y: auto;
}

.erp-transfer-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.erp-transfer-row {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 10px 12px;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    background: #fff;
}

.erp-transfer-row.is-overdue {
    border-color: var(--erp-danger);
}

/* The direction icon is the first thing scanned — out of this team, or into
   it — so it gets its own column rather than sitting inside the text. */
.erp-transfer-dir {
    flex: 0 0 26px;
    width: 26px;
    height: 26px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--erp-bg);
    color: var(--erp-muted);
    font-size: 11px;
}

.erp-transfer-body {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.erp-transfer-body strong {
    font-size: 12.5px;
    color: var(--erp-ink);
}

.erp-transfer-where,
.erp-transfer-when,
.erp-transfer-note {
    font-size: 11px;
    color: var(--erp-muted);
}

.erp-transfer-note {
    margin-top: 3px;
    font-style: italic;
}


/* --------------------------------------------------------------------------
   Material batch pricing
   What each material cost delivery by delivery, and when it was consumed at
   which price. Rows expand in place; the breakdown carries a pie (spend share)
   and a bar list (unit price).
   -------------------------------------------------------------------------- */

.erp-batch-basis {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    margin-bottom: var(--erp-gutter);
    padding: 11px 14px;
    border: 1px solid var(--erp-line);
    border-left: 3px solid var(--erp-info);
    border-radius: var(--erp-radius);
    background: #fff;
    font-size: 12px;
    color: var(--erp-muted);
    line-height: 1.55;
}

.erp-batch-basis i {
    margin-top: 2px;
    color: var(--erp-info);
}

.erp-batch-basis strong {
    color: var(--erp-ink);
}

/* ---- the list ---- */

.erp-batch-row {
    cursor: pointer;
}

.erp-batch-row:hover {
    background: var(--erp-bg);
}

.erp-batch-row:focus-visible {
    outline: 2px solid var(--erp-primary);
    outline-offset: -2px;
}

.erp-batch-caret {
    color: var(--erp-muted);
    font-size: 10px;
}

.erp-batch-caret i {
    transition: transform .15s ease;
}

.erp-batch-row.is-open {
    background: var(--erp-bg);
}

.erp-batch-row.is-open .erp-batch-caret i {
    transform: rotate(90deg);
}

.erp-batch-detail > td {
    padding: 0 !important;
    background: var(--erp-bg);
}

.erp-batch-detail-body {
    padding: 16px 18px;
}

.erp-batch-warn {
    font-size: 11px;
    color: var(--erp-danger);
}

.erp-batch-nomatch {
    margin: 0;
    padding: 14px 18px;
    font-size: 12.5px;
    color: var(--erp-muted);
}

/* Cheapest → dearest, with a marker where the weighted average landed. The
   two prices are printed either side, so the bar is never the only carrier. */
.erp-spreadbar {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 11px;
    color: var(--erp-muted);
    white-space: nowrap;
}

.erp-spreadbar-track {
    position: relative;
    flex: 1 1 auto;
    min-width: 54px;
    height: 4px;
    border-radius: 2px;
    background: linear-gradient(90deg, var(--erp-success), var(--erp-warning), var(--erp-danger));
}

.erp-spreadbar-avg {
    position: absolute;
    top: -3px;
    width: 2px;
    height: 10px;
    border-radius: 1px;
    background: var(--erp-ink);
    transform: translateX(-1px);
}

.erp-spreadbar-lo,
.erp-spreadbar-hi {
    font-variant-numeric: tabular-nums;
}

.erp-batch-tag {
    margin-left: 5px;
    padding: 1px 5px;
    border-radius: 4px;
    font-size: 9.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .03em;
}

.erp-batch-tag.is-high {
    background: rgba(233, 30, 99, .1);
    color: var(--erp-danger);
}

.erp-batch-tag.is-low {
    background: rgba(0, 194, 146, .12);
    color: var(--erp-success-dark);
}

/* ---- the breakdown ---- */

.erp-batchdetail-strip {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 12px;
}

.erp-batchdetail-strip > div {
    flex: 1 1 140px;
    padding: 9px 12px;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    background: #fff;
}

.erp-batchdetail-strip span {
    display: block;
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--erp-muted);
}

.erp-batchdetail-strip strong {
    display: block;
    margin-top: 2px;
    font-size: 15px;
    color: var(--erp-ink);
    font-variant-numeric: tabular-nums;
}

.erp-batchdetail-strip > div.is-warn strong {
    color: var(--erp-danger);
}

.erp-batchdetail-lede {
    display: flex;
    gap: 9px;
    align-items: flex-start;
    margin-bottom: 14px;
    padding: 10px 13px;
    border-radius: var(--erp-radius);
    background: #fff;
    border: 1px solid var(--erp-line);
    font-size: 12.5px;
    line-height: 1.6;
    color: var(--erp-muted);
}

.erp-batchdetail-lede i {
    margin-top: 3px;
    color: var(--erp-info);
}

.erp-batchdetail-lede strong {
    color: var(--erp-ink);
}

.erp-batchdetail-h {
    margin: 18px 0 8px;
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--erp-muted);
}

/* Pie and bars side by side: the same batches read two ways, so they must be
   comparable without scrolling between them. Stacks under 820px. */
.erp-batchdetail-charts {
    display: grid;
    grid-template-columns: minmax(220px, 1fr) minmax(260px, 1.35fr);
    gap: 16px;
    margin-bottom: 6px;
}

@media (max-width: 820px) {
    .erp-batchdetail-charts {
        grid-template-columns: 1fr;
    }
}

.erp-batchchart,
.erp-batchbars {
    margin: 0;
    padding: 12px 14px;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    background: #fff;
}

.erp-batchchart figcaption,
.erp-batchbars figcaption {
    margin-bottom: 10px;
}

.erp-batchchart figcaption strong,
.erp-batchbars figcaption strong {
    display: block;
    font-size: 12.5px;
    color: var(--erp-ink);
}

.erp-batchchart figcaption span,
.erp-batchbars figcaption span {
    font-size: 11px;
    color: var(--erp-muted);
}

/* Chart.js measures its canvas against the nearest positioned ancestor, so
   the wrapper owns the height and the canvas fills it. */
.erp-batchchart-canvas {
    position: relative;
    width: 100%;
    height: 190px;
}

.erp-batchbar-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 7px;
}

.erp-batchbar-list li {
    display: grid;
    grid-template-columns: minmax(80px, 1fr) minmax(60px, 2fr) auto;
    align-items: center;
    gap: 9px;
}

.erp-batchbar-key {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
}

.erp-batchbar-dot {
    flex: 0 0 9px;
    width: 9px;
    height: 9px;
    border-radius: 2px;
    background: var(--erp-muted);
}

.erp-batchbar-name {
    font-size: 11.5px;
    color: var(--erp-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-batchbar-track {
    height: 8px;
    border-radius: 4px;
    background: var(--erp-bg);
    overflow: hidden;
}

.erp-batchbar-fill {
    display: block;
    height: 100%;
    border-radius: 4px;
    background: var(--erp-info);
}

/* The dearest batch is the one the reader is looking for, so it is called out
   by colour AND by the "dearest" tag in the table — never colour alone. */
.erp-batchbar-fill.is-peak {
    background: var(--erp-danger);
}

.erp-batchbar-value {
    font-size: 11.5px;
    font-variant-numeric: tabular-nums;
    color: var(--erp-ink);
    text-align: right;
}

/* One day's consumption often spans two deliveries; each draw is its own chip
   so the prices stay separate instead of collapsing to a meaningless average. */
.erp-batch-draw {
    display: inline-flex;
    flex-direction: column;
    margin: 0 8px 3px 0;
    padding: 2px 7px;
    border-radius: 5px;
    background: var(--erp-bg);
    line-height: 1.35;
}

.erp-batch-draw-name {
    font-size: 11px;
    font-weight: 600;
    color: var(--erp-ink);
}

.erp-batch-draw-qty {
    font-size: 10.5px;
    color: var(--erp-muted);
    font-variant-numeric: tabular-nums;
}

/* .is-warn is a separate spelling of .is-danger that one screen uses. Kept
   working rather than renamed, because renaming it is a blade change on a screen
   this task has no reason to touch. */
.erp-sale-stat.is-warn { --erp-tone-deep: #b91c1c; --erp-tone-deeper: #7f1d1d; }


/* --------------------------------------------------------------------------
   Procurement trail
   Where a delivery came from, as a vertical line: BOQ → requirement →
   requisition → RFQ → quotation → order → receipt → inspection. Only the
   stages that actually exist are drawn, so the line is as long as the
   paperwork and no longer.
   -------------------------------------------------------------------------- */

.erp-trail-card {
    overflow: hidden;
}

/* Sticky on tall screens: the trail is reference material read against the
   findings table beside it, and scrolling away from it defeats the point. */
@media (min-width: 1200px) {
    .erp-trail-card {
        position: sticky;
        top: 12px;
        max-height: calc(100vh - 24px);
        display: flex;
        flex-direction: column;
    }
}

.erp-trail-head {
    padding: 13px 15px 11px;
    border-bottom: 1px solid var(--erp-line);
}

.erp-trail-head h3 {
    margin: 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--erp-ink);
}

.erp-trail-head p {
    margin: 3px 0 0;
    font-size: 11px;
    line-height: 1.5;
    color: var(--erp-muted);
}

.erp-trail {
    list-style: none;
    margin: 0;
    padding: 14px 15px 16px;
    overflow-y: auto;
}

.erp-trail-step {
    position: relative;
    padding-left: 30px;
    padding-bottom: 16px;
}

/* The connecting line, drawn from each marker down to the next. Stopped on the
   final step so the trail ends rather than trailing off into nothing. */
.erp-trail-step::before {
    content: "";
    position: absolute;
    left: 10px;
    top: 22px;
    bottom: 0;
    width: 2px;
    background: var(--erp-line);
}

.erp-trail-step.is-last {
    padding-bottom: 0;
}

.erp-trail-step.is-last::before {
    display: none;
}

.erp-trail-marker {
    position: absolute;
    left: 0;
    top: 1px;
    width: 22px;
    height: 22px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    border: 1px solid var(--erp-line);
    background: #fff;
    color: var(--erp-muted);
    font-size: 9px;
}

/* The step the reader is standing on. Ring plus colour plus the "you are here"
   label below — never colour alone. */
.erp-trail-step.is-current .erp-trail-marker {
    border-color: var(--erp-primary);
    background: var(--erp-primary);
    color: #fff;
    box-shadow: 0 0 0 3px rgba(0, 194, 146, .16);
}

.erp-trail-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 6px;
    margin-bottom: 1px;
}

.erp-trail-label {
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--erp-muted);
}

.erp-trail-doc {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px;
}

.erp-trail-doc a,
.erp-trail-doc strong {
    font-size: 13px;
    font-weight: 600;
    color: var(--erp-ink);
    text-decoration: none;
}

.erp-trail-doc a:hover {
    color: var(--erp-primary-dark);
    text-decoration: underline;
}

.erp-trail-here {
    padding: 1px 6px;
    border-radius: 4px;
    background: rgba(0, 194, 146, .12);
    color: var(--erp-success-dark);
    font-size: 9.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
}

.erp-trail-date {
    font-size: 10.5px;
    color: var(--erp-muted);
}

.erp-trail-title {
    margin: 2px 0 0;
    font-size: 11.5px;
    color: var(--erp-ink);
}

/* Two columns of label/value: these are the figures the stage agreed, and a
   single column would push the trail twice as long as the page beside it. */
.erp-trail-metrics {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 4px 10px;
    margin: 7px 0 0;
}

.erp-trail-metrics dt {
    font-size: 9.5px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--erp-muted);
}

.erp-trail-metrics dd {
    margin: 0;
    font-size: 12px;
    font-weight: 600;
    color: var(--erp-ink);
    font-variant-numeric: tabular-nums;
}

.erp-trail-people {
    list-style: none;
    margin: 6px 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.erp-trail-people li {
    font-size: 11px;
    color: var(--erp-ink);
}

.erp-trail-people span {
    display: inline-block;
    min-width: 74px;
    color: var(--erp-muted);
}

.erp-trail-note {
    margin: 6px 0 0;
    font-size: 10.5px;
    font-style: italic;
    line-height: 1.45;
    color: var(--erp-muted);
}

.erp-trail-skip {
    display: flex;
    gap: 6px;
    align-items: flex-start;
    margin: 7px 0 0;
    padding: 6px 8px;
    border-radius: 6px;
    background: var(--erp-bg);
    font-size: 10.5px;
    line-height: 1.45;
    color: var(--erp-muted);
}

.erp-trail-skip i {
    margin-top: 2px;
}


/* --------------------------------------------------------------------------
   Land acquisition progress
   A donut of steps by state with the headline percentage in the hole, and the
   steps themselves named in order beside it. The chart answers "how far", the
   list answers "what is holding it up" — a pie can only ever do the first.
   -------------------------------------------------------------------------- */

.erp-landprog-grid {
    display: grid;
    grid-template-columns: 210px minmax(0, 1fr);
    gap: 20px;
    align-items: start;
}

/* Under 860px the donut would be squeezed to a bangle and the step names
   would wrap to three lines each; stacked, both stay readable. */
@media (max-width: 860px) {
    .erp-landprog-grid {
        grid-template-columns: 1fr;
        gap: 14px;
    }
}

.erp-landprog-chart {
    margin: 0;
    text-align: center;
}

/* Chart.js measures its canvas against the nearest positioned ancestor, and
   the centred percentage is absolutely placed against this same box. */
.erp-landprog-canvas {
    position: relative;
    width: 100%;
    height: 170px;
}

.erp-landprog-centre {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    /* The ring must stay hoverable through the label, or the tooltip dies
       wherever the number sits. */
    pointer-events: none;
}

.erp-landprog-centre strong {
    font-size: 30px;
    line-height: 1;
    font-weight: 700;
    color: var(--erp-ink);
    font-variant-numeric: tabular-nums;
}

.erp-landprog-centre strong span {
    font-size: 15px;
    font-weight: 600;
    color: var(--erp-muted);
    margin-left: 1px;
}

.erp-landprog-centre small {
    margin-top: 2px;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: var(--erp-muted);
}

.erp-landprog-chart figcaption {
    margin-top: 8px;
    font-size: 11.5px;
    color: var(--erp-muted);
}

/* ---- the step list ---- */

.erp-landprog-head h3 {
    margin: 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--erp-ink);
}

.erp-landprog-head p {
    margin: 2px 0 9px;
    font-size: 12px;
    line-height: 1.5;
    color: var(--erp-muted);
}

.erp-landprog-head strong {
    color: var(--erp-ink);
}

.erp-landprog-steps {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 5px;
}

.erp-landprog-jump {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 7px 9px;
    border: 1px solid var(--erp-line);
    border-left: 3px solid var(--erp-line);
    border-radius: 8px;
    background: #fff;
    text-align: left;
    cursor: pointer;
    transition: border-color .12s ease, background .12s ease;
}

.erp-landprog-jump:hover {
    background: var(--erp-bg);
}

.erp-landprog-jump:focus-visible {
    outline: 2px solid var(--erp-primary);
    outline-offset: 1px;
}

/* The state edge. Colour here is a second reading only — the row also carries
   a state icon and the word beside it. */
.erp-landprog-step.is-done .erp-landprog-jump { border-left-color: var(--erp-success); }
.erp-landprog-step.is-active .erp-landprog-jump { border-left-color: var(--erp-warning); }
.erp-landprog-step.is-pending .erp-landprog-jump { border-left-color: #dfe5ec; }

.erp-landprog-num {
    flex: 0 0 19px;
    width: 19px;
    height: 19px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--erp-bg);
    color: var(--erp-muted);
    font-size: 10px;
    font-weight: 600;
}

.erp-landprog-step.is-done .erp-landprog-num {
    background: var(--erp-success);
    color: #fff;
}

.erp-landprog-face {
    flex: 1 1 auto;
    min-width: 0;
}

.erp-landprog-name {
    display: block;
    font-size: 12px;
    font-weight: 600;
    color: var(--erp-ink);
}

.erp-landprog-name i {
    margin-right: 5px;
    color: var(--erp-muted);
    font-size: 10px;
}

.erp-landprog-detail {
    display: block;
    font-size: 10.5px;
    line-height: 1.4;
    color: var(--erp-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-landprog-state {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 10px;
    font-weight: 600;
    color: var(--erp-muted);
    white-space: nowrap;
}

.erp-landprog-step.is-done .erp-landprog-state { color: var(--erp-success-dark); }
.erp-landprog-step.is-active .erp-landprog-state { color: #b26a00; }

.erp-landprog-basis {
    display: flex;
    gap: 7px;
    align-items: flex-start;
    margin: 14px 0 0;
    padding-top: 11px;
    border-top: 1px solid var(--erp-line);
    font-size: 11px;
    line-height: 1.5;
    color: var(--erp-muted);
}

.erp-landprog-basis i {
    margin-top: 2px;
}


/* --------------------------------------------------------------------------
   Top bar project picker
   Sits beside the company switcher and borrows its button, but does something
   different: the company switcher re-scopes the app, this one just navigates.
   -------------------------------------------------------------------------- */

.erp-projectpicker {
    margin-left: 8px;
}

.erp-projectpicker .erp-context-btn[disabled] {
    opacity: .55;
    cursor: default;
}

.erp-projectpicker-menu {
    min-width: 320px;
    max-width: 380px;
}

/* The list scrolls, the header, search and footer do not — a company with
   forty projects would otherwise push "All Projects" off the screen. */
.erp-projectpicker-scroll {
    max-height: 340px;
    overflow-y: auto;
    overscroll-behavior: contain;
}

.erp-projectpicker-search {
    position: relative;
    padding: 4px 10px 6px;
}

.erp-projectpicker-search i {
    position: absolute;
    left: 19px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 10.5px;
    color: var(--erp-muted);
    pointer-events: none;
}

.erp-projectpicker-search input {
    width: 100%;
    padding: 5px 9px 5px 26px;
    border: 1px solid var(--erp-line);
    border-radius: 7px;
    background: var(--erp-bg);
    font-size: 12px;
    color: var(--erp-ink);
}

.erp-projectpicker-search input:focus {
    outline: none;
    border-color: var(--erp-primary);
    background: #fff;
}

.erp-projectpicker-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 7px 12px;
}

.erp-projectpicker-face {
    min-width: 0;
    display: flex;
    flex-direction: column;
    line-height: 1.3;
}

.erp-projectpicker-name {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--erp-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-projectpicker-face small {
    font-size: 10.5px;
    color: var(--erp-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-projectpicker-item .erp-pill {
    flex: 0 0 auto;
    font-size: 9.5px;
}

/* [hidden] loses to .dropdown-item's display:flex without this, so a filtered
   item would stay on screen with everything else. */
.erp-projectpicker-item[hidden] {
    display: none !important;
}


/* --------------------------------------------------------------------------
   Project cost summary
   Budget against actual as bars, and where the actual went as a donut. The
   two forms are not interchangeable: estimated and actual are two measures of
   one quantity (compare by length), the categories are parts of one whole
   (compare by area).
   -------------------------------------------------------------------------- */

.erp-costsummary-sub {
    font-size: 11.5px;
    color: var(--erp-muted);
}

.erp-costsummary-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
    gap: 20px;
    align-items: start;
}

/* Under 900px the bars lose the width that makes them comparable and the
   legend wraps onto three lines a row; stacked, both stay readable. */
@media (max-width: 900px) {
    .erp-costsummary-grid {
        grid-template-columns: 1fr;
        gap: 16px;
    }
}

/* ---- estimated vs actual ---- */

.erp-costbar {
    display: flex;
    flex-direction: column;
    gap: 9px;
}

.erp-costbar-row {
    display: grid;
    grid-template-columns: 66px minmax(0, 1fr) auto;
    align-items: center;
    gap: 10px;
}

.erp-costbar-key {
    font-size: 10.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--erp-muted);
}

.erp-costbar-track {
    height: 12px;
    border-radius: 6px;
    background: var(--erp-bg);
    overflow: hidden;
}

.erp-costbar-fill {
    display: block;
    height: 100%;
    border-radius: 6px;
}

/* The estimate is the reference, so it is drawn recessive — the actual is the
   figure being read against it. */
.erp-costbar-fill.is-estimate { background: #cbd5e1; }
.erp-costbar-fill.is-actual { background: var(--erp-success); }
.erp-costbar-fill.is-over { background: var(--erp-danger); }

.erp-costbar-value {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--erp-ink);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.erp-costsummary-verdict,
.erp-costsummary-aside {
    display: flex;
    gap: 8px;
    align-items: flex-start;
    margin: 12px 0 0;
    font-size: 11.5px;
    line-height: 1.5;
    color: var(--erp-muted);
}

.erp-costsummary-verdict i { margin-top: 2px; color: var(--erp-success-dark); }
.erp-costsummary-verdict strong { color: var(--erp-ink); }

/* Over budget is carried by the icon and the words as well as the colour. */
.erp-costsummary-verdict.is-over i { color: var(--erp-danger); }
.erp-costsummary-verdict.is-over strong { color: var(--erp-danger); }

.erp-costsummary-aside {
    margin-top: 9px;
    padding-top: 9px;
    border-top: 1px dashed var(--erp-line);
}

.erp-costsummary-aside i { margin-top: 2px; }

/* ---- where the money went ---- */

.erp-costsummary-split {
    display: grid;
    grid-template-columns: 150px minmax(0, 1fr);
    gap: 14px;
    align-items: center;
}

@media (max-width: 560px) {
    .erp-costsummary-split {
        grid-template-columns: 1fr;
    }
}

/* Chart.js measures its canvas against the nearest positioned ancestor, and
   the centred total is placed against the same box. */
.erp-costsummary-canvas {
    position: relative;
    width: 100%;
    height: 150px;
}

.erp-costsummary-centre {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    /* The ring stays hoverable through the label, or the tooltip dies wherever
       the total happens to sit. */
    pointer-events: none;
}

.erp-costsummary-centre strong {
    font-size: 14px;
    line-height: 1.1;
    font-weight: 700;
    color: var(--erp-ink);
    font-variant-numeric: tabular-nums;
}

.erp-costsummary-centre small {
    margin-top: 1px;
    font-size: 9.5px;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--erp-muted);
}

.erp-costsummary-legend {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.erp-costsummary-legend li {
    display: grid;
    grid-template-columns: 9px minmax(0, 1fr) auto auto;
    align-items: center;
    gap: 8px;
}

.erp-costsummary-dot {
    width: 9px;
    height: 9px;
    border-radius: 2px;
    background: var(--erp-muted);
}

.erp-costsummary-name {
    font-size: 11.5px;
    color: var(--erp-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Which book a slice came from. Small and muted — it qualifies the label
   rather than competing with it, but it is text, so it survives a print and a
   screen reader both. */
.erp-costsummary-src {
    margin-left: 5px;
    padding: 1px 5px;
    border-radius: 4px;
    font-size: 9px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    vertical-align: 1px;
}

.erp-costsummary-src.is-task { background: var(--erp-bg); color: var(--erp-muted); }
.erp-costsummary-src.is-ledger { background: rgba(13, 148, 136, .12); color: #0d7f75; }

.erp-costsummary-amount {
    font-size: 11.5px;
    font-weight: 600;
    color: var(--erp-ink);
    font-variant-numeric: tabular-nums;
}

.erp-costsummary-share {
    min-width: 34px;
    text-align: right;
    font-size: 10.5px;
    color: var(--erp-muted);
    font-variant-numeric: tabular-nums;
}

.erp-costsummary-empty {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    padding: 12px 14px;
    border: 1px dashed var(--erp-line);
    border-radius: var(--erp-radius);
    color: var(--erp-muted);
}

.erp-costsummary-empty i { margin-top: 2px; font-size: 15px; }
.erp-costsummary-empty p { margin: 0; font-size: 11.5px; line-height: 1.5; }


/* --------------------------------------------------------------------------
   Project financial register
   Income and expense booked against one project. Posted figures are the
   ledger; anything unposted is shown apart from them, never blended in.
   -------------------------------------------------------------------------- */

.erp-fin-basis {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    margin-bottom: var(--erp-gutter);
    padding: 11px 14px;
    border: 1px solid var(--erp-line);
    border-left: 3px solid #0d9488;
    border-radius: var(--erp-radius);
    background: #fff;
    font-size: 12px;
    line-height: 1.55;
    color: var(--erp-muted);
}

.erp-fin-basis i { margin-top: 2px; color: #0d9488; }
.erp-fin-basis strong { color: var(--erp-ink); }

.erp-finsummary-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
    gap: 20px;
    align-items: start;
}

@media (max-width: 980px) {
    .erp-finsummary-grid { grid-template-columns: 1fr; gap: 16px; }
}

/* ---- the figures ---- */

.erp-finsummary-figures {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
}

.erp-finstat {
    padding: 11px 13px;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    background: #fff;
}

.erp-finstat span {
    display: block;
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--erp-muted);
}

.erp-finstat strong {
    display: block;
    margin-top: 3px;
    font-size: 17px;
    line-height: 1.1;
    color: var(--erp-ink);
    font-variant-numeric: tabular-nums;
}

.erp-finstat small {
    display: block;
    margin-top: 2px;
    font-size: 10.5px;
    color: var(--erp-muted);
}

/* Direction is carried by the label as well as the colour — "Net outflow" and
   "Net position" are different words, not the same word in red and green. */
.erp-finstat.is-income strong { color: var(--erp-success-dark); }
.erp-finstat.is-expense strong { color: var(--erp-ink); }
.erp-finstat.is-loss strong { color: var(--erp-danger); }
.erp-finstat.is-profit strong { color: var(--erp-success-dark); }

/* ---- the chart ---- */

.erp-finsummary-split {
    display: grid;
    grid-template-columns: 160px minmax(0, 1fr);
    gap: 14px;
    align-items: center;
}

@media (max-width: 600px) {
    .erp-finsummary-split { grid-template-columns: 1fr; }
}

.erp-finchart { margin: 0; }

.erp-finchart figcaption {
    margin-bottom: 6px;
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--erp-muted);
    text-align: center;
}

/* Chart.js measures against the nearest positioned ancestor, and the centred
   total is placed against the same box. */
.erp-finchart-canvas {
    position: relative;
    width: 100%;
    height: 158px;
}

.erp-finchart-centre {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    /* The ring stays hoverable through the label, or the tooltip dies wherever
       the total happens to sit. */
    pointer-events: none;
}

.erp-finchart-centre strong {
    font-size: 13.5px;
    font-weight: 700;
    color: var(--erp-ink);
    font-variant-numeric: tabular-nums;
}

.erp-finchart-centre small {
    font-size: 9.5px;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--erp-muted);
}

.erp-finlegend {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.erp-finlegend li {
    display: grid;
    grid-template-columns: 9px minmax(0, 1fr) auto auto;
    align-items: center;
    gap: 8px;
}

.erp-finlegend-dot { width: 9px; height: 9px; border-radius: 2px; background: var(--erp-muted); }

.erp-finlegend-name {
    font-size: 11.5px;
    color: var(--erp-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-finlegend-name small { color: var(--erp-muted); font-size: 10px; margin-left: 4px; }

.erp-finlegend-amount {
    font-size: 11.5px;
    font-weight: 600;
    color: var(--erp-ink);
    font-variant-numeric: tabular-nums;
}

.erp-finlegend-share {
    min-width: 34px;
    text-align: right;
    font-size: 10.5px;
    color: var(--erp-muted);
    font-variant-numeric: tabular-nums;
}

.erp-finsummary-empty {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    padding: 12px 14px;
    border: 1px dashed var(--erp-line);
    border-radius: var(--erp-radius);
    color: var(--erp-muted);
}

.erp-finsummary-empty i { margin-top: 2px; font-size: 15px; }
.erp-finsummary-empty p { margin: 0; font-size: 11.5px; line-height: 1.5; }

/* Money that is not in the books sits behind its own rule, not merely a
   different label — the separation has to survive a glance. */
.erp-finsummary-pending {
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px dashed var(--erp-line);
}

.erp-finsummary-pending p {
    display: flex;
    gap: 8px;
    align-items: flex-start;
    margin: 0 0 5px;
    font-size: 11.5px;
    line-height: 1.5;
    color: var(--erp-muted);
}

.erp-finsummary-pending p:last-child { margin-bottom: 0; }
.erp-finsummary-pending i { margin-top: 2px; color: var(--erp-warning); }
.erp-finsummary-pending strong { color: var(--erp-ink); }

/* ---- the entry form ---- */

.erp-fintype { display: flex; gap: 8px; }

.erp-fintype-btn {
    flex: 1 1 0;
    padding: 9px 12px;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    background: #fff;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--erp-muted);
    cursor: pointer;
    transition: border-color .12s ease, color .12s ease, background .12s ease;
}

.erp-fintype-btn i { margin-right: 6px; }

.erp-fintype-btn.is-active {
    border-color: var(--erp-primary);
    background: rgba(0, 194, 146, .08);
    color: var(--erp-ink);
}

.erp-fin-footer { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

.erp-fin-postnow {
    display: flex;
    gap: 8px;
    align-items: flex-start;
    margin: 0 auto 0 0;
    padding: 7px 10px;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    background: var(--erp-bg);
    cursor: pointer;
    max-width: 330px;
}

.erp-fin-postnow input { margin-top: 2px; }
.erp-fin-postnow span { font-size: 12px; font-weight: 600; color: var(--erp-ink); line-height: 1.35; }
.erp-fin-postnow small { display: block; font-weight: 400; font-size: 10.5px; color: var(--erp-muted); }

.erp-optional {
    font-size: 10px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--erp-muted);
    margin-left: 4px;
}


/* The chosen bank's own details, shown under the picker so whoever records a
   transfer can confirm the account before it becomes a posted voucher. */
.erp-fin-bankinfo {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 14px;
    margin-top: 7px;
    padding: 7px 10px;
    border: 1px solid var(--erp-line);
    border-left: 3px solid #0d9488;
    border-radius: var(--erp-radius);
    background: var(--erp-bg);
    font-size: 11.5px;
    color: var(--erp-ink);
}

.erp-fin-bankinfo span {
    display: inline-flex;
    flex-direction: column;
    line-height: 1.3;
    font-variant-numeric: tabular-nums;
}

.erp-fin-bankinfo small {
    font-size: 9.5px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--erp-muted);
}


/* --------------------------------------------------------------------------
   Suspected double entries
   The same spend logged on a task AND recorded in the ledger. Both sides are
   shown in full — this is a suspicion a person rules on, not a finding.
   -------------------------------------------------------------------------- */

.erp-dupes .erp-card-head h2 { color: var(--erp-warning); }

.erp-dupe {
    display: flex;
    gap: 16px;
    align-items: center;
    padding: 12px 0;
    border-bottom: 1px solid var(--erp-line);
}

.erp-dupe:last-child { border-bottom: 0; padding-bottom: 0; }
.erp-dupe:first-child { padding-top: 0; }

.erp-dupe-amount {
    flex: 0 0 120px;
    text-align: right;
}

.erp-dupe-amount strong {
    display: block;
    font-size: 16px;
    color: var(--erp-ink);
    font-variant-numeric: tabular-nums;
}

.erp-dupe-amount small {
    display: block;
    font-size: 10.5px;
    color: var(--erp-muted);
}

/* The two candidates side by side with "or" between them, because the choice
   is between them — a stacked list would read as two separate problems. */
.erp-dupe-sides {
    flex: 1 1 auto;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    gap: 12px;
    align-items: start;
}

@media (max-width: 780px) {
    .erp-dupe { flex-direction: column; align-items: stretch; gap: 8px; }
    .erp-dupe-amount { text-align: left; }
    .erp-dupe-sides { grid-template-columns: 1fr; }
}

.erp-dupe-side {
    padding: 9px 11px;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    background: #fff;
}

.erp-dupe-side strong {
    display: block;
    margin-top: 3px;
    font-size: 12.5px;
    color: var(--erp-ink);
}

.erp-dupe-side small {
    display: block;
    margin-top: 1px;
    font-size: 10.5px;
    color: var(--erp-muted);
}

.erp-dupe-side form { margin-top: 7px; }

.erp-dupe-note { font-style: italic; }

.erp-dupe-tag {
    display: inline-block;
    padding: 1px 6px;
    border-radius: 4px;
    font-size: 9px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
}

.erp-dupe-tag.is-task { background: var(--erp-bg); color: var(--erp-muted); }
.erp-dupe-tag.is-ledger { background: rgba(13, 148, 136, .12); color: #0d7f75; }

.erp-dupe-vs {
    align-self: center;
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--erp-muted);
}

.erp-costsummary-aside.is-warn i { color: var(--erp-warning); }


/* --------------------------------------------------------------------------
   Project store setup
   Declaring which materials a project will hold, before any arrive. Every
   shelf opened here reads zero — the panel says so, because a list of
   materials in a store could otherwise be mistaken for stock.
   -------------------------------------------------------------------------- */

.erp-matsetup-state {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    padding: 11px 13px;
    border: 1px dashed var(--erp-line);
    border-radius: var(--erp-radius);
    background: var(--erp-bg);
}

.erp-matsetup-state.is-ready {
    border-style: solid;
    border-left: 3px solid var(--erp-primary);
    background: #fff;
}

.erp-matsetup-state i { margin-top: 2px; color: var(--erp-muted); }
.erp-matsetup-state.is-ready i { color: var(--erp-primary); }

.erp-matsetup-state strong {
    display: block;
    font-size: 12.5px;
    color: var(--erp-ink);
}

.erp-matsetup-state span {
    display: block;
    font-size: 11px;
    line-height: 1.5;
    color: var(--erp-muted);
}

.erp-matsetup-code {
    margin-left: 4px;
    padding: 0 4px;
    border-radius: 3px;
    background: var(--erp-bg);
    font-size: 9.5px;
    letter-spacing: .03em;
}

/* ---- the dialog ---- */

.erp-matsetup-choice {
    display: flex;
    gap: 9px;
    align-items: flex-start;
    margin-bottom: 6px;
    padding: 9px 11px;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    background: #fff;
    cursor: pointer;
}

.erp-matsetup-choice:has(input:checked) {
    border-color: var(--erp-primary);
    background: rgba(0, 194, 146, .06);
}

.erp-matsetup-choice input { margin-top: 2px; }

.erp-matsetup-choice span {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--erp-ink);
    line-height: 1.35;
}

.erp-matsetup-choice small {
    display: block;
    font-weight: 400;
    font-size: 10.5px;
    color: var(--erp-muted);
}

.erp-matsetup-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 8px;
}

.erp-matsetup-count {
    font-size: 11px;
    color: var(--erp-muted);
    white-space: nowrap;
}

/* Scrolls on its own so the store choice and the footer stay put while a long
   catalogue is worked through. */
.erp-matsetup-list {
    max-height: 320px;
    overflow-y: auto;
    overscroll-behavior: contain;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    padding: 6px;
}

.erp-matsetup-item {
    display: flex;
    gap: 9px;
    align-items: center;
    padding: 6px 8px;
    border-radius: 6px;
    cursor: pointer;
}

.erp-matsetup-item:hover { background: var(--erp-bg); }

/* Already stocked: ticked, locked, and visibly settled rather than actionable. */
.erp-matsetup-item.is-held {
    opacity: .62;
    cursor: default;
}

.erp-matsetup-item-body { flex: 1 1 auto; min-width: 0; }

.erp-matsetup-item-body strong {
    display: block;
    font-size: 12px;
    color: var(--erp-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-matsetup-item-body small {
    display: block;
    font-size: 10.5px;
    color: var(--erp-muted);
}

.erp-matsetup-held {
    flex: 0 0 auto;
    padding: 1px 6px;
    border-radius: 4px;
    background: var(--erp-bg);
    font-size: 9px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--erp-muted);
}


/* ---- what is already marked ---- */

.erp-matsetup-marked {
    margin-top: 12px;
    padding-top: 11px;
    border-top: 1px solid var(--erp-line);
}

.erp-matsetup-marked-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
}

.erp-matsetup-marked-head span {
    font-size: 10.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--erp-muted);
}

.erp-matsetup-marked-head strong {
    font-size: 14px;
    color: var(--erp-ink);
    font-variant-numeric: tabular-nums;
}

.erp-matsetup-tally {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 10px;
    margin: 6px 0 8px;
}

.erp-matsetup-tallybit {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 10.5px;
    font-weight: 600;
}

.erp-matsetup-tallybit i { font-size: 8px; }
.erp-matsetup-tallybit.is-awaiting { color: var(--erp-muted); }
.erp-matsetup-tallybit.is-arrived { color: var(--erp-success-dark); }

/* Scrolls at about a dozen rows. A project can mark forty materials and the
   panel must not push the flow diagram off the bottom of the page. */
.erp-matsetup-markedlist {
    list-style: none;
    margin: 0;
    padding: 0;
    max-height: 260px;
    overflow-y: auto;
    overscroll-behavior: contain;
}

.erp-matsetup-markedlist li {
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 5px 2px;
    border-bottom: 1px solid var(--erp-line);
}

.erp-matsetup-markedlist li:last-child { border-bottom: 0; }

.erp-matsetup-markedlist i {
    flex: 0 0 12px;
    font-size: 10px;
    color: var(--erp-muted);
}

.erp-matsetup-markedlist li.is-arrived i { color: var(--erp-success); }

.erp-matsetup-markedname {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 11.5px;
    color: var(--erp-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-matsetup-markedname small {
    margin-left: 4px;
    font-size: 9.5px;
    color: var(--erp-muted);
}

.erp-matsetup-markedqty {
    flex: 0 0 auto;
    font-size: 11px;
    font-weight: 600;
    color: var(--erp-ink);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.erp-matsetup-markedqty small { margin-left: 2px; font-weight: 400; color: var(--erp-muted); }

/* Awaiting delivery reads as a state, not as a value. */
.erp-matsetup-markedqty em {
    font-style: normal;
    font-weight: 400;
    font-size: 10.5px;
    color: var(--erp-muted);
}


/* The "open on its own page" link, hidden on that page. Done in CSS rather
   than in Blade because the panel is re-rendered by AJAX without knowing which
   page asked for it, while this host attribute survives every swap. */
[data-teams-page] .erp-teams-openlink {
    display: none;
}


/* The land file's section strip rendered as links from a sub-page. Borrows the
   glass tabs and their per-section hues; only the spacing differs, because on a
   sub-page it sits under the header rather than on top of panels. */
/* Navigation, not content. Eleven sections wrap to two rows at most widths,
   and at the default tab size that was ~110px of chrome before the record even
   started — so this instance is deliberately smaller than the strip on the
   parcel page, where the tabs ARE the page. */
.erp-parcelnav {
    margin-bottom: 10px;
    padding: 6px 8px 2px;
    gap: 5px;
}

.erp-parcelnav .erp-tab {
    padding: 5px 9px;
    font-size: 11.5px;
    border-radius: 9px;
}

.erp-parcelnav .erp-tab i {
    font-size: 10px;
}

/* The counts are a glance, not a column of figures. */
.erp-parcelnav .erp-tab-count {
    font-size: 9.5px;
    padding: 0 4px;
}

/* An anchor carries the browser's link colour and underline into the button,
   which the tab rules built for <button> never had to deal with. */
.erp-parcelnav a.erp-tab,
.erp-parcelnav a.erp-tab:hover,
.erp-parcelnav a.erp-tab:focus {
    text-decoration: none;
}

/* The parcel itself, first in the row: a neutral so it reads as the way back
   rather than as an eleventh section. */
.erp-tabs.is-glass .erp-tab[data-tab="overview"] { --tab: 100 116 139; }


/* --------------------------------------------------------------------------
   Land payment voucher
   Gross − deductions = net, read as the sum it is. Three separate stat cards
   stated the same three figures but hid the arithmetic between them, and spent
   three times the height doing it.
   -------------------------------------------------------------------------- */

.erp-paystrip {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 11px 14px;
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    background: var(--erp-bg);
    flex-wrap: wrap;
}

.erp-paystrip > div:not(.erp-paystrip-op) {
    min-width: 0;
}

.erp-paystrip span {
    display: block;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--erp-muted);
}

.erp-paystrip strong {
    display: block;
    font-size: 16px;
    line-height: 1.15;
    color: var(--erp-ink);
    font-variant-numeric: tabular-nums;
}

.erp-paystrip small {
    display: block;
    font-size: 10px;
    color: var(--erp-muted);
}

/* The operators carry the relationship between the figures, so they are real
   glyphs rather than spacing the reader has to infer a meaning from. */
.erp-paystrip-op {
    font-size: 15px;
    color: var(--erp-muted);
    padding-top: 10px;
}

/* The net is what actually left the bank — the figure the voucher is about. */
.erp-paystrip .is-net strong {
    color: var(--erp-primary-dark);
    font-size: 18px;
}


/* --------------------------------------------------------------------------
   Tight cards
   A record page is a thing people open, read and leave. Four cards of default
   chrome — a 52px head, 32px of body padding and an 18px margin each — spent
   well over 300px on framing before a single value was shown, which is what
   pushed this page below the fold. Opt-in, so nothing else changes.
   -------------------------------------------------------------------------- */

.erp-tightcards .erp-card {
    margin-bottom: 10px;
}

.erp-tightcards .erp-card:last-child {
    margin-bottom: 0;
}

.erp-tightcards .erp-section-head {
    padding: 7px 14px;
    gap: 9px;
}

.erp-tightcards .erp-section-icon {
    height: 24px;
    width: 24px;
    font-size: 11px;
    border-radius: 6px;
}

.erp-tightcards .erp-card-body {
    padding: 11px 14px;
}

/* The pairs themselves close up with everything else, or the card shrinks
   around a grid that did not. */
.erp-tightcards .erp-deflist {
    gap: 9px 16px;
}

/* A table inside a tight card follows suit. */
.erp-tightcards .erp-table > :not(caption) > * > * {
    padding-top: 6px;
    padding-bottom: 6px;
}

/* The audit trail, as a footer line on the card it belongs to rather than a
   card of its own. */
.erp-paytrail {
    display: flex;
    gap: 7px;
    align-items: baseline;
    margin: 10px 0 0;
    padding-top: 9px;
    border-top: 1px solid var(--erp-line);
    font-size: 11px;
    line-height: 1.5;
    color: var(--erp-muted);
}

.erp-paytrail i {
    font-size: 10px;
}

.erp-paytrail strong {
    color: var(--erp-danger);
}


/* A tight record pulls its header in too — the default gutter below the page
   head was sized for pages that breathe, not for one asked to fit a screen. */
.erp-tightpage .erp-pagehead {
    margin-bottom: 10px;
}

.erp-tightpage .alert {
    padding: 8px 12px;
    margin-bottom: 10px;
    font-size: 12.5px;
}

/* ---------------------------------------------------------------------------
   Standard work breakdown (Projects → Task & Cost Setup)
--------------------------------------------------------------------------- */
.erp-tpl-table tr.erp-tpl-stage > td { background: var(--erp-surface-2); font-weight: 600; }
.erp-tpl-table tr.is-off > td { opacity: .5; }

/* The activity's indent, drawn rather than padded so the elbow lines up
   with the stage above it however long the name is. */
.erp-tpl-branch {
    display: inline-block;
    width: 22px;
    margin-left: 6px;
    border-left: 1px solid var(--erp-line);
    border-bottom: 1px solid var(--erp-line);
    height: 9px;
    vertical-align: middle;
    margin-right: 8px;
}

.erp-tpl-count {
    display: inline-block;
    margin-left: 6px;
    padding: 0 6px;
    border-radius: 999px;
    background: var(--erp-surface);
    border: 1px solid var(--erp-border);
    font-size: 10px;
    font-weight: 600;
    color: var(--erp-muted);
}

/* A switch rather than a bare checkbox: the column is scanned down, and a
   row of native ticks is much harder to read at a glance than a row of
   filled or empty tracks. */
.erp-tpl-toggle { display: inline-flex; cursor: pointer; margin: 0; }
.erp-tpl-toggle.is-locked { cursor: not-allowed; opacity: .6; }
.erp-tpl-toggle input { position: absolute; opacity: 0; width: 0; height: 0; }

.erp-tpl-toggle span {
    width: 34px;
    height: 18px;
    border-radius: 999px;
    background: var(--erp-line);
    position: relative;
    transition: background .15s ease;
}

.erp-tpl-toggle span::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: #fff;
    transition: transform .15s ease;
}

.erp-tpl-toggle input:checked + span { background: var(--erp-success); }
.erp-tpl-toggle input:checked + span::after { transform: translateX(16px); }
.erp-tpl-toggle input:focus-visible + span { box-shadow: 0 0 0 3px color-mix(in srgb, var(--erp-primary) 25%, transparent); }

.erp-tpl-check {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    margin-top: 12px;
    padding: 10px 12px;
    border: 1px solid var(--erp-border);
    border-radius: 9px;
    font-size: 12.5px;
    cursor: pointer;
}

.erp-tpl-check input[type="checkbox"] { margin-top: 2px; flex: 0 0 auto; }
.erp-tpl-check small { display: block; font-size: 11px; color: var(--erp-muted); margin-top: 2px; }

.erp-tpl-note {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 14px;
    padding: 9px 12px;
    border: 1px dashed var(--erp-line);
    border-radius: 9px;
    font-size: 12px;
    color: var(--erp-muted);
}

/* ---------------------------------------------------------------------------
   "Load from the standard breakdown" inside the task modal
--------------------------------------------------------------------------- */
.erp-tplpick {
    border: 1px solid var(--erp-border);
    border-radius: 10px;
    background: var(--erp-surface-2);
    padding: 11px 12px;
    margin-bottom: 4px;
}

.erp-tplpick-head {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--erp-muted);
    margin-bottom: 8px;
}

.erp-tplpick-row { display: flex; gap: 8px; align-items: center; }
.erp-tplpick-row > select { flex: 1 1 auto; min-width: 0; }
.erp-tplpick-row > .btn { flex: 0 0 auto; }

.erp-tplpick-check {
    display: flex;
    align-items: center;
    gap: 7px;
    margin: 9px 0 0;
    font-size: 12px;
    cursor: pointer;
}

.erp-tplpick-note {
    margin: 8px 0 0;
    font-size: 11.5px;
    line-height: 1.4;
    color: #a35c00;
}

/* Separates the two ways of adding a card, so neither reads as a step of
   the other. */
.erp-tplpick-or {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 14px 0 12px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--erp-muted);
}

.erp-tplpick-or::before,
.erp-tplpick-or::after { content: ""; flex: 1; height: 1px; background: var(--erp-line); }

/* ==========================================================================
   Document viewer — the in-page preview opened from any documents list
   ========================================================================== */

.erp-docview {
    border: none;
    border-radius: 14px;
    overflow: hidden;
}

.erp-docview .modal-header {
    align-items: center;
    background: linear-gradient(180deg, #fbfcfe, #f7f9fc);
    border-bottom: 1px solid var(--erp-line);
    gap: 12px;
    padding: 14px 18px;
}

.erp-docview-head {
    align-items: center;
    display: flex;
    gap: 12px;
    min-width: 0;
}

.erp-docview-head > i {
    color: var(--erp-primary, #00c292);
    font-size: 20px;
}

.erp-docview-head h5 {
    font-size: 15px;
    font-weight: 600;
    margin: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-docview-sub {
    color: var(--erp-muted);
    font-size: 11.5px;
}

.erp-docview-tools {
    align-items: center;
    display: flex;
    gap: 6px;
    margin-left: auto;
}

/* A fixed viewport height rather than one that grows with the file: a preview
   that pushes its own toolbar off the screen is worse than a scrollbar. */
.erp-docview-stage {
    background: #f2f4f8;
    display: flex;
    height: min(76vh, 820px);
}

.erp-docview-frame {
    border: 0;
    height: 100%;
    width: 100%;
}

.erp-docview-fallback {
    align-items: center;
    display: flex;
    flex-direction: column;
    justify-content: center;
    margin: auto;
    padding: 40px 24px;
    text-align: center;
}

.erp-docview-fallback-icon {
    color: var(--erp-muted);
    font-size: 46px;
    margin-bottom: 14px;
}

.erp-docview-fallback-title {
    font-size: 15px;
    font-weight: 600;
    margin: 0 0 4px;
}

.erp-docview-fallback-note {
    color: var(--erp-muted);
    font-size: 12.5px;
    margin: 0 0 18px;
    max-width: 380px;
}

.erp-docview-fallback-actions {
    display: flex;
    gap: 8px;
}

/* The document title in a list is a button so it can open the preview, but it
   has to read as the link it replaced. */
.erp-link-plain {
    background: none;
    border: 0;
    color: inherit;
    padding: 0;
    text-align: left;
}

.erp-link-plain:hover {
    text-decoration: underline;
}

/* An image previews as an image, never in a frame — nothing to execute, and
   it can be sized to fit instead of being scrolled inside a viewport. */
.erp-docview-image {
    display: block;
    margin: auto;
    max-height: 100%;
    max-width: 100%;
    object-fit: contain;
}

/* Legend swatch — only pies need one; a bar chart names its own axis. */
.erp-whchart-dot {
    border-radius: 50%;
    display: inline-block;
    flex: 0 0 auto;
    height: 8px;
    margin-right: 6px;
    width: 8px;
}

/* ==========================================================================
   Cost Breakdown Structure — the indented settings table
   ========================================================================== */

.erp-cbs-node {
    align-items: baseline;
    display: flex;
    gap: 7px;
}

/* The elbow that makes a child read as a child. Decorative only — the row's
   own indentation already carries the depth. */
.erp-cbs-branch {
    color: var(--erp-line);
    font-size: 9px;
    transform: rotate(90deg) scaleX(-1);
}

.erp-cbs-code {
    color: var(--erp-muted);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 11.5px;
    white-space: nowrap;
}

.erp-cbs-name {
    color: var(--erp-ink);
}

/* A group heads a branch; the weight is what lets someone find the section
   they want without reading every code. */
.erp-cbs-group .erp-cbs-name,
.erp-cbs-group .erp-cbs-code {
    color: var(--erp-ink);
    font-weight: 600;
}

/* Retired rows stay visible — they are still on historical costs — but step
   back so they are not mistaken for something you can code to today. */
.erp-cbs-retired > td {
    opacity: .55;
}

.erp-cbs-acct {
    font-size: 12px;
}

.erp-cbs-acct strong {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    margin-right: 5px;
}

.erp-cbs-unmapped {
    color: var(--erp-muted);
    font-size: 11.5px;
}

.erp-cbs-desc {
    color: var(--erp-muted);
    font-size: 11.5px;
    max-width: 260px;
}

/* A node the project renamed or renumbered after taking it from the company
   standard. Worth marking: a report lining this project up against the company
   breakdown has to know the two no longer read the same. */
.erp-cbs-diverged {
    color: #7c3aed;
    font-size: 11.5px;
}

/* --- Cost items by type -----------------------------------------------------
   Horizontal bars in one hue. Identity is carried by the row label, never by
   colour, so there is no categorical palette here to get wrong — see the
   partial's own note for why a stacked proportional bar was rejected. */

.erp-cbsmix {
    margin-top: 10px;
}

.erp-cbsmix-title {
    color: var(--erp-muted);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .04em;
    margin: 0 0 8px;
    text-transform: uppercase;
}

.erp-cbsmix-rows {
    display: grid;
    gap: 5px;
}

.erp-cbsmix-row {
    align-items: center;
    display: grid;
    /* Label, bar, figure. The label column is fixed so every bar starts on the
       same baseline — lengths are only comparable if they do. */
    grid-template-columns: 104px 1fr 74px;
    gap: 10px;
}

.erp-cbsmix-label {
    color: var(--erp-ink);
    font-size: 12px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-cbsmix-track {
    background: var(--erp-line);
    border-radius: 4px;
    display: block;
    height: 8px;
    overflow: hidden;
}

.erp-cbsmix-fill {
    background: #4338ca;
    /* Rounded only at the data end, anchored square to the baseline, so the
       bar cannot read as starting anywhere but zero. */
    border-radius: 0 4px 4px 0;
    display: block;
    height: 100%;
    min-width: 2px;
}

.erp-cbsmix-value {
    color: var(--erp-ink);
    font-size: 12px;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    text-align: right;
}

.erp-cbsmix-value small {
    color: var(--erp-muted);
    font-weight: 500;
    margin-left: 5px;
}

/* The "of N" that turns a count into a ratio without a second stat box. */
.erp-sale-stat-of {
    color: rgba(255, 255, 255, .8);
    font-size: 12px;
    font-weight: 500;
}

/* --- The company-library picker --------------------------------------------- */

.erp-cbs-picklist {
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    max-height: 46vh;
    overflow-y: auto;
}

.erp-cbs-pickrow {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    cursor: pointer;
    display: flex;
    gap: 8px;
    margin: 0;
    padding: 7px 10px;
}

.erp-cbs-pickrow:last-child { border-bottom: 0; }
.erp-cbs-pickrow:hover { background: var(--erp-tint, #f7f8fb); }

/* Already on the project: shown rather than hidden, so the list does not
   change shape every time somebody applies it. Stepped back, not removed. */
.erp-cbs-pickrow.is-taken {
    cursor: default;
    opacity: .6;
}

.erp-cbs-pickrow.is-taken:hover { background: transparent; }

.erp-cbs-pickrow .form-check-input {
    flex: 0 0 auto;
    margin: 0;
}

/* Dark mode for the pieces above that carry their own colour rather than a
   token. The rest follow --erp-ink / --erp-muted / --erp-line and flip on
   their own.

   Stepped, not flipped: an indigo picked to sit on a near-white surface is
   nearly invisible on a near-black one. Both steps were measured against their
   own surface (#fcfcfb light, #1a1a19 dark) and clear 3:1. */
:root[data-theme="dark"] .erp-cbsmix-fill { background: #818cf8; }
:root[data-theme="dark"] .erp-cbs-diverged { color: #c4b5fd; }
:root[data-theme="dark"] .erp-cbs-pickrow:hover { background: rgba(255, 255, 255, .04); }
:root[data-theme="dark"] .erp-cbs-pickrow.is-taken:hover { background: transparent; }


/* Running work that is past its date. Carried by the word "Overdue" and a
   warning glyph as well as the colour — the card has to say so, not just
   turn red. */
.erp-tprog-due.is-overdue {
    color: var(--erp-danger);
    font-weight: 600;
}

/* ==========================================================================
   Project estimating
   ========================================================================== */

/* The cost code heading its own build-up. Weighted so a reader scanning for
   one code finds it without reading every description under it. */
.erp-est-group > td {
    background: #f7f9fc;
    font-weight: 600;
}

.erp-est-group .erp-identity-meta {
    margin-left: 8px;
}

.erp-est-desc {
    padding-left: 22px !important;
}

/* Quick access on the project overview */
.erp-estquick {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 26px;
}

.erp-estquick-main {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.erp-estquick-label {
    color: var(--erp-muted);
    font-size: 10.5px;
    letter-spacing: .4px;
    text-transform: uppercase;
}

.erp-estquick-value {
    font-size: 19px;
}

.erp-estquick-foot {
    align-items: center;
    color: var(--erp-muted);
    display: flex;
    flex-wrap: wrap;
    font-size: 11.5px;
    gap: 6px;
}

.erp-estquick-actions {
    display: flex;
    gap: 8px;
    margin-left: auto;
}

/* =========================================================================
   Project estimates — approval chain
   =========================================================================
   Named erp-estchain, NOT erp-chain: procurement's material requirements
   already owns .erp-chain for its pill breadcrumb, and the two have different
   shapes. Reusing the name would restyle that screen from here.

   Drawn as a horizontal run of steps with a connector between them, so the
   sequence reads as a sequence. Every rung shows even when unreached — on an
   estimate sitting in review, what is still owed is the useful half. */

.erp-estchain {
    align-items: flex-start;
    display: flex;
    flex-wrap: wrap;
    gap: 0;
    list-style: none;
    margin: 0;
    padding: 0;
}

.erp-estchain-step {
    align-items: center;
    display: flex;
    flex: 0 1 auto;
    gap: 8px;
    min-width: 0;
    padding: 4px 0;
    position: relative;
}

/* The connector, drawn between steps rather than as markup so each step stays
   one element for a screen reader. */
.erp-estchain-step + .erp-estchain-step {
    padding-left: 30px;
}

.erp-estchain-step + .erp-estchain-step::before {
    background: var(--erp-line);
    content: "";
    height: 2px;
    left: 8px;
    position: absolute;
    top: 50%;
    width: 14px;
}

/* A reached step carries its colour forward into the connector that led to it,
   so the completed run reads as one continuous line. */
.erp-estchain-step.is-done + .erp-estchain-step.is-done::before {
    background: var(--erp-success);
}

.erp-estchain-dot {
    align-items: center;
    background: var(--erp-bg);
    border: 1px solid var(--erp-line);
    border-radius: 50%;
    color: var(--erp-muted);
    display: flex;
    flex: 0 0 auto;
    font-size: 10px;
    height: 26px;
    justify-content: center;
    width: 26px;
}

.erp-estchain-step.is-done .erp-estchain-dot {
    background: var(--erp-success);
    border-color: var(--erp-success);
    color: #fff;
}

/* Where it stands right now gets a ring rather than a different fill: the fill
   already means "reached", and overloading it would make the current step look
   like a different kind of event. */
.erp-estchain-step.is-current .erp-estchain-dot {
    box-shadow: 0 0 0 3px rgb(0 194 146 / .22);
}

.erp-estchain-step.is-retired .erp-estchain-dot {
    background: var(--erp-muted);
    border-color: var(--erp-muted);
}

.erp-estchain-body {
    display: flex;
    flex-direction: column;
    line-height: 1.3;
    min-width: 0;
}

.erp-estchain-body strong {
    color: var(--erp-ink);
    font-size: 12.5px;
    font-weight: 600;
    white-space: nowrap;
}

.erp-estchain-body small {
    color: var(--erp-muted);
    font-size: 10.5px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* An unreached step is present but recessive — it is a promise, not a fact. */
.erp-estchain-step:not(.is-done) .erp-estchain-body strong {
    color: var(--erp-muted);
    font-weight: 500;
}

/* The list-row form. Only reached rungs are drawn there, and the names come off
   entirely below the widest breakpoints — in a table cell the dots and dates
   are the summary. */
.erp-estchain.is-compact {
    gap: 0;
}

.erp-estchain.is-compact .erp-estchain-dot {
    font-size: 8.5px;
    height: 20px;
    width: 20px;
}

.erp-estchain.is-compact .erp-estchain-body strong { font-size: 11px; }
.erp-estchain.is-compact .erp-estchain-body small { font-size: 10px; }

.erp-estchain.is-compact .erp-estchain-step + .erp-estchain-step {
    padding-left: 22px;
}

.erp-estchain.is-compact .erp-estchain-step + .erp-estchain-step::before {
    left: 6px;
    width: 10px;
}

@media (max-width: 1399.98px) {
    .erp-estchain.is-compact .erp-estchain-body strong { display: none; }
}

/* The buttons that move it on, separated from the chain they act on. */
.erp-estchain-actions {
    border-top: 1px solid var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 14px;
    padding-top: 14px;
}

:root[data-theme="dark"] .erp-estchain-dot {
    background: var(--erp-surface-2);
}

:root[data-theme="dark"] .erp-estchain-step.is-done .erp-estchain-dot {
    background: var(--erp-success);
}

/* =========================================================================
   Project activity timeline
   =========================================================================
   The feed is the flex child that gives up its natural height and scrolls
   instead, which is what keeps a project with six hundred events the same
   height as one with six.

   It has its own page now (the Activity tab). It used to be a card beside the
   overview's detail card, sized by it through an out-of-flow .erp-ptl-col
   trick — that rule and the column are both gone; .erp-ptl.is-page below is
   what the page uses in their place. */

.erp-ptl {
    display: flex;
    flex-direction: column;

    /* Stacked (below xl) there is nothing to match, so the card carries its
       own tight height and the feed scrolls inside it. Side by side, the rule
       further down overrides this with the detail card's height. */
    height: clamp(360px, 56vh, 560px);

    /* Without this the card grows to its content and drags the row with it -
       the feed would stretch the page rather than scroll inside the card. */
    min-height: 0;
    overflow: hidden;
}

/* On its own page there is no card beside it to match, and no reason to stop
   at 560px: the feed fills the window and scrolls inside itself, so the PAGE
   never grows a scrollbar — which is the whole point of giving it a page.

   Deliberately a little short rather than a little tall. A page header and a
   sixteen-tab strip that wraps to two or three rows sit above this, and
   guessing high would put the scrollbar back on the page. Guessing low only
   means a little more scrolling inside the feed, which is what a feed does. */
.erp-ptl.is-page {
    height: calc(100vh - 280px);
    min-height: 420px;
}

.erp-ptl-head { flex: 0 0 auto; }
.erp-ptl-head h2 { margin-bottom: 1px; }

/* What is true right now, above the history. */
.erp-ptl-now {
    display: flex;
    flex: 0 0 auto;
    flex-wrap: wrap;
    gap: 6px;
    padding: 0 16px 10px;
}

.erp-ptl-chip {
    align-items: center;
    border: 1px solid var(--erp-line);
    border-radius: 999px;
    cursor: pointer;
    display: inline-flex;
    font-size: 11px;
    font-weight: 600;
    gap: 5px;
    padding: 4px 10px;
    transition: background .15s ease, border-color .15s ease;
    user-select: none;
}

.erp-ptl-chip i { font-size: 9.5px; }

.erp-ptl-chip.is-late { background: rgb(233 30 99 / .08); border-color: rgb(233 30 99 / .3); color: var(--erp-danger); }
.erp-ptl-chip.is-live { background: rgb(0 194 146 / .1); border-color: rgb(0 194 146 / .32); color: var(--erp-success-dark); }
.erp-ptl-chip.is-soon { background: rgb(255 152 0 / .1); border-color: rgb(255 152 0 / .32); color: #b45309; }
.erp-ptl-chip.is-quiet { color: var(--erp-muted); cursor: default; }
.erp-ptl-chip.is-open { box-shadow: inset 0 0 0 1px currentColor; }

.erp-ptl-tasks {
    background: var(--erp-bg);
    border-top: 1px solid var(--erp-line);
    flex: 0 1 auto;
    /* Capped as a share of the card rather than at a flat 132px: on the short
       end of the clamp, a fixed 132px panel plus the head and filters left
       the feed a couple of rows tall. */
    max-height: 28%;
    min-height: 0;
    overflow-y: auto;
    padding: 8px 16px;
}

.erp-ptl-tasks-head {
    color: var(--erp-muted);
    display: block;
    font-size: 10px;
    letter-spacing: .05em;
    margin-bottom: 4px;
    text-transform: uppercase;
}

.erp-ptl-task {
    align-items: baseline;
    display: flex;
    gap: 8px;
    justify-content: space-between;
    padding: 3px 0;
}

.erp-ptl-task-name {
    color: var(--erp-ink);
    font-size: 12px;
    overflow: hidden;
    text-decoration: none;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-ptl-task-name:hover { color: var(--erp-primary); }

.erp-ptl-task-meta {
    color: var(--erp-muted);
    flex: 0 0 auto;
    font-size: 10.5px;
    font-variant-numeric: tabular-nums;
}

/* Icon-only filters: seven worded checkboxes would take more room than the
   feed they filter in a column this narrow. */
.erp-ptl-filters {
    border-top: 1px solid var(--erp-line);
    display: flex;
    flex: 0 0 auto;
    gap: 4px;
    overflow-x: auto;
    padding: 8px 16px;
    scrollbar-width: none;
}

.erp-ptl-filters::-webkit-scrollbar { display: none; }

.erp-ptl-filter {
    background: var(--erp-bg);
    border: 1px solid transparent;
    border-radius: 7px;
    color: var(--erp-muted);
    cursor: pointer;
    flex: 0 0 auto;
    font-size: 11px;
    line-height: 1;
    padding: 6px 9px;
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}

.erp-ptl-filter:hover { color: var(--erp-ink); }

.erp-ptl-filter.is-active {
    background: var(--erp-primary);
    border-color: var(--erp-primary);
    color: #fff;
}

/* The scroller. */
.erp-ptl-feed {
    flex: 1 1 auto;
    /* Zero, not 220px. A flex child will not shrink below its min-height, so
       a floor here would push the card past the height set above the moment
       the chips and a task panel were both open — which is exactly when the
       feed needs to give way. */
    min-height: 0;
    overflow-y: auto;
    padding: 4px 16px 14px;
    scrollbar-width: thin;
}

.erp-ptl-feed::-webkit-scrollbar { width: 5px; }
.erp-ptl-feed::-webkit-scrollbar-thumb { background: var(--erp-line); border-radius: 3px; }

/* Day headers: forty bare timestamps read as noise, and "what happened on
   Tuesday" is the question people actually bring to a feed. */
.erp-ptl-day {
    background: var(--erp-surface, #fff);
    color: var(--erp-muted);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .06em;
    padding: 10px 0 5px;
    position: sticky;
    text-transform: uppercase;
    top: 0;
    z-index: 1;
}

.erp-ptl-item {
    display: flex;
    gap: 9px;
    padding-bottom: 12px;
    position: relative;
}

/* The connector down the run of events. Not on the last one, or the line
   dangles below the final dot. */
.erp-ptl-item:not(:last-child)::before {
    background: var(--erp-line);
    bottom: 0;
    content: "";
    left: 11px;
    position: absolute;
    top: 24px;
    width: 2px;
}

.erp-ptl-dot {
    align-items: center;
    background: var(--erp-bg);
    border: 1px solid var(--erp-line);
    border-radius: 50%;
    color: var(--erp-muted);
    display: flex;
    flex: 0 0 auto;
    font-size: 9px;
    height: 24px;
    justify-content: center;
    width: 24px;
    z-index: 1;
}

/* One hue per kind of activity, so the feed can be skimmed by colour before
   it is read. */
.erp-ptl-item.is-task .erp-ptl-dot     { background: #eef2ff; border-color: #c7d2fe; color: #4338ca; }
.erp-ptl-item.is-boq .erp-ptl-dot      { background: #f5f3ff; border-color: #ddd6fe; color: #6d28d9; }
.erp-ptl-item.is-cost .erp-ptl-dot     { background: #fff7ed; border-color: #fed7aa; color: #c2410c; }
.erp-ptl-item.is-consume .erp-ptl-dot  { background: #fef2f2; border-color: #fecaca; color: #b91c1c; }
.erp-ptl-item.is-stock .erp-ptl-dot    { background: #ecfeff; border-color: #a5f3fc; color: #0e7490; }
.erp-ptl-item.is-team .erp-ptl-dot     { background: #f0fdf4; border-color: #bbf7d0; color: #15803d; }
.erp-ptl-item.is-transfer .erp-ptl-dot { background: #fdf4ff; border-color: #f5d0fe; color: #a21caf; }

.erp-ptl-body {
    display: flex;
    flex-direction: column;
    line-height: 1.35;
    min-width: 0;
    padding-top: 2px;
}

.erp-ptl-title {
    color: var(--erp-ink);
    font-size: 12.5px;
    font-weight: 600;
}

.erp-ptl-title a { color: inherit; text-decoration: none; }
.erp-ptl-title a:hover { color: var(--erp-primary); }

.erp-ptl-detail,
.erp-ptl-when {
    color: var(--erp-muted);
    font-size: 11px;
    overflow-wrap: anywhere;
}

.erp-ptl-when { font-size: 10.5px; margin-top: 1px; }

.erp-ptl-more { padding: 6px 0 2px; }

.erp-ptl-empty,
.erp-ptl-end,
.erp-ptl-loading {
    color: var(--erp-muted);
    font-size: 12px;
    padding: 26px 8px;
    text-align: center;
}

.erp-ptl-empty i { display: block; font-size: 20px; margin-bottom: 8px; opacity: .5; }
.erp-ptl-empty p { margin: 0; }
.erp-ptl-end { padding: 14px 8px; font-size: 11px; }

[data-ptl-refresh].is-spinning i { animation: erp-ptl-spin 1s linear infinite; }

@keyframes erp-ptl-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
    [data-ptl-refresh].is-spinning i { animation: none; }
}

/* Below xl the two columns stack, so the feed is no longer matched to a card
   beside it - capped instead, or it would run for the length of the project. */
@media (max-width: 1199.98px) {
    .erp-ptl { max-height: 620px; }
}

:root[data-theme="dark"] .erp-ptl-dot,
:root[data-theme="dark"] .erp-ptl-tasks,
:root[data-theme="dark"] .erp-ptl-filter {
    background: var(--erp-surface-2);
}

:root[data-theme="dark"] .erp-ptl-day { background: var(--erp-surface); }

:root[data-theme="dark"] .erp-ptl-filter.is-active {
    background: var(--erp-primary);
    color: #06231c;
}

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

/* ==========================================================================
   Daily Transactions — the accountant's home screen
   ========================================================================== */

/* One row of record-this buttons. Always the same order: the value of this
   strip is that the hand learns where each one is.

   These sit directly above the balance cards, which are NOT clickable, so the
   whole job of this block is to look unmistakably pressable: raised at rest,
   a tinted icon chip, a trailing plus, a real hover tint and a press that
   actually moves. A flat card with a coloured edge reads as a statistic. */
.erp-daily-actions {
    display: grid;
    gap: 10px;
    grid-template-columns: repeat(auto-fit, minmax(178px, 1fr));
    margin-bottom: 14px;
}

.erp-daily-action {
    --tone: var(--erp-muted);

    align-items: center;
    background: var(--erp-surface, #fff);
    border: 1px solid var(--erp-line);
    border-radius: 11px;
    /* Raised at rest. The shadow is what separates a control from a panel. */
    box-shadow: 0 1px 0 rgba(20, 35, 60, .04), 0 2px 5px rgba(20, 35, 60, .06);
    color: var(--erp-ink);
    cursor: pointer;
    display: flex;
    gap: 11px;
    padding: 11px 13px;
    position: relative;
    text-align: left;
    text-decoration: none;
    transition: background-color .15s ease, border-color .15s ease,
                box-shadow .15s ease, transform .1s ease;
}

/* The tinted chip carries the colour, so the card itself stays calm until
   it is hovered. */
.erp-daily-action-chip {
    align-items: center;
    background: color-mix(in srgb, var(--tone) 12%, transparent);
    border-radius: 9px;
    color: var(--tone);
    display: flex;
    flex: 0 0 auto;
    height: 34px;
    justify-content: center;
    transition: background-color .15s ease, color .15s ease;
    width: 34px;
}

.erp-daily-action-chip i {
    font-size: 15px;
}

.erp-daily-action-text {
    display: flex;
    flex-direction: column;
    line-height: 1.3;
    min-width: 0;
}

.erp-daily-action-text strong {
    color: var(--erp-ink);
    font-size: 13px;
    font-weight: 600;
}

.erp-daily-action-text em {
    color: var(--erp-muted);
    font-size: 11px;
    font-style: normal;
}

/* The trailing cue says what pressing this does. Faint until hovered, so six
   of them in a row do not shout. */
.erp-daily-action-go {
    color: var(--erp-line);
    font-size: 11px;
    margin-left: auto;
    transition: color .15s ease, transform .15s ease;
}

.erp-daily-action:hover {
    background: color-mix(in srgb, var(--tone) 6%, var(--erp-surface, #fff));
    border-color: color-mix(in srgb, var(--tone) 38%, var(--erp-line));
    box-shadow: 0 4px 14px rgba(20, 35, 60, .11);
    transform: translateY(-2px);
}

.erp-daily-action:hover .erp-daily-action-chip {
    background: var(--tone);
    color: #fff;
}

.erp-daily-action:hover .erp-daily-action-go {
    color: var(--tone);
}

/* An arrow can be seen to travel; a plus cannot be rotated to any visible
   effect — it is four-fold symmetric, so it would sit there looking broken.
   Each gets the motion its own shape can actually show. */
.erp-daily-action:hover .erp-daily-action-go.is-go {
    transform: translateX(3px);
}

.erp-daily-action:hover .erp-daily-action-go.is-add {
    transform: scale(1.25);
}

/* Pressed. Movement on click is the feedback that tells somebody the button
   registered, and its absence is what makes a control feel dead. */
.erp-daily-action:active {
    box-shadow: inset 0 2px 5px rgba(20, 35, 60, .12);
    transform: translateY(0);
}

/* Keyboard users get the same affordance, in the tone of the action. */
.erp-daily-action:focus-visible {
    outline: 2px solid var(--tone);
    outline-offset: 2px;
}

/* Colour carries the meaning that matters at a glance: money out is warm,
   money in is green, and the neutral ones are neither. */
.erp-daily-action.is-expense { --tone: #ef4444; }
.erp-daily-action.is-receipt { --tone: #16a34a; }
.erp-daily-action.is-payment { --tone: #f59e0b; }
.erp-daily-action.is-transfer { --tone: #0ea5e9; }
.erp-daily-action.is-advance { --tone: #8b5cf6; }
.erp-daily-action.is-journal { --tone: #64748b; }

/* Leaves / arrives, shown before a transfer is saved. */
.erp-transfer-preview {
    align-items: center;
    background: #f7f9fc;
    border: 1px dashed var(--erp-line);
    border-radius: 9px;
    display: flex;
    gap: 18px;
    justify-content: center;
    margin: 4px 0 14px;
    padding: 10px 14px;
}

.erp-transfer-preview span {
    display: flex;
    flex-direction: column;
    text-align: center;
}

.erp-transfer-preview em {
    color: var(--erp-muted);
    font-size: 10.5px;
    font-style: normal;
    letter-spacing: .4px;
    text-transform: uppercase;
}

.erp-transfer-preview strong {
    font-size: 16px;
}

.erp-transfer-preview i {
    color: var(--erp-muted);
}


/* ==========================================================================
   Task progress photos
   ==========================================================================
   The gallery, the upload staging list and the lightbox. Photos live in the
   central documents table under the "photo" collection — there is no separate
   photo table — so everything here is presentation over shared storage.
   ========================================================================== */

/* --- The count badge on a list row ----------------------------------------- */

.erp-task-photocount {
    background: var(--erp-primary, #6c63ff);
    border-radius: 999px;
    color: #fff;
    font-size: 9px;
    font-weight: 700;
    line-height: 1;
    padding: 2px 4px;
    position: absolute;
    right: 0;
    top: 0;
    transform: translate(35%, -35%);
}

/* --- The gallery grid ------------------------------------------------------ */

.erp-photos-grid {
    display: grid;
    /* auto-fill rather than auto-fit: a single photo stays thumbnail-sized
       instead of stretching across the whole modal. */
    grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
    gap: 10px;
}

.erp-photo {
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    margin: 0;
    overflow: hidden;
    position: relative;
}

.erp-photo-thumb {
    background: var(--erp-tint, #f7f8fb);
    border: 0;
    cursor: zoom-in;
    display: block;
    padding: 0;
    width: 100%;
}

.erp-photo-thumb img {
    /* A fixed box with cover: site photos arrive in portrait and landscape,
       and a grid whose rows change height with each one is unreadable. */
    aspect-ratio: 4 / 3;
    display: block;
    height: 100%;
    object-fit: cover;
    width: 100%;
}

.erp-photo figcaption {
    display: grid;
    gap: 1px;
    padding: 6px 8px 7px;
}

.erp-photo-title {
    color: var(--erp-ink);
    font-size: 11.5px;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-photo-meta {
    color: var(--erp-muted);
    font-size: 10.5px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Always visible, not hover-revealed: a control that appears only on hover is
   a control that does not exist on a tablet, which is what a site engineer is
   holding. */
.erp-photo-remove {
    align-items: center;
    background: rgba(0, 0, 0, .55);
    border: 0;
    border-radius: 999px;
    color: #fff;
    display: flex;
    font-size: 10px;
    height: 22px;
    justify-content: center;
    position: absolute;
    right: 6px;
    top: 6px;
    width: 22px;
}

.erp-photo-remove:hover { background: var(--erp-danger, #b91c1c); }

.erp-photos-empty {
    align-items: center;
    color: var(--erp-muted);
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 26px 16px;
    text-align: center;
}

.erp-photos-empty i { font-size: 26px; opacity: .45; }
.erp-photos-empty p { font-size: 12px; margin: 0; max-width: 320px; }

/* A single row of thumbnails on the read-only detail panel. */
.erp-photos-strip {
    display: flex;
    gap: 6px;
    overflow-x: auto;
    padding-bottom: 4px;
}

.erp-photos-strip .erp-photo-thumb {
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    flex: 0 0 auto;
    overflow: hidden;
    width: 84px;
}

/* --- The drop zone --------------------------------------------------------- */

.erp-photo-drop {
    align-items: center;
    background: var(--erp-tint, #f7f8fb);
    border: 1.5px dashed var(--erp-line);
    border-radius: 12px;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    gap: 3px;
    margin: 0;
    padding: 18px 16px;
    text-align: center;
    transition: border-color .12s ease, background .12s ease;
}

.erp-photo-drop i { color: var(--erp-muted); font-size: 20px; margin-bottom: 2px; }
.erp-photo-drop strong { color: var(--erp-ink); font-size: 12.5px; }
.erp-photo-drop span { color: var(--erp-muted); font-size: 11px; }

.erp-photo-drop:hover,
.erp-photo-drop:focus-visible,
.erp-photo-drop.is-over {
    background: var(--erp-bg, #fff);
    border-color: var(--erp-primary, #6c63ff);
}

.erp-photo-drop:focus-visible { outline: 2px solid var(--erp-primary, #6c63ff); outline-offset: 2px; }

/* --- The staging list ------------------------------------------------------ */

.erp-photo-queue {
    display: grid;
    gap: 6px;
    margin-top: 10px;
}

.erp-photo-qrow {
    align-items: center;
    background: var(--erp-tint, #f7f8fb);
    border-radius: 9px;
    display: flex;
    gap: 9px;
    padding: 6px 8px;
}

.erp-photo-qthumb {
    border-radius: 6px;
    flex: 0 0 auto;
    height: 40px;
    object-fit: cover;
    width: 40px;
}

.erp-photo-qmeta {
    display: grid;
    flex: 1 1 auto;
    gap: 2px;
    min-width: 0;
}

.erp-photo-qname {
    color: var(--erp-ink);
    font-size: 11.5px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-photo-qsize { color: var(--erp-muted); font-size: 10.5px; }
.erp-photo-qcaption { font-size: 11.5px; margin-top: 2px; }

.erp-photo-qdrop {
    background: transparent;
    border: 0;
    color: var(--erp-muted);
    flex: 0 0 auto;
    font-size: 12px;
    padding: 4px 6px;
}

.erp-photo-qdrop:hover { color: var(--erp-danger, #b91c1c); }

/* --- The lightbox ---------------------------------------------------------- */

.erp-lightbox {
    align-items: center;
    background: rgba(0, 0, 0, .86);
    display: flex;
    gap: 8px;
    inset: 0;
    justify-content: center;
    padding: 24px;
    position: fixed;
    /* Above Bootstrap's modal (1055), because it opens from inside one. */
    z-index: 2000;
}

.erp-lightbox[hidden] { display: none; }

.erp-lightbox-body {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin: 0;
    max-height: 100%;
    min-width: 0;
}

.erp-lightbox-body img {
    border-radius: 8px;
    max-height: calc(100vh - 140px);
    max-width: 100%;
    object-fit: contain;
}

.erp-lightbox-body figcaption {
    color: rgba(255, 255, 255, .92);
    display: grid;
    gap: 2px;
    text-align: center;
}

.erp-lightbox-body figcaption strong { font-size: 13px; }
.erp-lightbox-body figcaption span { color: rgba(255, 255, 255, .62); font-size: 11.5px; }

.erp-lightbox-close,
.erp-lightbox-nav {
    align-items: center;
    background: rgba(255, 255, 255, .12);
    border: 0;
    border-radius: 999px;
    color: #fff;
    display: flex;
    flex: 0 0 auto;
    height: 40px;
    justify-content: center;
    width: 40px;
}

.erp-lightbox-close:hover,
.erp-lightbox-nav:hover { background: rgba(255, 255, 255, .24); }

.erp-lightbox-close {
    position: absolute;
    right: 18px;
    top: 18px;
}

/* On a phone the side arrows would eat the picture, so they move under it. */
@media (max-width: 575.98px) {
    .erp-lightbox { flex-wrap: wrap; padding: 16px; }
    .erp-lightbox-body { order: -1; width: 100%; }
}

/* --- Dark mode ------------------------------------------------------------- */

:root[data-theme="dark"] .erp-photo-drop { background: rgba(255, 255, 255, .04); }
:root[data-theme="dark"] .erp-photo-drop:hover,
:root[data-theme="dark"] .erp-photo-drop.is-over { background: rgba(255, 255, 255, .08); }
:root[data-theme="dark"] .erp-photo-qrow { background: rgba(255, 255, 255, .05); }
:root[data-theme="dark"] .erp-photo-thumb { background: rgba(255, 255, 255, .05); }

/* The one-line hint under each Record-a-transaction item. */
.erp-qc-hint {
    color: var(--erp-muted);
    font-size: 10.5px;
    margin-left: auto;
    padding-left: 12px;
}


/* ==========================================================================
   Bulk task removal — project dashboard board only
   ==========================================================================
   Selection mode is a class on the board host (#taskBoard), not state inside
   the cards: the cards are replaced wholesale on every AJAX refresh and the
   host is not, so the mode survives a refresh that the ticks do not.
   ========================================================================== */

/* Hidden until the board is put into selection mode. Rendered on every card
   regardless, so entering the mode costs no re-render. */
.erp-kcard-pick {
    display: none;
    left: 8px;
    margin: 0;
    position: absolute;
    top: 8px;
    z-index: 2;
}

.erp-kanban .is-selecting .erp-kcard-pick,
#taskBoard.is-selecting .erp-kcard-pick {
    display: block;
}

.erp-kcard-pick .form-check-input {
    cursor: pointer;
    height: 16px;
    margin: 0;
    width: 16px;
}

/* The card shifts right to make room, rather than the tick overlapping the
   title — a checkbox sitting on top of text is one nobody trusts. */
#taskBoard.is-selecting .erp-kcard {
    padding-left: 30px;
    position: relative;
}

/* Dragging and ticking are different intentions, and a card that does both at
   once is a card that reorders itself when you meant to select it. */
#taskBoard.is-selecting .erp-kcard {
    cursor: default;
}

#taskBoard.is-selecting .erp-kcard:has([data-task-pick]:checked) {
    box-shadow: inset 0 0 0 2px var(--erp-primary, #6c63ff);
}

/* The action bar. Outside the board host, so an AJAX refresh of the board
   cannot take it away mid-selection. */
.erp-bulkbar {
    align-items: center;
    background: var(--erp-tint, #f7f8fb);
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 0 16px 10px;
    padding: 8px 12px;
}

.erp-bulkbar[hidden] { display: none; }

.erp-bulkbar-count {
    color: var(--erp-muted);
    font-size: 12px;
    margin-right: auto;
}

.erp-bulkbar-count strong {
    color: var(--erp-ink);
    font-variant-numeric: tabular-nums;
}

/* The one destructive control on the bar, and the only thing on this board
   that is coloured for danger rather than for identity. */
.btn-erp-danger {
    background: var(--erp-danger, #b91c1c);
    border: 1px solid transparent;
    border-radius: 8px;
    color: #fff;
    font-size: 12px;
    font-weight: 600;
}

.btn-erp-danger:hover { background: #991b1b; color: #fff; }
.btn-erp-danger:disabled { opacity: .65; }

/* The toggle, while selection mode is on. */
[data-bulk-toggle].is-active {
    background: var(--erp-ink);
    border-color: var(--erp-ink);
    color: #fff;
}

:root[data-theme="dark"] .erp-bulkbar { background: rgba(255, 255, 255, .05); }
:root[data-theme="dark"] [data-bulk-toggle].is-active {
    background: rgba(255, 255, 255, .16);
    border-color: rgba(255, 255, 255, .45);
    color: #fff;
}

/* ==========================================================================
   BOQ revision history — the lineage and what each revision changed
   ========================================================================== */

.erp-revh {
    align-items: baseline;
    display: flex;
    flex-wrap: wrap;
    gap: 4px 12px;
    margin-bottom: 12px;
}

.erp-revh-title {
    font-size: 13.5px;
    font-weight: 600;
    margin: 0;
}

.erp-revh-sub {
    color: var(--erp-muted);
    font-size: 12px;
}

/* The lineage, oldest first. A horizontal rail rather than a vertical list:
   a bill on its sixth revision is a short story, not a log. */
.erp-revline {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    list-style: none;
    margin: 0 0 12px;
    padding: 0;
}

.erp-revline-step {
    align-items: center;
    background: var(--erp-surface, #fff);
    border: 1px solid var(--erp-line);
    border-radius: 999px;
    display: flex;
    gap: 7px;
    padding: 5px 12px 5px 9px;
}

.erp-revline-step a {
    color: inherit;
    text-decoration: none;
}

.erp-revline-step a:hover {
    text-decoration: underline;
}

.erp-revline-step strong {
    font-size: 12.5px;
}

.erp-revline-step em {
    color: var(--erp-muted);
    font-size: 11px;
    font-style: normal;
}

.erp-revline-dot {
    background: var(--erp-line);
    border-radius: 50%;
    flex: 0 0 auto;
    height: 7px;
    width: 7px;
}

/* Where you are now. The ring is what stops somebody reading a superseded
   revision believing it is the live one. */
.erp-revline-step.is-current {
    border-color: var(--erp-primary, #00c292);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--erp-primary, #00c292) 18%, transparent);
}

.erp-revline-step.is-current .erp-revline-dot {
    background: var(--erp-primary, #00c292);
}

.erp-revline-step.is-past {
    opacity: .72;
}

/* Was → is → the difference. */
.erp-revtotals {
    align-items: center;
    background: #f7f9fc;
    border: 1px dashed var(--erp-line);
    border-radius: 9px;
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    margin-bottom: 12px;
    padding: 10px 14px;
}

.erp-revtotals span {
    display: flex;
    flex-direction: column;
}

.erp-revtotals em {
    color: var(--erp-muted);
    font-size: 10.5px;
    font-style: normal;
    letter-spacing: .4px;
    text-transform: uppercase;
}

.erp-revtotals strong {
    font-size: 15px;
}

.erp-revtotals-delta {
    margin-left: auto;
    text-align: right;
}

.erp-revtotals-pct {
    font-size: 12px;
    font-weight: 400;
}

.erp-revcounts {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 10px;
}

/* A tinted left edge per change kind, so the three groups are separable at a
   glance without reading the first column. */
.erp-revdiff tbody tr.is-changed > td:first-child { box-shadow: inset 3px 0 0 #f59e0b; }
.erp-revdiff tbody tr.is-added > td:first-child { box-shadow: inset 3px 0 0 #16a34a; }
.erp-revdiff tbody tr.is-removed > td:first-child { box-shadow: inset 3px 0 0 #ef4444; }

/* The old figure, struck through beside the new one. */
.erp-revwas {
    color: var(--erp-muted);
    margin-right: 6px;
    text-decoration: line-through;
}


/* --------------------------------------------------------------------------
   Work order — the variations strip
   A variation is part of the account it changes, so it sits under it as a
   strip rather than taking a fourth card and its 100px of chrome to say three
   short things.
   -------------------------------------------------------------------------- */

.erp-wo-amend {
    display: grid;
    gap: 5px;
}

.erp-wo-amend-head {
    align-items: baseline;
    color: var(--erp-muted);
    display: flex;
    font-size: 10px;
    gap: 6px;
    letter-spacing: .05em;
    text-transform: uppercase;
}

.erp-wo-amend-head strong {
    color: var(--erp-ink);
    font-variant-numeric: tabular-nums;
}

.erp-wo-amend-row {
    align-items: center;
    border-top: 1px solid var(--erp-line);
    display: flex;
    gap: 9px;
    padding-top: 5px;
}

.erp-wo-amend-no {
    color: var(--erp-muted);
    flex: 0 0 auto;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 11px;
}

/* The reason takes the slack and truncates — it is the one field with no
   predictable length, and letting it wrap would undo the compaction. */
.erp-wo-amend-why {
    color: var(--erp-ink);
    flex: 1 1 auto;
    font-size: 11.5px;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-wo-amend-val {
    flex: 0 0 auto;
    font-size: 11.5px;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

/* The two tables on this page are the only things that can outgrow the screen,
   so they scroll inside the card rather than growing it — which is what keeps
   the page itself to one screen however long a schedule of rates runs. */
.erp-tightcards .erp-table-wrap {
    max-height: 46vh;
    overflow-y: auto;
}

/* The head has to stay put once its rows start moving underneath it, and it
   has to be the same band as every other header while it does. The tokens
   already carry the dark-theme values, so there is no separate dark rule. */
.erp-tightcards .erp-table-wrap thead th {
    background-color: var(--erp-table-head-solid);
    position: sticky;
    top: 0;
    z-index: 1;
}

/* On a phone the two columns stack, and a 46vh cap on each would put the
   second one two screens down. */
@media (max-width: 1199.98px) {
    .erp-tightcards .erp-table-wrap { max-height: none; }
}


/* --------------------------------------------------------------------------
   Budgeting several out-of-stock materials at once
   A tick on each empty shelf, then one sheet. A site runs out of six things in
   the same week, and six separate popups is six chances to give up halfway.
   -------------------------------------------------------------------------- */

/* The tick, on the red out-of-stock panel. Green on purpose: it is the one
   constructive thing to do from a card that is otherwise all bad news, and it
   has to read as an action rather than as part of the alert. */
.erp-boqpick {
    align-items: center;
    background: var(--erp-success, #00926e);
    border-radius: 999px;
    color: #fff;
    cursor: pointer;
    display: inline-flex;
    font-size: 11px;
    font-weight: 600;
    gap: 6px;
    line-height: 1;
    margin: 0;
    /* Symmetrical now that the label is a plus rather than the word "BOQ" —
       the lopsided padding existed to sit a checkbox beside text. */
    padding: 5px 9px;
    transition: background .12s ease, box-shadow .12s ease;
    user-select: none;
}

.erp-boqpick i { font-size: 10px; }

.erp-boqpick:hover { background: #00795c; }

.erp-boqpick input {
    accent-color: #fff;
    cursor: pointer;
    height: 13px;
    margin: 0;
    width: 13px;
}

/* Ticked. Marked by a ring as well as the box, because at 11px on a red panel
   the checkbox alone is easy to miss. */
.erp-boqpick:has(input:checked) {
    box-shadow: 0 0 0 2px rgba(255, 255, 255, .85);
}

.erp-boqpick:focus-within {
    box-shadow: 0 0 0 2px rgba(255, 255, 255, .85);
}

/* The action bar. Hidden until something is ticked — a permanently visible
   button that does nothing until you select is one people press and then
   wonder about. */
.erp-boqbar {
    align-items: center;
    background: var(--erp-tint, #f7f8fb);
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 10px;
    padding: 8px 12px;
}

.erp-boqbar[hidden] { display: none; }

.erp-boqbar-count {
    color: var(--erp-muted);
    font-size: 12px;
    margin-right: auto;
}

.erp-boqbar-count strong {
    color: var(--erp-ink);
    font-variant-numeric: tabular-nums;
}

/* The sheet itself. */
.erp-boqm-table td { vertical-align: middle; }

.erp-boqm-name {
    display: block;
    font-size: 12px;
    line-height: 1.3;
}

.erp-boqm-budgeted {
    color: #b96f00;
    font-size: 10.5px;
    margin-top: 2px;
}

.erp-boqm-budgeted i { font-size: 9px; }

:root[data-theme="dark"] .erp-boqbar { background: rgba(255, 255, 255, .05); }
:root[data-theme="dark"] .erp-boqm-budgeted { color: #fbbf24; }


/* --------------------------------------------------------------------------
   Quality inspection — accepting a line in full
   The ordinary case is that a delivery is fine, and typing the received
   quantity back into the accepted box to say so is work the screen should be
   doing. A tick does it; the box stays for the partial case.
   -------------------------------------------------------------------------- */

.erp-insp-accept {
    align-items: center;
    display: flex;
    gap: 7px;
}

.erp-insp-accept .form-control { flex: 1 1 auto; min-width: 0; }

.erp-insp-full {
    align-items: center;
    background: var(--erp-tint, #f7f8fb);
    border: 1px solid var(--erp-line);
    border-radius: 999px;
    cursor: pointer;
    display: inline-flex;
    flex: 0 0 auto;
    font-size: 10.5px;
    font-weight: 600;
    gap: 4px;
    line-height: 1;
    margin: 0;
    padding: 4px 8px 4px 6px;
    user-select: none;
}

.erp-insp-full input { accent-color: var(--erp-success, #00926e); cursor: pointer; height: 13px; margin: 0; width: 13px; }

/* Ticked: the whole pill goes green, so a full line is readable down the
   column without comparing two numbers on every row. */
.erp-insp-full:has(input:checked) {
    background: var(--erp-success, #00926e);
    border-color: var(--erp-success, #00926e);
    color: #fff;
}

.erp-insp-full:has(input:checked) input { accent-color: #fff; }

/* The header toggle sits with the column label rather than in a column of its
   own — it acts on the Accepted column and belongs to it. */
.erp-insp-allhead {
    align-items: center;
    cursor: pointer;
    display: inline-flex;
    font-size: 10px;
    font-weight: 500;
    gap: 3px;
    margin: 0 0 0 7px;
    text-transform: none;
    user-select: none;
}

.erp-insp-allhead input { accent-color: var(--erp-success, #00926e); cursor: pointer; height: 12px; margin: 0; width: 12px; }

:root[data-theme="dark"] .erp-insp-full { background: rgba(255, 255, 255, .06); }


/* --------------------------------------------------------------------------
   Contractor register — full-width cards
   One contractor per row, in two bands: identity, standing, rating and actions
   on the top line — what a list is scanned FOR — and the detail spread beneath,
   where the full width lets ten short values sit in one strip instead of a
   cramped two-column stack.
   -------------------------------------------------------------------------- */

.erp-conc-grid {
    display: grid;
    /* One per row. The card lays itself out horizontally instead. */
    grid-template-columns: 1fr;
    gap: 10px;
}

/* The card wears its contractor's STANDING as a stripe down the leading edge:
   green for a firm you can give work to, grey for one you have stopped using,
   red for one that is barred. That is the single fact that decides whether the
   rest of the card is worth reading, and on a register of forty firms it is
   faster to find as a colour than as a word. */
.erp-conc {
    background: var(--erp-surface, #fff);
    border: 1px solid var(--erp-line);
    border-left-color: var(--erp-tone-line, var(--erp-line));
    border-radius: 12px;
    box-shadow: inset 4px 0 0 var(--erp-tone, transparent);
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 12px 14px 12px 16px;
    transition: border-color .12s ease, box-shadow .12s ease;
}

.erp-conc:hover {
    border-color: var(--erp-tone, var(--erp-primary, #6c63ff));
    box-shadow: inset 4px 0 0 var(--erp-tone, transparent), 0 2px 10px rgba(0, 0, 0, .06);
}

/* The top band. Wraps rather than squeezing: on a narrow window the rating and
   the actions drop to their own line instead of crushing the name. */
.erp-conc-main {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 10px 14px;
}

.erp-conc-avatar {
    align-items: center;
    background: var(--erp-tone-soft, rgba(138, 148, 166, .14));
    border-radius: 10px;
    color: var(--erp-tone-ink, var(--erp-muted));
    display: flex;
    flex: 0 0 auto;
    font-size: 12px;
    font-weight: 700;
    height: 36px;
    justify-content: center;
    letter-spacing: .02em;
    text-transform: uppercase;
    width: 36px;
}

/* The identity takes the slack, so everything after it sits at a predictable
   distance from the right edge however long a firm's name runs. */
/* Sized for what it holds. The 220px basis and 1px gap were right when the line
   under the name was a single run of grey text; with three badges there the
   badges wrapped raggedly and sat close enough to the name to touch it. */
.erp-conc-id { display: grid; flex: 1 1 300px; gap: 5px; min-width: 0; }

.erp-conc-name {
    color: var(--erp-ink);
    font-size: 14px;
    font-weight: 600;
    overflow: hidden;
    text-decoration: none;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-conc-name:hover { color: var(--erp-primary, #6c63ff); }

.erp-conc-code {
    color: var(--erp-muted);
    font-size: 11px;
    /* Holds badges now, so it wraps instead of truncating: a clipped badge
       reads as a rendering fault, a wrapped one reads as a second line. */
    min-width: 0;
}

.erp-conc-status { flex: 0 0 auto; }

/* --- the rating ----------------------------------------------------------- */

.erp-conc-rate {
    align-items: center;
    background: var(--erp-tint, #f7f8fb);
    border-radius: 8px;
    display: flex;
    flex: 0 0 auto;
    gap: 9px;
    padding: 5px 10px;
}

.erp-conc-stars { display: flex; gap: 1px; }

.erp-conc-star {
    background: none;
    border: 0;
    color: var(--erp-line);
    font-size: 13px;
    line-height: 1;
    padding: 1px 2px;
}

.erp-conc-star.is-on { color: #f5a623; }

.erp-conc-stars.is-editable .erp-conc-star { cursor: pointer; }

/* Hovering lights the star under the cursor and everything before it, so the
   score about to be given is visible before the click rather than after. */
.erp-conc-stars.is-editable:hover .erp-conc-star { color: #f5a623; }
.erp-conc-stars.is-editable .erp-conc-star:hover ~ .erp-conc-star { color: var(--erp-line); }

.erp-conc-star:disabled { cursor: progress; opacity: .6; }

.erp-conc-ratenum {
    color: var(--erp-muted);
    font-size: 11px;
    line-height: 1.15;
    white-space: nowrap;
}

.erp-conc-ratenum strong {
    color: var(--erp-ink);
    font-size: 12.5px;
    font-variant-numeric: tabular-nums;
}

.erp-conc-ratenum small { display: block; font-size: 10px; }

.erp-conc-work {
    color: var(--erp-muted);
    flex: 0 0 auto;
    font-size: 11.5px;
}

/* Only the number alignment stays; the colour belongs to the badge, which knows
   whether there is any work to report. */
.erp-conc-work .erp-badge strong { font-variant-numeric: tabular-nums; }

/* Pinned right on a wide card; falls in with everything else once the row
   wraps, which is where margin-left:auto would strand it mid-line. */
.erp-conc-actions { display: flex; flex: 0 0 auto; gap: 2px; margin-left: auto; }

/* --- the detail strip ----------------------------------------------------- */

.erp-conc-facts {
    border-top: 1px solid var(--erp-tone-line, var(--erp-line));
    display: grid;
    /* As many columns as the width allows, down to one on a phone. The whole
       point of the full-width card: ten short values in one band. */
    grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
    gap: 8px 16px;
    margin: 0;
    padding-top: 9px;
}

.erp-conc-facts > div { min-width: 0; }

.erp-conc-facts dt {
    color: var(--erp-muted);
    font-size: 9.5px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.erp-conc-facts dd {
    color: var(--erp-ink);
    font-size: 11.5px;
    margin: 0;
    min-width: 0;
}

/* The long free-text values -- email, licence, bank -- still truncate. They do
   it through .text-truncate in the blade rather than from here, because four of
   these slots now hold badges and clipping those would cut them in half. */
.erp-conc-facts dd.text-truncate {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

:root[data-theme="dark"] .erp-conc-rate { background: rgba(255, 255, 255, .05); }


/* --------------------------------------------------------------------------
   The operational action button
   For the one thing on a header that DOES something, where the primary slot is
   already taken. Amber rather than a second green, so it reads as distinct from
   the page's primary rather than competing with it.

   #b45309 carries white text at 5.02:1 and the hover at 7.09:1 — both measured
   against #fff, both clear of WCAG AA.
   -------------------------------------------------------------------------- */

.btn-erp-flame {
    background: #b45309;
    border: 1px solid #b45309;
    border-radius: 6px;
    color: #fff;
    font-size: 12.5px;
    font-weight: 500;
    padding: 6px 13px;
    transition: all .18s ease;
}

.btn-erp-flame:hover,
.btn-erp-flame:focus {
    background: #92400e;
    border-color: #92400e;
    box-shadow: 0 6px 16px rgba(180, 83, 9, .28);
    color: #fff;
}

.btn-erp-flame:focus-visible {
    outline: 2px solid #b45309;
    outline-offset: 2px;
}

.btn-erp-flame.btn-sm { font-size: 12px; padding: 5px 11px; }

.btn-erp-flame:disabled { opacity: .65; }

/* --------------------------------------------------------------------------
   The inspection checklist folded into the Log-visit form
   A disclosure rather than a set of fields: most visits are a walk and a note,
   and a checklist picker sitting open on every one of them reads as something
   somebody forgot to fill in. Shut, it is one line that says the option exists.
   -------------------------------------------------------------------------- */

.erp-vinsp-slot[hidden] { display: none !important; }

.erp-vinsp {
    background: var(--erp-tint, #f7f8fb);
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    overflow: hidden;
}

.erp-vinsp.is-open { background: var(--erp-surface, #fff); }

.erp-vinsp-head {
    align-items: center;
    background: none;
    border: 0;
    display: flex;
    gap: 10px;
    padding: 9px 12px;
    text-align: left;
    width: 100%;
}

.erp-vinsp-head:hover { background: rgba(0, 0, 0, .025); }

.erp-vinsp-icon {
    align-items: center;
    background: var(--erp-surface, #fff);
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    color: var(--erp-primary, #6c63ff);
    display: flex;
    flex: 0 0 auto;
    font-size: 12px;
    height: 28px;
    justify-content: center;
    width: 28px;
}

/* Takes the slack, so the chip and the chevron sit a fixed distance from the
   right edge whatever the lead text does. */
.erp-vinsp-lead {
    color: var(--erp-ink);
    display: grid;
    flex: 1 1 auto;
    font-size: 13px;
    font-weight: 600;
    gap: 1px;
    min-width: 0;
}

.erp-vinsp-lead small {
    color: var(--erp-muted);
    font-size: 11px;
    font-weight: 400;
}

/* What the panel currently amounts to, readable without opening it. */
.erp-vinsp-state {
    background: var(--erp-surface, #fff);
    border: 1px solid var(--erp-line);
    border-radius: 20px;
    color: var(--erp-muted);
    flex: 0 0 auto;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .02em;
    padding: 2px 9px;
    white-space: nowrap;
}

.erp-vinsp.is-open .erp-vinsp-state {
    border-color: var(--erp-primary, #6c63ff);
    color: var(--erp-primary, #6c63ff);
}

.erp-vinsp-chev {
    color: var(--erp-muted);
    flex: 0 0 auto;
    font-size: 11px;
    transition: transform .15s ease;
}

.erp-vinsp.is-open .erp-vinsp-chev { transform: rotate(180deg); }

.erp-vinsp-body {
    border-top: 1px solid var(--erp-line);
    padding: 12px 12px 4px;
}

/* --- the preview of what will be copied ----------------------------------- */

.erp-vinsp-lines {
    background: var(--erp-tint, #f7f8fb);
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    /* Capped and scrolled. A forty-line checklist would otherwise push the
       photos and the Save button far below the fold, and the answers are the
       part being worked through — they should scroll under a fixed frame
       rather than dragging the whole form down with them. */
    max-height: 244px;
    overflow-y: auto;
    padding: 9px 11px;
    margin-bottom: 12px;
}

.erp-vinsp-hint {
    color: var(--erp-muted);
    font-size: 11.5px;
}

.erp-vinsp-cap {
    color: var(--erp-ink);
    font-size: 11.5px;
    font-weight: 600;
    margin: 0 0 6px;
}

/* No marker: the line numbers its own text, so a list bullet beside it would
   be a second, disagreeing count. */
.erp-vinsp-list {
    color: var(--erp-ink);
    font-size: 12px;
    list-style: none;
    margin: 0;
    padding-left: 0;
}

/* Text left, answers right, and the answers stay put down the column so the
   eye runs straight down one edge ticking them off. Wraps to two rows on a
   phone rather than squeezing four buttons into forty pixels. */
.erp-vinsp-line {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    gap: 6px 10px;
    padding: 6px 0;
}

.erp-vinsp-line:first-child { padding-top: 0; }
.erp-vinsp-line:last-child { border-bottom: 0; padding-bottom: 0; }

.erp-vinsp-text {
    flex: 1 1 180px;
    line-height: 1.35;
    min-width: 0;
}

.erp-vinsp-line .erp-check-opts { flex: 0 0 auto; margin-left: auto; }

:root[data-theme="dark"] .erp-vinsp-head:hover { background: rgba(255, 255, 255, .04); }
:root[data-theme="dark"] .erp-vinsp-lines { background: rgba(255, 255, 255, .03); }
:root[data-theme="dark"] .erp-vinsp-line .erp-check-opt { background: var(--erp-card, #1b2431); }

/* =========================================================================
   Resource analysis, and the BOQ build-up drawer
   =========================================================================
   A bill line and its recipe are two rows of one table. The drawer is styled
   as a panel rather than as more table rows, so the eye can tell "this is
   what the line above is made of" from "this is the next line". */

.erp-boq-rows td { vertical-align: middle; }
.erp-boq-rows td .select2-container { width: 100% !important; }
.erp-boq-rows td > .erp-identity-meta { display: inline-block; padding: 4px 0; }

/* A heading is not a priced line, and should not read like one. */
.erp-boq-rows tr.is-section td { background: var(--erp-bg); }
.erp-boq-rows tr.is-section td:nth-child(2) input { font-weight: 700; }

/* A rate the line's own build-up disagrees with. Not an error - an estimator
   may hold a rate at what was already quoted - so it is marked, not blocked. */
.erp-boq-rows tr.has-rate-drift td:nth-child(6) input {
    border-color: rgb(255 152 0 / .55);
    background: rgb(255 152 0 / .06);
}

.erp-boq-drawer > td { background: var(--erp-bg); padding: 0 !important; }

.erp-boq-build { padding: 10px 14px 14px; }

.erp-boq-build-head {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    justify-content: space-between;
    padding-bottom: 8px;
}

.erp-boq-build-head > span:first-child {
    color: var(--erp-ink);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .02em;
}

.erp-boq-build-head em {
    color: var(--erp-muted);
    font-style: normal;
    font-weight: 500;
}

.erp-boq-build-table { background: var(--erp-surface, #fff); border-radius: 8px; font-size: 12px; }
.erp-boq-build-table td,
.erp-boq-build-table th { padding: 6px 8px; vertical-align: middle; }
.erp-boq-build-table thead th { font-size: 10.5px; letter-spacing: .04em; text-transform: uppercase; }
.erp-boq-build-table .select2-container { width: 100% !important; }

/* The analysis editor's own table, same idea on its own page. */
.erp-ra-table td { vertical-align: middle; }
.erp-ra-table td .select2-container { width: 100% !important; }

.erp-ra-used { list-style: none; margin: 0; padding: 0; }

.erp-ra-used li {
    border-top: 1px solid var(--erp-line);
    padding: 6px 0;
}

.erp-ra-used li:first-child { border-top: 0; }
.erp-ra-used .erp-identity-meta { display: block; }

/* The explosion panel on a bill: what it says to BUY. */
.erp-boq-explosion-src {
    color: var(--erp-muted);
    font-size: 11px;
    margin: 2px 0 0;
    padding-left: 14px;
}

@media (max-width: 991.98px) {
    .erp-boq-build { padding: 8px; }
    .erp-boq-build-head { font-size: 11px; }
}

/* =========================================================================
   Off-grid column splits
   =========================================================================
   Bootstrap's twelve columns cannot express 80/20 or 70/30: the nearest it
   offers are 10/2 (83.3/16.7) and 8/4 (66.7/33.3). These follow the same shape
   the framework's own column classes use - flex: 0 0 auto plus an explicit
   width - so gutters, wrapping and nesting all behave exactly as they do for
   a col-*.

   Named for what they are, so a later reader can tell at a glance what the
   split is without measuring it.

   From xl up only. Below that the two stack to full width, which is what the
   plain .col-12 they sit on already does. */

@media (min-width: 1200px) {
    .erp-col-80 { flex: 0 0 auto; width: 80%; }
    .erp-col-70 { flex: 0 0 auto; width: 70%; }
    .erp-col-30 { flex: 0 0 auto; width: 30%; }
    .erp-col-20 { flex: 0 0 auto; width: 20%; }
}

/* -------------------------------------------------------------------------
   The analysis editor's number columns
   -------------------------------------------------------------------------
   Coefficient, wastage and rate are typed over and over by one person for an
   afternoon, and read back to check a build-up that a whole bill hangs off.
   The shared -sm controls are sized for a filter bar, not for that: six
   decimals at 12px in a 90px box is a number nobody can proof-read.

   Scoped to this table rather than loosened globally, because the same -sm
   size is right everywhere it is used for filtering. */

.erp-ra-table td input.form-control,
.erp-ra-table td .form-select {
    font-size: 13.5px;
    min-height: 36px;
    padding: 7px 10px;
}

.erp-ra-table td input[type="number"] {
    font-size: 14.5px;
    font-weight: 600;
    /* Digits of equal width, so a column of figures lines up and a stray
       decimal point is visible at a glance. */
    font-variant-numeric: tabular-nums;
    letter-spacing: .01em;
}

/* The description under the picker stays secondary - it is a label, not a
   figure, and should not compete with the numbers beside it. */
.erp-ra-table td input[data-description] {
    font-size: 12.5px;
    min-height: 32px;
}

.erp-ra-table td .select2-selection--single {
    min-height: 36px;
}

.erp-ra-table td .select2-selection__rendered {
    font-size: 13.5px;
    line-height: 22px;
}

.erp-ra-table td[data-amount],
.erp-ra-table tfoot th {
    font-size: 14px;
    font-variant-numeric: tabular-nums;
}

/* -------------------------------------------------------------------------
   The analysis side panel's tabs
   -------------------------------------------------------------------------
   The shared tab strip is built for a full-width page header: gutter padding
   either side, roomy hit areas, and a bottom rule that runs the width of the
   content. In a 20% column that spends most of the available width on padding
   and pushes the second tab onto its own line.

   These trim it to the column without touching the shared rules, which are
   right everywhere else they are used. */

.erp-ra-tabs {
    gap: 4px;
    margin-bottom: 12px;
    padding: 0;
}

.erp-ra-tabs .erp-tab {
    flex: 1 1 auto;
    gap: 6px;
    justify-content: center;
    padding: 10px 8px;
}

/* The panel holds a card, and a card brings its own padding. The shared
   gutter on top of it would inset the card from a column that has none to
   spare. */
.erp-ra-tabs + .erp-tab-panels .erp-tab-panel { padding: 0; }

/* Below xl the two columns stack and the strip is full width again, where the
   icons have room to sit beside their labels comfortably. */
@media (max-width: 1399.98px) and (min-width: 1200px) {
    .erp-ra-tabs .erp-tab { font-size: 12px; padding: 10px 6px; }
    .erp-ra-tabs .erp-tab i { font-size: 11px; }
}

/* -------------------------------------------------------------------------
   The BOQ drawer's number columns
   -------------------------------------------------------------------------
   Same reasoning as .erp-ra-table above: a coefficient carrying six decimals
   is typed once and then read back by whoever has to defend the rate, and the
   12px filter-bar size is not a size anybody can proof-read a figure at.

   The drawer keeps a slightly smaller base than the analysis editor - it is a
   panel nested inside a bill line, and matching the parent table exactly would
   stop it reading as subordinate to the line above it.

   RAISED, and the sizes were only half the problem. The eight columns declare
   876px between them and the drawer gave the table 612px, so the browser
   compressed every column toward min-content - roughly a 30% squeeze landing
   almost entirely on the selects and the number inputs, because text cells and
   the stated pixel widths resist it and form controls do not. Enlarging the
   type inside a box with no room only clips it, so the drawer was widened to
   stop over-constraining the table (see .erp-boq-drawer) and these sizes then
   have somewhere to go. */

.erp-boq-build-table td input.form-control,
.erp-boq-build-table td .form-select {
    font-size: 13.5px;
    min-height: 38px;
    padding: 7px 10px;
}

.erp-boq-build-table td input[type="number"] {
    font-size: 15px;
    font-weight: 600;
    /* Equal-width digits, so a column of coefficients lines up and a stray
       decimal point shows without reading the number. */
    font-variant-numeric: tabular-nums;
}

.erp-boq-build-table td input[data-res-description] {
    font-size: 12.5px;
    min-height: 34px;
}

/* Matched to the native controls above. A Select2 is a div wearing the look of
   a select, so its height does not follow min-height on .form-select and has
   to be stated again - miss this and the resource picker sits 4px shorter than
   the unit beside it, which reads as a rendering fault rather than a size. */
.erp-boq-build-table td .select2-selection--single { min-height: 38px; }

.erp-boq-build-table td .select2-selection__rendered {
    font-size: 13.5px;
    line-height: 24px;
}

/* Select2 positions its arrow against a 28px box of its own, so a taller
   control leaves it floating near the top edge. Stretching it to the full
   height lets it centre itself. */
.erp-boq-build-table td .select2-selection--single .select2-selection__arrow {
    height: 100%;
    top: 0;
}

/* The two computed columns. Total qty is the one somebody is really looking
   for when they open a drawer - what this line will actually consume - so it
   carries the weight. */
.erp-boq-build-table td[data-res-cost],
.erp-boq-build-table td[data-res-total],
.erp-boq-build-table tfoot th {
    font-size: 13.5px;
    font-variant-numeric: tabular-nums;
}

.erp-boq-build-table td[data-res-total] { font-size: 14px; }

/* -------------------------------------------------------------------------
   The BOQ line table's own inputs
   -------------------------------------------------------------------------
   The outer table, not the drawer nested in it. Quantity and rate here decide
   what the bill comes to, so they read a step larger than the build-up
   underneath them - the hierarchy on screen matching the hierarchy in the
   figures. */

.erp-boq-rows td input.form-control,
.erp-boq-rows td .form-select {
    font-size: 13.5px;
    min-height: 36px;
    padding: 7px 10px;
}

.erp-boq-rows td input[type="number"] {
    font-size: 15px;
    font-weight: 600;
    /* Equal-width digits: a column of quantities lines up, and a rate typed
       with one zero too many is visible without counting them. */
    font-variant-numeric: tabular-nums;
}

.erp-boq-rows td .select2-selection--single { min-height: 36px; }

.erp-boq-rows td .select2-selection__rendered {
    font-size: 13.5px;
    line-height: 22px;
}

/* The line total, and the bill total it rolls into. */
.erp-boq-rows td[data-amount] {
    font-size: 15px;
    font-variant-numeric: tabular-nums;
}

.erp-boq-rows tfoot th {
    font-size: 15px;
    font-variant-numeric: tabular-nums;
}

/* The count on a bill line's build-up toggle. A closed drawer and an empty one
   look identical from the outside; the number is what tells them apart. */
.erp-boq-build-badge {
    background: var(--erp-primary);
    border-radius: 20px;
    color: #fff;
    display: inline-block;
    font-size: 10px;
    font-weight: 700;
    line-height: 1;
    margin-left: 5px;
    min-width: 16px;
    padding: 3px 5px;
    text-align: center;
    vertical-align: middle;
}

/* Compact, side-by-side row actions: two icon buttons in a narrow column. */
.erp-boq-act {
    padding: .2rem .4rem;
    line-height: 1;
}

/* The procurement panel's tabs on a bill. Same trim as the analysis side
   panel: the shared strip is sized for a page header and spends too much of a
   sidebar column on padding. */

.erp-boq-tabs {
    gap: 4px;
    margin-bottom: 12px;
    padding: 0;
}

.erp-boq-tabs .erp-tab {
    flex: 1 1 auto;
    gap: 6px;
    justify-content: center;
    padding: 10px 6px;
}

/* The panels sit inside a card that already has padding of its own. */
.erp-boq-tabs + .erp-tab-panels .erp-tab-panel { padding: 0; }


/* Five tabs in a third-width column. They wrap to a second row rather than
   shrinking past readability, and each keeps a sensible minimum. */
.erp-boq-tabs .erp-tab { flex: 1 1 auto; min-width: 74px; }

@media (max-width: 1599.98px) and (min-width: 992px) {
    .erp-boq-tabs .erp-tab { font-size: 11.5px; gap: 4px; padding: 9px 4px; min-width: 66px; }
    .erp-boq-tabs .erp-tab i { font-size: 11px; }
}

/* A store that could supply part of a demand. Name left, quantity right, so a
   column of them can be scanned for the one that covers it. */
.erp-src-row {
    align-items: baseline;
    border-top: 1px solid var(--erp-line);
    display: flex;
    font-size: 12px;
    gap: 10px;
    justify-content: space-between;
    padding: 3px 0;
}

.erp-src-row:first-child { border-top: 0; }
.erp-src-row strong { font-variant-numeric: tabular-nums; }

/* =========================================================================
   Project lifecycle stepper
   =========================================================================
   Ten stages in one band at the top of a project. Stacked they would push the
   overview down a screen, and a project that passes through nine documents is
   exactly the case where nobody scrolls to reach the tenth - so the track
   scrolls inside itself rather than growing the page. */

.erp-lifecycle { overflow: hidden; }

.erp-lifecycle-head {
    align-items: flex-start;
    display: flex;
    gap: 12px;
    justify-content: space-between;
    padding: 14px 16px 10px;
}

.erp-lifecycle-head h2 { font-size: 14px; font-weight: 700; margin: 0 0 2px; }
.erp-lifecycle-head p { color: var(--erp-muted); font-size: 12px; margin: 0; }

.erp-lifecycle-pct strong {
    color: var(--erp-primary);
    font-size: 22px;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    line-height: 1;
}

.erp-lifecycle-bar {
    background: var(--erp-line);
    height: 4px;
    margin: 0 16px 14px;
    overflow: hidden;
}

.erp-lifecycle-bar span {
    background: var(--erp-primary);
    display: block;
    height: 100%;
    transition: width .3s ease;
}

.erp-lifecycle-track {
    display: flex;
    gap: 0;
    overflow-x: auto;
    padding: 0 16px 16px;
    scrollbar-width: thin;
}

.erp-lifecycle-track::-webkit-scrollbar { height: 5px; }
.erp-lifecycle-track::-webkit-scrollbar-thumb { background: var(--erp-line); border-radius: 3px; }

.erp-lcstep {
    align-items: center;
    color: inherit;
    display: flex;
    flex: 1 0 96px;
    flex-direction: column;
    gap: 5px;
    min-width: 96px;
    padding: 0 4px;
    position: relative;
    text-align: center;
    text-decoration: none;
}

/* The connector, drawn behind the dots. Not on the first step, or the line
   starts in mid-air to the left of it. */
.erp-lcstep:not(:first-child)::before {
    background: var(--erp-line);
    content: "";
    height: 2px;
    left: calc(-50% + 15px);
    position: absolute;
    right: calc(50% + 15px);
    top: 14px;
}

.erp-lcstep.is-done:not(:first-child)::before { background: var(--erp-success, #00926e); }

.erp-lcstep-dot {
    align-items: center;
    background: var(--erp-surface, #fff);
    border: 2px solid var(--erp-line);
    border-radius: 50%;
    color: var(--erp-muted);
    display: flex;
    flex: 0 0 auto;
    font-size: 11px;
    height: 30px;
    justify-content: center;
    position: relative;
    width: 30px;
    z-index: 1;
}

.erp-lcstep-label {
    color: var(--erp-muted);
    font-size: 11px;
    font-weight: 600;
    line-height: 1.25;
}

.erp-lcstep-detail {
    color: var(--erp-muted);
    font-size: 10px;
    opacity: .75;
}

/* Done. */
.erp-lcstep.is-done .erp-lcstep-dot {
    background: var(--erp-success, #00926e);
    border-color: var(--erp-success, #00926e);
    color: #fff;
}

.erp-lcstep.is-done .erp-lcstep-label { color: var(--erp-ink); }

/* The one somebody has to act on. Marked by a ring as well as colour, because
   colour alone never carries a state. */
.erp-lcstep.is-current .erp-lcstep-dot {
    background: var(--erp-primary);
    border-color: var(--erp-primary);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--erp-primary) 18%, transparent);
    color: #fff;
}

.erp-lcstep.is-current .erp-lcstep-label { color: var(--erp-primary); font-weight: 700; }

.erp-lcstep.is-pending { opacity: .62; }

a.erp-lcstep:hover .erp-lcstep-label { color: var(--erp-primary); }
a.erp-lcstep:hover .erp-lcstep-dot { border-color: var(--erp-primary); }

/* The instruction under the track. A row of ticks says where the project is;
   this says what has to happen next. */
.erp-lifecycle-next {
    align-items: center;
    background: var(--erp-bg);
    border-top: 1px solid var(--erp-line);
    display: flex;
    font-size: 12.5px;
    gap: 10px;
    padding: 11px 16px;
}

.erp-lifecycle-next > i { color: var(--erp-primary); flex: 0 0 auto; }
.erp-lifecycle-next > span { flex: 1 1 auto; line-height: 1.45; }
.erp-lifecycle-next .btn { flex: 0 0 auto; }

@media (max-width: 767.98px) {
    .erp-lifecycle-next { align-items: flex-start; flex-wrap: wrap; }
    .erp-lcstep { flex: 0 0 88px; min-width: 88px; }
}

@media print {
    .erp-lifecycle-track { overflow: visible; }
}

/* --------------------------------------------------------------------------
   Project work breakdown
   Shaping the structure, not working it. The numbering down the left edge is
   what makes this read as a WBS rather than as a list of tasks, so it is given
   a fixed gutter and a connector rail: the eye follows one vertical line and
   the indent level is legible without counting.
   -------------------------------------------------------------------------- */

.erp-wbs-intro {
    align-items: flex-start;
    background: var(--erp-tint, #f7f8fb);
    border: 1px solid var(--erp-line);
    border-radius: 10px;
    display: flex;
    gap: 10px;
    margin: 12px 0;
    padding: 10px 14px;
}

.erp-wbs-intro > i { color: var(--erp-primary, #6c63ff); font-size: 13px; margin-top: 2px; }

.erp-wbs-intro p {
    color: var(--erp-muted);
    font-size: 12.5px;
    line-height: 1.5;
    margin: 0;
    max-width: 90ch;
}

.erp-wbs-intro strong { color: var(--erp-ink); }

/* The breakdown takes the room; the standard list is a reference column beside
   it. Stacks on a narrow screen, breakdown first — it is what the page is. */
.erp-wbs-grid {
    align-items: start;
    display: grid;
    gap: 14px;
    grid-template-columns: minmax(0, 1fr) 330px;
}

@media (max-width: 1100px) {
    .erp-wbs-grid { grid-template-columns: minmax(0, 1fr); }
}

.erp-wbs-card, .erp-wbs-aside { margin: 0; }

.erp-wbs-head {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    gap: 12px;
    padding: 12px 16px;
}

.erp-wbs-title { flex: 1 1 auto; min-width: 0; }

.erp-wbs-title h2 {
    color: var(--erp-ink);
    font-size: 15px;
    font-weight: 600;
    margin: 0;
}

.erp-wbs-title p {
    color: var(--erp-muted);
    font-size: 11.5px;
    margin: 1px 0 0;
}

/* --- the selection bar ---------------------------------------------------- */

.erp-wbs-bar {
    align-items: center;
    background: var(--erp-tint, #f7f8fb);
    border-bottom: 1px solid var(--erp-line);
    color: var(--erp-ink);
    display: flex;
    flex-wrap: wrap;
    font-size: 12.5px;
    gap: 8px;
    padding: 8px 16px;
}

.erp-wbs-bar > span:first-child { margin-right: auto; }
.erp-wbs-bar[hidden] { display: none !important; }

/* --- the tree ------------------------------------------------------------- */

.erp-wbs { padding: 8px 16px 16px; }

.erp-wbs-list, .erp-wbs-sub { list-style: none; margin: 0; padding: 0; }

.erp-wbs-stage { padding-top: 6px; }

.erp-wbs-row {
    align-items: center;
    border-radius: 8px;
    display: flex;
    gap: 10px;
    min-height: 34px;
    padding: 4px 8px;
}

.erp-wbs-row:hover { background: var(--erp-tint, #f7f8fb); }

.erp-wbs-row.is-stage {
    background: var(--erp-tint, #f7f8fb);
    font-weight: 600;
}

.erp-wbs-row.is-stage:hover { background: rgba(108, 99, 255, .07); }

/* The number is the spine of the whole page: fixed width so every code lines
   up in one column whether it is "1" or "12.10", tabular figures so the digits
   do not shift about as rows are added and removed. */
.erp-wbs-code {
    color: var(--erp-muted);
    flex: 0 0 auto;
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    letter-spacing: .02em;
    text-align: right;
    width: 42px;
}

.erp-wbs-row.is-stage .erp-wbs-code { color: var(--erp-primary, #6c63ff); font-size: 12px; }

.erp-wbs-name {
    color: var(--erp-ink);
    flex: 1 1 auto;
    font-size: 12.5px;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-wbs-row.is-stage .erp-wbs-name { font-size: 13px; }

.erp-wbs-name small {
    color: var(--erp-muted);
    font-size: 10.5px;
    font-weight: 400;
    margin-left: 7px;
}

.erp-wbs-count, .erp-wbs-status { flex: 0 0 auto; }

.erp-wbs-count {
    color: var(--erp-muted);
    font-size: 11px;
    font-weight: 400;
}

/* The activities, rail and all. The rail is the border on the <ol>; each row
   grows a short arm out to meet it, which is what makes the nesting readable
   at a glance instead of by indent alone. */
.erp-wbs-sub {
    border-left: 1px solid var(--erp-line);
    margin: 2px 0 2px 21px;
    padding-left: 0;
}

.erp-wbs-sub > li { position: relative; }

.erp-wbs-sub > li::before {
    background: var(--erp-line);
    content: "";
    height: 1px;
    left: 0;
    position: absolute;
    top: 50%;
    width: 9px;
}

.erp-wbs-sub .erp-wbs-code { width: 52px; }

/* --- remove, and why you sometimes cannot --------------------------------- */

.erp-wbs-del, .erp-wbs-lock {
    align-items: center;
    background: none;
    border: 0;
    border-radius: 6px;
    display: flex;
    flex: 0 0 auto;
    font-size: 11px;
    height: 24px;
    justify-content: center;
    width: 24px;
}

.erp-wbs-del { color: var(--erp-muted); cursor: pointer; }

/* Add an activity under this stage, with the full form. Quiet until the row is
   hovered: every stage carries one, and thirty always-lit plus signs down the
   page would compete with the numbering, which is what the eye should follow. */
.erp-wbs-plus {
    align-items: center;
    background: none;
    border: 1px solid transparent;
    border-radius: 6px;
    color: var(--erp-muted);
    cursor: pointer;
    display: flex;
    flex: 0 0 auto;
    font-size: 10px;
    height: 24px;
    justify-content: center;
    opacity: .45;
    transition: opacity .12s ease, background .12s ease, border-color .12s ease, color .12s ease;
    width: 24px;
}

.erp-wbs-row:hover .erp-wbs-plus { opacity: 1; }

.erp-wbs-plus:hover {
    background: var(--erp-primary, #6c63ff);
    border-color: var(--erp-primary, #6c63ff);
    color: #fff;
    opacity: 1;
}

/* Keyboard users never hover, so focus has to do the same job. */
.erp-wbs-plus:focus-visible { opacity: 1; outline: 2px solid var(--erp-primary, #6c63ff); outline-offset: 1px; }

.erp-wbs-del:hover {
    background: rgba(220, 38, 38, .1);
    color: var(--erp-danger, #dc2626);
}

.erp-wbs-del:disabled { cursor: progress; opacity: .5; }

/* Muted, not red: this is not a warning, it is the reason a button is absent. */
.erp-wbs-lock { color: var(--erp-line); cursor: help; }

/* --- selecting ------------------------------------------------------------ */

/* The tick boxes only exist in selection mode. A checkbox on every row all the
   time invites the accidental click that a delete should never be one step
   from — same rule the task board follows. */
.erp-wbs-pick { display: none; flex: 0 0 auto; margin: 0; }
.is-selecting .erp-wbs-pick { display: flex; }
.erp-wbs-pick.is-locked { opacity: .3; }
.erp-wbs-pick input { cursor: pointer; margin: 0; }
.erp-wbs-pick.is-locked input { cursor: not-allowed; }

/* --- typing a new one ----------------------------------------------------- */

.erp-wbs-add, .erp-wbs-addstage {
    align-items: center;
    display: flex;
    gap: 10px;
    padding: 3px 8px;
}

.erp-wbs-addstage { border-top: 1px dashed var(--erp-line); margin-top: 10px; padding-top: 10px; }

.erp-wbs-add .erp-wbs-code, .erp-wbs-addstage .erp-wbs-code { opacity: .4; }

/* Underline rather than a box: it is an invitation, not a field somebody has
   left blank, and a row of empty form controls down the page reads as work
   outstanding. It becomes a real input on focus. */
.erp-wbs-input {
    background: none;
    border: 0;
    border-bottom: 1px dashed transparent;
    color: var(--erp-ink);
    flex: 1 1 auto;
    font-size: 12.5px;
    min-width: 0;
    padding: 3px 2px;
}

.erp-wbs-input::placeholder { color: var(--erp-muted); opacity: .75; }

.erp-wbs-add:hover .erp-wbs-input, .erp-wbs-addstage:hover .erp-wbs-input {
    border-bottom-color: var(--erp-line);
}

.erp-wbs-input:focus {
    border-bottom-color: var(--erp-primary, #6c63ff);
    outline: none;
}

.erp-wbs-input:disabled { opacity: .5; }

/* --------------------------------------------------------------------------
   The standard breakdown, beside it
   -------------------------------------------------------------------------- */

.erp-wbslib { display: flex; flex-direction: column; max-height: calc(100vh - 220px); }

.erp-wbslib-head {
    align-items: flex-start;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    gap: 8px;
    padding: 12px 14px;
}

.erp-wbslib-head > div:first-child { flex: 1 1 auto; min-width: 0; }

.erp-wbslib-head h3 {
    color: var(--erp-ink);
    font-size: 13.5px;
    font-weight: 600;
    margin: 0;
}

.erp-wbslib-head p { color: var(--erp-muted); font-size: 11px; margin: 1px 0 0; }

.erp-wbslib-empty, .erp-wbslib-none {
    color: var(--erp-muted);
    font-size: 12px;
    margin: 0;
    padding: 14px;
}

.erp-wbslib-search {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    gap: 8px;
    padding: 8px 14px;
}

.erp-wbslib-search > i { color: var(--erp-muted); font-size: 11px; }

.erp-wbslib-search input {
    background: none;
    border: 0;
    color: var(--erp-ink);
    flex: 1 1 auto;
    font-size: 12.5px;
    min-width: 0;
    outline: none;
    padding: 2px 0;
}

/* Scrolls on its own so the breakdown beside it never has to. */
.erp-wbslib-list { overflow-y: auto; padding: 6px; }

.erp-wbslib-item {
    align-items: center;
    border-radius: 8px;
    display: grid;
    gap: 2px 8px;
    grid-template-columns: minmax(0, 1fr) auto;
    padding: 7px 8px;
}

.erp-wbslib-item:hover { background: var(--erp-tint, #f7f8fb); }
.erp-wbslib-item[hidden] { display: none !important; }

/* Already on the board: still listed, visibly spent. Hiding it would leave
   somebody hunting for a stage that is right there under their nose. */
.erp-wbslib-item.is-on { opacity: .55; }

.erp-wbslib-main { min-width: 0; }

.erp-wbslib-name {
    color: var(--erp-ink);
    display: block;
    font-size: 12.5px;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-wbslib-meta { color: var(--erp-muted); font-size: 10.5px; }

.erp-wbslib-add { flex: 0 0 auto; white-space: nowrap; }
.erp-wbslib-add i + span { margin-left: 5px; }

.erp-wbslib-on {
    color: var(--erp-muted);
    flex: 0 0 auto;
    font-size: 11px;
    font-weight: 600;
    white-space: nowrap;
}

.erp-wbslib-peek { grid-column: 1 / -1; }

.erp-wbslib-peek summary {
    color: var(--erp-muted);
    cursor: pointer;
    font-size: 10.5px;
    padding: 2px 0;
}

.erp-wbslib-peek summary:hover { color: var(--erp-primary, #6c63ff); }

/* The activities inside a stage, each addable on its own. This is the second
   grain of adding: take the whole stage, or top up one already on the board
   with the activity somebody pruned by mistake. */
.erp-wbslib-kids {
    list-style: none;
    margin: 2px 0 4px;
    padding: 0 0 0 4px;
}

.erp-wbslib-kids li {
    align-items: center;
    border-radius: 6px;
    display: flex;
    gap: 6px;
    min-height: 22px;
    padding: 1px 4px;
}

.erp-wbslib-kids li:hover { background: rgba(108, 99, 255, .07); }
.erp-wbslib-kids li.is-on { opacity: .5; }
.erp-wbslib-kids li.is-on:hover { background: none; }

.erp-wbslib-kidname {
    color: var(--erp-muted);
    flex: 1 1 auto;
    font-size: 11px;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-wbslib-kidadd, .erp-wbslib-kidon, .erp-wbslib-kidwait {
    align-items: center;
    border-radius: 5px;
    display: flex;
    flex: 0 0 auto;
    font-size: 9px;
    height: 18px;
    justify-content: center;
    width: 18px;
}

.erp-wbslib-kidadd {
    background: none;
    border: 1px solid var(--erp-line);
    color: var(--erp-muted);
    cursor: pointer;
    transition: background .12s ease, border-color .12s ease, color .12s ease;
}

.erp-wbslib-kidadd:hover {
    background: var(--erp-primary, #6c63ff);
    border-color: var(--erp-primary, #6c63ff);
    color: #fff;
}

.erp-wbslib-kidadd:disabled { cursor: progress; opacity: .6; }

.erp-wbslib-kidon { color: var(--erp-success, #16a34a); }

/* Not a disabled button: there is nothing to press until the stage is on the
   board, and a dead control invites clicking it to find out why. */
.erp-wbslib-kidwait { color: var(--erp-line); cursor: help; }

:root[data-theme="dark"] .erp-wbs-intro,
:root[data-theme="dark"] .erp-wbs-bar,
:root[data-theme="dark"] .erp-wbs-row.is-stage { background: rgba(255, 255, 255, .04); }
:root[data-theme="dark"] .erp-wbs-row:hover,
:root[data-theme="dark"] .erp-wbslib-item:hover,
:root[data-theme="dark"] .erp-wbslib-kids li:hover { background: rgba(255, 255, 255, .05); }

/* ===========================================================================
   BOQ Scope Library — two panes, neither of which moves the page
   ===========================================================================
   The library is ~124 packages holding potentially thousands of items. Stacked
   vertically that is a page a mile long, where the thing you came to compare is
   never on screen at the same time as the thing you are comparing it to.

   So the card is given a fixed height derived from the viewport and each pane
   scrolls inside itself. The page itself never scrolls.

   The height is calc(100vh - 300px): the page header, the stat strip and the
   layout's own padding come to roughly that, and the min-height floor stops the
   panes collapsing to nothing on a laptop in a video call with the browser
   chrome eating half the screen.
   ------------------------------------------------------------------------- */

.erp-scope-shell {
    padding: 0;
    overflow: hidden;
}

.erp-scope-split {
    display: grid;
    grid-template-columns: minmax(280px, 30%) 1fr;
    height: calc(100vh - 300px);
    min-height: 460px;
}

.erp-scope-side {
    display: flex;
    flex-direction: column;
    min-height: 0;                      /* or the body below refuses to scroll */
    border-right: 1px solid var(--erp-line);
    background: var(--erp-surface-2, #fbfbfd);
}

.erp-scope-side-head {
    padding: 14px 14px 12px;
    border-bottom: 1px solid var(--erp-line);
    flex: 0 0 auto;
}

.erp-scope-filters {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.erp-scope-filters .erp-search-sm { width: 100%; }
.erp-scope-filters .erp-select-sm { flex: 1 1 0; min-width: 0; font-size: 12.5px; }

.erp-scope-side-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 6px 0 14px;
}

.erp-scope-group { margin-bottom: 4px; }

.erp-scope-group-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 9px 14px 5px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--erp-muted);
}

.erp-scope-group-head i { font-size: 11px; opacity: .75; }

.erp-scope-group-count {
    margin-left: auto;
    font-weight: 600;
    opacity: .7;
}

.erp-scope-row {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 7px 14px;
    border: 0 none;
    border-left: 3px solid transparent;
    background: transparent;
    text-align: left;
    font-size: 12.5px;
    color: var(--erp-text);
    cursor: pointer;
    transition: background .12s ease, border-color .12s ease;
}

.erp-scope-row:hover { background: var(--erp-hover, rgba(0, 0, 0, .035)); }

.erp-scope-row.is-active {
    background: var(--erp-accent-soft, rgba(37, 99, 235, .08));
    border-left-color: var(--erp-accent, #2563eb);
    font-weight: 600;
}

.erp-scope-row.is-off { opacity: .55; }

.erp-scope-row-code {
    flex: 0 0 auto;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    font-size: 11.5px;
    letter-spacing: .02em;
}

.erp-scope-row-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* A zero here is a package nobody has templated yet, and finding those is
   most of why anybody opens this screen — so it is muted until it is not. */
.erp-scope-row-count {
    flex: 0 0 auto;
    min-width: 22px;
    padding: 1px 6px;
    border-radius: 9px;
    background: var(--erp-line);
    color: var(--erp-muted);
    font-size: 10.5px;
    font-weight: 700;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.erp-scope-row-count.is-filled {
    background: var(--erp-accent-soft, rgba(37, 99, 235, .12));
    color: var(--erp-accent, #2563eb);
}

.erp-scope-main {
    display: flex;
    flex-direction: column;
    min-height: 0;
    min-width: 0;
}

.erp-scope-pane-head {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    flex-wrap: wrap;
    padding: 16px 18px;
    border-bottom: 1px solid var(--erp-line);
    flex: 0 0 auto;
}

.erp-scope-pane-id {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    flex: 1 1 320px;
    min-width: 0;
}

.erp-scope-pane-icon {
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 38px;
    height: 38px;
    border-radius: 10px;
    background: var(--erp-accent-soft, rgba(37, 99, 235, .1));
    color: var(--erp-accent, #2563eb);
}

.erp-scope-pane-id h3 {
    margin: 0;
    font-size: 15px;
    font-weight: 700;
}

.erp-scope-pane-id h3 span {
    font-weight: 500;
    color: var(--erp-muted);
    margin-left: 6px;
}

.erp-scope-pane-id p {
    margin: 2px 0 0;
    font-size: 12.5px;
    color: var(--erp-muted);
}

.erp-scope-pane-actions {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    margin-left: auto;
}

.erp-scope-pane-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 6px 0 16px;
}

/* Below a tablet the two panes stop being two panes. Side by side at 400px
   neither is wide enough to read, so the list becomes a short scroller above
   the tree and the page is allowed its one scroll. */
@media (max-width: 991.98px) {
    .erp-scope-split {
        grid-template-columns: 1fr;
        height: auto;
        min-height: 0;
    }

    .erp-scope-side { border-right: 0 none; border-bottom: 1px solid var(--erp-line); }
    .erp-scope-side-body { max-height: 260px; }
    .erp-scope-pane-body { overflow: visible; }
}

/* ---------------------------------------------------------------------------
   BOQ approval chain — Draft > Submitted > Reviewed > Approved
   ---------------------------------------------------------------------------
   A horizontal strip rather than a stacked list, because four steps stacked is
   four rows of scroll for information that fits on one line. Wraps to two rows
   on a phone rather than shrinking below legibility.
   ------------------------------------------------------------------------- */

.erp-boq-chain {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    padding: 14px 16px;
}

.erp-boq-chain-step {
    display: flex;
    align-items: center;
    gap: 9px;
    flex: 1 1 200px;
    min-width: 0;
    padding: 8px 12px;
    border-radius: 10px;
    background: var(--erp-surface-2, #fbfbfd);
    opacity: .6;
}

.erp-boq-chain-step.is-done { opacity: 1; }

.erp-boq-chain-step.is-current {
    opacity: 1;
    background: var(--erp-accent-soft, rgba(37, 99, 235, .08));
    box-shadow: inset 0 0 0 1px var(--erp-accent, #2563eb);
}

.erp-boq-chain-dot {
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: var(--erp-line);
    color: var(--erp-muted);
    font-size: 12px;
}

.erp-boq-chain-step.is-done .erp-boq-chain-dot {
    background: var(--erp-ok-soft, rgba(22, 163, 74, .14));
    color: var(--erp-ok, #16a34a);
}

.erp-boq-chain-step.is-current .erp-boq-chain-dot {
    background: var(--erp-accent, #2563eb);
    color: #fff;
}

.erp-boq-chain-body {
    min-width: 0;
    line-height: 1.35;
}

.erp-boq-chain-body strong {
    display: block;
    font-size: 12.5px;
    font-weight: 600;
}

.erp-boq-chain-body span {
    display: block;
    font-size: 11.5px;
    color: var(--erp-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-boq-chain-body span.is-pending { opacity: .7; }

/* =========================================================================
   Task material availability
   =========================================================================
   The dialog behind "Check Material" on the board and the dashboard list:
   what the ticked tasks need from the active bill, against what is on site. */

.erp-tma-lede {
    color: var(--erp-muted);
    font-size: 11.5px;
    margin: 2px 0 0;
    max-width: 62ch;
}

.erp-tma-bar {
    align-items: flex-end;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 12px;
    padding-bottom: 12px;
}

.erp-tma-bar .erp-field { min-width: 190px; }
.erp-tma-bar .form-label { font-size: 11px; }

.erp-tma-loading {
    color: var(--erp-muted);
    font-size: 12.5px;
    padding: 34px 0;
    text-align: center;
}

/* The dead ends — no bill, nothing mapped, nothing ticked. Each says why and
   offers the way out, so none of them reads as "these tasks need nothing". */
.erp-tma-empty {
    color: var(--erp-muted);
    padding: 30px 20px;
    text-align: center;
}

.erp-tma-empty i { color: #c3ccd6; font-size: 30px; }
.erp-tma-empty h4 { color: var(--erp-ink); font-size: 14px; font-weight: 700; margin: 12px 0 5px; }
.erp-tma-empty p { font-size: 12.5px; margin: 0 auto 14px; max-width: 58ch; }

/* The facts strip. */
.erp-tma-head { margin-bottom: 12px; }

.erp-tma-facts {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.erp-tma-fact {
    background: var(--erp-surface-2, #f7f9fc);
    border: 1px solid var(--erp-line);
    border-radius: 9px;
    flex: 1 1 130px;
    padding: 7px 11px;
}

.erp-tma-fact strong { display: block; font-size: 13px; }

.erp-tma-cap {
    color: var(--erp-muted);
    display: block;
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: .07em;
    text-transform: uppercase;
}

.erp-tma-fact.is-short { background: rgba(208, 59, 59, .07); border-color: rgba(208, 59, 59, .3); }
.erp-tma-fact.is-short strong { color: #b32d2d; }
.erp-tma-fact.is-ok { background: rgba(0, 194, 146, .08); border-color: rgba(0, 194, 146, .3); }
.erp-tma-fact.is-ok strong { color: #00926e; }

.erp-tma-note {
    color: var(--erp-muted);
    font-size: 11px;
    line-height: 1.5;
    margin: 8px 0 0;
}

.erp-tma-warn {
    background: rgba(255, 152, 0, .1);
    border: 1px solid rgba(255, 152, 0, .32);
    border-radius: 9px;
    color: #b45309;
    font-size: 11.5px;
    margin-bottom: 12px;
    padding: 8px 11px;
}

/* The table. Its own scroller so the dialog's own scroll stays with the page
   furniture rather than the data. */
.erp-tma-scroll {
    border: 1px solid var(--erp-line);
    border-radius: 9px;
    max-height: 46vh;
    overflow: auto;
}

.erp-tma-table { font-size: 12px; margin: 0; }

.erp-tma-table thead th {
    background: var(--erp-surface-2, #f7f9fc);
    position: sticky;
    top: 0;
    /* Above the rows as they scroll under it, below anything the dialog
       itself puts on top. */
    z-index: 1;
}

/* A task header row, and its materials indented beneath it — the grouping is
   the whole point of the report, so it has to be visible at a glance. */
.erp-tma-task > td {
    background: #eef2f7;
    border-top: 1px solid var(--erp-line);
    font-size: 12px;
    padding-bottom: 6px;
    padding-top: 8px;
}

.erp-tma-task.is-short > td { background: rgba(208, 59, 59, .07); }

.erp-tma-sub {
    color: var(--erp-muted);
    font-size: 10.5px;
    font-weight: 400;
    margin-left: 8px;
}

.erp-tma-line > td { padding-bottom: 5px; padding-top: 5px; }
.erp-tma-line:hover > td { background: #fafbfc; }
.erp-tma-line > td:first-child { padding-left: 26px; }

.erp-tma-item { font-weight: 600; }

.erp-tma-code {
    color: var(--erp-muted);
    display: block;
    font-size: 10px;
}

/* The whole holding, beside this task's allocated share of it. Quiet, because
   the allocated number is the one being read. */
.erp-tma-onhand {
    color: var(--erp-muted);
    display: block;
    font-size: 10px;
}

.erp-tma-shortqty { color: #b32d2d; }
.erp-tma-none { color: var(--erp-muted); }

/* What a demand would actually ask for — per product, not per task. */
.erp-tma-demand {
    background: var(--erp-surface-2, #f7f9fc);
    border: 1px solid var(--erp-line);
    border-radius: 9px;
    margin-top: 12px;
    padding: 10px 12px;
}

.erp-tma-demand-head {
    align-items: baseline;
    display: flex;
    flex-wrap: wrap;
    font-size: 12.5px;
    gap: 8px;
    margin-bottom: 8px;
}

.erp-tma-demand-head span { color: var(--erp-muted); font-size: 11px; }

.erp-tma-chips { display: flex; flex-wrap: wrap; gap: 6px; }

.erp-tma-chip {
    background: #fff;
    border: 1px solid rgba(208, 59, 59, .3);
    border-radius: 999px;
    font-size: 11px;
    padding: 3px 10px;
}

.erp-tma-chip strong { color: #b32d2d; margin-left: 5px; }

.erp-tma-clear {
    background: rgba(0, 194, 146, .08);
    border: 1px solid rgba(0, 194, 146, .3);
    border-radius: 9px;
    color: #00926e;
    font-size: 12.5px;
    font-weight: 600;
    margin-top: 12px;
    padding: 10px 12px;
}

@media (max-width: 640px) {
    .erp-tma-bar .erp-field { min-width: 100%; }
    .erp-tma-scroll { max-height: none; }
}

:root[data-theme="dark"] .erp-tma-task > td { background: #22303f; }
:root[data-theme="dark"] .erp-tma-line:hover > td { background: rgba(255, 255, 255, .03); }
:root[data-theme="dark"] .erp-tma-chip { background: var(--erp-surface); }
:root[data-theme="dark"] .erp-tma-empty i { color: #3a4654; }

/* The selection bar, flashed when "Check Material" is pressed with nothing
   ticked. The button turns selection on for you; this is what says where to
   look next, instead of leaving a silent mode change nobody notices. */
.erp-bulkbar.is-flash {
    animation: erp-bulkbar-flash 1.1s ease;
}

@keyframes erp-bulkbar-flash {
    0%, 100% { box-shadow: 0 0 0 0 rgba(42, 120, 214, 0); }
    15%      { box-shadow: 0 0 0 3px rgba(42, 120, 214, .35); }
    60%      { box-shadow: 0 0 0 3px rgba(42, 120, 214, .18); }
}

@media (prefers-reduced-motion: reduce) {
    .erp-bulkbar.is-flash { animation: none; box-shadow: 0 0 0 3px rgba(42, 120, 214, .28); }
}

/* =========================================================================
   CBS template — library select-all
   ========================================================================= */

.erp-cbs-all {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    justify-content: space-between;
    margin-top: 8px;
}

.erp-cbs-all-box {
    align-items: center;
    cursor: pointer;
    display: inline-flex;
    font-size: 12px;
    font-weight: 600;
    gap: 7px;
    margin: 0;
    user-select: none;
}

.erp-cbs-all-box input:disabled { cursor: default; }
.erp-cbs-all-box:has(input:disabled) { cursor: default; opacity: .55; }

.erp-cbs-all-count { color: var(--erp-muted); font-size: 11px; }

/* Ticked rows the filter is hiding — they still post, so the count that says
   so has to be the one thing in this strip that catches the eye. */
.erp-cbs-all-count.is-warning { color: #b45309; font-weight: 600; }

/* =========================================================================
   WBS template builder — bulk selection
   ========================================================================= */

.erp-wbsbar {
    align-items: center;
    background: var(--erp-tint, #f7f8fb);
    border-bottom: 1px solid var(--erp-line);
    border-top: 1px solid var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    padding: 9px var(--erp-gutter, 16px);
}

.erp-wbsbar-count { font-size: 12.5px; }
.erp-wbsbar-note { color: var(--erp-muted); font-size: 11.5px; flex: 1 1 auto; }

/* An activity going with its stage, whether or not it was ticked itself. Shown
   rather than left looking loose, so the count and the table agree about what
   Remove is about to take. */
.erp-tpl-row.is-doomed > td {
    background: rgba(208, 59, 59, .06);
    text-decoration: line-through;
    text-decoration-color: rgba(208, 59, 59, .45);
}

:root[data-theme="dark"] .erp-tpl-row.is-doomed > td { background: rgba(208, 59, 59, .12); }

/* =========================================================================
   Estimate template - the bulk line grid
   ========================================================================= */

.erp-etgrid-wrap { max-height: 46vh; overflow: auto; }
.erp-etgrid { min-width: 1020px; }
.erp-etgrid > thead th {
    background: var(--erp-surface-2, #f7f9fc);
    font-size: 11px;
    position: sticky;
    top: 0;
    z-index: 1;
}
.erp-etgrid > tbody > tr > td { padding: 6px 5px; vertical-align: top; }

/* What the formula works out to against the sample parameters. The one thing
   that catches an expression which parses perfectly and is wrong by a factor
   of a thousand, so it sits directly under the field rather than in a tooltip. */
.erp-etsample {
    display: block;
    font-size: 10.5px;
    margin-top: 3px;
    min-height: 13px;
}
.erp-etsample.is-ok { color: #00926e; font-weight: 600; }
.erp-etsample.is-bad { color: #b32d2d; }
.erp-etsample.is-fixed { color: var(--erp-muted); }

.erp-etparams { max-height: 320px; overflow-y: auto; min-width: 250px; }
.erp-etparams .dropdown-item { padding-bottom: 4px; padding-top: 4px; }
.erp-etparams code { font-size: 11px; }
.erp-etparams small { font-size: 10px; }

/* Plain tabs inside a dialog: the glass treatment belongs to page navigation
   and reads as far too heavy at this size. */
.erp-tabs-plain .nav-link { font-size: 12.5px; padding: 6px 12px; }

/* The bulk line grid, full screen.
   -------------------------------------------------------------------------
   The wrapper keeps its own scroll rather than letting the modal body do it,
   because the grid's header row is sticky against THAT box. Handing the scroll
   to the body would leave the headings scrolling away with the rows, which on
   a seven-column grid is the one thing you cannot afford to lose.

   So the chain is made to flex instead: body -> tab content -> active pane ->
   wrapper, each giving up its natural height so the last one can take what is
   left. min-height:0 at every level is what lets a flex child shrink below its
   content and actually scroll. */
#etBulkModal .modal-content { height: 100%; }

#etBulkModal .modal-body {
    display: flex;
    flex-direction: column;
    min-height: 0;
}

#etBulkModal .tab-content { flex: 1 1 auto; min-height: 0; }

#etBulkModal .tab-pane.active {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
}

#etBulkModal .erp-etgrid-wrap {
    flex: 1 1 auto;
    max-height: none;
    min-height: 140px;
}

/* The paste tab gets the same room rather than a fixed ten rows. */
#etBulkModal #etPasteTab .erp-field { display: flex; flex-direction: column; min-height: 0; }
#etBulkModal #etPasteTab textarea { flex: 1 1 auto; min-height: 180px; resize: none; }

/* Below that width a full-screen modal is the only sensible shape anyway, and
   the grid scrolls sideways as it already does. */
@media (max-width: 720px) {
    #etBulkModal .erp-etgrid-wrap { min-height: 200px; }
}

/* A group of actions in a card header.
   -------------------------------------------------------------------------
   .erp-card-head spreads its children with space-between, which is right for
   a title and one button and wrong the moment there are three: they string out
   across the row instead of sitting together. Wrapping them makes the header
   two children again, and margin-left:auto holds the group right even when the
   title is short enough that space-between would not.

   It wraps rather than overflowing, so a narrow window stacks the buttons
   under the title instead of pushing the card sideways. */
.erp-card-head-actions {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-left: auto;
}

/* A card header that opens and closes its own body.
   -------------------------------------------------------------------------
   The button carries no chrome of its own: it is the heading, and a heading
   that suddenly looked like a button would read as an action rather than as a
   title. The caret is the whole affordance. */
.erp-card-toggle {
    align-items: center;
    background: none;
    border: 0;
    color: inherit;
    cursor: pointer;
    display: inline-flex;
    font: inherit;
    gap: 7px;
    padding: 0;
    text-align: left;
}

.erp-card-toggle:hover { color: var(--erp-primary); }

.erp-card-toggle:focus-visible {
    outline: 2px solid var(--erp-primary);
    outline-offset: 3px;
    border-radius: 4px;
}

.erp-card-caret {
    color: var(--erp-muted);
    font-size: 11px;
    transition: transform .18s ease;
}

.erp-card-toggle[aria-expanded="true"] .erp-card-caret { transform: rotate(90deg); }

@media (prefers-reduced-motion: reduce) {
    .erp-card-caret { transition: none; }
}

/* ==========================================================================
   Parameter subject headings
   Ninety-two parameters in one flat run is not a list anybody reads. The same
   headings appear in four places — the list page, the formula card, and both
   insert menus — so they share one visual language: small, upper-case, quiet.
   They are signposts to scan past, not content to read.

   Built only on --erp-ink / --erp-muted / --erp-line / --erp-surface-2, which
   are all redefined under [data-theme="dark"], so no separate dark block is
   needed here.
   ========================================================================== */

/* A full-width banner row inside the table. Sticky is deliberately NOT used:
   a sticky group row would fight the sticky <thead> erp-table already has, and
   two competing sticky bands read worse than none. */
.erp-param-group > td {
    background: var(--erp-surface-2);
    border-top: 1px solid var(--erp-line);
    color: var(--erp-muted);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .08em;
    padding: 10px 12px 8px;
    text-transform: uppercase;
}

.erp-param-group:first-child > td { border-top: 0; }

.erp-param-group-count {
    background: var(--erp-line);
    border-radius: 10px;
    color: var(--erp-ink);
    display: inline-block;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0;
    margin-left: 8px;
    min-width: 20px;
    padding: 1px 7px;
    text-align: center;
}

/* The chip card. Each group is its own block so its chips wrap within their
   own heading instead of flowing under the next one's. */
.erp-param-chipgroup + .erp-param-chipgroup { margin-top: 14px; }

.erp-param-chipgroup-head {
    color: var(--erp-muted);
    display: block;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .08em;
    margin-bottom: 6px;
    text-transform: uppercase;
}

/* The insert menus. Bootstrap's dropdown-header indents to match dropdown-item
   padding, which these code/label rows do not use. */
.erp-etparams .erp-param-menu-head {
    color: var(--erp-muted);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .08em;
    margin: 0;
    padding: 8px 12px 2px;
    text-transform: uppercase;
}

/* The divider goes on every heading but the menu's first. The heading is always
   the only child of its <li>, so the :not() has to test the LI, not the h6. */
.erp-etparams li:not(:first-child) > .erp-param-menu-head {
    border-top: 1px solid var(--erp-line);
    margin-top: 4px;
    padding-top: 8px;
}

/* ==========================================================================
   Quick-add panel
   Creating a small piece of master data without leaving the form that needed
   it. Expands inline under its own field rather than opening a second modal,
   so nothing already typed is at risk.
   ========================================================================== */

/* Sits on the field's label row, so it stays quiet until looked for. */
.erp-quickadd-open {
    background: none;
    border: 0;
    color: var(--erp-primary);
    cursor: pointer;
    font-size: 11px;
    font-weight: 600;
    line-height: 1;
    padding: 2px 4px;
}

.erp-quickadd-open:hover { text-decoration: underline; }

.erp-quickadd-open i { font-size: 9px; }

.erp-quickadd {
    background: var(--erp-surface-2);
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    margin-top: 8px;
    padding: 10px;
}

.erp-quickadd .form-label {
    font-size: 11px;
    margin-bottom: 4px;
}

.erp-quickadd .erp-help { margin-top: 6px; }

/* The attribute, not a class: the panel is toggled with .hidden, and a
   display rule from .erp-quickadd would otherwise win and keep it visible. */
.erp-quickadd[hidden] { display: none; }

/* ==========================================================================
   Parameters on the project setup screen
   Grouped under the same headings as the admin list, so the order somebody
   sets there is the order a planner is asked here.
   ========================================================================== */

.erp-param-step-group + .erp-param-step-group { margin-top: 18px; }

/* A button, not a div: the heading is the control that opens the section, so
   it has to be reachable by keyboard and announce its state. */
.erp-param-step-head {
    align-items: center;
    background: none;
    border: 0;
    border-bottom: 1px solid var(--erp-line);
    color: var(--erp-muted);
    cursor: pointer;
    display: flex;
    font-size: 11px;
    font-weight: 700;
    gap: 8px;
    letter-spacing: .08em;
    margin-bottom: 12px;
    padding: 4px 0 6px;
    text-align: left;
    text-transform: uppercase;
    width: 100%;
}

.erp-param-step-head:hover { color: var(--erp-ink); }

.erp-param-step-head:focus-visible {
    border-radius: 4px;
    outline: 2px solid var(--erp-primary);
    outline-offset: 2px;
}

.erp-param-step-caret {
    font-size: 9px;
    transition: transform .15s ease;
}

.erp-param-step-head[aria-expanded="true"] .erp-param-step-caret { transform: rotate(90deg); }

/* Pushed to the right of the heading row. Only shown while something in the
   section is still blank, so a finished section is quiet. */
.erp-param-step-blank {
    color: var(--erp-warning, #f59e0b);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0;
    margin-left: auto;
    text-transform: none;
}

.erp-param-step-body[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
    .erp-param-step-caret { transition: none; }
}

.erp-param-step-count {
    background: var(--erp-line);
    border-radius: 10px;
    color: var(--erp-ink);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0;
    padding: 1px 7px;
}

/* The × on a field. Invisible until the field is approached, because a form of
   twenty-seven boxes each wearing a permanent delete cross reads as dangerous
   rather than as flexible. */
.erp-param-drop {
    background: none;
    border: 0;
    color: var(--erp-muted);
    cursor: pointer;
    font-size: 11px;
    line-height: 1;
    opacity: 0;
    padding: 2px 4px;
    transition: opacity .12s ease, color .12s ease;
}

.erp-field:hover .erp-param-drop,
.erp-param-drop:focus-visible { opacity: 1; }

.erp-param-drop:hover { color: var(--erp-danger); }

.erp-param-drop:disabled { cursor: default; opacity: .4; }

.erp-param-added {
    font-size: 9px;
    letter-spacing: .04em;
    padding: 1px 6px;
    text-transform: uppercase;
    vertical-align: middle;
}

.erp-param-add {
    background: var(--erp-surface-2);
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    margin-top: 16px;
    padding: 12px;
}

/* Only the fields scroll; the wizard rail and the summary stay in view. */
.erp-param-scroll {
    max-height: calc(100vh - 280px);
    min-height: 320px;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-right: 6px;
    scrollbar-gutter: stable;
}

.erp-wiz-sticky {
    max-height: calc(100vh - 100px);
    overflow-y: auto;
    position: sticky;
    top: 84px;
}

@media (max-width: 991.98px) {
    .erp-param-scroll { max-height: none; overflow: visible; }
}

@media (max-width: 1199.98px) {
    .erp-wiz-sticky { position: static; }
}

/* Key inputs: the handful of numbers everything else is calculated from. */
.erp-param-primary {
    background: var(--erp-surface-2);
    border: 1px solid var(--erp-line);
    border-left: 3px solid var(--erp-primary);
    border-radius: 8px;
    margin-bottom: 16px;
    padding: 14px 14px 2px;
}

.erp-param-primary-head {
    align-items: flex-start;
    display: flex;
    gap: 12px;
    justify-content: space-between;
    margin-bottom: 12px;
}

.erp-param-primary-head strong { color: var(--erp-ink); display: block; font-size: 13px; }
.erp-param-primary-head span { color: var(--erp-muted); font-size: 12px; }

.erp-param-rest-note {
    color: var(--erp-muted);
    font-size: 12px;
    margin: 0 0 10px;
}

/* A proposed value reads differently from a typed one. */
.erp-param-step-body .form-control.is-auto,
.erp-param-primary .form-control.is-auto {
    background-color: rgba(59, 130, 246, .06);
    color: var(--erp-muted);
}

.erp-param-origin { font-size: 11px; }
.erp-param-origin:empty { display: none; }
.erp-param-origin:not(:empty) + .erp-param-range::before { content: '· '; }

.erp-param-reset {
    background: none;
    border: 0;
    color: var(--erp-muted);
    cursor: pointer;
    font-size: 11px;
    line-height: 1;
    padding: 2px 4px;
}

.erp-param-reset:hover { color: var(--erp-primary); }
.erp-param-reset[hidden] { display: none; }

/* Side read-out of the form. Sticky so it stays beside whatever section is open. */
.erp-param-summary {
    background: var(--erp-surface, #fff);
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    font-size: 12px;
    padding: 12px;
    position: sticky;
    top: 84px;
}

.erp-param-summary-head {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    color: var(--erp-ink);
    display: flex;
    font-size: 12px;
    font-weight: 700;
    gap: 8px;
    letter-spacing: .04em;
    margin-bottom: 8px;
    padding-bottom: 8px;
    text-transform: uppercase;
}

.erp-param-summary-sync { color: var(--erp-muted); margin-left: auto; }
.erp-param-summary-sync[hidden] { display: none; }

.erp-param-summary-list { margin: 0 0 10px; }

.erp-param-summary-list > div {
    display: flex;
    gap: 8px;
    justify-content: space-between;
    padding: 4px 0;
}

.erp-param-summary-list > div + div { border-top: 1px dashed var(--erp-line); }
.erp-param-summary-list dt { color: var(--erp-muted); font-weight: 500; }
.erp-param-summary-list dd { color: var(--erp-ink); font-weight: 600; margin: 0; text-align: right; white-space: nowrap; }
.erp-param-summary-list dd small { color: var(--erp-muted); font-weight: 400; }
.erp-param-summary-list dd .is-auto { color: var(--erp-primary); }

.erp-param-summary-stats {
    background: var(--erp-surface-2);
    border-radius: 6px;
    display: grid;
    gap: 4px;
    margin-bottom: 10px;
    padding: 8px 10px;
}

.erp-param-summary-stats > div { align-items: center; display: flex; gap: 6px; }
.erp-param-summary-stats strong { margin-left: auto; }

.erp-param-dot { border-radius: 50%; display: inline-block; height: 8px; width: 8px; }
.erp-param-dot.is-manual { background: var(--erp-ink); }
.erp-param-dot.is-auto { background: var(--erp-primary); }
.erp-param-dot.is-blank { background: var(--erp-warning, #f59e0b); }

.erp-param-summary-missing {
    color: var(--erp-danger, #dc2626);
    font-weight: 600;
    margin-bottom: 8px;
}

.erp-param-summary-missing[hidden] { display: none; }

.erp-param-add .form-label { font-size: 12px; }

/* Keeps the picker from stretching across a wide card — it is a secondary
   action, not the point of the screen. */
.erp-param-add .d-flex { max-width: 560px; }

/* ==========================================================================
   Drag to reorder on the parameter list
   ========================================================================== */

.erp-param-grip {
    color: var(--erp-muted);
    cursor: grab;
    font-size: 11px;
    margin-right: 8px;
    opacity: .5;
}

tr:hover > td > .erp-param-grip { opacity: 1; }

[data-param-sort] tr[draggable="true"] { cursor: default; }

[data-param-sort] tr.is-dragging {
    background: var(--erp-surface-2);
    opacity: .5;
}

/* While the new order is in flight. Not a spinner: the rows are already in the
   right place on screen and a spinner would suggest they might jump back. */
[data-param-sort].is-saving { opacity: .75; }

[data-param-sort].is-saving tr[draggable="true"] { pointer-events: none; }

/* ==========================================================================
   Setup review tick list
   What a template WOULD apply, before it is applied. A suggestion you can
   only take whole is a rule, so this is where parts of it get declined.
   ========================================================================== */

.erp-picktree {
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    margin-bottom: 14px;
    overflow: hidden;
}

.erp-picktree-bar {
    align-items: flex-start;
    background: var(--erp-surface-2);
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: space-between;
    padding: 10px 12px;
}

.erp-picktree-bar-actions { display: flex; gap: 6px; }

/* Capped and scrolled: a 345-code breakdown must not push the Apply button
   off the bottom of the page. */
.erp-picktree-body {
    max-height: 340px;
    overflow-y: auto;
}

.erp-picktree-group + .erp-picktree-group { border-top: 1px solid var(--erp-line); }

.erp-picktree-head {
    align-items: center;
    display: flex;
    gap: 8px;
    padding: 7px 12px;
}

.erp-picktree-head:hover { background: var(--erp-surface-2); }

.erp-picktree-check {
    align-items: center;
    cursor: pointer;
    display: flex;
    flex: 1;
    gap: 8px;
    margin: 0;
    min-width: 0;
}

.erp-picktree-check > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-picktree-head > .erp-picktree-check > span { font-weight: 600; }

.erp-picktree-n {
    color: var(--erp-muted);
    font-size: 10px;
    font-variant-numeric: tabular-nums;
}

.erp-picktree-toggle {
    background: none;
    border: 0;
    color: var(--erp-muted);
    cursor: pointer;
    font-size: 10px;
    padding: 2px 6px;
    transition: transform .15s ease;
}

.erp-picktree-toggle.is-open { transform: rotate(180deg); }

.erp-picktree-children {
    border-top: 1px dashed var(--erp-line);
    padding: 4px 12px 8px 34px;
}

.erp-picktree-child {
    font-size: 12px;
    padding: 3px 0;
}

.erp-picktree-child code { font-size: 10px; margin-right: 4px; }

@media (prefers-reduced-motion: reduce) {
    .erp-picktree-toggle { transition: none; }
}

/* ==========================================================================
   Estimate lines — bulk selection
   Only rendered while the estimate is a draft. Once it has gone for review
   its build-up is the thing under review.
   ========================================================================== */

.erp-estbar {
    align-items: center;
    background: var(--erp-surface-2);
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    display: flex;
    gap: 12px;
    justify-content: space-between;
    margin-bottom: 10px;
    padding: 8px 12px;
}

.erp-estbar[hidden] { display: none; }

.erp-estbar strong { font-variant-numeric: tabular-nums; }

.erp-estbar-actions { display: flex; gap: 6px; }

/* A ticked row stays visible as such when it scrolls away from the bar. */
.erp-est-table tr.is-picked > td {
    background: color-mix(in srgb, var(--erp-primary) 8%, transparent);
}

.erp-est-table thead th:first-child,
.erp-est-table tbody td:first-child { vertical-align: middle; }

/* --- Inline cell editing --------------------------------------------------
   An editable cell has to LOOK different from one that only displays a figure,
   or the feature is undiscoverable and the first anyone learns of it is when
   they change a rate by accident. The resting state is a dotted underline —
   present enough to invite a click, quiet enough not to turn the column into a
   grid of buttons — and the hover state commits to it. */
.erp-cell-edit {
    cursor: pointer;
    position: relative;
}

.erp-cell-edit [data-est-cell-text] {
    border-bottom: 1px dashed var(--erp-line);
    transition: border-color .12s ease, background-color .12s ease;
}

.erp-cell-edit:hover [data-est-cell-text],
.erp-cell-edit:focus-visible [data-est-cell-text] {
    border-bottom-color: var(--erp-primary);
    color: var(--erp-primary);
}

/* The cell is focusable, so it needs the same visible ring any control gets —
   keyboard users cannot see a hover. */
.erp-cell-edit:focus-visible {
    outline: 2px solid var(--erp-primary);
    outline-offset: -2px;
    border-radius: 4px;
}

.erp-cell-edit.is-editing {
    background: color-mix(in srgb, var(--erp-primary) 6%, transparent);
}

/* In flight. The row is about to be replaced by the server's version, so the
   cell stops inviting a second edit that would be computed from a stale line. */
.erp-cell-edit.is-saving {
    cursor: progress;
    opacity: .55;
    pointer-events: none;
}

.erp-cell-input {
    min-width: 0;
    width: 100%;
}

/* The inputs sit inside a compact table row, so they keep its rhythm rather
   than pushing every other row taller while one cell is open. */
td.erp-cell-edit .erp-cell-input {
    font-size: 12.5px;
    padding: 2px 6px;
    height: auto;
}

/* ==========================================================================
   Estimate -> BOQ planning
   The estimate sets a target; the bill is prepared against it. The variance
   column is the first honest feedback this system has on estimate accuracy,
   so it is the thing the styling serves.
   ========================================================================== */

/* Colour marks the SIZE of a variance, not its direction. "Under the estimate"
   is not automatically good — usually it means the section is unfinished — so
   the sign is left to speak for itself. */
.erp-var { font-variant-numeric: tabular-nums; white-space: nowrap; }
.erp-var.is-close { color: var(--erp-primary); }
.erp-var.is-near { color: #b96f00; }
.erp-var.is-far { color: var(--erp-danger); }

.erp-var-pct {
    display: block;
    font-size: 10px;
    font-weight: 500;
    opacity: .8;
}

/* A row measured against a figure that has since moved. Marked on the row, not
   only in the banner, because the banner is a count and this is the one. */
.erp-plan-stale > td { background: rgba(245, 158, 11, .06); }
.erp-plan-stale > td:first-child { box-shadow: inset 3px 0 0 #f59e0b; }

/* The target card. Tinted and ruled so it never reads as part of the bill
   below it — confusing the two is exactly the mistake this screen prevents. */
.erp-prep-target { border-left: 3px solid var(--erp-line); }
.erp-prep-target .erp-table tbody td { color: var(--erp-muted); }

.erp-prep-strip {
    display: flex;
    flex-wrap: wrap;
    gap: 28px;
    margin-bottom: 14px;
    padding: 10px 14px;
    background: var(--erp-surface-2);
    border-radius: 8px;
}

.erp-prep-strip strong { display: block; font-size: 16px; }

.erp-prep-label {
    color: var(--erp-muted);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
}

.erp-prep-input { max-width: 120px; margin-left: auto; }

.erp-prep-preview {
    background: var(--erp-surface-2);
    border-radius: 8px;
    margin-top: 10px;
    padding: 8px 12px;
    text-align: right;
}

.erp-prep-preview strong { font-size: 17px; margin-left: 8px; }

/* The library picker. Capped and scrolled — 200 pay items must not push the
   Add button off the bottom of the dialog. */
.erp-prep-library {
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    margin-top: 10px;
    max-height: 340px;
    overflow-y: auto;
    padding: 6px;
}

.erp-prep-libitem {
    align-items: center;
    border-radius: 6px;
    cursor: pointer;
    display: flex;
    gap: 10px;
    margin: 0;
    padding: 6px 8px;
}

.erp-prep-libitem:hover { background: var(--erp-surface-2); }

.erp-prep-libmain { flex: 1; font-size: 12.5px; min-width: 0; }
.erp-prep-libmain code { font-size: 10.5px; margin-right: 4px; }

.erp-prep-libused {
    background: rgba(245, 158, 11, .14);
    border-radius: 10px;
    color: #b96f00;
    font-size: 9.5px;
    margin-left: 8px;
    padding: 1px 7px;
}

.erp-prep-libmeta { color: var(--erp-muted); font-size: 11px; width: 60px; }
.erp-prep-librate { font-size: 11px; font-variant-numeric: tabular-nums; text-align: right; width: 90px; }

/* ==========================================================================
   QA progress chain
   Shown at the top of every QA record. One shape across inspection plans,
   inspection requests, NCRs, observations, snags and tests, so a user who
   learns it on one screen has learned it on all of them.
   ========================================================================== */
.erp-qa-chain {
    background: var(--erp-surface);
    border: 1px solid var(--erp-border);
    border-radius: 12px;
    padding: 16px 18px 14px;
    margin-bottom: 16px;
}

.erp-qa-chain-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 10px;
}

.erp-qa-chain-title { min-width: 0; }

.erp-qa-chain-ref {
    display: inline-block;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--erp-muted);
    margin-bottom: 2px;
}

.erp-qa-chain-title strong {
    display: block;
    font-size: 15px;
    font-weight: 600;
    color: var(--erp-ink);
    line-height: 1.35;
}

.erp-qa-chain-note {
    display: block;
    font-size: 12.5px;
    color: var(--erp-muted);
    margin-top: 2px;
}

.erp-qa-chain-pct {
    flex: 0 0 auto;
    text-align: right;
    line-height: 1.1;
}

.erp-qa-chain-pct strong { font-size: 22px; font-weight: 700; color: var(--erp-ink); }
.erp-qa-chain-pct strong small { font-size: 13px; font-weight: 600; margin-left: 1px; }
.erp-qa-chain-pct span { display: block; font-size: 11px; color: var(--erp-muted); }

.erp-qa-chain-bar { margin-bottom: 14px; }

.erp-qa-chain-steps {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0;
    list-style: none;
    margin: 0;
    padding: 0;
}

.erp-qa-chain-steps > li {
    position: relative;
    flex: 1 1 0;
    min-width: 92px;
    text-align: center;
    padding: 0 4px;
}

/* The connector sits behind the dots and stops at the last step, so the chain
   reads as a line rather than a row of unrelated badges. */
.erp-qa-chain-steps > li:not(:last-child)::after {
    content: '';
    position: absolute;
    top: 13px;
    left: calc(50% + 15px);
    right: calc(-50% + 15px);
    height: 2px;
    background: var(--erp-border);
}

.erp-qa-chain-steps > li.is-done::after { background: var(--erp-success); }

.erp-qa-chain-dot {
    position: relative;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: 2px solid var(--erp-border);
    background: var(--erp-surface);
    color: var(--erp-muted);
    font-size: 11.5px;
    font-weight: 700;
}

.erp-qa-chain-index { font-size: 12px; line-height: 1; }

.erp-qa-chain-label {
    display: block;
    margin-top: 6px;
    font-size: 11.5px;
    line-height: 1.3;
    color: var(--erp-muted);
    word-break: break-word;
}

.erp-qa-chain-steps > li.is-done .erp-qa-chain-dot {
    border-color: var(--erp-success);
    background: var(--erp-success);
    color: #fff;
}
.erp-qa-chain-steps > li.is-done .erp-qa-chain-label { color: var(--erp-ink); }

.erp-qa-chain-steps > li.is-current .erp-qa-chain-dot {
    border-color: var(--erp-primary);
    color: var(--erp-primary);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--erp-primary) 14%, transparent);
}
.erp-qa-chain-steps > li.is-current .erp-qa-chain-label { color: var(--erp-ink); font-weight: 600; }

.erp-qa-chain-steps > li.is-failed .erp-qa-chain-dot {
    border-color: var(--erp-danger);
    background: var(--erp-danger);
    color: #fff;
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--erp-danger) 14%, transparent);
}
.erp-qa-chain-steps > li.is-failed .erp-qa-chain-label { color: var(--erp-danger); font-weight: 600; }

.erp-qa-chain-steps > li.is-skipped .erp-qa-chain-dot { border-style: dashed; }
.erp-qa-chain-steps > li.is-skipped .erp-qa-chain-label { text-decoration: line-through; }

@media (max-width: 575.98px) {
    .erp-qa-chain-head { flex-direction: column; gap: 8px; }
    .erp-qa-chain-pct { text-align: left; }
    .erp-qa-chain-steps > li { min-width: 76px; }
    .erp-qa-chain-label { font-size: 10.5px; }
}

/* ==========================================================================
   QA section navigation
   Shown on every QA page beneath the project tabs, so somebody deep in an NCR
   can still see which part of QA they are in and reach any other part.
   ========================================================================== */
.erp-qa-nav {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    padding: 4px;
    margin: 14px 0 0;
    border: 1px solid var(--erp-border);
    border-radius: 10px;
    background: var(--erp-surface-2);
}

.erp-qa-nav-link {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 7px 13px;
    border-radius: 7px;
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1.2;
    color: var(--erp-muted);
    white-space: nowrap;
    text-decoration: none;
}

.erp-qa-nav-link:hover { color: var(--erp-ink); text-decoration: none; }

.erp-qa-nav-link.is-on {
    background: var(--erp-surface);
    color: var(--erp-ink);
    box-shadow: 0 1px 3px rgba(15, 23, 42, .12);
}

.erp-qa-nav-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 19px;
    height: 19px;
    padding: 0 5px;
    border-radius: 9px;
    background: var(--erp-danger);
    color: #fff;
    font-size: 10.5px;
    font-weight: 700;
}

.erp-qa-nav-link.is-on .erp-qa-nav-count { background: var(--erp-danger); }

:root[data-theme="dark"] .erp-qa-nav-link.is-on { box-shadow: 0 1px 3px rgba(0, 0, 0, .4); }

@media (max-width: 575.98px) {
    .erp-qa-nav { overflow-x: auto; flex-wrap: nowrap; }
    .erp-qa-nav-link { padding: 7px 10px; font-size: 12px; }
}

/* Compact progress chain for table rows. */
.erp-qa-cell { min-width: 132px; }
.erp-qa-cell .erp-progress { margin-bottom: 4px; }
.erp-qa-cell-meta { font-size: 11.5px; color: var(--erp-muted); white-space: nowrap; }
.erp-qa-cell-meta strong { color: var(--erp-ink); font-weight: 700; }

/* --------------------------------------------------------------------------
   Rate analysis workbench

   A backlog on the left and one recipe on the right. The layout's whole job is
   that neither pane scrolls the other: an estimator working through nine
   hundred pay items keeps the list still while the editor changes under it, so
   both panes own their own scrollbar and the page itself never grows one.

   Built from the tokens rather than fixed colours, so dark mode needs no
   second copy of any of this.
   -------------------------------------------------------------------------- */

.erp-wb {
    display: grid;
    /* The list is a fixed reading width; the editor takes what is left,
       because it is the pane that holds a table. */
    grid-template-columns: 340px minmax(0, 1fr);
    gap: var(--erp-gutter);
    /* The page head plus the app chrome. Fixed so both panes scroll
       internally instead of the window doing it for them. */
    height: calc(100vh - 190px);
    min-height: 460px;
}

/* Without min-height a grid child refuses to shrink and the inner overflow
   never engages — the panes grow instead of scrolling. */
.erp-wb-side,
.erp-wb-main {
    min-height: 0;
}

.erp-wb-side .erp-card,
.erp-wb-main .erp-card {
    display: flex;
    flex-direction: column;
    height: 100%;
    margin-bottom: 0;
    overflow: hidden;
}

.erp-wb-side .erp-card-body,
.erp-wb-main .erp-card-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

.erp-wb-filters {
    display: grid;
    gap: 8px;
}

/* Coverage ----------------------------------------------------------------- */

.erp-wb-coverage {
    padding: 14px 16px 12px;
    border-bottom: 1px solid var(--erp-line);
}

.erp-wb-coverage-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    font-size: 12.5px;
    color: var(--erp-muted);
}

.erp-wb-coverage-head strong {
    color: var(--erp-ink);
    font-size: 15px;
}

.erp-wb-coverage-pct {
    font-weight: 700;
    color: var(--erp-success);
}

/* Two bars in one track, laid side by side rather than stacked: what is
   active and what is merely drafted are different kinds of progress, and
   showing them as one number would let a library of unreviewed drafts read
   as finished. */
.erp-wb-bar {
    display: flex;
    height: 6px;
    margin: 8px 0;
    border-radius: 999px;
    background: var(--erp-line);
    overflow: hidden;
}

.erp-wb-bar-active { background: var(--erp-success); }
.erp-wb-bar-draft { background: var(--erp-warning); }

.erp-wb-coverage-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    font-size: 11px;
    color: var(--erp-muted);
}

.erp-wb-coverage-legend i { font-size: 7px; vertical-align: middle; }

/* Tabs --------------------------------------------------------------------- */

.erp-wb-tabs {
    display: flex;
    gap: 2px;
    padding: 8px 10px;
    border-bottom: 1px solid var(--erp-line);
}

.erp-wb-tab {
    flex: 1 1 0;
    padding: 5px 6px;
    border: 0;
    border-radius: 7px;
    background: transparent;
    color: var(--erp-muted);
    font-size: 11.5px;
    font-weight: 600;
    line-height: 1.3;
}

.erp-wb-tab:hover { background: var(--erp-surface-2); color: var(--erp-ink); }

.erp-wb-tab.is-active {
    background: color-mix(in srgb, var(--erp-primary) 14%, transparent);
    color: var(--erp-primary-dark);
}

.erp-wb-tab-count { display: block; font-size: 13px; font-weight: 700; }

/* The list ----------------------------------------------------------------- */

.erp-wb-list { margin: 0; padding: 0; list-style: none; }

.erp-wb-group {
    position: sticky;
    top: 0;
    z-index: 1;
    padding: 6px 16px;
    background: var(--erp-surface-2);
    border-bottom: 1px solid var(--erp-line);
    color: var(--erp-muted);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
}

.erp-wb-row {
    display: flex;
    align-items: center;
    gap: 6px;
    padding-left: 10px;
    border-bottom: 1px solid var(--erp-line);
}

.erp-wb-check { flex: 0 0 auto; margin: 0; }

/* Keeps every title on the same left edge whether or not its row can be
   ticked. A list that jogs in and out by a checkbox width is much harder to
   scan than one that does not. */
.erp-wb-check-spacer { flex: 0 0 auto; width: 16px; }

.erp-wb-item {
    display: flex;
    flex: 1 1 auto;
    align-items: flex-start;
    gap: 9px;
    min-width: 0;
    padding: 9px 12px 9px 4px;
    border: 0;
    background: transparent;
    text-align: left;
}

.erp-wb-item:hover { background: var(--erp-surface-2); }

.erp-wb-item.is-open {
    background: color-mix(in srgb, var(--erp-primary) 10%, transparent);
    box-shadow: inset 3px 0 0 var(--erp-primary);
}

.erp-wb-item-dot {
    flex: 0 0 auto;
    width: 7px;
    height: 7px;
    margin-top: 5px;
    border-radius: 50%;
    background: var(--erp-line);
}

.erp-wb-item-dot.is-active { background: var(--erp-success); }
.erp-wb-item-dot.is-draft { background: var(--erp-warning); }
.erp-wb-item-dot.is-none { background: var(--erp-muted); opacity: .45; }

.erp-wb-item-body { min-width: 0; }

.erp-wb-item-title {
    display: block;
    color: var(--erp-ink);
    font-size: 12.5px;
    font-weight: 500;
    line-height: 1.35;
}

.erp-wb-item-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 7px;
    margin-top: 2px;
    font-size: 10.5px;
    color: var(--erp-muted);
}

.erp-wb-item-meta code {
    padding: 0 3px;
    border-radius: 3px;
    background: var(--erp-surface-2);
    color: var(--erp-muted);
    font-size: 10px;
}

.erp-wb-pager { padding: 10px 12px; border-top: 1px solid var(--erp-line); }
.erp-wb-pager .pagination { margin: 0; justify-content: center; }

/* The editor --------------------------------------------------------------- */

.erp-wb-editor-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 16px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--erp-line);
}

.erp-wb-editor-scope {
    color: var(--erp-muted);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .05em;
    text-transform: uppercase;
}

.erp-wb-editor-title {
    margin: 2px 0 0;
    color: var(--erp-ink);
    font-size: 17px;
    font-weight: 700;
    line-height: 1.3;
}

.erp-wb-grid-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 8px;
}

.erp-wb-grid-title {
    color: var(--erp-ink);
    font-size: 13px;
    font-weight: 600;
}

.erp-wb-add { display: flex; flex-wrap: wrap; gap: 4px; }

.erp-wb-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 16px;
    margin-top: 16px;
}

.erp-wb-markups {
    display: grid;
    grid-template-columns: auto 92px auto 92px auto minmax(150px, 220px);
    align-items: center;
    gap: 8px;
}

.erp-wb-markups .form-label { font-size: 11.5px; color: var(--erp-muted); white-space: nowrap; }

.erp-wb-rate {
    padding: 8px 16px;
    border: 1px solid color-mix(in srgb, var(--erp-primary) 35%, transparent);
    border-radius: 10px;
    background: color-mix(in srgb, var(--erp-primary) 8%, transparent);
    text-align: right;
}

.erp-wb-rate-label {
    display: block;
    color: var(--erp-muted);
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .05em;
    text-transform: uppercase;
}

.erp-wb-rate-value {
    color: var(--erp-primary-dark);
    font-size: 19px;
    font-weight: 700;
}

.erp-wb-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-top: 18px;
    padding-top: 14px;
    border-top: 1px solid var(--erp-line);
}

.erp-wb-hint { color: var(--erp-muted); font-size: 11.5px; }
.erp-wb-hint i { margin-right: 5px; }

.erp-wb-placeholder { display: flex; align-items: center; justify-content: center; height: 100%; }

/* One pane at a time below a laptop. Two columns on a tablet would give the
   editor's eight-column table about two hundred pixels, which is not a table
   anybody can price from. */
@media (max-width: 1199.98px) {
    .erp-wb {
        grid-template-columns: minmax(0, 1fr);
        height: auto;
    }

    .erp-wb-side .erp-card { max-height: 420px; }
    .erp-wb-main .erp-card { height: auto; }
    .erp-wb-main .erp-card-body { overflow: visible; }
}

/* ==========================================================================
   Template preview on the project form
   Says which work breakdown, cost breakdown and estimate a project of the
   chosen type and category will actually be set up with — before it exists.
   ========================================================================== */
.erp-template-preview { display: flex; flex-direction: column; gap: 10px; }

.erp-tp-row {
    display: flex;
    align-items: flex-start;
    gap: 11px;
    padding: 10px 12px;
    border: 1px solid var(--erp-border);
    border-radius: 9px;
    background: var(--erp-surface-2);
}

.erp-tp-row > i {
    flex: 0 0 auto;
    width: 26px; height: 26px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 7px;
    background: var(--erp-surface);
    border: 1px solid var(--erp-border);
    color: var(--erp-primary);
    font-size: 12px;
}

.erp-tp-row.is-missing { border-style: dashed; }
.erp-tp-row.is-missing > i { color: var(--erp-warning); }

.erp-tp-body { min-width: 0; line-height: 1.45; }
.erp-tp-body strong { display: block; font-size: 12.5px; color: var(--erp-ink); }
.erp-tp-body span { display: block; font-size: 12px; color: var(--erp-muted); }
.erp-tp-body a { font-weight: 600; }

.erp-tp-alt { font-size: 11.5px !important; color: var(--erp-warning) !important; margin-top: 2px; }

/* The template preview sits inside the form row that decides it, so it needs
   its own frame rather than a card's. */
.erp-tp-panel {
    border: 1px solid var(--erp-border);
    border-radius: 10px;
    background: var(--erp-surface-2);
    padding: 11px 13px 13px;
    margin-bottom: 4px;
}

.erp-tp-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 9px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--erp-ink);
}

.erp-tp-panel .erp-tp-row { background: var(--erp-surface); }

/* ==========================================================================
   Project setup wizard
   Four decisions, one at a time, with the whole journey always visible beside
   them. The rail is the part that makes "one at a time" an improvement rather
   than a way of hiding how much is left.
   ========================================================================== */
.erp-wiz {
    border: 1px solid var(--erp-border);
    border-radius: 12px;
    background: var(--erp-surface);
    padding: 14px 15px;
    position: sticky;
    top: 16px;
}

.erp-wiz-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.erp-wiz-head strong { display: block; font-size: 13.5px; color: var(--erp-ink); }
.erp-wiz-head span { font-size: 11.5px; color: var(--erp-muted); }
.erp-wiz-pct { text-align: right; line-height: 1.1; }
.erp-wiz-pct strong { font-size: 19px; font-weight: 700; color: var(--erp-ink); }
.erp-wiz-pct strong small { font-size: 12px; }
.erp-wiz-pct span { display: block; font-size: 10.5px; color: var(--erp-muted); }
.erp-wiz-bar { margin: 10px 0 12px; }

.erp-wiz-steps { list-style: none; margin: 0; padding: 0; }

.erp-wiz-steps > li { position: relative; }

/* The connector runs behind the dots so the four read as one journey. */
.erp-wiz-steps > li:not(:last-child)::after {
    content: '';
    position: absolute;
    left: 15px; top: 30px; bottom: -4px;
    width: 2px;
    background: var(--erp-border);
}
.erp-wiz-steps > li.is-done::after { background: var(--erp-success); }

.erp-wiz-steps a {
    display: flex; align-items: flex-start; gap: 10px;
    padding: 6px 4px; border-radius: 8px;
    text-decoration: none; color: inherit;
}
.erp-wiz-steps a:hover { background: var(--erp-surface-2); text-decoration: none; }

.erp-wiz-dot {
    position: relative; z-index: 1; flex: 0 0 auto;
    width: 30px; height: 30px; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    border: 2px solid var(--erp-border);
    background: var(--erp-surface);
    color: var(--erp-muted);
    font-size: 12px; font-weight: 700;
}

.erp-wiz-label { min-width: 0; padding-top: 3px; }
.erp-wiz-label strong { display: block; font-size: 12.5px; font-weight: 600; color: var(--erp-muted); line-height: 1.3; }
.erp-wiz-label span { display: block; font-size: 11px; color: var(--erp-muted); }

.erp-wiz-steps > li.is-done .erp-wiz-dot { border-color: var(--erp-success); background: var(--erp-success); color: #fff; }
.erp-wiz-steps > li.is-done .erp-wiz-label strong { color: var(--erp-ink); }

.erp-wiz-steps > li.is-current .erp-wiz-dot {
    border-color: var(--erp-primary); color: var(--erp-primary);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--erp-primary) 14%, transparent);
}
.erp-wiz-steps > li.is-current .erp-wiz-label strong { color: var(--erp-primary); font-weight: 700; }

.erp-wiz-steps > li.is-failed .erp-wiz-dot { border-color: var(--erp-danger); background: var(--erp-danger); color: #fff; }
.erp-wiz-steps > li.is-failed .erp-wiz-label strong { color: var(--erp-danger); }

.erp-wiz-steps > li.is-skipped .erp-wiz-dot { border-style: dashed; }
.erp-wiz-steps > li.is-skipped .erp-wiz-label strong { text-decoration: line-through; }

.erp-wiz-steps > li.is-blocked .erp-wiz-dot { opacity: .55; }
.erp-wiz-steps > li.is-blocked .erp-wiz-label { opacity: .7; }

.erp-wiz-aside {
    margin-top: 14px; padding: 13px 15px;
    border: 1px solid var(--erp-border); border-radius: 12px;
    background: var(--erp-surface-2);
}
.erp-wiz-aside h6 { font-size: 12px; font-weight: 700; color: var(--erp-ink); margin: 0 0 4px; }
.erp-wiz-aside p { font-size: 11.5px; line-height: 1.5; color: var(--erp-muted); margin: 0 0 11px; }
.erp-wiz-aside p:last-child { margin-bottom: 0; }

.erp-wiz-nav {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--erp-border);
}
.erp-wiz-nav-note { font-size: 11.5px; color: var(--erp-muted); }

.erp-wiz-done { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; margin-top: 14px; }
.erp-wiz-done-card {
    display: block; padding: 14px 15px; border-radius: 11px;
    border: 1px solid var(--erp-border); background: var(--erp-surface);
    text-decoration: none; color: inherit;
}
.erp-wiz-done-card:hover { border-color: var(--erp-primary); text-decoration: none; }
.erp-wiz-done-card i { color: var(--erp-success); font-size: 15px; }
.erp-wiz-done-card strong { display: block; margin-top: 7px; font-size: 13px; color: var(--erp-ink); }
.erp-wiz-done-card span { font-size: 11.5px; color: var(--erp-muted); }

@media (max-width: 1199.98px) {
    .erp-wiz { position: static; }
    .erp-wiz-steps { display: flex; gap: 4px; overflow-x: auto; }
    .erp-wiz-steps > li { flex: 1 0 140px; }
    .erp-wiz-steps > li:not(:last-child)::after { left: auto; right: -2px; top: 19px; bottom: auto; width: auto; height: 2px; left: 38px; }
    .erp-wiz-steps a { flex-direction: column; gap: 6px; }
}

/* The setup pane while a step is being fetched.

   Dimmed and inert rather than blanked: keeping the previous step on screen
   makes a step change read as a transition instead of a page that went away
   and came back. */
#setupPane.is-loading {
    opacity: .45;
    pointer-events: none;
    transition: opacity .12s ease-in;
}

/* ==========================================================================
   Equipment & Fleet
   ==========================================================================
   Three small patterns the module leans on. Kept here with the rest of the
   design system rather than in a module stylesheet, so a theme change reaches
   them like everything else. */

/* A headline number with its label and a line of context underneath. Used
   where a figure needs explaining -- "not recovered 16,800" means nothing
   without "carried by overheads, not by a job". */
.erp-metric {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 12px 14px;
    border: 1px solid var(--erp-border);
    border-radius: 10px;
    background: var(--erp-surface-2, transparent);
    height: 100%;
}
.erp-metric > small {
    font-size: 11.5px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--erp-text-muted);
}
.erp-metric > strong {
    font-size: 22px;
    line-height: 1.15;
    font-weight: 650;
}

/* Label on the left, figure on the right. A definition list without the
   markup argument. */
.erp-keyval {
    list-style: none;
    margin: 0;
    padding: 0;
}
.erp-keyval > li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 7px 0;
    border-bottom: 1px dashed var(--erp-border);
    font-size: 13px;
}
.erp-keyval > li:last-child { border-bottom: 0; }
.erp-keyval > li > span { color: var(--erp-text-muted); }
.erp-keyval > li > strong { font-weight: 650; }

/* A short list of things that happened, each with a coloured icon. The icon
   colour carries the urgency, so the list can be scanned without reading it. */
.erp-feed {
    list-style: none;
    margin: 0;
    padding: 0;
}
.erp-feed > li {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 10px 14px;
    border-bottom: 1px solid var(--erp-border);
    font-size: 13px;
}
.erp-feed > li:last-child { border-bottom: 0; }
.erp-feed-icon {
    flex: 0 0 26px;
    width: 26px;
    height: 26px;
    display: grid;
    place-items: center;
    border-radius: 7px;
    font-size: 11px;
    background: #e2e8f0;
    color: #475569;
}
.erp-feed-icon.is-danger  { background: #fee2e2; color: #b91c1c; }
.erp-feed-icon.is-warning { background: #fef3c7; color: #b45309; }
.erp-feed-icon.is-success { background: #dcfce7; color: #15803d; }
.erp-feed-icon.is-info    { background: #dbeafe; color: #1d4ed8; }

/* Tone on a stat tile, so an alert row reads at a glance: red is somebody
   stopped, amber is somebody about to be.

   These set the deep pair rather than a background, which means they inherit the
   whole treatment -- highlight, ring, hover lift, tinted shadow -- instead of
   each one restating it. They also sit outside the hue rotation above, because a
   red tile has to be red wherever it lands in the row. */
.erp-stat.is-danger  { --erp-tone-deep: #b91c1c; --erp-tone-deeper: #7f1d1d; }
.erp-stat.is-warning { --erp-tone-deep: #b45309; --erp-tone-deeper: #78350f; }
.erp-stat.is-info    { --erp-tone-deep: #1d4ed8; --erp-tone-deeper: #1e3a8a; }
.erp-stat.is-success { --erp-tone-deep: #047857; --erp-tone-deeper: #065f46; }

/* Poor utilisation. The existing bar has no "this is bad" tone, and an idle
   machine is exactly what this report exists to make obvious. */
.erp-progress-bar.is-poor { background: #dc2626; }

/* A field the form filled in from a category, flashed briefly so nothing
   appears in a box the person did not type in without them noticing. */
.is-inherited {
    animation: erp-inherit-flash 1.8s ease-out;
}
@keyframes erp-inherit-flash {
    0%, 40% { background-color: #dbeafe; }
    100%    { background-color: transparent; }
}

/* ==========================================================================
   Detail workspace — a tabbed record that fills the viewport
   ==========================================================================
   Height comes from --erp-fill-h, which erp-app.js measures from where the
   pane actually starts, so the page itself never scrolls and the tab strip
   stays reachable. Only the body below the strip scrolls.

   Below lg the stylesheet gives up and lets the page scroll normally: a fixed
   workspace on a phone is a viewport you cannot escape. */
.erp-detail {
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-height: 0;
}
.erp-detail > .erp-tabs { flex: 0 0 auto; }

.erp-detail-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow: visible;
}

@media (min-width: 992px) {
    .erp-detail {
        height: var(--erp-fill-h, calc(100dvh - var(--erp-topbar-h, 96px) - 260px));
        min-height: 420px;
    }
    .erp-detail-body {
        overflow-y: auto;
        overscroll-behavior: contain;
        /* Room for the scrollbar so content does not sit under it, and a little
           breathing space at the foot of a long panel. */
        padding-right: 4px;
        padding-bottom: 4px;
    }
    /* A card that is the whole panel should not also carry a top margin: the
       gap above it belongs to the tab strip. */
    .erp-detail-panel > .erp-card:first-child { margin-top: 0 !important; }
}

/* A count on a tab that means something is WRONG.

   Deliberately not .erp-tab-count, which already exists: that one inherits the
   tab's own accent and so reads as "here are N items". This one has to say
   "three of these have lapsed" in a colour that means it, which an inherited
   accent cannot do.

   Only ever used for something wrong or outstanding. A badge that merely
   counts rows reads as work to do, and teaches people to ignore the ones
   that are. */
.erp-tab-badge {
    display: inline-grid;
    place-items: center;
    min-width: 18px;
    height: 18px;
    margin-left: 6px;
    padding: 0 5px;
    border-radius: 9px;
    font-size: 10.5px;
    font-weight: 700;
    line-height: 1;
    background: #e2e8f0;
    color: #475569;
}
.erp-tab-badge.is-danger  { background: #fee2e2; color: #b91c1c; }
.erp-tab-badge.is-warning { background: #fef3c7; color: #b45309; }
.erp-tab-badge.is-info    { background: #dbeafe; color: #1d4ed8; }

/* The tab strip is a row of buttons here rather than links, so it needs the
   same reset every other button in the system gets. */
.erp-tabs button.erp-tab {
    border: 0;
    font: inherit;
    cursor: pointer;
}

/* ==========================================================================
   New Bill dialog
   ========================================================================== */

.erp-formgroup-head {
    border-bottom: 1px solid var(--erp-line);
    color: var(--erp-muted);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .07em;
    margin: 18px 0 12px;
    padding-bottom: 5px;
    text-transform: uppercase;
}

.erp-formgroup-head:first-child { margin-top: 0; }

/* Start-from, as cards rather than a dropdown: the four options differ in
   what they DO, and a one-line description per option is what makes that
   visible before the choice instead of after it. */
.erp-startfrom {
    display: grid;
    gap: 8px;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}

.erp-startfrom-opt {
    align-items: flex-start;
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    cursor: pointer;
    display: flex;
    gap: 8px;
    margin: 0;
    padding: 10px;
    transition: border-color .12s ease, background .12s ease;
}

.erp-startfrom-opt:hover { border-color: var(--erp-primary); }

.erp-startfrom-opt:has(input:checked) {
    background: var(--erp-surface-2);
    border-color: var(--erp-primary);
}

.erp-startfrom-opt.is-disabled { cursor: not-allowed; opacity: .55; }
.erp-startfrom-opt.is-disabled:hover { border-color: var(--erp-line); }

.erp-startfrom-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.erp-startfrom-body strong { font-size: 12.5px; }
.erp-startfrom-body span { color: var(--erp-muted); font-size: 11px; line-height: 1.35; }

/* The two pickers the dialog loads over AJAX. Capped and scrolled so a company
   with two hundred bills cannot push the Create button off the dialog. */
.erp-boq-sources,
.erp-boq-docs {
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    max-height: 190px;
    overflow-y: auto;
    padding: 6px;
}

.erp-boq-source,
.erp-boq-doc {
    align-items: flex-start;
    border-radius: 6px;
    cursor: pointer;
    display: flex;
    gap: 9px;
    margin: 0;
    padding: 6px 8px;
}

.erp-boq-source:hover,
.erp-boq-doc:hover { background: var(--erp-surface-2); }

.erp-boq-source-main,
.erp-boq-doc-main { display: flex; flex-direction: column; flex: 1; font-size: 12.5px; min-width: 0; }

.erp-boq-source-meta,
.erp-boq-doc-meta { color: var(--erp-muted); font-size: 10.5px; }

.erp-boq-doc-main code { font-size: 10.5px; }

/* ==========================================================================
   BOQ summary
   A grand total on its own is a number to be taken on trust. The waterfall is
   the same number arranged so it can be audited at a glance.
   ========================================================================== */

.erp-waterfall {
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    overflow: hidden;
}

.erp-wf-row {
    align-items: baseline;
    display: flex;
    font-size: 12.5px;
    gap: 12px;
    justify-content: space-between;
    padding: 8px 12px;
}

.erp-wf-row + .erp-wf-row { border-top: 1px solid var(--erp-line); }

.erp-wf-row strong { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Running totals are tinted so the eye can find the three rungs of the ladder
   without reading every line. */
.erp-wf-row.is-sub { background: var(--erp-surface-2); font-weight: 600; }

.erp-wf-row.is-grand {
    background: var(--erp-surface-2);
    border-top: 2px solid var(--erp-ink);
    font-weight: 700;
}

.erp-wf-row.is-grand strong { font-size: 17px; }
.erp-wf-row.is-grand small { color: var(--erp-muted); font-size: 11px; font-weight: 500; }

.erp-wf-note {
    color: var(--erp-muted);
    display: block;
    font-size: 10.5px;
    font-weight: 400;
}

/* Budget comparison. The bar is capped at 100% so it cannot overflow its
   track, but the number beside it never is - a bill at 300% of budget has to
   read as 300%, not as a full bar. */
.erp-budgetbar {
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    margin-top: 12px;
    padding: 10px 12px;
}

.erp-budgetbar.is-over { border-color: var(--erp-danger); }

.erp-budgetbar-head,
.erp-budgetbar-foot {
    display: flex;
    gap: 10px;
    justify-content: space-between;
}

.erp-budgetbar-head { font-size: 12px; margin-bottom: 7px; }
.erp-budgetbar-head strong { font-variant-numeric: tabular-nums; }

.erp-budgetbar-foot {
    align-items: baseline;
    color: var(--erp-muted);
    font-size: 10.5px;
    margin-top: 6px;
}

.erp-budgetbar-track {
    background: var(--erp-line);
    border-radius: 999px;
    height: 8px;
    overflow: hidden;
}

.erp-budgetbar-fill {
    background: var(--erp-primary);
    height: 100%;
    transition: width .3s ease;
}

.erp-budgetbar.is-over .erp-budgetbar-fill { background: var(--erp-danger); }

@media (prefers-reduced-motion: reduce) {
    .erp-budgetbar-fill { transition: none; }
}

/* ==========================================================================
   BOQ grid — provisional and PC rows
   A bill read at a glance must not let an allowance look like a measured
   price. The tint is the cheapest way to say which is which, and the left
   edge carries it so it survives a horizontally scrolled grid.
   ========================================================================== */

.erp-boq-rows tr.is-provisional > td { background: rgba(245, 158, 11, .07); }
.erp-boq-rows tr.is-provisional > td:first-child { box-shadow: inset 3px 0 0 #f59e0b; }

.erp-boq-rows tr.is-pcsum > td { background: rgba(3, 169, 243, .07); }
.erp-boq-rows tr.is-pcsum > td:first-child { box-shadow: inset 3px 0 0 #0281bb; }

/* The Type select should read as a label until somebody goes near it — three
   dozen bordered dropdowns down a grid is noise, and most rows are measured. */
.erp-boq-rows [data-pricing] {
    background-image: none;
    border-color: transparent;
}

.erp-boq-rows tr:hover [data-pricing],
.erp-boq-rows [data-pricing]:focus {
    background-color: var(--erp-surface);
    border-color: var(--erp-line);
}

.erp-boq-rows tr.is-provisional [data-pricing],
.erp-boq-rows tr.is-pcsum [data-pricing] { font-weight: 600; }

/* --------------------------------------------------------------------------
   Bill of quantities editor

   Three regions over one document: a section tree, a spreadsheet, and a rate
   build-up drawer. The whole thing is sized to the viewport so the page never
   scrolls -- only the grid does. On a four-hundred-line bill the column
   headings and the totals have to stay put, or a figure halfway down belongs
   to a column nobody can see.
   -------------------------------------------------------------------------- */

.erp-boq-editor {
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-height: 0;
}

@media (min-width: 992px) {
    .erp-boq-editor {
        height: var(--erp-fill-h, calc(100dvh - var(--erp-topbar-h, 96px) - 260px));
        min-height: 460px;
    }
}

/* The bar: status, save state, grand total. */
.erp-boq-bar {
    align-items: center;
    background: var(--erp-surface, #fff);
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    box-shadow: var(--erp-shadow);
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    justify-content: space-between;
    padding: 9px 14px;
}

.erp-boq-bar-left { align-items: center; display: flex; flex-wrap: wrap; gap: 10px; }
.erp-boq-bar-sep { background: var(--erp-line); height: 18px; width: 1px; }

/* The save state. Three states and no "unsaved": nothing is held back, so
   claiming there were unsaved changes would be a lie the page tells. */
.erp-boq-saved {
    align-items: center;
    color: #047857;
    display: inline-flex;
    font-size: 12px;
    font-weight: 600;
    gap: 6px;
}

.erp-boq-saved.is-saving { color: var(--erp-muted); }
.erp-boq-saved.is-saving i { animation: erp-boq-spin 1s linear infinite; }
.erp-boq-saved.is-failed { color: #b91c1c; }

@keyframes erp-boq-spin { to { transform: rotate(360deg); } }

.erp-boq-bar-total { text-align: right; }
.erp-boq-bar-total small { color: var(--erp-muted); display: block; font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em; }
.erp-boq-bar-total strong { font-size: 19px; font-weight: 700; letter-spacing: -.3px; }
.erp-boq-bar-total em { color: var(--erp-muted); font-size: 12px; font-style: normal; font-weight: 600; }

/* The two panes. */
.erp-boq-panes {
    display: grid;
    gap: 10px;
    grid-template-columns: 1fr;
    min-height: 0;
}

@media (min-width: 992px) {
    .erp-boq-panes { grid-template-columns: 250px 1fr; }
}

.erp-boq-tree,
.erp-boq-gridpane {
    background: var(--erp-surface, #fff);
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    box-shadow: var(--erp-shadow);
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
}

.erp-boq-tree-head {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    justify-content: space-between;
    padding: 8px 10px;
}

.erp-boq-tree-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 6px; }

.erp-boq-treeitem {
    align-items: center;
    background: none;
    border: 0;
    border-radius: 7px;
    display: flex;
    gap: 8px;
    padding: 7px 8px;
    text-align: left;
    width: 100%;
}

.erp-boq-treeitem:hover { background: var(--erp-tone-soft, rgba(10, 162, 224, .1)); }
.erp-boq-treecode { color: var(--erp-muted); flex: 0 0 auto; font-size: 11px; font-weight: 700; }
.erp-boq-treename { flex: 1 1 auto; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.erp-boq-treesum { color: var(--erp-ink); flex: 0 0 auto; font-size: 11px; font-variant-numeric: tabular-nums; font-weight: 700; }

.erp-boq-tools {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    padding: 8px 10px;
}

.erp-boq-tools-sep { background: var(--erp-line); height: 20px; width: 1px; }
.erp-boq-hint { color: var(--erp-muted); font-size: 11px; }
.erp-boq-hint kbd {
    background: var(--erp-surface-2, #f7f9fc);
    border: 1px solid var(--erp-line);
    border-radius: 4px;
    font-size: 10px;
    padding: 1px 4px;
}

.erp-boq-gridwrap { flex: 1 1 auto; min-height: 0; overflow: auto; }

/* The header has to stay while four hundred rows move under it. */
.erp-boq-grid thead th {
    position: sticky;
    top: 0;
    z-index: 2;
}

/* A cell being typed into. Contenteditable with no affordance looks like
   static text, and people do not try to type in static text. */
.erp-boq-grid [data-cell][contenteditable] {
    border: 1px solid transparent;
    border-radius: 4px;
    display: block;
    min-height: 20px;
    padding: 2px 5px;
}

.erp-boq-grid [data-cell][contenteditable]:hover { border-color: var(--erp-line); }

.erp-boq-grid [data-cell][contenteditable]:focus {
    background: #fff;
    border-color: var(--erp-info, #0aa2e0);
    box-shadow: 0 0 0 2px rgba(10, 162, 224, .18);
    outline: none;
}

.erp-boq-grid [data-numeric] { font-variant-numeric: tabular-nums; text-align: right; }
.erp-boq-amount { font-variant-numeric: tabular-nums; font-weight: 600; }

/* A section is a banner, not a row with six empty cells. A bill of forty
   sections drawn the other way reads as a page of blanks. */
.erp-boq-row.is-section > td {
    background: var(--erp-tone-soft, #eef4f8) !important;
    font-weight: 700;
}

.erp-boq-sectionname { display: inline-block; min-width: 160px; padding: 2px 5px; }
.erp-boq-sectiontotal { color: var(--erp-muted); float: right; font-size: 11.5px; font-variant-numeric: tabular-nums; }

/* Provisional and PC money is not firm money, and a client reading the total
   has to be able to see which part of it is an allowance. */
.erp-boq-row.is-provisional > td { background: #fffaf2 !important; }
.erp-boq-row.is-pc_sum > td { background: #f7f5ff !important; }

.erp-boq-row.is-flash > td {
    animation: erp-boq-flash 900ms ease-out;
}

@keyframes erp-boq-flash {
    0% { background: #fde68a !important; }
    100% { background: transparent; }
}

.erp-boq-actions { text-align: right; white-space: nowrap; }

.erp-boq-act {
    background: none;
    border: 0;
    border-radius: 5px;
    color: var(--erp-muted);
    font-size: 11px;
    padding: 3px 5px;
}

.erp-boq-act:hover { background: var(--erp-surface-2, #f1f5f9); color: var(--erp-ink); }
.erp-boq-act.is-danger:hover { background: #fee2e2; color: #b91c1c; }

.erp-boq-ratebtn {
    background: none;
    border: 0;
    color: var(--erp-info, #0aa2e0);
    font-size: 11px;
    margin-left: 4px;
    padding: 0 2px;
}

/* The waterfall, always on screen. The ORDER is what makes the grand total
   right, so it is shown rather than reduced to one number to be trusted. */
.erp-boq-foot {
    border-top: 1px solid var(--erp-line);
    display: flex;
    flex-wrap: wrap;
    gap: 18px;
    justify-content: flex-end;
    padding: 9px 14px;
}

.erp-boq-foot span { text-align: right; }
.erp-boq-foot small { color: var(--erp-muted); display: block; font-size: 10.5px; }
.erp-boq-foot strong { font-size: 13px; font-variant-numeric: tabular-nums; }
.erp-boq-foot .is-sep { border-left: 1px solid var(--erp-line); padding-left: 18px; }
.erp-boq-foot .is-grand strong { color: var(--erp-ink); font-size: 16px; }

/* The drawer. Beside the grid rather than over it: a build-up is consulted as
   much as it is edited, and the row it explains should stay readable. */
.erp-boq-drawer {
    background: var(--erp-surface, #fff);
    border-left: 1px solid var(--erp-line);
    bottom: 0;
    box-shadow: -8px 0 24px -12px rgba(15, 23, 42, .4);
    display: flex;
    flex-direction: column;
    max-width: 96vw;
    position: fixed;
    right: 0;
    top: 0;
    /* WIDENED FROM 640px, because 640 was quietly breaking the table inside it.
       The build-up's eight columns declare 876px and the body padding takes 28,
       so the table was handed 612px and the browser compressed every column to
       fit. Stated pixel widths and plain text cells resist that compression;
       form controls do not, so the whole 264px shortfall came off the selects
       and the number inputs - the only cells anybody types into.
       940 clears the column total with slack left for the body's scrollbar,
       which appears on exactly the long build-ups that need the room most, and
       max-width keeps the panel inside the viewport on a laptop. It is still a
       drawer rather than a full screen: on a 1366 display this leaves over
       400px of the bill visible beside it, and the line this build-up explains
       staying in view is the whole reason it is a drawer and not a page. */
    width: 940px;
    z-index: 1045;
}

.erp-boq-drawer[hidden] { display: none; }

.erp-boq-drawer-head {
    align-items: flex-start;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    justify-content: space-between;
    padding: 12px 14px;
}

.erp-boq-drawer-sub { color: var(--erp-muted); display: block; font-size: 11.5px; }
.erp-boq-drawer-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 12px 14px; }

.erp-boq-drawer-foot {
    align-items: center;
    border-top: 1px solid var(--erp-line);
    display: flex;
    gap: 14px;
    justify-content: space-between;
    padding: 12px 14px;
}

.erp-boq-drawer-total small { color: var(--erp-muted); display: block; font-size: 10.5px; }
.erp-boq-drawer-total strong { font-size: 18px; font-variant-numeric: tabular-nums; }

.erp-boq-paste-preview {
    background: var(--erp-surface-2, #f7f9fc);
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    font-size: 12px;
    padding: 10px 12px;
}

:root[data-theme="dark"] .erp-boq-grid [data-cell][contenteditable]:focus { background: var(--erp-surface-2); }
:root[data-theme="dark"] .erp-boq-row.is-provisional > td { background: rgba(217, 119, 6, .12) !important; }
:root[data-theme="dark"] .erp-boq-row.is-pc_sum > td { background: rgba(108, 99, 255, .12) !important; }

/* Dragging a row among its siblings. The drop target is marked on its top edge
   rather than filled, so the row underneath stays readable while deciding. */
.erp-boq-row.is-dragging > td { opacity: .45; }
.erp-boq-row.is-dropinto > td { box-shadow: inset 0 2px 0 var(--erp-info, #0aa2e0); }
.erp-boq-grid [data-row] { cursor: default; }

/* What an import noticed but did not act on. On screen rather than in a toast:
   eleven unmatched units are a list somebody works through, not a message that
   scrolls away. */
.erp-boq-notes {
    background: #fffbeb;
    border: 1px solid #fcd34d;
    border-radius: 8px;
    font-size: 12px;
    margin: 8px 10px 0;
    padding: 10px 12px;
}

:root[data-theme="dark"] .erp-boq-notes {
    background: rgba(217, 119, 6, .14);
    border-color: rgba(217, 119, 6, .4);
}

/* The drawer's backdrop. Light rather than the usual heavy modal scrim: the row
   the build-up explains is behind it and should stay readable, but the grid has
   to stop looking clickable or people keep trying to type into it. */
.erp-boq-backdrop {
    background: rgba(15, 23, 42, .28);
    bottom: 0;
    left: 0;
    position: fixed;
    right: 0;
    top: 0;
    z-index: 1044;
}

.erp-boq-backdrop[hidden] { display: none; }

.erp-boq-drawer:focus { outline: none; }

/* --------------------------------------------------------------------------
   A bill as a printed document

   This is a contract attachment before it is a screen: printed, initialled and
   bound into a tender. What the styles do is get the application out of the
   way at print and leave a page that reads as paper.
   -------------------------------------------------------------------------- */

.erp-print-head {
    align-items: flex-start;
    border-bottom: 2px solid var(--erp-ink);
    display: flex;
    justify-content: space-between;
    margin-bottom: 14px;
    padding-bottom: 12px;
}

.erp-print-head h2 { font-size: 20px; margin: 0 0 2px; }

.erp-print-terms {
    display: grid;
    gap: 8px 20px;
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    margin: 0 0 14px;
}

.erp-print-terms dt {
    color: var(--erp-muted);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .05em;
    text-transform: uppercase;
}

.erp-print-terms dd { font-size: 12.5px; font-weight: 600; margin: 0; }
.erp-print-docs { font-size: 12px; margin-bottom: 14px; }

.erp-print-table { font-size: 12px; }
.erp-print-table td { vertical-align: top; }

.erp-print-summary { margin-left: auto; max-width: 420px; }
.erp-print-summary td { font-size: 12.5px; padding: 5px 10px; }
.erp-print-sub td { border-top: 1px solid var(--erp-line); }
.erp-print-grand td { border-top: 2px solid var(--erp-ink); font-size: 14px; }

/* Signature blocks. A bill bound into a tender is initialled, and leaving
   nowhere to do it means somebody writes across the figures. */
.erp-print-sign {
    display: grid;
    gap: 30px;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    margin-top: 46px;
}

.erp-print-sign span {
    border-bottom: 1px solid var(--erp-ink);
    display: block;
    height: 34px;
}

.erp-print-sign small { color: var(--erp-muted); font-size: 10.5px; }

@media print {
    .erp-noprint, .erp-topbar-stack, .erp-submenubar, .erp-tabs { display: none !important; }
    .erp-printdoc { border: 0; box-shadow: none; }
    /* A bill runs to pages, and a row split across a page break is a row
       somebody reads twice or not at all. */
    .erp-print-table tr, .erp-print-summary tr { break-inside: avoid; }
    .erp-print-table thead { display: table-header-group; }
}

/* ==========================================================================
   Stats strip — separated variant
   The base .erp-sale-strip is deliberately ONE joined band: the container
   supplies the surface, tiles butt together, and a translucent hairline
   divides them. This variant breaks it into free-standing cards instead.

   A modifier rather than a change to the base, because fifty-one pages use
   that strip and restyling all of them is not what "make the cards separated
   on this page" asks for. Promoting it is one line: move these rules onto
   .erp-sale-strip itself.
   ========================================================================== */

.erp-sale-strip.is-separated {
    /* The container stops being a surface. Left as a band it would show as
       dark slate bars in the new gaps, which reads as the gaps being filled
       rather than empty. */
    background: none;
    border: 0;
    box-shadow: none;
    gap: var(--erp-gutter, 12px);
    /* Dropped so each tile's own corners are not clipped by the frame. */
    overflow: visible;
}

.erp-sale-strip.is-separated > .erp-sale-stat {
    /* Each tile now carries what the strip used to: its own corners, its own
       shadow, and no dividing rule. */
    border-left: 0;
    border-radius: var(--erp-radius);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .13),
        0 4px 16px -10px rgba(15, 23, 42, .5);
}

/* The base resets the first tile's divider; with no dividers at all that rule
   has nothing to do, but it is harmless and left alone so the two variants
   share one selector list. */

/* ==========================================================================
   Unit tile — the compact floor plan
   Four facts and two actions. Everything else moved into the edit dialog; the
   tile exists to be scanned, not read.

   The status drives one custom property and everything else follows from it,
   so adding a status means adding one line rather than editing five rules.
   ========================================================================== */

.erp-unit-tile {
    --unit: #5b6675;

    background: color-mix(in srgb, var(--unit) 6%, var(--erp-surface));
    border: 1px solid color-mix(in srgb, var(--unit) 28%, transparent);
    border-left: 3px solid var(--unit);
    border-radius: 8px;
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 7px 9px 6px;
    transition: transform .12s ease, box-shadow .12s ease;
}

.erp-unit-tile:hover {
    box-shadow: 0 4px 14px -8px rgba(15, 23, 42, .4);
    transform: translateY(-1px);
}

/* The same five hues the pills use, so a tile and its status pill elsewhere on
   the page cannot disagree about what "booked" looks like. */
.erp-unit-tile.is-available { --unit: #00926e; }
.erp-unit-tile.is-booked    { --unit: #0281bb; }
.erp-unit-tile.is-sold      { --unit: #574fd6; }
.erp-unit-tile.is-hold      { --unit: #b96f00; }
.erp-unit-tile.is-overdue   { --unit: #c2185b; }
.erp-unit-tile.is-neutral   { --unit: #5b6675; }

/* Gone already. Settled back so the eye skips it — "what is still available"
   is the question this page is opened to answer. */
.erp-unit-tile.is-settled { opacity: .72; }
.erp-unit-tile.is-settled:hover { opacity: 1; }

.erp-unit-tile-no {
    align-items: center;
    display: flex;
    font-size: 13px;
    font-weight: 650;
    gap: 4px;
    line-height: 1.2;
}

.erp-unit-tile-corner { color: var(--unit); font-size: 8px; }

.erp-unit-tile-spec {
    color: var(--erp-muted);
    font-size: 10.5px;
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.erp-unit-tile-price {
    font-size: 12px;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

.erp-unit-tile-price .is-unpriced {
    color: var(--erp-muted);
    font-size: 10.5px;
    font-weight: 500;
}

.erp-unit-tile-state {
    align-items: center;
    color: var(--unit);
    display: inline-flex;
    font-size: 10px;
    font-weight: 600;
    gap: 3px;
    margin-top: 1px;
}

/* Bottom corner, always visible. A touch screen has no hover, so actions
   revealed by a mouse are actions a tablet user simply does not have. */
.erp-unit-tile-actions {
    display: flex;
    gap: 0;
    justify-content: flex-end;
    margin: 4px -5px -3px 0;
    padding-top: 4px;
}

.erp-unit-tile-actions .btn {
    font-size: 10.5px;
    line-height: 1;
    padding: 3px 5px;
}

@media (hover: none) {
    .erp-unit-tile:hover { transform: none; }
}

@media (max-width: 575.98px) {
    /* Two across on a phone rather than one: the tile is small enough that a
       single column wastes the width and doubles the scrolling. */
    .erp-unit-grid { grid-template-columns: repeat(2, 1fr); padding: 10px; }
}

@media (prefers-reduced-motion: reduce) {
    .erp-unit-tile { transition: none; }
    .erp-unit-tile:hover { transform: none; }
}

/* ==========================================================================
   Contractor ledger — tabbed, and the page itself does not scroll
   Three stacked cards became three panels. The long thing (a statement of a
   few hundred entries) now scrolls INSIDE its panel with its head pinned, so
   the stats and the tab bar stay on screen instead of being pushed off the
   top by the very table somebody is reading.
   ========================================================================== */

.erp-conshow {
    background: var(--erp-surface);
    border: 1px solid var(--erp-line);
    border-radius: var(--erp-radius);
    box-shadow: var(--erp-shadow);
    display: flex;
    flex-direction: column;
    /* The remaining viewport, measured from the topbar. Everything above this
       block - pagehead and the stats strip - is a known height, so the panel
       can take what is left rather than growing the document. */
    height: calc(100dvh - var(--erp-topbar-h, 96px) - 196px);
    min-height: 320px;
    overflow: hidden;
}

.erp-conshow .erp-tabs { flex-shrink: 0; }

.erp-conshow-panels {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    overflow: hidden;
}

/* A panel is a column: a fixed bar, then the part that scrolls. */
.erp-conshow-panels > .erp-tab-panel.is-active {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    padding: 0;
}

.erp-conshow-bar {
    align-items: center;
    border-bottom: 1px solid var(--erp-line);
    display: flex;
    flex-shrink: 0;
    flex-wrap: wrap;
    gap: 8px;
    margin: 0;
    padding: 10px var(--erp-gutter);
}

/* The only thing that scrolls on this page. */
.erp-conshow-scroll {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: 0 var(--erp-gutter) var(--erp-gutter);
}

/* Pinned, or a long statement loses its column headings on the first scroll
   and the debit and credit columns become indistinguishable. */
.erp-conshow-scroll .erp-table thead th {
    background-color: var(--erp-table-head-solid, var(--erp-surface-2));
    position: sticky;
    top: 0;
    z-index: 2;
}

/* The closing balance stays in view at the foot for the same reason the head
   stays at the top: it is the figure the page is opened for. */
.erp-conshow-scroll .erp-table tfoot td {
    background-color: var(--erp-surface-2);
    bottom: 0;
    position: sticky;
    z-index: 2;
}

/* Details reads as a two-column sheet now it has the whole width, rather than
   the narrow column it was squeezed into beside the ledger. */
.erp-conshow-deflist {
    display: grid;
    gap: 0 28px;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    padding-top: var(--erp-gutter);
}

@media (max-width: 991.98px) {
    /* Short viewports and phones: let the document scroll again rather than
       trapping a table in 200px. A fixed panel is an improvement only while
       there is height to fix it to. */
    .erp-conshow { height: auto; overflow: visible; }
    .erp-conshow-panels, .erp-conshow-scroll { overflow: visible; }
}

/* -------------------------------------------------------------------------
   The measurement sheet (takeoff) drawer
   -------------------------------------------------------------------------
   The sheet behind a bill line's quantity: nos x L x B x H, with deduction
   rows. It shares .erp-boq-drawer with the rate build-up, so only the table
   and the few state markers are stated here.

   The sizing follows the build-up table deliberately rather than being chosen
   fresh. Both are drawers full of number inputs somebody types measurements
   into at speed, and two different input heights 40px apart in the same panel
   read as a rendering fault. See the note above .erp-boq-build-table for why
   these are 38px and 15px and not smaller.
   ------------------------------------------------------------------------- */

.erp-boq-takeoff-table { background: var(--erp-surface, #fff); border-radius: 8px; font-size: 12px; }

.erp-boq-takeoff-table td,
.erp-boq-takeoff-table th { padding: 6px 8px; vertical-align: middle; }

.erp-boq-takeoff-table thead th {
    font-size: 10.5px;
    letter-spacing: .04em;
    text-transform: uppercase;
}

/* A column this unit is not measured with. Dimmed rather than hidden: the
   legitimate exceptions (a sloping area measured with a factor) have to stay
   reachable, and the server allows them. */
.erp-boq-takeoff-table thead th.is-muted { color: var(--erp-muted); opacity: .55; }

.erp-boq-takeoff-table td input.form-control {
    font-size: 13.5px;
    min-height: 38px;
    padding: 7px 10px;
}

.erp-boq-takeoff-table td input[type="number"] {
    font-size: 15px;
    font-weight: 600;
    /* Equal-width digits, so a column of dimensions lines up and a misplaced
       decimal point is visible without reading the number — which on a
       measurement sheet is the difference between 1.25 and 12.5. */
    font-variant-numeric: tabular-nums;
}

.erp-boq-takeoff-table td input.is-muted {
    background: var(--erp-surface-2, #f7f9fc);
    color: var(--erp-muted);
}

.erp-boq-takeoff-table td input.is-muted:focus {
    background: var(--erp-surface, #fff);
    color: var(--erp-ink);
}

/* The row number, so a warning that names "row 3" can be found. */
.erp-boq-takeoff-no { color: var(--erp-muted); font-size: 11px; }

.erp-boq-takeoff-qty {
    font-size: 14px;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

/* A deduction row, marked so a sheet can be scanned for them. The tint is on
   the row and the sign is on the figure: either alone is missable, and a
   deduction read as an addition is an error of twice its own size. */
.erp-boq-takeoff-table tr.is-deduction { background: color-mix(in srgb, var(--erp-danger, #dc2626) 5%, transparent); }
.erp-boq-takeoff-table tr.is-deduction .erp-boq-takeoff-qty { color: var(--erp-danger, #dc2626); }

/* What this unit is measured with, stated before anybody types a figure. */
.erp-boq-takeoff-guide {
    background: var(--erp-surface-2, #f7f9fc);
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    color: var(--erp-ink);
    font-size: 12px;
    margin-bottom: 10px;
    padding: 8px 10px;
}

.erp-boq-takeoff-guide i { color: var(--erp-info, #0aa2e0); margin-right: 4px; }

/* A sheet whose deductions exceed its measurements. Shown as it is typed; the
   server refuses the save with a message naming the figure. */
[data-takeoff-total].is-bad { color: var(--erp-danger, #dc2626); }

/* -------------------------------------------------------------------------
   A measured quantity, in the grid
   -------------------------------------------------------------------------
   Not contenteditable, and it has to LOOK that way. A cell that refuses to be
   typed in without saying why reads as broken, so the figure wears the same
   quiet tint the computed amount column does and the ruler button beside it
   turns solid to show where the number came from.
   ------------------------------------------------------------------------- */

.erp-boq-qty.is-measured {
    border-bottom: 1px dotted var(--erp-info, #0aa2e0);
    cursor: default;
    font-variant-numeric: tabular-nums;
}

.erp-boq-ratebtn.is-on { color: var(--erp-success, #16a34a); }

/* -------------------------------------------------------------------------
   Cost per square foot
   -------------------------------------------------------------------------
   The figure a development is decided on in this market. It sits directly
   under the grand total it derives from, tinted so the eye finds it without
   being as heavy as the total itself — it is a restatement of that number,
   not a second one.
   ------------------------------------------------------------------------- */

.erp-wf-row.is-rate {
    background: color-mix(in srgb, var(--erp-primary) 6%, transparent);
    font-weight: 600;
}

.erp-wf-row.is-rate strong { color: var(--erp-primary-dark, var(--erp-primary)); font-size: 15px; }
.erp-wf-row.is-rate small { color: var(--erp-muted); font-size: 11px; font-weight: 500; }

/* The areas already on record, offered in the create dialog. Each carries its
   own source in a title, because a six-figure area retyped by hand is where a
   digit goes missing — and a cost per sft that is ten times out is the most
   believable wrong number in the module. */
.erp-boq-areahints {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

.erp-boq-areahint {
    background: var(--erp-surface-2, #f7f9fc);
    border: 1px solid var(--erp-line);
    border-radius: 6px;
    display: flex;
    flex-direction: column;
    font-size: 11px;
    line-height: 1.25;
    padding: 5px 8px;
    text-align: left;
}

.erp-boq-areahint strong { font-size: 12.5px; font-variant-numeric: tabular-nums; }
.erp-boq-areahint span { color: var(--erp-muted); font-size: 10px; }
.erp-boq-areahint:hover { border-color: var(--erp-primary); }

.erp-boq-areahint.is-on {
    background: color-mix(in srgb, var(--erp-primary) 10%, transparent);
    border-color: var(--erp-primary);
}

/* -------------------------------------------------------------------------
   Committed & billed — the bill against what has happened to it
   ------------------------------------------------------------------------- */

/* Three totals in the order the money moves: measured, let, certified. */
.erp-mini-stat {
    background: var(--erp-surface-2, #f7f9fc);
    border: 1px solid var(--erp-line);
    border-radius: 8px;
    padding: 9px 11px;
}

.erp-mini-stat small {
    color: var(--erp-muted);
    display: block;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .03em;
    text-transform: uppercase;
}

.erp-mini-stat strong { font-size: 16px; font-variant-numeric: tabular-nums; }
.erp-mini-stat strong em { color: var(--erp-muted); font-size: 11px; font-style: normal; font-weight: 500; }
.erp-mini-stat .erp-help { display: block; margin-top: 2px; }

/* A line let or billed beyond what the bill measures. Tinted on the ROW as
   well as marked on the figure: a red number in a column of black numbers is
   missable when the table runs to four hundred rows, and this is the one thing
   on the table somebody has to act on. */
.erp-row-bad { background: color-mix(in srgb, var(--erp-danger, #dc2626) 6%, transparent); }

/* The count of exceptions, carried on the tab itself — an over-commitment
   nobody opens the tab to find is an over-commitment nobody finds. */
.erp-tab-count.is-bad {
    background: var(--erp-danger, #dc2626);
    color: #fff;
}

/* The bill line a work-order line was let against. Shown rather than hidden:
   the link decides whether the line is checked against the bill at all, so a
   reader has to be able to tell which lines carry one. */
.erp-wo-boqchip {
    background: color-mix(in srgb, var(--erp-primary) 12%, transparent);
    border-radius: 5px;
    color: var(--erp-primary-dark, var(--erp-primary));
    display: inline-block;
    font-size: 11px;
    font-weight: 600;
    padding: 3px 7px;
}

.erp-wo-boqchip.is-none {
    background: none;
    color: var(--erp-muted);
    font-weight: 400;
}

/* A bill line with nothing left to let. Offered but not tickable — hiding it
   would leave somebody hunting for a line that is on the bill in front of
   them. */
.erp-row-muted { opacity: .55; }

/* A quantity that came off a measurement sheet, marked in the read-only bill.
   The difference between a figure that has been checked and one somebody
   typed is worth one glyph. */
.erp-qty-measured-mark {
    color: var(--erp-info, #0aa2e0);
    font-size: 10px;
    margin-left: 3px;
}

/* -------------------------------------------------------------------------
   The building design panel on the setup step
   -------------------------------------------------------------------------
   The two-way link between a project's drawings and its parameters. It shows
   the figures side by side rather than offering a button that overwrites
   things the user cannot see — a disagreement between the drawing and the
   wizard is the one thing on this panel somebody has to decide about.
   ------------------------------------------------------------------------- */

.erp-param-design {
    background: var(--erp-surface-2, #f7f9fc);
    border: 1px solid var(--erp-line);
    border-radius: 9px;
    padding: 11px 12px;
}

.erp-param-design-head {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    justify-content: space-between;
    margin-bottom: 6px;
}

.erp-param-design-head strong { font-size: 13px; }
.erp-param-design-head i { color: var(--erp-primary); }

.erp-param-design-table { background: var(--erp-surface, #fff); font-size: 12px; }
.erp-param-design-table td,
.erp-param-design-table th { padding: 5px 8px; }

.erp-param-design-table thead th {
    font-size: 10px;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.erp-param-design-table td { font-variant-numeric: tabular-nums; }

/* A figure that disagrees with the drawing. Tinted AND named in the callout
   above the table: a tint alone is missable, and this is the row the whole
   panel exists to surface. */
.erp-param-design-table tr.is-clash {
    background: color-mix(in srgb, var(--erp-warning, #f59e0b) 10%, transparent);
}
