/* =============================================================================
   MYKA DESIGN SYSTEM — CANONICAL COMPONENT LAYER
   =============================================================================

   Implements docs/ui/MYKA_DESIGN_SYSTEM.md.

   This file is the component library referenced by MYKA_DESIGN_SYSTEM.md §27.
   Every list page and every modal in the ERP is assembled from these classes.
   Pages MUST NOT restyle them and MUST NOT introduce their own colour, spacing,
   radius or shadow values (§0.3, §28.1).

   NAMESPACE
   ---------
   All classes and tokens use the `mds-` / `--mds-` prefix.

   Why a new namespace instead of editing the existing --erp-* registry:
   MYKA_DESIGN_SYSTEM.md §2.1 forbids removing or redefining a published token,
   because tokens are a public API consumed by screens nobody is currently
   looking at. The existing --erp-* values (radius 6px, spacing 4/8/12/16/24)
   do not match the standard's scale, so redefining them would silently reshape
   every screen in the product. Instead this layer:

     - derives Tier-2 semantic tokens FROM the existing --erp-* primitives, so
       light/dark theming and any future rebrand keep working through one file;
     - adds the Tier-3 component tokens the standard requires but the registry
       does not yet have (density, z-scale, focus ring, motion).

   The result is additive: linking this file changes nothing on a page that does
   not use `mds-` classes.

   LOAD ORDER
   ----------
   head.php loads: styles.css -> custom.css -> modern-theme.css -> brand-theme.css
   This file is page CSS and is linked AFTER those, from the view.

   DENSITY
   -------
   Set data-mds-density="comfortable|standard|compact" on <html> or on any
   container. Default is standard (§2.8).
   ============================================================================= */


/* =============================================================================
   1. TOKENS
   ============================================================================= */

:root {

  /* ---- Tier 2: semantic surfaces (derived from the --erp-* primitives) ---- */
  --mds-bg-canvas:      var(--erp-bg-page,   #F8F8F9);
  --mds-bg-surface:     var(--erp-bg-card,   #ffffff);
  --mds-bg-raised:      var(--erp-bg-card,   #ffffff);
  --mds-bg-sunken:      var(--erp-bg-hover,  #F8F8F9);
  --mds-bg-hover:       rgba(23, 25, 30, 0.035);
  --mds-bg-selected:    var(--erp-accent-soft, rgba(214, 69, 47, 0.10));
  --mds-bg-disabled:    var(--erp-bg-hover,  #F8F8F9);

  /* ---- Tier 2: text ---- */
  --mds-text-primary:   var(--erp-text-primary, #1F2126);
  --mds-text-secondary: var(--erp-text-muted,   #6B6F78);
  --mds-text-tertiary:  var(--erp-text-light,   #9A9DA6);
  --mds-text-disabled:  var(--erp-text-light,   #9A9DA6);
  --mds-text-inverse:   #ffffff;
  --mds-text-link:      var(--erp-primary,      #17191E);

  /* ---- Tier 2: structure ---- */
  --mds-border:         var(--erp-border,       #E5E5E9);
  --mds-border-strong:  var(--erp-border-hover, #D3D3D8);
  --mds-border-subtle:  rgba(23, 25, 30, 0.06);

  /* ---- Tier 2: accent (single product accent, §2.2.1) ---- */
  --mds-accent:         var(--erp-accent,        #D6452F);
  --mds-accent-hover:   var(--erp-accent-hover,  #B3341F);
  --mds-accent-active:  #932A18;
  --mds-accent-subtle:  var(--erp-accent-soft,   rgba(214, 69, 47, 0.10));
  --mds-accent-border:  var(--erp-accent-border, rgba(214, 69, 47, 0.25));
  --mds-accent-fg:      var(--erp-accent-contrast, #ffffff);

  /* ---- Tier 2: status roles. fg / bg / border for each (§2.2.1) ---- */
  --mds-success-fg:     var(--erp-success,    #059669);
  --mds-success-bg:     var(--erp-success-bg, #d1fae5);
  --mds-success-border: rgba(5, 150, 105, 0.22);

  /* Fallback aligned to --erp-warning (#d97706). It previously read #b45309,
     the value --mds-warning-border is still built from. Because modern-theme.css
     is global the fallback arm never applied, so #d97706 is — and always was —
     what actually painted; this records reality rather than changing it. Whether
     the warning foreground SHOULD be the darker #b45309 that its own border
     implies is a design decision, logged in FINAL_KNOWN_LIMITATIONS.md. */
  --mds-warning-fg:     var(--erp-warning,    #d97706);
  --mds-warning-bg:     var(--erp-warning-bg, #fef3c7);
  --mds-warning-border: rgba(180, 83, 9, 0.22);

  --mds-danger-fg:      var(--erp-danger,     #dc2626);
  --mds-danger-bg:      var(--erp-danger-bg,  #fee2e2);
  --mds-danger-border:  rgba(220, 38, 38, 0.22);

  --mds-info-fg:        #1d4ed8;
  --mds-info-bg:        #eaf0fb;
  --mds-info-border:    rgba(29, 78, 216, 0.18);

  --mds-neutral-fg:     var(--erp-text-muted, #6B6F78);
  --mds-neutral-bg:     rgba(23, 25, 30, 0.055);
  --mds-neutral-border: rgba(23, 25, 30, 0.10);

  /* ---- Tier 2: typography (§2.3.2) ---- */
  --mds-font-sans: var(--erp-font-sans, system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif);
  --mds-font-mono: var(--erp-font-mono, ui-monospace, Consolas, monospace);

  --mds-fs-display: 1.5rem;    /* 24px — KPI hero only              */
  --mds-fs-h1:      1.25rem;   /* 20px — page title                 */
  --mds-fs-h2:      1rem;      /* 16px — card / modal title         */
  --mds-fs-h3:      0.875rem;  /* 14px — form section title         */
  --mds-fs-body:    0.875rem;  /* 14px — default, input values      */
  --mds-fs-data:    0.8125rem; /* 13px — table cells                */
  --mds-fs-label:   0.75rem;   /* 12px — column heads, field labels */
  --mds-fs-caption: 0.6875rem; /* 11px — timestamps, helper text    */

  --mds-lh-h1:      1.4;
  --mds-lh-body:    1.43;
  --mds-lh-data:    1.38;

  --mds-fw-normal:   400;
  --mds-fw-medium:   500;
  --mds-fw-semibold: 600;

  /* ---- Tier 2: 4px spacing grid (§2.4) ---- */
  --mds-space-1:  2px;
  --mds-space-2:  4px;
  --mds-space-3:  8px;
  --mds-space-4:  12px;
  --mds-space-5:  16px;
  --mds-space-6:  20px;
  --mds-space-7:  24px;
  --mds-space-8:  32px;
  --mds-space-9:  40px;
  --mds-space-10: 48px;

  /* ---- Tier 2: radius (§2.5). Deliberately small — large radii cost density */
  --mds-radius-sm:   3px;
  --mds-radius-md:   6px;
  --mds-radius-lg:   8px;
  --mds-radius-pill: 999px;

  /* ---- Tier 2: elevation. Depth == dismissibility (§2.5) ---- */
  --mds-shadow-0: none;
  --mds-shadow-1: 0 2px 6px rgba(23, 25, 30, 0.08);
  --mds-shadow-2: 0 4px 12px rgba(23, 25, 30, 0.10);
  --mds-shadow-3: 0 12px 32px rgba(23, 25, 30, 0.16);
  --mds-shadow-4: 0 16px 40px rgba(23, 25, 30, 0.20);

  /* ---- Tier 2: z-index scale (§2.5). No arbitrary values anywhere ---- */
  --mds-z-sticky:   100;
  --mds-z-header:   200;
  --mds-z-dropdown: 1000;
  --mds-z-backdrop: 1040;
  --mds-z-modal:    1050;
  --mds-z-popover:  1060;
  --mds-z-toast:    1080;
  --mds-z-tooltip:  1090;

  /* ---- Tier 2: motion (§2.7). Nothing exceeds 240ms ---- */
  --mds-duration-fast: 120ms;
  --mds-duration-base: 180ms;
  --mds-ease-out: cubic-bezier(0.16, 0.84, 0.44, 1);

  /* ---- Focus ring (§23.4). 2px, offset 2px, never removed ---- */
  --mds-focus-ring: 0 0 0 2px var(--mds-bg-surface), 0 0 0 4px var(--mds-accent);

  /* ---- Tier 3: component tokens, STANDARD density (§2.8 default) ---- */
  --mds-row-h:        36px;
  --mds-control-h:    32px;
  --mds-cell-pad-y:   8px;
  --mds-cell-pad-x:   12px;
  --mds-fs-cell:      var(--mds-fs-data);
  --mds-badge-h:      20px;
}

/* Density: comfortable */
[data-mds-density="comfortable"] {
  --mds-row-h:      44px;
  --mds-control-h:  40px;
  --mds-cell-pad-y: 12px;
  --mds-cell-pad-x: 16px;
  --mds-fs-cell:    var(--mds-fs-body);
  --mds-badge-h:    22px;
}

/* Density: compact */
[data-mds-density="compact"] {
  --mds-row-h:      30px;
  --mds-control-h:  28px;
  --mds-cell-pad-y: 6px;
  --mds-cell-pad-x: 10px;
  --mds-fs-cell:    var(--mds-fs-label);
  --mds-badge-h:    18px;
}

/* Coarse pointers get comfortable targets automatically (§22.3, §23.5) */
@media (pointer: coarse) {
  :root {
    --mds-row-h:     44px;
    --mds-control-h: 40px;
  }
}

/* Dark theme: re-derive rather than alpha-blend (§2.2.2) */
[data-bs-theme="dark"] {
  --mds-bg-hover:       rgba(255, 255, 255, 0.045);
  --mds-border-subtle:  rgba(255, 255, 255, 0.07);

  --mds-info-fg:        #93c5fd;
  --mds-info-bg:        rgba(59, 130, 246, 0.16);
  --mds-info-border:    rgba(147, 197, 253, 0.28);

  --mds-neutral-bg:     rgba(255, 255, 255, 0.06);
  --mds-neutral-border: rgba(255, 255, 255, 0.12);

  --mds-success-border: rgba(52, 211, 153, 0.28);
  --mds-warning-border: rgba(251, 191, 36, 0.28);
  --mds-danger-border:  rgba(248, 113, 113, 0.28);

  --mds-shadow-1: 0 2px 6px rgba(0, 0, 0, 0.40);
  --mds-shadow-2: 0 4px 12px rgba(0, 0, 0, 0.45);
  --mds-shadow-3: 0 12px 32px rgba(0, 0, 0, 0.55);
  --mds-shadow-4: 0 16px 40px rgba(0, 0, 0, 0.60);
}


/* =============================================================================
   2. PAGE SHELL  (§3.1)
   ============================================================================= */

.mds-page {
  display: flex;
  flex-direction: column;
  min-height: calc(100dvh - var(--mds-shell-offset, 60px));
  padding: 0;
  font-family: var(--mds-font-sans);
  color: var(--mds-text-primary);
  background: var(--mds-bg-canvas);
}

/* -----------------------------------------------------------------------------
   BOUNDED CONTENT SCROLL  —  .mds-page--fill
   -----------------------------------------------------------------------------
   Opt-in layout for List pages, where the DATA is the only thing that scrolls.

   Why this exists rather than letting the document scroll:

   A wide table needs `overflow-x: auto` on its container. CSS then computes
   `overflow-y` to `auto` as well — there is no way to have one axis scrollable
   and the other visible. That silently turns the table container into a scroll
   container, which means `position: sticky` on `thead` sticks to the top of the
   CONTAINER, not the viewport. If the document is what actually scrolls, the
   container scrolls away and takes the "sticky" header with it: the header is
   gone by row 20, which is precisely the failure §9.4 exists to prevent.

   The fix is to make the table container the page's single scroll region, and
   to pin the header, toolbar, filter bar and pagination outside it. Then:
     - §3.2 holds: exactly one vertical scroll region.
     - §9.4 holds: the column header is genuinely always visible.
     - §9.10 holds: the totals footer is genuinely sticky to the table's bottom.
     - §10.2 holds: "Showing X-Y of N" never scrolls out of reach.

   --mds-shell-offset is the height of everything above .mds-page (the app
   topbar and any wrapper padding). The page script measures it at runtime and
   sets it on the element, because that chrome differs between shells; the 60px
   default is only what applies before script runs.
   -------------------------------------------------------------------------- */

.mds-page--fill {
  height: calc(100dvh - var(--mds-shell-offset, 60px));
  min-height: 0;
  overflow: hidden;
}

/* The min-height:0 on every link of the chain is required: a flex item's
   default min-height is auto, which refuses to shrink below its content and
   would push the scroll region past the bottom of the screen. */
.mds-page--fill .mds-page-body {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding-bottom: var(--mds-space-4);
}

/* A flex chain is only as long as its links: EVERY element between the card
   and .mds-table-scroll must itself be a column flex container that is allowed
   to shrink, or the bounded height stops there.

   This rule was originally written as `.mds-card--table > .mds-list`, which
   assumed the list partial is a direct child of the card. It is not, and cannot
   be: every list page injects its AJAX response into a wrapper element (the
   #<entity>ListBody div) so that the skeleton and the result can be swapped.
   The child combinator stopped at that wrapper, so .mds-list stayed
   `display: block`, `flex: 1 1 auto` on .mds-table-scroll was inert, the table
   rendered at its full content height, and .mds-card's `overflow: hidden`
   clipped it — a list that could not be scrolled at all.

   The wrapper is therefore matched explicitly. `:not([hidden])` is required:
   these selectors are more specific than the UA's `[hidden] { display: none }`,
   so without it the hidden skeleton would be forced back into view.
   `:not(.mds-print-header)` protects the print-only identification block, which
   is hidden by a class selector for the same reason. */
.mds-page--fill .mds-card--table,
.mds-page--fill .mds-card--table > div:not([hidden]):not(.mds-print-header),
.mds-page--fill .mds-card--table .mds-list {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

.mds-page--fill .mds-card--table .mds-table-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
}

.mds-page--fill .mds-pagination { flex: 0 0 auto; }

/* Header, toolbar and filter bar sit outside the scroll region, so they are
   permanently visible and their sticky positioning becomes a no-op. It is left
   in place for pages that do not opt into the bounded layout. */

/* The page body must never scroll horizontally (§3.2). Only the table
   container may, and it does so inside its own overflow box. */
.mds-page,
.mds-page > * {
  max-width: 100%;
}

/* Standard page gutter (§3.3) */
.mds-page-header,
.mds-toolbar,
.mds-filterbar,
.mds-page-body,
.mds-page-footer {
  padding-left: var(--mds-space-7);
  padding-right: var(--mds-space-7);
}

@media (max-width: 1199.98px) {
  .mds-page-header, .mds-toolbar, .mds-filterbar,
  .mds-page-body, .mds-page-footer {
    padding-left: var(--mds-space-5);
    padding-right: var(--mds-space-5);
  }
}

@media (max-width: 767.98px) {
  .mds-page-header, .mds-toolbar, .mds-filterbar,
  .mds-page-body, .mds-page-footer {
    padding-left: var(--mds-space-4);
    padding-right: var(--mds-space-4);
  }
}

.mds-page-body { flex: 1 1 auto; padding-bottom: var(--mds-space-7); }


/* =============================================================================
   3. PAGE HEADER  (§5)
   Max 96px including breadcrumb. Sticky. Identity left, actions right.
   ============================================================================= */

.mds-page-header {
  position: sticky;
  top: 0;
  z-index: var(--mds-z-header);
  background: var(--mds-bg-surface);
  border-bottom: 1px solid var(--mds-border);
  padding-top: var(--mds-space-2);
  padding-bottom: var(--mds-space-2);
}

.mds-breadcrumb {
  display: flex;
  align-items: center;
  gap: var(--mds-space-2);
  margin: 0 0 var(--mds-space-1);
  padding: 0;
  list-style: none;
  font-size: var(--mds-fs-caption);
  color: var(--mds-text-tertiary);
  line-height: 16px;
}

.mds-breadcrumb a {
  color: var(--mds-text-secondary);
  text-decoration: none;
}
.mds-breadcrumb a:hover { color: var(--mds-text-primary); text-decoration: underline; }
.mds-breadcrumb-sep { color: var(--mds-text-tertiary); }
.mds-breadcrumb [aria-current="page"] { color: var(--mds-text-secondary); }

.mds-page-header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--mds-space-5);
  min-height: 32px;
}

.mds-page-identity {
  display: flex;
  align-items: baseline;
  gap: var(--mds-space-3);
  min-width: 0;
}

.mds-page-title {
  margin: 0;
  font-size: var(--mds-fs-h1);
  font-weight: var(--mds-fw-semibold);
  line-height: var(--mds-lh-h1);
  color: var(--mds-text-primary);
  letter-spacing: -0.005em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Record count. Must state its scope when filtered (§5.2). */
.mds-page-count {
  font-size: var(--mds-fs-label);
  color: var(--mds-text-secondary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.mds-page-meta {
  margin: 2px 0 0;
  font-size: var(--mds-fs-caption);
  color: var(--mds-text-secondary);
  line-height: 16px;
}

/* Page actions. Least-committing left, single primary rightmost (§5.3, §14.3) */
.mds-page-actions {
  display: flex;
  align-items: center;
  gap: var(--mds-space-3);
  flex: 0 0 auto;
}

.mds-page-actions-divider {
  width: 1px;
  height: 20px;
  background: var(--mds-border);
}

/* Unsaved marker (§5.4) */
.mds-unsaved {
  display: inline-flex;
  align-items: center;
  gap: var(--mds-space-2);
  font-size: var(--mds-fs-caption);
  font-weight: var(--mds-fw-medium);
  color: var(--mds-warning-fg);
}


/* =============================================================================
   4. TOOLBAR  (§6)
   ============================================================================= */

.mds-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--mds-space-5);
  min-height: 44px;
  background: var(--mds-bg-surface);
  border-bottom: 1px solid var(--mds-border);
}

.mds-toolbar-group { display: flex; align-items: center; gap: var(--mds-space-2); }

/* Selection toolbar REPLACES the toolbar in place — it never stacks on top,
   because an appearing bar pushes the table down and moves the row the user
   was about to click (§6.3, Law 5). */
.mds-toolbar[hidden],
.mds-selection-bar[hidden] { display: none !important; }

.mds-selection-bar {
  display: flex;
  align-items: center;
  gap: var(--mds-space-4);
  min-height: 44px;
  background: var(--mds-accent-subtle);
  border-bottom: 1px solid var(--mds-accent-border);
}

.mds-selection-count {
  font-size: var(--mds-fs-body);
  font-weight: var(--mds-fw-semibold);
  font-variant-numeric: tabular-nums;
}


/* =============================================================================
   5. FILTER BAR  (§7)
   Row 1: controls. Row 2: applied-filter chips (present only when filtered).
   Search first and growing; every other control fixed width so that choosing
   a value never reflows its neighbours (Law 5).
   ============================================================================= */

.mds-filterbar {
  background: var(--mds-bg-surface);
  border-bottom: 1px solid var(--mds-border);
  padding-top: var(--mds-space-3);
  padding-bottom: var(--mds-space-3);
}

.mds-filter-row {
  display: flex;
  align-items: center;
  gap: var(--mds-space-3);
  flex-wrap: wrap;
}

.mds-filter-search {
  position: relative;
  flex: 1 1 280px;
  min-width: 220px;
  max-width: 460px;
}

.mds-filter-search .mds-input { padding-left: 30px; }

.mds-filter-search-icon {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 13px;
  color: var(--mds-text-tertiary);
  pointer-events: none;
}

.mds-filter-search-clear {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  width: 20px;
  height: 20px;
  display: none;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: var(--mds-radius-sm);
  background: transparent;
  color: var(--mds-text-tertiary);
  cursor: pointer;
  font-size: 12px;
}
.mds-filter-search-clear:hover { background: var(--mds-bg-hover); color: var(--mds-text-primary); }
.mds-filter-search.is-filled .mds-filter-search-clear { display: inline-flex; }

/* Fixed-width filter controls (§7.2) */
.mds-filter-control { flex: 0 0 160px; width: 160px; }
[data-mds-density="compact"] .mds-filter-control { flex-basis: 140px; width: 140px; }

.mds-filter-spacer { flex: 1 1 auto; min-width: 0; }

/* VIEW CONTROLS ON THE FILTER ROW
   -------------------------------
   §6 gives content-shaping controls (density, grouping, view switch) their own
   toolbar band. That band earns its 44px when it carries bulk actions or a view
   switcher. On a list that has neither — as most master lists do — a whole
   horizontal band spent on one <select> is the excessive whitespace this system
   exists to prevent, and it costs roughly two data rows at 1366x768.

   So the rule is conditional: render the toolbar band only when it has more
   than view preferences to carry. Otherwise the preferences sit at the right of
   the filter row, opposite the filters, which keeps them in a fixed position
   without spending a band on them. */
.mds-filter-tools {
  display: flex;
  align-items: center;
  gap: var(--mds-space-3);
  flex: 0 0 auto;
  margin-left: auto;
}

.mds-filter-tools .mds-select {
  height: 28px;
  font-size: var(--mds-fs-label);
}

.mds-filter-note {
  font-size: var(--mds-fs-caption);
  color: var(--mds-text-tertiary);
  white-space: nowrap;
}

/* Applied-filter chips. The entire active query, visible in one line (§7.3). */
.mds-chips {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--mds-space-2);
  margin-top: var(--mds-space-3);
}
.mds-chips:empty { display: none; }

.mds-chips-label {
  font-size: var(--mds-fs-caption);
  color: var(--mds-text-tertiary);
  margin-right: var(--mds-space-1);
}

.mds-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--mds-space-2);
  height: 22px;
  padding: 0 var(--mds-space-2) 0 var(--mds-space-3);
  border: 1px solid var(--mds-border);
  border-radius: var(--mds-radius-pill);
  background: var(--mds-bg-sunken);
  font-size: var(--mds-fs-caption);
  color: var(--mds-text-primary);
  white-space: nowrap;
}

.mds-chip-key { color: var(--mds-text-secondary); }

.mds-chip-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  padding: 0;
  border: 0;
  border-radius: var(--mds-radius-pill);
  background: transparent;
  color: var(--mds-text-tertiary);
  cursor: pointer;
  font-size: 10px;
  line-height: 1;
}
.mds-chip-remove:hover { background: var(--mds-neutral-bg); color: var(--mds-text-primary); }

/* "Clear all" is deliberately weaker than the chips it clears */
.mds-chips-clear {
  border: 0;
  background: none;
  padding: 0 var(--mds-space-2);
  font-size: var(--mds-fs-caption);
  color: var(--mds-text-secondary);
  text-decoration: underline;
  cursor: pointer;
}
.mds-chips-clear:hover { color: var(--mds-text-primary); }


/* =============================================================================
   6. CARD  (§19)  — partitions, does not float. No shadow.
   ============================================================================= */

.mds-card {
  background: var(--mds-bg-surface);
  border: 1px solid var(--mds-border);
  border-radius: var(--mds-radius-md);
  box-shadow: var(--mds-shadow-0);
  overflow: hidden;
}

.mds-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--mds-space-4);
  padding: var(--mds-space-3) var(--mds-space-5);
  border-bottom: 1px solid var(--mds-border);
  min-height: 40px;
}

.mds-card-title {
  margin: 0;
  font-size: var(--mds-fs-h2);
  font-weight: var(--mds-fw-semibold);
  color: var(--mds-text-primary);
}

.mds-card-body { padding: var(--mds-space-5); }
/* A card whose content is a table gives the table the full card width */
.mds-card--table > .mds-table-scroll { border-radius: 0; }

/* CLIPPED SEARCHABLE-SELECT MENU.
   `.mds-card` clips to its border-radius so a table, list or header cannot
   square off its corners. That clip also cuts every absolutely-positioned
   overlay a child opens: a typeahead menu (`.tt-menu` inside the
   `span.twitter-typeahead` typeahead.js wraps around the input) and a
   bootstrap-select menu are both position:absolute inside the card, so the part
   of the list that extends past the card's bottom edge is simply not painted.
   On the purchase-invoice entry screen the supplier search owns a short card of
   its own, so nearly the whole suggestion list disappeared and the "Invoice
   Information" card below read as if it were covering it. It was never a
   z-index conflict — `.tt-menu` already carries z-index:1000 in custom.css and
   the following cards are unpositioned.

   The corner clip is only ever needed for content that reaches the card edge,
   which is `.mds-card--table`; that variant keeps clipping. Any other card that
   hosts one of these two menus opts out, so the menu escapes the card and
   paints over what follows. This is the page-level counterpart of the modal
   fix in public/assets/js/core/mds-selectpicker.js, which re-parents the same
   menu out of `.mds-modal-body`. */
.mds-card:not(.mds-card--table):has(.twitter-typeahead),
.mds-card:not(.mds-card--table):has(.bootstrap-select) {
  overflow: visible;
}


/* =============================================================================
   7. DATA TABLE  (§9)
   Sticky header, fixed row height, hairline separators, NO zebra striping,
   right-aligned tabular numerics, sticky action column, sticky totals footer.
   ============================================================================= */

.mds-table-scroll {
  overflow-x: auto;
  overflow-y: visible;
  -webkit-overflow-scrolling: touch;
}

.mds-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--mds-fs-cell);
  line-height: var(--mds-lh-data);
  color: var(--mds-text-primary);
  background: var(--mds-bg-surface);
}

/* ---- header ---- */
.mds-table thead th {
  position: sticky;
  top: 0;
  z-index: var(--mds-z-sticky);
  padding: var(--mds-space-3) var(--mds-cell-pad-x);
  background: var(--mds-bg-sunken);
  border-bottom: 2px solid var(--mds-border-strong);
  font-size: var(--mds-fs-label);
  font-weight: var(--mds-fw-medium);
  color: var(--mds-text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  text-align: left;
  white-space: nowrap;
  user-select: none;
}

/* Header alignment always matches its data (§9.3) */
.mds-table th.mds-num,
.mds-table td.mds-num { text-align: right; }

.mds-table th.mds-center,
.mds-table td.mds-center { text-align: center; }

/* Sortable header. 2-state toggle; a third click does NOT clear (§9.8). */
.mds-th-sort {
  display: inline-flex;
  align-items: center;
  gap: var(--mds-space-2);
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-transform: inherit;
  letter-spacing: inherit;
  cursor: pointer;
  text-align: inherit;
}
.mds-num > .mds-th-sort { justify-content: flex-end; }

.mds-th-sort .mds-sort-icon {
  font-size: 9px;
  opacity: 0;
  transition: opacity var(--mds-duration-fast) var(--mds-ease-out);
}
.mds-th-sort:hover .mds-sort-icon,
.mds-th-sort:focus-visible .mds-sort-icon { opacity: 0.45; }

/* The active sort column shows its arrow permanently */
.mds-table th[aria-sort] .mds-sort-icon { opacity: 1; color: var(--mds-accent); }
.mds-table th[aria-sort] .mds-th-sort   { color: var(--mds-text-primary); font-weight: var(--mds-fw-semibold); }

/* ---- body ---- */
.mds-table tbody td {
  height: var(--mds-row-h);
  padding: 0 var(--mds-cell-pad-x);
  border-bottom: 1px solid var(--mds-border);
  vertical-align: middle;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mds-table tbody tr { background: var(--mds-bg-surface); }

/* Hover fills the whole row, including sticky cells */
.mds-table tbody tr:hover > td { background: var(--mds-bg-hover); }

/* Selection is distinguishable while hovered */
.mds-table tbody tr.is-selected > td { background: var(--mds-bg-selected); }
.mds-table tbody tr.is-selected:hover > td { background: var(--mds-bg-selected); }

.mds-table tbody tr.is-clickable { cursor: pointer; }

/* Keyboard focus on a row (§9.5, §23.4) */
.mds-table tbody tr:focus-visible {
  outline: 2px solid var(--mds-accent);
  outline-offset: -2px;
}

/* ---- cell content primitives ---- */

/* Numbers: tabular figures + fixed decimals make a column scannable (§9.3) */
.mds-num {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

.mds-cell-strong { font-weight: var(--mds-fw-medium); color: var(--mds-text-primary); }
.mds-cell-muted  { color: var(--mds-text-secondary); }

/* Secondary line inside an identity cell only (§9.6).
   The two lines must fit inside the fixed row height at EVERY density, or the
   cell's overflow:hidden clips the descenders and the row silently stops
   telling the truth. Standard: 18 + 14 = 32 <= 36. Compact: 16 + 12 = 28 <= 30. */
.mds-cell-sub {
  display: block;
  font-size: var(--mds-fs-caption);
  color: var(--mds-text-tertiary);
  line-height: 14px;
}

[data-mds-density="compact"] .mds-table tbody td { line-height: 16px; }
[data-mds-density="compact"] .mds-cell-sub { line-height: 12px; font-size: 10px; }

/* Em dash for "looked and there is nothing". Never a blank cell (§9.6, §26.5) */
.mds-nil { color: var(--mds-text-tertiary); }

.mds-mono { font-family: var(--mds-font-mono); font-size: 0.95em; }

/* AMOUNT COLOUR
   -------------
   A non-zero amount is NOT a status. An outstanding receivable is ordinary
   business, not an error, so it renders in body colour with medium weight —
   §2.2.1 forbids the danger role from being used decoratively, and a column
   where every populated cell is red trains the user to stop seeing red at all.
   Danger is reserved for values that are genuinely negative, and success for
   values that are genuinely a credit. Zero is dimmed so that the rows carrying
   a figure win the vertical scan. */
.mds-amount      { color: var(--mds-text-primary); font-weight: var(--mds-fw-medium); }
.mds-amount-neg  { color: var(--mds-danger-fg);  font-weight: var(--mds-fw-medium); }
.mds-amount-pos  { color: var(--mds-success-fg); font-weight: var(--mds-fw-medium); }
.mds-amount-zero { color: var(--mds-text-tertiary); }

/* ---- sticky columns ---- */
.mds-table .mds-col-actions {
  position: sticky;
  right: 0;
  z-index: 2;
  width: 1%;
  padding-left: var(--mds-space-3);
  padding-right: var(--mds-space-3);
  background: var(--mds-bg-surface);
  box-shadow: -1px 0 0 var(--mds-border);
  text-align: right;
}
.mds-table thead .mds-col-actions { background: var(--mds-bg-sunken); z-index: 3; }
.mds-table tbody tr:hover > .mds-col-actions { background: var(--mds-bg-hover); }
.mds-table tbody tr.is-selected > .mds-col-actions { background: var(--mds-bg-selected); }

.mds-table .mds-col-select {
  width: 40px;
  padding-left: var(--mds-space-4);
  padding-right: 0;
}

/* ---- row actions (§9.7). Always present — never hover-revealed. ---- */
.mds-row-actions {
  display: inline-flex;
  align-items: center;
  gap: var(--mds-space-1);
  justify-content: flex-end;
}

.mds-row-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: var(--mds-radius-sm);
  background: transparent;
  color: var(--mds-text-tertiary);
  cursor: pointer;
  font-size: 13px;
  transition: color var(--mds-duration-fast), background-color var(--mds-duration-fast);
}
/* Low contrast at rest, full contrast on row hover: present but quiet. */
.mds-table tbody tr:hover .mds-row-action { color: var(--mds-text-secondary); }
.mds-row-action:hover { background: var(--mds-neutral-bg); color: var(--mds-text-primary); }
.mds-row-action--danger:hover { background: var(--mds-danger-bg); color: var(--mds-danger-fg); }
.mds-row-action[disabled] { opacity: 0.4; cursor: not-allowed; }

/* ---- TOTALS (§9.10) ----
   Two totals, in two different places, for a structural reason.

   `position: sticky` on a tfoot cannot pin the totals above unscrolled rows:
   a sticky element may never leave its containing block, and for a table cell
   that block is the table. So a "sticky" tfoot only engages once the END of
   the table is already in view — which is exactly when it was going to be
   visible anyway. Pretending otherwise produces a totals row that silently
   isn't there for the first 40 rows.

   Therefore:
     PAGE total    -> tfoot, at the end of this page's rows, where a subtotal
                      belongs and where column alignment is free.
     FILTERED total-> .mds-totals-bar, outside the scroll region, permanently
                      visible. This is the figure that gets copied into a
                      report or an email, so it is the one that must never be
                      mistaken for the other. */

.mds-table tfoot td {
  height: var(--mds-totals-h, 30px);
  padding: 0 var(--mds-cell-pad-x);
  background: var(--mds-bg-sunken);
  border-top: 1px solid var(--mds-border-strong);
  font-size: var(--mds-fs-label);
  font-weight: var(--mds-fw-semibold);
  white-space: nowrap;
}
.mds-table tfoot .mds-totals-label {
  font-weight: var(--mds-fw-medium);
  color: var(--mds-text-secondary);
  text-transform: none;
}
.mds-table tfoot .mds-col-actions { box-shadow: -1px 0 0 var(--mds-border); background: var(--mds-bg-sunken); }

/* Always-visible totals bar. Labelled with its scope and its row count,
   because an unlabelled total that reflects only the visible page is the most
   consequential thing a table can get wrong. */
.mds-totals-bar {
  display: flex;
  align-items: center;
  gap: var(--mds-space-7);
  flex: 0 0 auto;
  min-height: 30px;
  padding: 0 var(--mds-space-5);
  background: var(--mds-bg-sunken);
  border-top: 1px solid var(--mds-border-strong);
  font-size: var(--mds-fs-label);
}

.mds-totals-bar-scope {
  color: var(--mds-text-secondary);
  white-space: nowrap;
}
.mds-totals-bar-scope strong {
  color: var(--mds-text-primary);
  font-weight: var(--mds-fw-semibold);
}

.mds-totals-bar-figures {
  display: flex;
  align-items: baseline;
  gap: var(--mds-space-7);
  /* margin:0 is load-bearing: <dl> carries a UA default of 1em top and bottom,
     which silently added 24px to this bar and cost a data row. */
  margin: 0 0 0 auto;
  flex-wrap: wrap;
  justify-content: flex-end;
}

.mds-totals-bar-figure { display: inline-flex; align-items: baseline; gap: var(--mds-space-3); white-space: nowrap; }
.mds-totals-bar-figure dt { color: var(--mds-text-secondary); margin: 0; font-weight: var(--mds-fw-normal); }
.mds-totals-bar-figure dd {
  margin: 0;
  font-variant-numeric: tabular-nums;
  font-weight: var(--mds-fw-semibold);
  color: var(--mds-text-primary);
}


/* =============================================================================
   8. STATUS BADGES  (§16)  — text is mandatory; never a bare coloured dot.
   ============================================================================= */

.mds-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--mds-space-2);
  height: var(--mds-badge-h);
  padding: 0 var(--mds-space-3);
  border: 1px solid transparent;
  border-radius: var(--mds-radius-pill);
  font-size: var(--mds-fs-label);
  font-weight: var(--mds-fw-medium);
  line-height: 1;
  white-space: nowrap;
  vertical-align: middle;
}

.mds-badge--success { background: var(--mds-success-bg); color: var(--mds-success-fg); border-color: var(--mds-success-border); }
.mds-badge--warning { background: var(--mds-warning-bg); color: var(--mds-warning-fg); border-color: var(--mds-warning-border); }
.mds-badge--danger  { background: var(--mds-danger-bg);  color: var(--mds-danger-fg);  border-color: var(--mds-danger-border); }
.mds-badge--info    { background: var(--mds-info-bg);    color: var(--mds-info-fg);    border-color: var(--mds-info-border); }
.mds-badge--neutral { background: var(--mds-neutral-bg); color: var(--mds-neutral-fg); border-color: var(--mds-neutral-border); }

/* Count badge — a number, not a status */
.mds-count-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 18px;
  padding: 0 6px;
  border-radius: var(--mds-radius-pill);
  background: var(--mds-neutral-bg);
  color: var(--mds-text-secondary);
  font-size: var(--mds-fs-caption);
  font-weight: var(--mds-fw-medium);
  font-variant-numeric: tabular-nums;
}


/* =============================================================================
   9. BUTTONS  (§14)
   ============================================================================= */

.mds-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--mds-space-2);
  height: var(--mds-control-h);
  padding: 0 14px;
  border: 1px solid transparent;
  border-radius: var(--mds-radius-sm);
  font-family: inherit;
  font-size: var(--mds-fs-data);
  font-weight: var(--mds-fw-medium);
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  text-decoration: none;
  transition: background-color var(--mds-duration-fast) var(--mds-ease-out),
              border-color var(--mds-duration-fast) var(--mds-ease-out),
              color var(--mds-duration-fast) var(--mds-ease-out);
}

/* No transform, no shadow change, no size change on hover (§14.7):
   movement under the pointer causes overshoot on precision clicks. */
.mds-btn:hover { text-decoration: none; }

.mds-btn--primary {
  background: var(--mds-accent);
  border-color: var(--mds-accent);
  color: var(--mds-accent-fg);
}
.mds-btn--primary:hover  { background: var(--mds-accent-hover);  border-color: var(--mds-accent-hover); color: var(--mds-accent-fg); }
.mds-btn--primary:active { background: var(--mds-accent-active); border-color: var(--mds-accent-active); }

.mds-btn--secondary {
  background: var(--mds-bg-surface);
  border-color: var(--mds-border-strong);
  color: var(--mds-text-primary);
}
.mds-btn--secondary:hover  { background: var(--mds-bg-hover); color: var(--mds-text-primary); }
.mds-btn--secondary:active { background: var(--mds-bg-sunken); }

.mds-btn--ghost {
  background: transparent;
  border-color: transparent;
  color: var(--mds-text-secondary);
}
.mds-btn--ghost:hover  { background: var(--mds-bg-hover); color: var(--mds-text-primary); }
.mds-btn--ghost:active { background: var(--mds-neutral-bg); }

.mds-btn--danger {
  background: var(--mds-danger-fg);
  border-color: var(--mds-danger-fg);
  color: #ffffff;
}
.mds-btn--danger:hover { filter: brightness(0.92); color: #ffffff; }

.mds-btn--sm { height: 28px; padding: 0 10px; font-size: var(--mds-fs-label); }
.mds-btn--lg { height: 40px; padding: 0 20px; font-size: var(--mds-fs-body); }

/* Icon-only: square at the same height, needs tooltip + accessible name */
.mds-btn--icon { width: var(--mds-control-h); padding: 0; }
.mds-btn--icon.mds-btn--sm { width: 28px; }
.mds-btn--icon.mds-btn--lg { width: 40px; }

/* Disabled buttons are explained, never silently inert (§5.3, §14.6) */
.mds-btn[disabled],
.mds-btn.is-disabled {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: auto;
}

/* Busy state. Width MUST NOT change — a shrinking button moves its
   neighbours and the user aiming at Cancel hits something else (§14.6). */
.mds-btn.is-busy {
  position: relative;
  color: transparent !important;
  pointer-events: none;
}
.mds-btn.is-busy::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 14px;
  height: 14px;
  margin: -7px 0 0 -7px;
  border: 2px solid currentColor;
  border-radius: 50%;
  border-top-color: transparent;
  color: var(--mds-accent-fg);
  animation: mds-spin 600ms linear infinite;
}
.mds-btn--secondary.is-busy::after,
.mds-btn--ghost.is-busy::after { color: var(--mds-text-secondary); }

@keyframes mds-spin { to { transform: rotate(360deg); } }


/* =============================================================================
   10. FORM CONTROLS  (§12)
   Labels above. Fixed control height. Errors below the field, never colour only.
   ============================================================================= */

.mds-form-section { margin-bottom: var(--mds-space-8); }
.mds-form-section:last-child { margin-bottom: 0; }

.mds-form-section-title {
  display: flex;
  align-items: center;
  gap: var(--mds-space-3);
  margin: 0 0 var(--mds-space-4);
  padding-bottom: var(--mds-space-3);
  border-bottom: 1px solid var(--mds-border);
  font-size: var(--mds-fs-h3);
  font-weight: var(--mds-fw-semibold);
  color: var(--mds-text-primary);
}

.mds-form-section-title .mds-count-badge { margin-left: auto; }

.mds-form-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--mds-space-5) var(--mds-space-5);
}

/* Field width is a type hint the user reads before the label (§12.1) */
.mds-col-2  { grid-column: span 2; }
.mds-col-3  { grid-column: span 3; }
.mds-col-4  { grid-column: span 4; }
.mds-col-5  { grid-column: span 5; }
.mds-col-6  { grid-column: span 6; }
.mds-col-8  { grid-column: span 8; }
.mds-col-12 { grid-column: span 12; }

.mds-field { display: flex; flex-direction: column; min-width: 0; }

.mds-label {
  display: block;
  margin: 0 0 var(--mds-space-2);
  font-size: var(--mds-fs-label);
  font-weight: var(--mds-fw-medium);
  color: var(--mds-text-secondary);
  line-height: 16px;
}

/* Required is marked; optional is not — in a transactional ERP most fields
   are required, so marking the minority keeps the marker salient (§12.2). */
.mds-required {
  color: var(--mds-danger-fg);
  margin-left: 2px;
  font-weight: var(--mds-fw-semibold);
}

.mds-input,
.mds-select,
.mds-textarea {
  width: 100%;
  height: var(--mds-control-h);
  padding: 0 10px;
  border: 1px solid var(--mds-border-strong);
  border-radius: var(--mds-radius-sm);
  background: var(--mds-bg-surface);
  color: var(--mds-text-primary);
  font-family: inherit;
  font-size: var(--mds-fs-body);
  line-height: normal;
  transition: border-color var(--mds-duration-fast) var(--mds-ease-out);
}

.mds-textarea {
  height: auto;
  min-height: 60px;
  padding: 6px 10px;
  line-height: var(--mds-lh-body);
  resize: vertical;
}

.mds-select {
  padding-right: 26px;
  appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%236b7280' stroke-width='1.6'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
  background-size: 14px;
}
[data-bs-theme="dark"] .mds-select {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%2394a3b8' stroke-width='1.6'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
}

.mds-input::placeholder,
.mds-textarea::placeholder { color: var(--mds-text-tertiary); }

.mds-input:hover,
.mds-select:hover,
.mds-textarea:hover { border-color: var(--mds-text-tertiary); }

.mds-input[readonly],
.mds-input[disabled],
.mds-select[disabled],
.mds-textarea[disabled] {
  background: var(--mds-bg-disabled);
  color: var(--mds-text-disabled);
  cursor: not-allowed;
}

/* Amount / quantity inputs align with the columns they will end up in */
.mds-input--num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* Prefixed input (currency adornment inside the field) */
.mds-input-group { position: relative; display: flex; align-items: center; }
.mds-input-group .mds-input { padding-left: 26px; }
.mds-input-prefix {
  position: absolute;
  left: 9px;
  font-size: var(--mds-fs-data);
  color: var(--mds-text-tertiary);
  pointer-events: none;
}

/* Helper text is replaced by the error, never shown alongside it (§12.4) */
.mds-help {
  margin: var(--mds-space-2) 0 0;
  font-size: var(--mds-fs-caption);
  color: var(--mds-text-tertiary);
  line-height: 14px;
}

.mds-error {
  display: none;
  align-items: flex-start;
  gap: var(--mds-space-2);
  margin: var(--mds-space-2) 0 0;
  font-size: var(--mds-fs-caption);
  color: var(--mds-danger-fg);
  line-height: 14px;
}

/* Border AND text. Never colour alone (Law 8). */
.mds-field.is-invalid .mds-input,
.mds-field.is-invalid .mds-select,
.mds-field.is-invalid .mds-textarea { border-color: var(--mds-danger-fg); }
.mds-field.is-invalid .mds-error { display: flex; }
.mds-field.is-invalid .mds-help  { display: none; }

/* Checkbox / radio */
.mds-check {
  display: inline-flex;
  align-items: center;
  gap: var(--mds-space-3);
  cursor: pointer;
  font-size: var(--mds-fs-body);
  color: var(--mds-text-primary);
  min-height: 24px;
}
.mds-check input { width: 15px; height: 15px; margin: 0; accent-color: var(--mds-accent); cursor: pointer; flex: 0 0 auto; }
.mds-check-sub {
  display: block;
  font-size: var(--mds-fs-caption);
  color: var(--mds-text-tertiary);
  line-height: 14px;
}

/* Sticky form action bar (§12.5) */
.mds-form-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--mds-space-3);
  padding: var(--mds-space-4) var(--mds-space-7);
  background: var(--mds-bg-surface);
  border-top: 1px solid var(--mds-border);
}
.mds-form-actions-left { margin-right: auto; }


/* =============================================================================
   11. PAGINATION  (§10)
   ============================================================================= */

.mds-pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--mds-space-5);
  flex-wrap: wrap;
  min-height: 34px;
  padding: var(--mds-space-1) var(--mds-space-5);
  border-top: 1px solid var(--mds-border);
  background: var(--mds-bg-surface);
  font-size: var(--mds-fs-label);
  color: var(--mds-text-secondary);
}

/* "Showing X–Y of N" is mandatory — it is the only element that tells the
   user whether they are looking at all the data (§10.2). */
.mds-pagination-range { font-variant-numeric: tabular-nums; white-space: nowrap; }
.mds-pagination-range strong { color: var(--mds-text-primary); font-weight: var(--mds-fw-semibold); }

.mds-pagination-size { display: flex; align-items: center; gap: var(--mds-space-3); }
.mds-pagination-size .mds-select { width: 72px; height: 28px; font-size: var(--mds-fs-label); }

.mds-pagination-pages { display: flex; align-items: center; gap: var(--mds-space-1); }

.mds-page-btn {
  min-width: 28px;
  height: 28px;
  padding: 0 6px;
  border: 1px solid transparent;
  border-radius: var(--mds-radius-sm);
  background: transparent;
  color: var(--mds-text-secondary);
  font-size: var(--mds-fs-label);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}
.mds-page-btn:hover:not([disabled]):not([aria-current]) { background: var(--mds-bg-hover); color: var(--mds-text-primary); }
.mds-page-btn[aria-current="page"] {
  background: var(--mds-accent-subtle);
  border-color: var(--mds-accent-border);
  color: var(--mds-text-primary);
  font-weight: var(--mds-fw-semibold);
}
/* Disabled at the boundaries, not hidden (§10.2) */
.mds-page-btn[disabled] { opacity: 0.35; cursor: not-allowed; }
.mds-page-gap { padding: 0 4px; color: var(--mds-text-tertiary); }


/* =============================================================================
   12. EMPTY / ERROR / FORBIDDEN STATES  (§17, §15.4)
   Four distinct states, because they demand four different user responses.
   ============================================================================= */

.mds-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--mds-space-3);
  padding: var(--mds-space-10) var(--mds-space-7);
  text-align: center;
}

/* In the bounded layout an empty state occupies the region the table would
   have filled, so the card does not collapse to a strip and the pagination
   bar stays where it always is. */
.mds-page--fill .mds-list > .mds-state { flex: 1 1 auto; }

.mds-state-icon {
  font-size: 32px;
  line-height: 1;
  color: var(--mds-text-tertiary);
  margin-bottom: var(--mds-space-1);
}
.mds-state--error .mds-state-icon { color: var(--mds-danger-fg); }
.mds-state--forbidden .mds-state-icon { color: var(--mds-warning-fg); }

.mds-state-title {
  margin: 0;
  font-size: var(--mds-fs-h2);
  font-weight: var(--mds-fw-semibold);
  color: var(--mds-text-primary);
}

.mds-state-text {
  margin: 0;
  max-width: 420px;
  font-size: var(--mds-fs-data);
  color: var(--mds-text-secondary);
  line-height: var(--mds-lh-body);
}

.mds-state-ref {
  font-family: var(--mds-font-mono);
  font-size: var(--mds-fs-caption);
  color: var(--mds-text-tertiary);
}

.mds-state-actions { display: flex; gap: var(--mds-space-3); margin-top: var(--mds-space-2); }


/* =============================================================================
   13. LOADING  (§18)
   Skeletons match final dimensions exactly, so nothing shifts on arrival.
   ============================================================================= */

.mds-skeleton {
  display: block;
  border-radius: var(--mds-radius-sm);
  background: linear-gradient(
    90deg,
    var(--mds-neutral-bg) 25%,
    var(--mds-bg-hover) 37%,
    var(--mds-neutral-bg) 63%
  );
  background-size: 400% 100%;
  animation: mds-shimmer 1.4s ease-in-out infinite;
}
@keyframes mds-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }

.mds-skeleton-text { height: 10px; }
.mds-skeleton-row td { height: var(--mds-row-h); }

/* Region refreshing in place: dim but do not blank, and do not block reading */
.mds-region-busy { position: relative; }
.mds-region-busy > * { opacity: 0.45; transition: opacity var(--mds-duration-base); }
.mds-region-busy::after {
  content: "";
  position: absolute;
  inset: 0;
  cursor: progress;
}

.mds-inline-spinner {
  display: inline-block;
  width: 14px;
  height: 14px;
  border: 2px solid var(--mds-border-strong);
  border-top-color: var(--mds-accent);
  border-radius: 50%;
  animation: mds-spin 600ms linear infinite;
  vertical-align: -2px;
}


/* =============================================================================
   14. MODAL  (§13)
   Fixed header, scrolling body, fixed footer. Max 80vh. Never stacked.
   ============================================================================= */

.mds-modal .modal-dialog { margin: 1.75rem auto; }

.mds-modal .modal-content {
  border: 1px solid var(--mds-border);
  border-radius: var(--mds-radius-lg);
  background: var(--mds-bg-surface);
  box-shadow: var(--mds-shadow-3);
  max-height: 80vh;
  overflow: hidden;
}

/* Sizes (§13.1) */
.mds-modal--sm .modal-dialog { max-width: 420px; }
.mds-modal--md .modal-dialog { max-width: 600px; }
.mds-modal--lg .modal-dialog { max-width: 840px; }
.mds-modal--xl .modal-dialog { max-width: 1080px; }

.mds-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--mds-space-4);
  flex: 0 0 auto;
  min-height: 56px;
  padding: 0 var(--mds-space-5);
  border-bottom: 1px solid var(--mds-border);
  background: var(--mds-bg-surface);
}

.mds-modal-title {
  margin: 0;
  font-size: var(--mds-fs-h2);
  font-weight: var(--mds-fw-semibold);
  color: var(--mds-text-primary);
}

.mds-modal-subtitle {
  margin: 2px 0 0;
  font-size: var(--mds-fs-caption);
  color: var(--mds-text-secondary);
}

.mds-modal-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 0;
  border-radius: var(--mds-radius-sm);
  background: transparent;
  color: var(--mds-text-secondary);
  font-size: 15px;
  cursor: pointer;
}
.mds-modal-close:hover { background: var(--mds-bg-hover); color: var(--mds-text-primary); }

.mds-modal-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--mds-space-5);
  background: var(--mds-bg-surface);
}

.mds-modal-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--mds-space-3);
  flex: 0 0 auto;
  min-height: 64px;
  padding: var(--mds-space-4) var(--mds-space-5);
  border-top: 1px solid var(--mds-border);
  background: var(--mds-bg-surface);
}
.mds-modal-footer-left { margin-right: auto; }

/* Modal-level error summary. On failed submit, focus moves here and each
   entry links to its field, because on a long form the first error may be
   several screens away (§12.4). */
.mds-form-summary {
  display: none;
  margin: 0 0 var(--mds-space-5);
  padding: var(--mds-space-4);
  border: 1px solid var(--mds-danger-border);
  border-radius: var(--mds-radius-md);
  background: var(--mds-danger-bg);
}
.mds-form-summary.is-visible { display: block; }
.mds-form-summary-title {
  margin: 0 0 var(--mds-space-2);
  font-size: var(--mds-fs-data);
  font-weight: var(--mds-fw-semibold);
  color: var(--mds-danger-fg);
}
.mds-form-summary ul { margin: 0; padding-left: var(--mds-space-6); }
.mds-form-summary li { font-size: var(--mds-fs-caption); line-height: 18px; }
.mds-form-summary a { color: var(--mds-danger-fg); }


/* =============================================================================
   15. INLINE ALERT  (§15.2)
   ============================================================================= */

.mds-alert {
  display: flex;
  align-items: flex-start;
  gap: var(--mds-space-3);
  padding: var(--mds-space-3) var(--mds-space-4);
  border: 1px solid var(--mds-border);
  border-radius: var(--mds-radius-md);
  background: var(--mds-bg-sunken);
  font-size: var(--mds-fs-data);
  line-height: var(--mds-lh-body);
}
.mds-alert-icon { flex: 0 0 auto; margin-top: 1px; }
.mds-alert-title { display: block; font-weight: var(--mds-fw-semibold); }
.mds-alert-text  { display: block; color: var(--mds-text-secondary); font-size: var(--mds-fs-caption); }

.mds-alert--info    { background: var(--mds-info-bg);    border-color: var(--mds-info-border);    color: var(--mds-info-fg); }
.mds-alert--success { background: var(--mds-success-bg); border-color: var(--mds-success-border); color: var(--mds-success-fg); }
.mds-alert--warning { background: var(--mds-warning-bg); border-color: var(--mds-warning-border); color: var(--mds-warning-fg); }
.mds-alert--danger  { background: var(--mds-danger-bg);  border-color: var(--mds-danger-border);  color: var(--mds-danger-fg); }
.mds-alert--info .mds-alert-text,
.mds-alert--success .mds-alert-text,
.mds-alert--warning .mds-alert-text,
.mds-alert--danger .mds-alert-text { color: inherit; opacity: 0.85; }


/* =============================================================================
   16. ACCESSIBILITY  (§23)
   ============================================================================= */

/* Focus is visible on every control, including ghost buttons and table rows.
   `outline: none` without a compliant replacement is prohibited anywhere. */
.mds-btn:focus-visible,
.mds-row-action:focus-visible,
.mds-page-btn:focus-visible,
.mds-chip-remove:focus-visible,
.mds-chips-clear:focus-visible,
.mds-modal-close:focus-visible,
.mds-th-sort:focus-visible,
.mds-filter-search-clear:focus-visible,
.mds-tabs .nav-link:focus-visible {
  outline: 2px solid var(--mds-accent);
  outline-offset: 2px;
}

/* The tab strip is a horizontal scroll container on small screens; an offset
   ring on the first tab would be clipped by it. Inset instead of removed —
   §23.4 prohibits removing the ring, not moving it. */
.mds-tabs .nav-link:focus-visible { outline-offset: -2px; }

.mds-input:focus,
.mds-select:focus,
.mds-textarea:focus {
  outline: 2px solid var(--mds-accent);
  outline-offset: -1px;
  border-color: var(--mds-accent);
}

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

/* Skip link — without it a keyboard user tabs the whole menu on every page */
.mds-skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: var(--mds-z-tooltip);
  padding: var(--mds-space-3) var(--mds-space-5);
  background: var(--mds-accent);
  color: var(--mds-accent-fg);
  border-radius: 0 0 var(--mds-radius-sm) 0;
  font-size: var(--mds-fs-data);
}
.mds-skip-link:focus { left: 0; }

.mds-sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .mds-skeleton { animation: none; background: var(--mds-neutral-bg); }
}


/* =============================================================================
   17. TABS  (§4.5)

   Extracted from the Expenses module, which is the behavioural reference. That
   screen's hand-rolled `expense-module-tabs` strip is the only tab pattern the
   product had; this is that pattern, tokenised, with nothing added and nothing
   redesigned.

   Underline tabs, deliberately. Not pills, not a card strip, not a segmented
   control: an underline costs one border, adds no vertical rhythm of its own,
   and reads as "these are views of one thing" rather than "these are separate
   destinations" — which is what a tabbed ERP screen actually means.

   Markup is the Bootstrap tab contract, unchanged, so `data-bs-toggle="tab"`,
   `shown.bs.tab`, roles and keyboard navigation all come from Bootstrap and no
   JavaScript is added by the Design System:

     <ul class="nav mds-tabs" role="tablist">
       <li class="nav-item" role="presentation">
         <button class="nav-link active" data-bs-toggle="tab"
                 data-bs-target="#pane-x" type="button" role="tab">Label</button>
       </li>
     </ul>
     <div class="tab-content"> <div class="tab-pane fade show active" id="pane-x" role="tabpanel"> … </div> </div>
   ============================================================================= */

.mds-tabs {
  gap: 0;
  margin-bottom: var(--mds-space-5);
  border-bottom: 2px solid var(--mds-border);
}

.mds-tabs .nav-link {
  padding: var(--mds-space-3) var(--mds-space-5);
  border: none;
  border-bottom: 2px solid transparent;
  /* Sits ON the strip's own border rather than above it, so the active
     underline replaces that segment instead of stacking a second line. */
  margin-bottom: -2px;
  border-radius: 0;
  background: transparent;
  font-family: inherit;
  font-size: var(--mds-fs-data);
  font-weight: var(--mds-fw-medium);
  line-height: 1;
  color: var(--mds-text-secondary);
  transition: color var(--mds-duration-fast) var(--mds-ease-out),
              border-color var(--mds-duration-fast) var(--mds-ease-out);
}

.mds-tabs .nav-link:hover {
  color: var(--mds-text-primary);
  border-bottom-color: var(--mds-border-strong);
}

/* §4.5: the active tab MUST carry a 2px accent underline AND semibold weight.
   Both, not either — the underline alone fails for a user who cannot separate
   the accent from the body colour, and weight alone is too quiet at this size.
   This is one of the few places a brand colour is also information: it answers
   "which view am I in" at a glance. */
.mds-tabs .nav-link.active {
  color: var(--mds-accent);
  border-bottom-color: var(--mds-accent);
  font-weight: var(--mds-fw-semibold);
  background: transparent;
}

.mds-tabs .nav-link[disabled],
.mds-tabs .nav-link.disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* A count belongs on the tab, not in the pane — it is the reason a user picks
   one tab over another. Composed from the existing badge, not a new element. */
.mds-tabs .nav-link .mds-count-badge { margin-left: var(--mds-space-2); }

/* §4.5 prohibits horizontal scrolling: "if they do not fit, there are too many"
   — the cap is 7 tabs. On a narrow viewport the strip therefore WRAPS rather
   than scrolls, so no tab can be hidden off-screen with nothing to indicate it.
   The strip's own bottom border is dropped when wrapped, because a second row
   sitting on it reads as two separate strips. */
@media (max-width: 767.98px) {
  .mds-tabs { flex-wrap: wrap; row-gap: 0; border-bottom-width: 0; }
  .mds-tabs .nav-link { margin-bottom: 0; }
}


/* =============================================================================
   18. CONFIRMATION DIALOG  (§12.6, §13, Law 7)

   The one dialog in the product that asks "are you sure". It is a composition
   of the existing modal, not a new shell: `.mds-modal--sm` supplies the width,
   `.mds-modal-header/-body/-footer` supply the structure, `.mds-btn--danger`
   supplies the action. Only the two classes below are new, and both exist to
   make the RECORD unmistakable.

   Four rules this component encodes so that 100+ screens cannot each decide
   them differently:

     1. The record is named. "Delete this customer?" is not a question anyone
        can answer safely; "Delete ACME Traders?" is.
     2. The consequence is stated in one sentence, in plain language, including
        whether it can be undone.
     3. Cancel is on the LEFT and is the SECONDARY button; the destructive
        action is on the right and is the only danger-coloured element.
     4. Focus lands on CANCEL. A confirm dialog that focuses its own destructive
        button turns a reflexive Enter into a deletion.

   Built and destroyed by public/assets/js/core/mds-confirm.js. No module
   ships confirm markup.
   ============================================================================= */

/* The record being acted on. Sunken, bordered, monospaced where the caller
   passes an identifier — the same treatment a voucher number gets in a table,
   so the thing on screen reads as the same object the user was just looking
   at. */
.mds-confirm-record {
  display: block;
  margin: var(--mds-space-4) 0 0;
  padding: var(--mds-space-3) var(--mds-space-4);
  border: 1px solid var(--mds-border);
  border-left: 2px solid var(--mds-accent);
  border-radius: var(--mds-radius-sm);
  background: var(--mds-bg-sunken);
  font-size: var(--mds-fs-data);
  font-weight: var(--mds-fw-semibold);
  color: var(--mds-text-primary);
  word-break: break-word;
}

/* The consequence sentence. Body colour, not danger: the dialog is already
   red where it matters, and a wall of red text is read as decoration. */
.mds-confirm-text {
  margin: 0;
  font-size: var(--mds-fs-body);
  line-height: var(--mds-lh-body);
  color: var(--mds-text-secondary);
}

.mds-confirm .mds-modal-body { padding-top: var(--mds-space-5); }


/* =============================================================================
   19. RESPONSIVE  (§22)
   Content is reflowed or made scrollable — never removed.
   ============================================================================= */

@media (max-width: 991.98px) {
  .mds-filter-search { flex: 1 1 100%; max-width: 100%; }
  .mds-filter-control { flex: 1 1 160px; width: auto; }
  .mds-filter-spacer { display: none; }
  .mds-page-header-row { flex-wrap: wrap; }
}

@media (max-width: 767.98px) {
  .mds-form-grid > [class*="mds-col-"] { grid-column: span 12; }
  .mds-modal .modal-dialog { max-width: none; margin: 0; }
  .mds-modal .modal-content { max-height: 100vh; height: 100vh; border-radius: 0; }
  .mds-pagination { justify-content: center; }
}


/* =============================================================================
   20. PRINT  (§24)
   Reproduce the data, not the interface.
   ============================================================================= */

@media print {
  @page { size: A4; margin: 12mm; }

  body { background: #fff !important; }

  /* Interface chrome does not print */
  .mds-toolbar,
  .mds-selection-bar,
  .mds-filterbar,
  .mds-pagination,
  .mds-page-actions,
  .mds-row-actions,
  .mds-col-actions,
  .mds-col-select,
  .mds-skeleton,
  .mds-skip-link,
  .modal-backdrop,
  #main-wrapper > aside,
  .left-sidebar,
  .app-header,
  .topbar { display: none !important; }

  .mds-page,
  .mds-page-header { background: #fff !important; position: static !important; padding: 0 !important; }

  .mds-card { border: 0 !important; }

  /* The identification block: what this is, and what it covers (§21.3) */
  .mds-print-header { display: block !important; margin-bottom: 8pt; }
  .mds-print-header .mds-print-title { font-size: 12pt; font-weight: 600; }
  .mds-print-header .mds-print-meta  { font-size: 8pt; color: #000; }

  .mds-table { font-size: 9.5pt; color: #000; }
  .mds-table thead { display: table-header-group; }   /* repeat on every page */
  .mds-table tfoot { display: table-footer-group; }
  .mds-table tr { break-inside: avoid; }
  .mds-table thead th {
    position: static !important;
    background: #eeeeee !important;
    color: #000 !important;
    border-bottom: 0.5pt solid #000 !important;
  }
  .mds-table tbody td {
    border-bottom: 0.5pt solid #cccccc !important;
    white-space: normal;                              /* wrap, never clip */
  }
  .mds-table tfoot td { position: static !important; background: #eeeeee !important; color: #000 !important; }

  .mds-badge {
    border: 0.5pt solid #666 !important;
    background: transparent !important;
    color: #000 !important;
  }

  .mds-amount-neg, .mds-amount-pos, .mds-amount-zero { color: #000 !important; }

  a[href]::after { content: none !important; }
}

/* The print identification block is print-only */
.mds-print-header { display: none; }
