/* Shared responsive utilities (docs/coding-rules/responsive-design.md is the contract; read it before changing this file).
   Loaded after layout.css and components.css and BEFORE the module stylesheets, so a module may set tokens and column templates for a utility
   but must not re-implement it. Breakpoints (rem): phone < 40, compact 40-47.9375, tablet 48-64, desktop > 64, desktop compact 64.0625-85.

   .page-actions      header action button group
   .kpi-grid          KPI card grid (--3, --4, --4-compact plans)
   .filter-bar        filter toolbar card (+ __form, columns from --filter-columns)
   .table-wrap        table scroll container (card look: layout.css); .table-scroll is the same without the card look
   .cell-truncate     explicit, intentional text truncation (needs the full text in title / data-full-text) */

/* ---------------------------------------------------------------------------------------------------
   Header action buttons
   A .page-header is a wrapping flex row with space-between, which leaves a group that wrapped onto its own row at the LEFT. From tablet up the
   group grows to fill the free width of its row and right-aligns its buttons, whether it shares the title's row or wraps below it; buttons keep
   their content widths and gap, and wrapped rows stay at the right. A single action button that is a direct child of the header is right-aligned
   the same way. Below tablet the module's approved mobile layouts apply (.header-action-pair, full-width groups).
   --------------------------------------------------------------------------------------------------- */
/* Reservation Detail has its own header (.rd-header, a wrapping flex row like .page-header) with the same group. At every width the group fills the
   free width of its row and keeps its buttons at their content widths, right-aligned, wrapping as whole buttons that stay at the right. */
.rd-header > .page-actions {
  flex: 1 1 auto;
  justify-content: flex-end;
}
@media (min-width: 48rem) {
  .page-header > .page-actions {
    align-items: center;
    display: flex;
    flex: 1 1 auto;
    flex-wrap: wrap;
    gap: var(--space-3);
    justify-content: flex-end;
  }
  .page-header > .button:last-child {
    margin-left: auto;
  }
}

/* ---------------------------------------------------------------------------------------------------
   KPI cards
   Cards of one row are as tall as the tallest one (equal rows, no fixed height, so nothing clips) from two columns up; on one column each card
   keeps its own height. The column plan belongs to the grid, because it depends on the card content and on the content width left beside the
   sidebar:
     --4          1 column; 2 from 640px; 4 above 1200px  (Additional Revenue, Expenses, category lists, Reports)
     --4-compact  1 column; 2 from 640px; 4 above 1024px  (Dashboard; the icon sits above the text up to 1360px)
     --3          1 column below 1024px; 3 above           (Staff)
   --------------------------------------------------------------------------------------------------- */
.kpi-grid {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: minmax(0, 1fr);
}
.kpi-grid > * {
  min-width: 0;
}
.kpi-grid .kpi-card__value {
  white-space: nowrap;
}
/* The grid is a size container, so the icon placement below follows the width the cards of a row really have (KPI-ALIGNMENT-001/002). */
.kpi-grid {
  container: kpi-grid / inline-size;
}
/* A card whose text cannot fit beside its icon (a long amount such as 4,589,290,000 VND) wraps the text under the icon instead of clipping it.
   This safety net stays for the plans that do not choose their icon placement by width (--3, --4-compact); --4 does, see below. */
.kpi-grid:not(.kpi-grid--4) > .kpi-card {
  flex-wrap: wrap;
}
/* --4 (Additional Revenue, Expenses, category lists, Reports): the icon placement is a property of the GRID, never of one card's text. A card
   needs about 22rem to keep the longest realistic amount (99,999,999,999 VND) beside its icon; when the cards of the current plan are narrower
   than that, EVERY card of the grid puts the icon above its text (same padding, colours and icon size), so the icons of one row always share one
   position, whatever the length of a label or a value. Thresholds = 22rem x columns + gaps: 4 columns 91rem, 2 columns 45rem, 1 column 21rem. */
@media (min-width: 75.0625rem) {
  @container kpi-grid (max-width: 90.9375rem) {
    .kpi-grid--4 > .kpi-card {
      align-items: flex-start;
      flex-direction: column;
      gap: var(--space-3);
    }
  }
}
@media (min-width: 40.0625rem) and (max-width: 75rem) {
  @container kpi-grid (max-width: 44.9375rem) {
    .kpi-grid--4 > .kpi-card {
      align-items: flex-start;
      flex-direction: column;
      gap: var(--space-3);
    }
  }
}
@media (max-width: 40rem) {
  @container kpi-grid (max-width: 20.9375rem) {
    .kpi-grid--4 > .kpi-card {
      align-items: flex-start;
      flex-direction: column;
      gap: var(--space-3);
    }
  }
}
.kpi-grid .kpi-card__body {
  flex: 1 1 0;
  min-width: min-content;
}
@media (min-width: 40.0625rem) {
  .kpi-grid--4,
  .kpi-grid--4-compact {
    grid-auto-rows: 1fr;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (min-width: 64.0625rem) {
  .kpi-grid--3 {
    grid-auto-rows: 1fr;
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  .kpi-grid--4-compact {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}
@media (min-width: 75.0625rem) {
  .kpi-grid--4 {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}
/* Four across in a content column under about 1000px leaves each card 170-250px, and the icon beside the text then left the text only 100-140px,
   so titles and descriptions broke every other word: the icon sits above the text instead (padding, colours and icon size are unchanged). */
@media (min-width: 64.0625rem) and (max-width: 85rem) {
  .kpi-grid--4-compact > .kpi-card {
    flex-direction: column;
    gap: var(--space-3);
  }
}

/* ---------------------------------------------------------------------------------------------------
   Filter toolbars
   The card, the form grid and the scroll behavior are shared; a module supplies --filter-columns on its own form class (every column
   minmax(var(--filter-min-*), Nfr), Clear auto). From tablet up the controls are ONE row; when they do not fit, the card scrolls sideways
   (native scrollbar, touch, trackpad) instead of wrapping or squeezing a control, whatever the sidebar state or window width, because the rule
   depends on the card's own width. overflow-y is hidden (the card padding leaves room for the focus ring); position: fixed popovers (the shared
   range picker places itself when it finds a scrolling card) and native select lists are not clipped. Below tablet the module's approved
   stacked / two-column --filter-columns apply and the card does not scroll.
   --------------------------------------------------------------------------------------------------- */
.filter-bar {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  padding: var(--space-3) var(--space-4);
}
.filter-bar__form {
  align-items: end;
  display: grid;
  gap: var(--space-3);
  grid-template-columns: var(--filter-columns);
}
@media (min-width: 48rem) {
  .filter-bar {
    max-width: 100%;
    min-width: 0;
    overflow-x: auto;
    overflow-y: hidden;
  }
  .filter-bar select {
    white-space: nowrap;
  }
}

/* ---------------------------------------------------------------------------------------------------
   Tables
   Every table sits in .table-wrap, the one scroll container. Header cells never wrap (th is nowrap globally; .table-cell-wrap opts a cell in to
   wrapping). A table that has a min-width is scrolled by its .table-wrap instead of being squeezed. The wrap never widens its parent.
   --------------------------------------------------------------------------------------------------- */
.table-wrap,
.table-scroll {
  max-width: 100%;
  min-width: 0;
}
/* .table-scroll is the same scroll container without the card look, for tables that sit inside a card of their own. */
.table-scroll {
  overflow-x: auto;
}

/* Explicit truncation: one line, cut with an ellipsis, and ONLY where the full text is available elsewhere (title / data-full-text / the approved
   tooltip). It needs a width limit from its container (max-width or a constrained cell). Never use it for names, codes or amounts. */
.cell-truncate {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
:where(span, a, p, div, li).cell-truncate {
  display: block;
}
