@charset "UTF-8";
/* master-lists.css — Master lists (Items, Customers ...): small table, row actions, htmx
   messages, list filters.
   Loaded in order by templates/partials/styles.html. */

/* ================ Master lists: small table, row actions =================
   Used by the master pages (Item Group first). Kept at the end of the file
   so each rule outranks the stock one it adjusts. */

/* small table: tighter rows than the stock .table. The older .table--mini
   rule only matched "td", which .table tbody td outranks, so it never
   actually tightened anything. */
.table--mini thead th {
  padding: 7px var(--s-3);
}
.table--mini tbody td {
  padding: 6px var(--s-3);
  font-size: var(--fs-sm);
}

/* the # (serial number) column: narrow, muted, numbers lined up */
.table .col-no {
  width: 48px;
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
}

/* edit + delete: soft tinted buttons that fill in on hover. They are the
   row's real actions, so — like the view/cart icons — always visible, not
   faded until the row is hovered (which a touch screen cannot do). */
.icon-btn--edit,
.icon-btn--delete {
  border: 1px solid transparent;
  border-radius: var(--r-md);
}
.icon-btn--edit {
  color: var(--primary-600);
  background: var(--primary-50);
}
.icon-btn--edit:hover {
  color: #fff;
  background: var(--primary-600);
}
.icon-btn--delete {
  color: var(--danger);
  background: var(--danger-bg);
}
.icon-btn--delete:hover {
  color: #fff;
  background: var(--danger);
}
.table .actions .icon-btn--edit,
.table .actions .icon-btn--delete {
  opacity: 1;
}
.table--mini .actions .icon-btn {
  width: 28px;
  height: 28px;
  font-size: 0.95rem;
}

/* a small centred dialog — a confirm needs a sentence, not 880px */
.modal--sm .modal__panel {
  width: min(420px, calc(100vw - 2 * var(--s-4)));
}
/* on a phone every modal is a full-width bottom sheet, this one included */
@media (max-width: 640px) {
  .modal--sm .modal__panel {
    width: 100%;
  }
}

/* rows become cards on a phone; the # cell is then a full row like the
   others instead of a squeezed 48px column */
@media (max-width: 640px) {
  .table--stack td.col-no {
    width: 100%;
  }
}

/* An export menu at the right end of a toolbar opens leftwards from its
   button. The older phone rule anchors every menu to the left, which suits
   one that wraps to the start of the row but pushed this one off-screen. */
@media (max-width: 560px) {
  .toolbar > .export-menu:last-child .export-menu__panel {
    left: auto;
    right: 0;
  }
}

/* ---- striped rows: <table class="table table--striped"> -----------------
   Every second row is shaded so a long list is easy to follow across.
   "of :not([hidden])" counts only the rows still showing, so the stripes stay
   even while the filter box hides some. Hover still wins, in a brand tint. */
/* a light stripe — half way between the row colour and --surface-3 */
.table--striped tbody tr:nth-child(even of :not([hidden])) {
  background: color-mix(in srgb, var(--surface-3) 50%, var(--surface));
}
/* and a header row a little darker than the stripes, so it reads as the
   table's title */
.table--striped thead th {
  background: var(--surface-3);
  color: var(--text-2);
  border-bottom-color: var(--border-strong);
}
.table--striped tbody tr:hover {
  background: var(--primary-50);
}

/* on a phone the rows are separate cards — shading every other card would
   just look uneven, so the stripes stop there */
@media (max-width: 640px) {
  .table--stack.table--striped tbody tr:nth-child(even of :not([hidden])),
  .table--stack.table--striped tbody tr:hover {
    background: transparent;
  }
}


/* ======================= htmx modals and messages ==========================
   Forms and confirms are fetched into <div id="modal"> already open, so the
   modal's own open transition never runs — these animations give them their
   entrance instead. #modal sits outside the app shell; its modals go above
   the sidebar (z 1000) so the backdrop covers the whole screen. */
#modal .modal {
  z-index: 1100;
}
#modal .modal__backdrop {
  animation: modal-fade var(--t) var(--ease);
}
#modal .modal--right .modal__panel {
  animation: panel-from-right var(--t) var(--ease);
}
#modal .modal--sm .modal__panel {
  animation: modal-fade var(--t) var(--ease);
}
@keyframes modal-fade {
  from { opacity: 0; }
}
@keyframes panel-from-right {
  from { transform: translateX(100%); }
}

/* The message slot in the title row (#pageNote, main_base.html): it fills
   the space between the title and the page's buttons, and its message sits
   against the buttons. */
.page-head__note {
  flex: 1 1 auto;
  display: flex;
  justify-content: flex-end;
  min-width: 0;
}
.page-note {
  margin: 0;
  max-width: 100%;
  padding: 7px var(--s-4);
  border-radius: var(--r-full);
  font-size: var(--fs-sm);
  font-weight: 600;
  animation: modal-fade var(--t) var(--ease);
}
@media (max-width: 640px) {
  /* phone: a line of its own, under the title and buttons */
  .page-head__note {
    order: 3;
    flex-basis: 100%;
    justify-content: flex-start;
  }
}

@media (prefers-reduced-motion: reduce) {
  #modal .modal__backdrop,
  #modal .modal__panel,
  .page-note {
    animation: none;
  }
}


/* ========================== Master list filters ===========================
   The filter form sits in the toolbar as if its fields were the toolbar's
   own, so the search box and the dropdown line up with the Export button. */
.toolbar__filters {
  display: contents;
}
.select--sm {
  width: auto;
  min-width: 150px;
  min-height: 0;
  height: 42px;
  padding-top: 0;
  padding-bottom: 0;
  font-size: var(--fs-sm);
}

/* the text beside a stock .switch (e.g. "Active" in an Edit panel):
   muted when off, green when the checkbox is on */
.switch__label {
  font-size: var(--fs-md);
  color: var(--text-3);
}
.switch input:checked ~ .switch__label {
  color: var(--success);
  font-weight: 600;
}

/* a filter dropdown beside the search box (Items: Group / Unit): as low as
   the compact search box, and not wider than its longest sensible name */
.toolbar__select {
  height: 32px;
  min-width: 130px;
  max-width: 200px;
}

@media (max-width: 640px) {
  /* Phone: the search box first, on a line of its own; the dropdown and the
     Export button share the line under it. (The topbar's phone rule moves
     every .search to the end of its row — right for the topbar, not here.) */
  .toolbar .search {
    order: -1;
    flex: 1 1 100%;
    max-width: none;
  }
  .toolbar .select--sm {
    flex: 1 1 auto;
  }
}

/* ---- pagination under a master list ------------------------------------ */
.panel__foot .pagination {
  width: 100%;
}
.pagination__pages {
  flex-wrap: wrap;
  align-items: center;
}
/* the "…" between page numbers (1 2 3 … 9 10) */
.pagination__gap {
  min-width: 24px;
  text-align: center;
  color: var(--text-3);
}
/* while the next page is on its way */
#rows.htmx-request {
  opacity: 0.6;
  transition: opacity var(--t-fast);
}

/* ---- a list table that scrolls by itself, not the page -----------------
   <div class="table-wrap table-wrap--tall table-wrap--fill">
   On a page holding one, the page is exactly one screen tall, and the table
   gets whatever height is left under the title row and toolbar: the rows
   scroll inside it (header row pinned), the pagination footer stays in view,
   and the page itself never scrolls. With only a few rows it is simply as
   tall as they are.

   It works as a chain of flex boxes, each allowed to shrink (min-height: 0)
   so the one at the bottom — the table — is the one that scrolls:
     .app (one screen) > .app__main > .content > .panel > #rows > .table-wrap
   On a phone the rows become cards and the page scrolls as usual
   (.table-wrap--tall's phone rule). Browsers without :has() keep the plain
   .table-wrap--tall height. */
@supports selector(:has(*)) {
  @media (min-width: 641px) {
    .app:has(.table-wrap--fill) {
      height: 100vh;
      height: 100dvh;
    }
    .app:has(.table-wrap--fill) .app__main,
    .content:has(.table-wrap--fill) {
      min-height: 0;
    }
    /* grow into the whole area left on screen (the footer sits at the
       bottom whatever the row count), and shrink to it when rows overflow */
    .content > .panel:has(.table-wrap--fill),
    .panel > :has(> .table-wrap--fill),
    .table-wrap--fill {
      flex: 1 1 auto;
      min-height: 0;
      display: flex;
      flex-direction: column;
    }
    .table-wrap--fill {
      display: block;
      max-height: none;
      overscroll-behavior-y: contain;
    }
    /* the toolbar and the pagination footer keep their size */
    .panel:has(.table-wrap--fill) .toolbar,
    .panel:has(.table-wrap--fill) .panel__foot {
      flex-shrink: 0;
    }

    /* the same for a report that is a chart alone (Sales Man Progress Chart,
       .rchart--only): one screen, the chart scrolls inside, not the page */
    .app:has(.rchart--only) {
      height: 100vh;
      height: 100dvh;
    }
    .app:has(.rchart--only) .app__main,
    .content:has(.rchart--only) {
      min-height: 0;
    }
    .content:has(.rchart--only) {
      padding: var(--s-3) var(--s-4);
    }
    .content > .panel:has(.rchart--only),
    .panel > :has(> .rchart--only) {
      flex: 1 1 auto;
      min-height: 0;
      display: flex;
      flex-direction: column;
    }
    .rchart.rchart--only {
      flex: 1 1 auto;
      min-height: 0;
      max-height: none;
      overflow-y: auto;
      overscroll-behavior-y: contain;
    }
    .panel:has(.rchart--only) .toolbar,
    .panel:has(.rchart--only) .panel__foot,
    .panel:has(.rchart--only) .rnote {
      flex-shrink: 0;
    }
  }
}
