/*
 * MiniERP shared design tokens. Every view reuses these CSS variables
 * instead of hard-coded colors/spacing, so Bootstrap 5 components look
 * and behave consistently across all modules.
 */
:root {
  /*
   * Purple-blue-gray design system. Every value below was checked against
   * WCAG 2.1 AA contrast (4.5:1 normal text / 3:1 large text or UI) for
   * every text-on-background pairing the token actually drives elsewhere
   * in this file (navbar, buttons, status badges, sidebar-active state,
   * focus ring) — see the ratios noted next to each token. Ratios were
   * computed with the standard relative-luminance formula (WCAG 1.4.3),
   * not eyeballed.
   */
  --mini-color-primary: #4338ca;          /* indigo-700: 7.9:1 vs #ffffff */
  --mini-color-primary-contrast: #ffffff; /* on primary: 7.9:1, on focus: 6.7:1 */
  --mini-color-primary-soft: #eef0fd;     /* tint of primary, decorative-only (gradients/hover washes), never used for text */
  --mini-color-surface: #ffffff;
  --mini-color-surface-alt: #f5f4fb;      /* light indigo-tinted gray */
  --mini-color-text: #1f2430;             /* 15.52:1 vs surface, 14.2:1 vs surface-alt */
  --mini-color-text-muted: #6b6478;       /* muted violet-gray: 5.65:1 vs surface, 5.17:1 vs surface-alt */
  --mini-color-border: #d9d5ea;           /* decorative divider, same low contrast class as prior token */
  --mini-color-focus: #1d4ed8;            /* blue-700 accent, distinct from primary: 6.7:1 vs surface, 6.13:1 vs surface-alt (ring sits on the page background outside the focused control, per outline-offset) */
  --mini-color-success: #1a7f37;          /* unchanged: 5.08:1 vs #ffffff, already AA */
  --mini-color-warning: #9a6700;          /* unchanged: 4.87:1 vs #ffffff, already AA */
  --mini-color-danger: #b42318;           /* unchanged: 6.57:1 vs #ffffff, already AA */

  /* Decorative gradient built only from the semantic tokens above, so any
   * future palette change here flows through automatically. Used by
   * styles.css for page/card background treatments; never used for text. */
  --mini-gradient-brand: linear-gradient(135deg, var(--mini-color-primary) 0%, var(--mini-color-focus) 100%);

  --mini-font-family: 'Segoe UI', system-ui, -apple-system, Roboto, sans-serif;
  --mini-space-1: 0.25rem;
  --mini-space-2: 0.5rem;
  --mini-space-3: 1rem;
  --mini-space-4: 1.5rem;
  --mini-radius: 0.5rem;
  --mini-elevation-1: 0 1px 3px rgba(0, 0, 0, 0.08);
  --mini-navbar-height: 56px;
  --mini-sidebar-width: 260px;
}

body {
  font-family: var(--mini-font-family);
  color: var(--mini-color-text);
  background-color: var(--mini-color-surface-alt);
}

.navbar.bg-primary {
  background-color: var(--mini-color-primary) !important;
}

/*
 * App shell: slim top navbar + left sidebar (see resources/views/layout.php
 * and resources/views/partials/nav.php). The sidebar is a Bootstrap 5
 * *responsive* offcanvas (`offcanvas-lg`): Bootstrap only applies
 * `position: fixed` (plus backdrop/`visibility: hidden` toggle state) to
 * `.offcanvas-lg` inside its own `@media (max-width: 991.98px)` block —
 * at `lg` and above it leaves `position`/`visibility` alone entirely
 * (verified against the actual bootstrap@5.3.3 CDN build this project
 * links). BUT the slide-in direction class, `.offcanvas-start`, carries
 * its own `transform: translateX(-100%)` completely UNSCOPED (not inside
 * any `@media` block at all) — that one applies at every width, so
 * without an explicit reset the sidebar stayed permanently shifted
 * off-screen even at desktop width (present in the DOM, never visible;
 * this was a real bug, found only by reading Bootstrap's actual CSS
 * source line-by-line, not by reasoning about `.offcanvas-lg` alone).
 * The rule below adds the desktop positioning (its own breakpoint, so it
 * can never apply while the mobile offcanvas is toggled open) AND resets
 * that transform back to `none`.
 */
.app-navbar {
  min-height: var(--mini-navbar-height);
}

#company-switcher,
#locale-switcher {
  width: auto;
  max-width: 11rem;
}

/* Navbar company logo (see partials/nav.php + sidebar-menu.js's
 * loadAndRenderCompanyLogo()) -- sized to fit inside --mini-navbar-height
 * alongside the brand text, with a small light backing so a logo with
 * transparent edges still reads clearly against --mini-color-primary. */
.navbar-company-logo {
  height: calc(var(--mini-navbar-height) - 2 * var(--mini-space-2));
  max-width: 8rem;
  width: auto;
  object-fit: contain;
  border-radius: var(--mini-radius);
  background-color: var(--mini-color-surface);
  padding: 2px;
}

.app-sidebar .sidebar-heading-wrap {
  margin: var(--mini-space-3) 0 var(--mini-space-1);
}

/*
 * The group heading is now a `<button data-bs-toggle="collapse">` (wrapped
 * in the `<h3>` above for semantics) instead of a plain heading, so its
 * group can expand/collapse via Bootstrap 5's own `collapse` component —
 * see nav.php's `$navGroupHeading`. Visually it stays identical to the old
 * plain heading (same size/weight/case/color), just reset of default
 * button chrome plus the chevron's layout.
 */
.app-sidebar .sidebar-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  background: none;
  border: 0;
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--mini-color-text-muted);
  padding: var(--mini-space-1) var(--mini-space-2);
  border-radius: var(--mini-radius);
  cursor: pointer;
}

.app-sidebar .sidebar-heading:hover {
  background-color: var(--mini-color-surface-alt);
}

.app-sidebar .sidebar-heading.active {
  color: var(--mini-color-primary);
}

/* Chevron rotates purely off the toggle button's own `aria-expanded`
 * state -- no custom JS beyond Bootstrap's collapse plugin, which already
 * keeps `aria-expanded` in sync as the group opens/closes. */
.app-sidebar .sidebar-group-chevron {
  flex-shrink: 0;
  transition: transform 0.15s ease-in-out;
}

.app-sidebar .sidebar-heading[aria-expanded="true"] .sidebar-group-chevron {
  transform: rotate(180deg);
}

.app-sidebar .nav-icon {
  display: inline-flex;
  flex-shrink: 0;
  width: 1rem;
  justify-content: center;
}

.app-sidebar .nav-link {
  display: flex;
  align-items: center;
  gap: var(--mini-space-2);
  color: var(--mini-color-text);
  border-radius: var(--mini-radius);
  padding: var(--mini-space-2);
}

.app-sidebar .nav-link:hover {
  background-color: var(--mini-color-surface-alt);
}

.app-sidebar .nav-link.active {
  background-color: var(--mini-color-primary);
  color: var(--mini-color-primary-contrast);
  font-weight: 600;
}

@media (min-width: 992px) {
  #sidebarMenu.offcanvas-lg {
    position: fixed;
    top: var(--mini-navbar-height);
    bottom: 0;
    left: 0;
    width: var(--mini-sidebar-width);
    /* `.offcanvas-start` (the slide-in direction class) carries an
     * UNSCOPED `transform: translateX(-100%)` -- not inside any
     * `@media` block, so it applies at every breakpoint, not just the
     * narrow one. Bootstrap only resets it back to `none` via its
     * `.show`/`.showing` toggle-state classes, or via a
     * `.navbar-expand-{bp} .offcanvas` descendant rule that assumes the
     * offcanvas is nested inside that exact navbar wrapper class. This
     * sidebar is a sibling of `.app-navbar`, not nested inside a
     * `.navbar-expand-lg` element, so neither reset ever applied here:
     * the sidebar was permanently shifted off-screen at every viewport
     * width, including desktop -- present in the DOM, never visible.
     * `#sidebarMenu.offcanvas-lg` (an ID selector) already outweighs
     * the plain-class `.offcanvas-start` rule on specificity alone, so
     * no `!important` is needed to win this one.
     *
     * A THIRD unscoped rule exists too: the bare `.offcanvas` class
     * (every responsive variant, including `.offcanvas-lg`, also carries
     * this literal class) sets `visibility: hidden` with no `@media`
     * guard at all -- found via DevTools after the transform fix alone
     * still left it invisible. Same specificity argument applies: this
     * ID-anchored rule outweighs the plain-class one, no `!important`
     * needed. */
    transform: none;
    visibility: visible;
    /* Bootstrap's own `.offcanvas-lg` rule at this same breakpoint sets
     * `background-color: transparent !important` (it assumes a
     * permanent sidebar supplies its own background); `!important` here
     * is required to win against that, not for specificity. */
    background-color: var(--mini-color-surface) !important;
    border-right: 1px solid var(--mini-color-border);
    overflow-y: auto;
    padding: var(--mini-space-2) 0;
  }

  .app-main {
    margin-left: var(--mini-sidebar-width);
  }
}

.btn-primary {
  background-color: var(--mini-color-primary);
  border-color: var(--mini-color-primary);
}

.card {
  border-radius: var(--mini-radius);
  box-shadow: var(--mini-elevation-1);
  border-color: var(--mini-color-border);
}

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
.btn:focus-visible {
  outline: 2px solid var(--mini-color-focus);
  outline-offset: 2px;
}

.status-badge-active {
  color: var(--mini-color-success);
  font-weight: 600;
}

.status-badge-inactive {
  color: var(--mini-color-text-muted);
  font-weight: 600;
}

/* Signed stock-movement quantities (see stock-movements.js). The sign
 * character itself (always rendered, never color-only) already conveys
 * direction; color is reinforcement, not the sole signal. */
.quantity-badge-positive {
  color: var(--mini-color-success);
  font-weight: 600;
}

.quantity-badge-negative {
  color: var(--mini-color-danger);
  font-weight: 600;
}

/* Order status (see orders.js / order-show.js). As with the other status
 * badges, the status word itself is always rendered as text, so color is
 * reinforcement, not the sole signal. */
.order-status-badge-draft {
  color: var(--mini-color-text-muted);
  font-weight: 600;
}

.order-status-badge-confirmed {
  color: var(--mini-color-primary);
  font-weight: 600;
}

.order-status-badge-fulfilled {
  color: var(--mini-color-success);
  font-weight: 600;
}

.order-status-badge-invoiced {
  color: var(--mini-color-success);
  font-weight: 600;
}

.order-status-badge-cancelled {
  color: var(--mini-color-danger);
  font-weight: 600;
}

/* Invoice status (see invoices.js / invoice-show.js). As with the other
 * status badges, the status word itself is always rendered as text, so
 * color is reinforcement, not the sole signal. */
.invoice-status-badge-draft {
  color: var(--mini-color-text-muted);
  font-weight: 600;
}

.invoice-status-badge-issued {
  color: var(--mini-color-primary);
  font-weight: 600;
}

.invoice-status-badge-paid {
  color: var(--mini-color-success);
  font-weight: 600;
}

.invoice-status-badge-cancelled {
  color: var(--mini-color-danger);
  font-weight: 600;
}

/* Cost bill status (see cost-bills.js / cost-bill-show.js) -- the
 * accounts-payable mirror of invoice status above, same four states.
 * As with the other status badges, the status word itself is always
 * rendered as text, so color is reinforcement, not the sole signal. */
.cost-bill-status-badge-draft {
  color: var(--mini-color-text-muted);
  font-weight: 600;
}

.cost-bill-status-badge-issued {
  color: var(--mini-color-primary);
  font-weight: 600;
}

.cost-bill-status-badge-paid {
  color: var(--mini-color-success);
  font-weight: 600;
}

.cost-bill-status-badge-cancelled {
  color: var(--mini-color-danger);
  font-weight: 600;
}

/* Goods receipt status (see goods-receipts.js / goods-receipt-show.js).
 * As with the other status badges, the status word itself is always
 * rendered as text, so color is reinforcement, not the sole signal. */
.goods-receipt-status-badge-draft {
  color: var(--mini-color-text-muted);
  font-weight: 600;
}

.goods-receipt-status-badge-confirmed {
  color: var(--mini-color-success);
  font-weight: 600;
}

.goods-receipt-status-badge-cancelled {
  color: var(--mini-color-danger);
  font-weight: 600;
}

/* Delivery note status (see delivery-notes.js / delivery-note-show.js).
 * As with the other status badges, the status word itself is always
 * rendered as text, so color is reinforcement, not the sole signal. */
.delivery-note-status-badge-draft {
  color: var(--mini-color-text-muted);
  font-weight: 600;
}

.delivery-note-status-badge-shipped {
  color: var(--mini-color-success);
  font-weight: 600;
}

.delivery-note-status-badge-cancelled {
  color: var(--mini-color-danger);
  font-weight: 600;
}

/* Business year status (see business-years.js). As with the other status
 * badges, the status word itself is always rendered as text, so color is
 * reinforcement, not the sole signal. */
.business-year-status-badge-draft {
  color: var(--mini-color-text-muted);
  font-weight: 600;
}

.business-year-status-badge-open {
  color: var(--mini-color-success);
  font-weight: 600;
}

.business-year-status-badge-closing {
  color: var(--mini-color-warning);
  font-weight: 600;
}

.business-year-status-badge-closed {
  color: var(--mini-color-text-muted);
  font-weight: 600;
}

/* API key status (see api-keys.js). As with the other status badges,
 * the status word itself is always rendered as text, so color is
 * reinforcement, not the sole signal. */
.api-key-status-badge-active {
  color: var(--mini-color-success);
  font-weight: 600;
}

.api-key-status-badge-revoked {
  color: var(--mini-color-danger);
  font-weight: 600;
}

.api-key-status-badge-expired {
  color: var(--mini-color-text-muted);
  font-weight: 600;
}

/* Quotation status (see quotations.js / quotation-show.js). As with the
 * other status badges, the status word itself is always rendered as
 * text, so color is reinforcement, not the sole signal. */
.quotation-status-badge-draft {
  color: var(--mini-color-text-muted);
  font-weight: 600;
}

.quotation-status-badge-sent {
  color: var(--mini-color-primary);
  font-weight: 600;
}

.quotation-status-badge-accepted {
  color: var(--mini-color-success);
  font-weight: 600;
}

.quotation-status-badge-rejected {
  color: var(--mini-color-danger);
  font-weight: 600;
}

.quotation-status-badge-cancelled {
  color: var(--mini-color-danger);
  font-weight: 600;
}

/* Document-share customer response status (see shared-document.js, the
 * public document-sharing microsite, and quotation-show.js's/
 * order-show.js's own Share modal). As with the other status badges,
 * the status word itself is always rendered as text, so color is
 * reinforcement, not the sole signal. */
.document-share-response-badge-pending {
  color: var(--mini-color-text-muted);
  font-weight: 600;
}

.document-share-response-badge-confirmed {
  color: var(--mini-color-success);
  font-weight: 600;
}

.document-share-response-badge-declined {
  color: var(--mini-color-danger);
  font-weight: 600;
}

/*
 * Remote select (see public/assets/js/components/remote-select.js): an
 * AJAX-backed combobox + listbox taking over a plain container element.
 * The container is positioned so the listbox (absolutely positioned)
 * drops directly below the input regardless of where it sits in the
 * surrounding form grid.
 */
.remote-select {
  position: relative;
}

.remote-select-listbox {
  position: absolute;
  z-index: 1060; /* above Bootstrap modals' own backdrop/content (1050/1055) so a picker inside a modal still shows its dropdown on top */
  top: 100%;
  left: 0;
  right: 0;
  margin: 0;
  padding: var(--mini-space-1) 0;
  list-style: none;
  max-height: 16rem;
  overflow-y: auto;
  background-color: var(--mini-color-surface);
  border: 1px solid var(--mini-color-border);
  border-radius: var(--mini-radius);
  box-shadow: var(--mini-elevation-1);
}

.remote-select-option {
  display: flex;
  flex-direction: column;
  padding: var(--mini-space-2) var(--mini-space-3);
  cursor: pointer;
}

.remote-select-option-sublabel {
  font-size: 0.8125rem;
  color: var(--mini-color-text-muted);
}

.remote-select-option.is-active,
.remote-select-option:hover {
  background-color: var(--mini-color-surface-alt);
}

/* The current selection keeps a persistent marker (not color alone) so
 * it reads correctly even to someone who can't perceive the highlight
 * tint -- same "never color-only" rule the status badges above follow. */
.remote-select-option.is-selected .remote-select-option-label::after {
  content: ' ✓';
}

.remote-select-status {
  padding: var(--mini-space-2) var(--mini-space-3);
  color: var(--mini-color-text-muted);
  font-size: 0.875rem;
}

.remote-select-input:focus-visible {
  outline: 2px solid var(--mini-color-focus);
  outline-offset: 2px;
}

/*
 * Document items table (see
 * public/assets/js/components/document-items-table.js): the shared
 * product/description + quantity + unit + price + VAT + note row editor
 * meant to replace the near-identical hand-rolled line-row markup
 * `orders.js`/`invoices.js`/`delivery-notes.js`/`cost-bills.js`/
 * `goods-receipts.js` each built on their own. Reuses the same
 * `row g-2 align-items-end` gutter/alignment language those pages
 * already use -- this is layout polish on top of Bootstrap's own grid,
 * not a new visual language.
 */
.document-items-table-row {
  padding-bottom: var(--mini-space-2);
  border-bottom: 1px solid var(--mini-color-border);
}

.document-items-table-row:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}

/* The "unit" field is always read-only (a picked product's own unit
 * string, never user-entered) -- muted like the status badges' own
 * secondary/muted text, so it reads as display-only at a glance without
 * relying on the `readonly` attribute's often-subtle native styling
 * alone. */
.document-items-table-unit-input {
  background-color: var(--mini-color-surface-alt);
  color: var(--mini-color-text-muted);
}

/* Custom-price is a secondary, usually-empty override next to the
 * primary unit-price input; its placeholder text (an i18n string, not
 * color alone) is what actually conveys "optional" -- this is purely a
 * visual-hierarchy cue, not a status indicator. */
.document-items-table-custom-price-input::placeholder {
  color: var(--mini-color-text-muted);
}

/*
 * Dashboard widgets (see public/assets/js/dashboard.js): a small
 * decorative icon ahead of each widget's title, and a fixed-height
 * container for the "overview" widget's Chart.js canvas -- Chart.js's
 * `maintainAspectRatio: false` needs an explicitly sized parent, it
 * does not size itself to its content.
 */
.dashboard-widget-icon {
  color: var(--mini-color-primary);
}

.dashboard-overview-chart-wrap {
  position: relative;
  height: 220px;
}
