/* Solar ERP — small overrides on top of Bootstrap 5 defaults.
   This file backs every page (base.html); the sidebar/topnav/breadcrumb
   rules below are shared chrome, not specific to any one module. */

/* ---------------------------------------------------------------------
   Theme System (Task UI-THEME-01).

   Single theme configuration, CSS-variable-driven, no duplicate
   stylesheets: each theme is exactly one block below that redefines a
   small set of color variables — nothing else in this file, and no
   other stylesheet, changes between themes. `<html data-theme="...">`
   selects the active theme; base.html's early inline script sets that
   attribute from localStorage before first paint (same pattern already
   used for the collapsible-sidebar preference, WEB-02 Part B) so a
   returning user never sees a flash of the wrong theme. Persistence is
   deliberately client-side only (localStorage, key "erp_theme") — this
   task does not add a database column or a per-user server-side
   setting.

   Bootstrap 5.3 itself IS CSS-variable-based, but most of its
   individual components (buttons, links, form focus rings, dropdown/
   list-group "active" states, ...) hardcode their own leaf-level color
   variables at compile time rather than deriving them from the global
   `--bs-primary` at runtime — overriding `--bs-primary` alone does
   *not* re-theme a Bootstrap button. The "Theme wiring" block after the
   two color blocks below re-points exactly the leaf variables this
   app's rendered pages actually consume (confirmed against the real
   compiled bootstrap.min.css, not assumed) back to the two theme
   blocks' variables. That wiring block is written once and never
   duplicated per theme — adding a third theme in the future means
   adding one more `[data-theme="..."]` color block, nothing else.

   Classic Blue's values are copied 1:1 from Bootstrap 5.3.3's own
   compiled defaults, so the existing look is byte-for-byte unchanged
   (verified against the real bootstrap.min.css fetched from the same
   CDN URL base.html loads). China Red's values (Task SYSTEM-CONFIG-02,
   superseding UI-THEME-01's original dark-red pick) are a softer,
   modern, professional red suited to long-term enterprise use rather
   than a bright/saturated flag red or the earlier dark-red pick —
   Success/Warning/Error and all background colors are untouched by
   design (only the "primary" brand chain changes), and neither theme
   changes layout, spacing, typography, animation, icon glyphs, or
   component size — color-only, exactly as scoped. `--erp-primary-
   disabled` is derived automatically from `--erp-primary` via
   `color-mix()` (lightened toward the page background) rather than a
   third hand-picked value per theme — adding a future theme still
   only means adding one more color block, the disabled shade always
   follows automatically.
   --------------------------------------------------------------------- */

:root,
:root[data-theme="classic-blue"] {
    --erp-primary: #0d6efd;
    --erp-primary-rgb: 13, 110, 253;
    --erp-primary-hover: #0b5ed7;
    --erp-primary-hover-rgb: 11, 94, 215;
    --erp-primary-active: #0a58ca;
    --erp-primary-focus-border: #86b7fe;
}

:root[data-theme="china-red"] {
    /* China Red — softer, modern, professional red (Task SYSTEM-CONFIG-02).
       Hover is a lighter step (visible pressed-state feedback on light
       surfaces), Active a darker step, per this task's own spec. */
    --erp-primary: #C83A4A;
    --erp-primary-rgb: 200, 58, 74;
    --erp-primary-hover: #D64D5B;
    --erp-primary-hover-rgb: 214, 77, 91;
    --erp-primary-active: #B72F3E;
    --erp-primary-focus-border: #E4949D;
}

:root {
    /* Automatically derived from whichever theme block above is active
       — never a fourth hand-picked color per theme. */
    --erp-primary-disabled: color-mix(in srgb, var(--erp-primary) 45%, white);
}

/* Theme wiring — maps the two color blocks above onto the specific
   Bootstrap leaf variables and app-specific rules that actually render
   primary-brand color on this app's pages: top nav / list-page hero
   headers (`.bg-primary`), sidebar's active nav item and the quotation
   approval stepper's "current" step (already `var(--bs-primary)`-based
   in this file's own pre-existing rules below, so no separate entry
   needed here), buttons (`.btn-primary`/`.btn-outline-primary`), plain
   links, the login page's brand heading (`.text-primary`), form/
   checkbox focus rings, checked checkboxes (bulk row-select), the
   active item in a dropdown menu (e.g. the language switcher) or list
   group, and the active tab's label color. Verified against
   bootstrap.min.css that each selector below is the one actually
   consuming these variables — not a speculative/unused override. */

:root {
    --bs-primary: var(--erp-primary);
    --bs-primary-rgb: var(--erp-primary-rgb);
    --bs-link-color: var(--erp-primary);
    --bs-link-color-rgb: var(--erp-primary-rgb);
    --bs-link-hover-color: var(--erp-primary-hover);
    --bs-link-hover-color-rgb: var(--erp-primary-hover-rgb);
}

.btn-primary {
    --bs-btn-bg: var(--erp-primary);
    --bs-btn-border-color: var(--erp-primary);
    --bs-btn-hover-bg: var(--erp-primary-hover);
    --bs-btn-hover-border-color: var(--erp-primary-hover);
    --bs-btn-active-bg: var(--erp-primary-active);
    --bs-btn-active-border-color: var(--erp-primary-active);
    --bs-btn-disabled-bg: var(--erp-primary-disabled);
    --bs-btn-disabled-border-color: var(--erp-primary-disabled);
    --bs-btn-focus-shadow-rgb: var(--erp-primary-rgb);
}

.btn-outline-primary {
    --bs-btn-color: var(--erp-primary);
    --bs-btn-border-color: var(--erp-primary);
    --bs-btn-hover-bg: var(--erp-primary);
    --bs-btn-hover-border-color: var(--erp-primary);
    --bs-btn-active-bg: var(--erp-primary);
    --bs-btn-active-border-color: var(--erp-primary);
    --bs-btn-disabled-color: var(--erp-primary-disabled);
    --bs-btn-disabled-border-color: var(--erp-primary-disabled);
    --bs-btn-focus-shadow-rgb: var(--erp-primary-rgb);
}

.form-control:focus,
.form-select:focus,
.form-check-input:focus {
    border-color: var(--erp-primary-focus-border);
    box-shadow: 0 0 0 0.25rem rgba(var(--erp-primary-rgb), 0.25);
}

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

.dropdown-item.active,
.dropdown-item:active {
    --bs-dropdown-link-active-bg: var(--erp-primary);
}

.list-group-item.active {
    --bs-list-group-active-bg: var(--erp-primary);
    --bs-list-group-active-border-color: var(--erp-primary);
}

.nav-tabs .nav-link.active {
    --bs-nav-tabs-link-active-color: var(--erp-primary);
}

/* Appearance settings page (Task UI-THEME-01) — the theme picker cards'
   swatches are deliberately literal, hardcoded hex previews (not
   `var(--erp-primary)`), since their whole purpose is to show what each
   theme's actual colors look like side by side regardless of which
   theme is currently active. */
.theme-swatch {
    display: inline-block;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 0.25rem;
}

.theme-option-card {
    cursor: pointer;
    border: 2px solid var(--bs-border-color);
}

.theme-option-card.theme-option-selected {
    border-color: var(--erp-primary);
}

.navbar-brand {
    letter-spacing: 0.02em;
}

.card {
    border: none;
}

/* ---------------------------------------------------------------------
   App shell: top nav + left sidebar + main content.
   Sidebar is a Bootstrap "offcanvas-md" component — below the md
   breakpoint it slides in as an overlay (toggled from the top nav);
   at md and above Bootstrap itself renders it as a normal, always
   visible column, so no extra responsive JS is needed here.
   --------------------------------------------------------------------- */

.app-shell {
    min-height: 0;
    flex: 1 1 auto;
    overflow: hidden;
}

.app-sidebar {
    --bs-offcanvas-width: 240px;
}

@media (min-width: 768px) {
    .app-sidebar {
        width: 240px;
        flex: 0 0 240px;
        transition: width 0.2s ease, flex-basis 0.2s ease;
        align-self: stretch;
        /* Task UI-UX-01 / Menu Control footer (2026-08-29 revision):
           the scrollbar now belongs to .offcanvas-body specifically
           (below), not this outer element — .app-sidebar itself is
           only a bounded-height flex column (never scrolls, never
           grows past .app-shell's own height, see its `overflow:hidden`
           here). This is what makes .app-sidebar-footer a true fixed
           bottom bar instead of a `position: sticky` one: sticky only
           pins once its scrolling ancestor's content actually
           overflows, so with few submenus expanded (nothing to scroll
           yet) a sticky footer would sit right after the nav list
           instead of at the true bottom of the sidebar — reported live
           as "the button floats." A flex sibling that never scrolls has
           no such dependency on scroll state. */
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }

    /* Bootstrap 5.3's real .offcanvas-body default is actually
       `flex-grow: 0; overflow-y: visible` (verified against the shipped
       CSS — NOT the `flex-grow:1`/`overflow-y:auto` a first version of
       this rule assumed) — it never scrolls or grows on its own. Both
       are set explicitly here instead, so .offcanvas-body — not
       .app-sidebar — is the one bounded-and-scrolling element; only
       then is .app-sidebar-footer (a flex sibling after it, never
       inside it) excluded from that scrolling box, i.e. genuinely
       fixed rather than merely sticky. */
    .app-sidebar .offcanvas-body {
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        /* Display polish only: the browser's default scrollbar is
           light/white, which looks jarring against .app-sidebar's dark
           background — especially right where it terminates above the
           dark Menu Control footer. Themed to match instead of removed
           (still a real, usable scrollbar). Firefox via
           scrollbar-color/-width; Chrome/Edge/Safari via the
           ::-webkit-scrollbar family below. */
        scrollbar-width: thin;
        scrollbar-color: rgba(255, 255, 255, 0.3) transparent;
    }

    .app-sidebar .offcanvas-body::-webkit-scrollbar {
        width: 8px;
    }

    .app-sidebar .offcanvas-body::-webkit-scrollbar-track {
        background: transparent;
    }

    .app-sidebar .offcanvas-body::-webkit-scrollbar-thumb {
        background-color: rgba(255, 255, 255, 0.3);
        border-radius: 4px;
    }

    .app-sidebar .offcanvas-body::-webkit-scrollbar-thumb:hover {
        background-color: rgba(255, 255, 255, 0.45);
    }
}

/* Menu Control footer (Expand All / Collapse All) — always the last
   flex item in .app-sidebar, after the scrollable .offcanvas-body
   (never inside it), so it never scrolls and never needs
   `position: sticky` to appear "pinned." */
.app-sidebar-footer {
    flex-shrink: 0;
    background-color: var(--bs-dark, #212529);
    border-top: 1px solid rgba(255, 255, 255, 0.15);
}

html.erp-sidebar-collapsed .app-sidebar-footer {
    display: none !important;
}

/* ---------------------------------------------------------------------
   Sticky top navigation (Task WEB-02, Part B) — the top nav stays
   visible while a long page scrolls. z-index 1030 sits above ordinary
   page content but below Bootstrap's own modal/offcanvas layers
   (1045/1050+), so opening a modal or the mobile offcanvas sidebar
   still draws correctly on top of it.

   Task UI-UX-01 note: with the html/body rule below removing all
   page-level scrolling, "sticky" and "fixed" are visually identical
   here (there is no longer any ancestor scroll for it to stick
   *during* — it simply never moves), so the pre-existing rule is kept
   as-is rather than switched to `position: fixed` — same behavior,
   smaller diff, and the existing WEB-02 regression test that asserts
   this exact rule stays valid.
   --------------------------------------------------------------------- */

.app-topnav {
    position: sticky;
    top: 0;
    z-index: 1030;
    flex-shrink: 0;
}

/* ---------------------------------------------------------------------
   Task UI-UX-01 — ERP Fixed Workspace Layout.

   Goal: the ERP behaves like a desktop application — top nav, sidebar,
   module header (per-page <header>), and search/filter toolbar all
   stay fixed in place; only the actual working content (a table's
   rows, a form's body, a detail page's sections) scrolls. Achieved
   entirely with flexbox + position:sticky/fixed + overflow — no JS
   layout math anywhere in this section.

   Shell: <body> becomes a full-viewport flex column of exactly two
   rows — the top nav (natural height, never grows/shrinks) and
   .app-shell (everything else, takes all remaining height). Because
   <body> itself never scrolls, the sticky .app-topnav above can never
   move, and no pixel-height offset constants are needed anywhere
   below (the old brittle "topnav is Npx tall" approach this avoids).
   --------------------------------------------------------------------- */

html, body {
    height: 100%;
}

body {
    overflow: hidden;
}

/* .app-main: the column to the right of the sidebar (breadcrumb, the
   page's own module header, the scrollable content, the footer).
   Bounded to the remaining viewport height so its one growing child
   (.app-content-scroll) knows exactly how much space it can scroll
   within, instead of pushing <body> taller. */
.app-main {
    min-width: 0; /* prevents a wide table-responsive child from pushing the sidebar */
    overflow: hidden;
    min-height: 0;
}

/* .app-content-scroll is the <main> element that wraps every page's
   {% block content %}. Two modes:
   1. Default — the whole block scrolls together as one unit (correct
      for the Dashboard, detail pages, and standalone forms: per the
      task's own Content Area rule, "Detail Pages"/"Forms" are exactly
      what's allowed to scroll here).
   2. List-page grid mode — engaged automatically whenever the page's
      top-level content container carries `.app-list-page` (added to
      the small set of true list pages: the whole page below the
      module header IS a toolbar + one table, nothing else). In that
      mode the toolbar and any summary cards stay fixed and only the
      table's own rows scroll, "Excel-style" — see the rules below. */
.app-content-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
}

/* The page's own module header (title/subtitle/breadcrumb-adjacent
   <header>) is always the first child of .app-content-scroll (every
   page template's own convention, unchanged by this task). Sticky
   here means: in default mode it stays pinned while the rest of the
   page scrolls beneath it; in list-page grid mode nothing scrolls
   past it anyway, so the rule is inert but harmless. One rule, both
   modes, zero per-template markup change needed for this part. */
.app-content-scroll > header {
    position: sticky;
    top: 0;
    z-index: 20;
}

/* List-page grid mode. `:has()` lets .app-content-scroll itself react
   to which child it contains, so no wrapper markup change is needed
   beyond the one `.app-list-page` marker class already on each list
   page's own top-level container. */
.app-content-scroll:has(> .app-list-page) {
    overflow: hidden;
}

.app-content-scroll > .app-list-page {
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

/* Everything in a list page except the card that actually holds the
   table (summary/stat cards, the search/filter toolbar, any alert
   banner) keeps its natural size and never scrolls — this is what
   makes the Search Toolbar "always visible" without needing its own
   marker class or sticky rule. */
.app-content-scroll > .app-list-page > *:not(.card:has(.table-responsive)) {
    flex-shrink: 0;
}

/* The one card that holds the table becomes the scroll region. */
.app-content-scroll .app-list-page .card:has(.table-responsive) {
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

.app-content-scroll .app-list-page .card:has(.table-responsive) > .table-responsive {
    flex: 1 1 auto;
    overflow: auto;
    min-height: 0;
}

/* List-page grid mode, tree-sidebar variant (Task MASTER-DATA-02).
   The Reference Data page nests its table one level deeper than the
   plain grid mode above assumes — a Bootstrap row/col split between
   the Reference Data Type tree panel and the toolbar+table column —
   opted into via `.app-list-page-tree` alongside the existing
   `.app-list-page` marker. `.card:has(.table-responsive)` above
   already matches through that extra nesting (it's a descendant
   selector, not a direct-child one), so the only real gap is that the
   generic rule's "keep everything else at its natural size" clause
   (`.app-content-scroll > .app-list-page > *:not(...)`, above) catches
   the whole `.row` — including the column that legitimately needs to
   flex/scroll — before it ever reaches the table card two levels down.
   These rules re-open that one wrapper level for `.app-list-page-tree`
   only, without touching the plain single-card list pages' own
   behavior. Same specificity as the generic rule above (four classes
   each) — wins on source order, not a specificity hack. */
.app-content-scroll > .app-list-page.app-list-page-tree > .row {
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
}

.app-list-page-tree > .row > [class*="col-"] {
    /* Explicit height, not just align-items:stretch (the flex default,
       which computes to "normal" on `.row` and empirically does not
       reliably cross-size a `.col-*` down to its flex line's height —
       verified live: without this, the column silently renders at its
       full content height instead of the row's bounded height, which
       is what let the table/pagination escape the scroll region in
       the first place). */
    height: 100%;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.app-list-page-tree > .row > [class*="col-"] > *:not(.card:has(.table-responsive)) {
    flex-shrink: 0;
}

/* Safety net for the tree panel's own card (no table inside, so the
   rules above never give it a scroll region) — only engages if the
   Reference Data Type list ever grows taller than the available
   height; explicitly excludes any card that DOES hold a table so it
   can never fight with that card's own `overflow: hidden` (line 323)
   and create a double scrollbar. */
.app-list-page-tree > .row > [class*="col-"] > .card:not(:has(.table-responsive)) {
    min-height: 0;
    max-height: 100%;
    overflow-y: auto;
}

/* Sticky table header — applies everywhere a table is wrapped in
   .table-responsive, not just list-page grid mode: a table embedded
   partway down a longer detail page also keeps its header row
   visible while its own rows scroll past, using whichever ancestor
   is actually the scrolling one (the grid's own .table-responsive in
   list-page mode, or .app-content-scroll itself otherwise). Explicit
   background avoids the illusion of "gaps" as rows scroll underneath
   a transparent header. */
.table-responsive thead th {
    position: sticky;
    top: 0;
    z-index: 5;
    background-color: var(--bs-tertiary-bg, #f8f9fa);
}

/* Standalone form pages (not modals) whose Save/Cancel controls need
   to stay visible without scrolling to the bottom of a long form —
   opt in per page with `.app-sticky-actions` on the existing button
   wrapper (no markup restructuring, just one class). */
.app-content-scroll .app-sticky-actions {
    position: sticky;
    bottom: 0;
    z-index: 15;
    background-color: var(--bs-body-bg, #fff);
    padding-top: 0.75rem;
    padding-bottom: 0.75rem;
}

/* ---------------------------------------------------------------------
   Dialogs (Task UI-UX-01) — every Bootstrap modal in the app already
   follows the same modal-header/modal-body/modal-footer structure
   (STD-01's standard dialog pattern), so this is one global rule set,
   not a per-dialog change: header and footer keep their natural size
   and never scroll; only the body does, once its content plus the
   fixed header/footer would otherwise exceed the viewport. Equivalent
   to Bootstrap's own `.modal-dialog-scrollable`, applied universally
   instead of requiring that class on every individual dialog.
   --------------------------------------------------------------------- */

.modal-dialog {
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 3.5rem);
}

.modal-content {
    max-height: calc(100vh - 3.5rem);
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

/* Several dialogs (e.g. Product Add/Edit) wrap modal-header/-body/-footer
   in a single <form> for one-submit-button convenience, making the form
   -- not the three regions themselves -- the direct flex child of
   .modal-content. Without this, the form is a plain block box that
   renders at full content height and gets silently clipped by
   .modal-content's overflow: hidden instead of scrolling. */
.modal-content > form {
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
}

.modal-header,
.modal-footer {
    flex-shrink: 0;
}

.modal-body {
    overflow-y: auto;
    min-height: 0;
}

/* ---------------------------------------------------------------------
   Collapsible sidebar (Task WEB-02, Part B). The "erp-sidebar-collapsed"
   class is applied to <html> (not .app-shell) by a small synchronous
   script in base.html's <head>, so a returning user's remembered
   preference (localStorage) takes effect before first paint — no
   flash of the expanded sidebar. Only applies at the md+ breakpoint,
   where the sidebar already renders inline (below md it's Bootstrap's
   own slide-in offcanvas overlay, an unrelated, pre-existing
   responsive behavior this feature doesn't change).
   --------------------------------------------------------------------- */

@media (min-width: 768px) {
    html.erp-sidebar-collapsed .app-sidebar {
        width: 64px;
        flex: 0 0 64px;
    }

    html.erp-sidebar-collapsed .app-sidebar-nav .nav-label {
        display: none;
    }

    html.erp-sidebar-collapsed .app-sidebar-nav .nav-link,
    html.erp-sidebar-collapsed .app-sidebar-nav .nav-link.disabled {
        justify-content: center;
        padding-left: 0.5rem;
        padding-right: 0.5rem;
    }

    html.erp-sidebar-collapsed .app-sidebar-nav .nav-icon {
        margin-right: 0;
    }

    html.erp-sidebar-collapsed .app-sidebar-nav .nav-section-title {
        display: none;
    }

    /* Task CONFIG-CENTER-01: a multi-level nav tree makes no sense in
       icon-only mode — force every nested System/Configuration/
       Reference Data submenu closed while the sidebar is collapsed,
       regardless of which page is active (Bootstrap's own .collapse
       JS state is untouched; this is a pure display override, so the
       submenu reopens correctly if the sidebar is expanded again). */
    html.erp-sidebar-collapsed .app-sidebar-nav .collapse {
        display: none !important;
    }
}

/* Section group label (e.g. "Administration"), Task SYS-01 — the
   sidebar's first grouped section; plain, non-interactive, hidden
   entirely while the sidebar is collapsed (see above). */
.app-sidebar-nav .nav-section-title {
    display: block;
    letter-spacing: 0.04em;
    padding: 0.25rem 0.75rem;
}

.app-sidebar-nav .nav-link {
    color: rgba(255, 255, 255, 0.85);
    border-radius: 0.375rem;
    display: flex;
    align-items: center;
}

.app-sidebar-nav .nav-icon {
    display: inline-block;
    width: 1.4em;
    text-align: center;
    margin-right: 0.4rem;
    flex-shrink: 0;
}

.app-sidebar-nav .nav-link:hover {
    color: #fff;
    background-color: rgba(255, 255, 255, 0.08);
}

.app-sidebar-nav .nav-link.active {
    color: #fff;
    background-color: var(--bs-primary);
}

.app-sidebar-nav .nav-link.disabled {
    color: rgba(255, 255, 255, 0.35);
    cursor: default;
}

/* Task CONFIG-CENTER-01 — nested System/Configuration/Reference Data
   submenu levels. Same nav-link look as the top-level sidebar (color/
   hover/active rules above apply unchanged, since these are still
   .app-sidebar-nav .nav-link elements), just indented and slightly
   smaller per level so the 3-level tree stays scannable. */
.app-sidebar-subnav {
    padding-left: 1.1rem;
    margin-top: 0.15rem;
}

.app-sidebar-subnav .nav-link {
    font-size: 0.875rem;
    padding-top: 0.3rem;
    padding-bottom: 0.3rem;
}

.app-breadcrumb .breadcrumb {
    font-size: 0.875rem;
}

/* ---------------------------------------------------------------------
   Sortable table headers (Product Master table; reusable by future
   module tables built on the same pattern).
   --------------------------------------------------------------------- */

.sortable-th {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}

.sortable-th .sort-icon {
    display: inline-block;
    width: 1em;
    opacity: 0.4;
}

.sortable-th.sort-active .sort-icon {
    opacity: 1;
}

/* ---------------------------------------------------------------------
   Approval workflow stepper (Quotation Detail page). Pure CSS/HTML —
   the "current" / "done" / branch state is applied by quotation_detail.js
   toggling these classes based on data already fetched from the API;
   no business logic lives in this stylesheet or in that JS.
   --------------------------------------------------------------------- */

.erp-stepper {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
}

.erp-stepper .erp-step {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0.75rem;
    border-radius: 999px;
    background-color: var(--bs-secondary-bg, #e9ecef);
    color: var(--bs-secondary-color, #6c757d);
    font-size: 0.8rem;
    white-space: nowrap;
}

.erp-stepper .erp-step + .erp-step {
    margin-left: 0.4rem;
}

.erp-stepper .erp-step.done {
    background-color: var(--bs-success);
    color: #fff;
}

.erp-stepper .erp-step.current {
    background-color: var(--bs-primary);
    color: #fff;
    font-weight: 600;
}

.erp-stepper .erp-step-sep {
    color: var(--bs-secondary-color, #adb5bd);
    margin: 0 0.15rem;
}

/* ---------------------------------------------------------------------
   Print support (Quotation Detail page's reserved "Print" action —
   see QUOTATION_ARCHITECTURE.md's future-compatibility note: this is
   browser-native window.print(), not PDF generation). Hides
   navigation chrome and interactive controls so only the quotation
   content itself prints.
   --------------------------------------------------------------------- */

/* ---------------------------------------------------------------------
   Workspace loading skeleton — Task ERP-FRONTEND-ARCH-01. Content-area
   only (see workspace_navigator.js's showLoading/hideLoading): a few
   pulsing placeholder bars instead of a blank area or a full-page
   spinner, per the Enterprise Frontend Architecture Policy's "never
   blank the Workspace, max loading scope is the current content area"
   rule. Uses the same theme-aware `--bs-*` variables as the rest of
   this file (UI-THEME-01) so it reads correctly in light and dark.
   --------------------------------------------------------------------- */

.erp-skeleton-line {
    height: 1rem;
    border-radius: 0.375rem;
    background-color: var(--bs-secondary-bg, #e9ecef);
    margin-bottom: 0.75rem;
    animation: erp-skeleton-pulse 1.2s ease-in-out infinite;
}

.erp-skeleton-line:nth-child(1) { width: 40%; height: 1.5rem; }
.erp-skeleton-line:nth-child(2) { width: 90%; }
.erp-skeleton-line:nth-child(3) { width: 75%; }
.erp-skeleton-line:nth-child(4) { width: 85%; }
.erp-skeleton-line:last-child { margin-bottom: 0; }

@keyframes erp-skeleton-pulse {
    0%, 100% { opacity: 0.6; }
    50% { opacity: 1; }
}

@media print {
    .app-topnav,
    .app-sidebar,
    .app-breadcrumb,
    footer,
    #approval-stepper,
    #approval-branch-note,
    .card-header .btn-group,
    #actions-panel,
    #new-version-modal {
        display: none !important;
    }

    .app-main {
        width: 100% !important;
    }
}

/* Task QUOTATION-PRESENTATION-01 — Internal View presentation-level RBAC.
   The API already redacts unit_cost/margin_amount/margin_percent for a
   caller lacking quotation.view_cost; this hides the now-always-empty
   Cost/Margin table columns themselves (screen and print) rather than
   leaving a visibly blank column. Applied once, on <body>, by
   quotation_detail.js — never a static rule, since visibility depends
   on the logged-in user's own permission. */
.hide-quotation-cost-columns .cost-col {
    display: none !important;
}

/* ---------------------------------------------------------------------
   Quotation Customer View (Task QUOTATION-PRESENTATION-01).

   A dedicated, print-optimized document — commercial information only,
   never Cost/Margin/Internal Cost/Profit/approval workflow/version
   history/internal buttons (the backing API response has no such field
   to begin with — see app.schemas.quotation_presentation). On screen,
   `.cv-document` previews as an A4-proportioned page inside the normal
   app chrome; `@media print` hides that chrome entirely and sets real
   A4 page dimensions, matching this task's own "A4 portrait, clean
   page breaks" requirement.
   --------------------------------------------------------------------- */

.cv-toolbar {
    max-width: 210mm;
    margin: 0 auto;
}

.cv-document {
    background: #fff;
    color: #212529;
    width: 100%;
    max-width: 210mm;
    margin: 0 auto; /* block-level centering, deliberately not a flex
                        parent — a flex `justify-content: center`
                        wrapper interacted badly with this document's
                        own wide-table content, forcing the whole page
                        to overflow horizontally on narrow viewports
                        even though .cv-items-table-wrap's own
                        overflow-x:auto correctly contained it */
    padding: 14mm 12mm;
    box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.15);
    font-size: 0.92rem;
}

.cv-letterhead {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: flex-start;
    gap: 1rem;
    border-bottom: 2px solid #212529;
    padding-bottom: 0.75rem;
    margin-bottom: 1rem;
}

.cv-letterhead > * {
    min-width: 0;
}

.cv-company-name {
    font-size: 1.15rem;
    font-weight: 700;
}

.cv-company-line {
    font-size: 0.82rem;
    color: #495057;
}

.cv-company-line:empty {
    display: none;
}

.cv-doc-meta {
    text-align: right;
    flex: 1 1 220px;
    max-width: 100%;
}

.cv-doc-title {
    font-size: 1.4rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    margin-bottom: 0.35rem;
}

.cv-meta-list {
    margin: 0;
    font-size: 0.82rem;
}

.cv-meta-list > div {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
}

.cv-meta-list dt {
    font-weight: 400;
    color: #6c757d;
}

.cv-meta-list dd {
    margin: 0;
    font-weight: 600;
}

.cv-section {
    margin-bottom: 1rem;
    break-inside: avoid;
}

.cv-section-title {
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #495057;
    border-bottom: 1px solid #dee2e6;
    padding-bottom: 0.25rem;
    margin-bottom: 0.5rem;
}

.cv-field-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 0.15rem 1.5rem;
    margin: 0;
    font-size: 0.85rem;
}

.cv-field-grid > div {
    display: flex;
    gap: 0.4rem;
    padding: 0.1rem 0;
    min-width: 0;
}

.cv-field-grid dd {
    min-width: 0;
    overflow-wrap: break-word;
}

.cv-field-grid dt {
    color: #6c757d;
    flex: 0 0 auto;
}

.cv-field-grid dd {
    margin: 0;
    font-weight: 500;
}

.cv-items-table-wrap {
    /* Wide content scrolls inside its own container — the page/body
       must never scroll horizontally (matches this project's own
       .table-responsive convention used everywhere else). */
    overflow-x: auto;
}

.cv-items-table {
    width: 100%;
    min-width: 560px;
    border-collapse: collapse;
    font-size: 0.82rem;
}

.cv-items-table th,
.cv-items-table td {
    border: 1px solid #dee2e6;
    padding: 0.35rem 0.5rem;
}

.cv-items-table thead th {
    background: #f1f3f5;
    font-weight: 600;
    text-align: left;
}

.cv-col-no {
    width: 2.5rem;
}

.cv-totals {
    width: 260px;
    margin-left: auto;
    margin-top: 0.5rem;
    font-size: 0.85rem;
}

.cv-totals > div {
    display: flex;
    justify-content: space-between;
    padding: 0.15rem 0;
}

.cv-totals .cv-grand-total {
    border-top: 2px solid #212529;
    margin-top: 0.25rem;
    padding-top: 0.35rem;
    font-weight: 700;
    font-size: 1rem;
}

.cv-signature-block {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 2rem;
    margin-top: 2.5rem;
    break-inside: avoid;
}

.cv-signature-line {
    border-top: 1px solid #212529;
    margin-top: 2.5rem;
}

.cv-signature-label {
    font-size: 0.8rem;
    color: #6c757d;
    margin-top: 0.35rem;
}

@media (max-width: 576px) {
    /* Narrow on-screen viewport — a 2-column field grid leaves too
       little room per value, forcing ugly character-by-character
       wrapping. Stack to one column, and stack each field's own
       label above its value, matching this project's general
       responsive-collapse convention (Permanent UI Standard). */
    .cv-field-grid {
        grid-template-columns: 1fr;
    }

    .cv-field-grid > div {
        flex-direction: column;
        gap: 0.05rem;
    }

    .cv-meta-list > div {
        justify-content: space-between;
    }

    .cv-document {
        padding: 6mm 4mm;
    }
}

@media print {
    .app-topnav,
    .app-sidebar,
    .app-breadcrumb,
    footer,
    .cv-toolbar {
        display: none !important;
    }

    .app-main {
        width: 100% !important;
    }

    body {
        background: #fff !important;
    }

    .cv-document {
        box-shadow: none;
        max-width: none;
        width: 100%;
        padding: 0;
        margin: 0;
    }

    @page {
        size: A4 portrait;
        margin: 14mm 12mm;
    }

    .cv-items-table thead {
        display: table-header-group;
    }

    .cv-items-table tr {
        break-inside: avoid;
    }
}

/* ---------------------------------------------------------------------
   Dashboard-card row helpers — moved here from per-page inline <style>
   blocks (were in each template's own {% block extra_scripts %}).
   Root cause: WorkspaceNavigator (workspace_navigator.js) intercepts
   sidebar navigation and swaps only <main>, the breadcrumb, and the
   sidebar list, then re-executes <script> tags from the fetched page —
   it never re-inserts a page's own <style> block (extra_scripts renders
   outside <main>, and only `body script` elements are read back out).
   Any page reached via a sidebar click rather than a full reload was
   therefore silently missing these rules — reported live as "cards are
   one row after a refresh, but wrap to multiple rows again after
   clicking to another page and back." Living here instead means the
   rule loads once with the rest of style.css and is never lost.
   --------------------------------------------------------------------- */

/* procurement.html, purchase_orders.html — 9 dashboard cards, one row. */
@media (min-width: 1200px) {
    .col-xl-dash9 { flex: 0 0 auto; width: 11.1111%; }
}

/* receiving_workspace.html — 7 dashboard cards (name kept as originally
   authored; 12.5% = an 8-slot row, comfortably fits 7 with room to
   spare, so left as-is rather than renamed/retuned). */
@media (min-width: 1200px) {
    .col-xl-1-5 { flex: 0 0 auto; width: 12.5%; }
}

/* warehouse_dashboard.html — 7 dashboard cards, one row. */
@media (min-width: 992px) {
    .col-lg-1-7 { flex: 0 0 auto; width: 14.2857%; }
}

/* receiving_workspace.html, putaway_workspace.html — active queue-item
   highlight (same inline-<style>-loses-scope root cause as above). */
.queue-item.active { background-color: var(--bs-primary-bg-subtle, #e7f1ff); }
