/* ===========================================================================
   Data grid — one table system for every screen in the app.

   The problem it fixes: the working grids had grown to twenty-odd columns and
   carried a hard `min-width` in the region of 1800px, so the PAGE scrolled
   sideways. On a 1366px office laptop that means the Save button, the row you
   are typing in, and the column header naming it are never on screen together,
   and you lose your place every time you look up. A form you have to scroll
   sideways to fill in is a form that gets filled in wrong.

   The rules here are the fix, and they are deliberately boring:

     1. THE TABLE SCROLLS, NOT THE PAGE. `.dg` is a bounded viewport-relative
        box. Anything that does not fit scrolls INSIDE it, with the header and
        the first column pinned, so you never lose the row's identity or the
        column's name.

     2. COLUMNS HAVE A PRIORITY. Not every field earns space on every screen.
        `.dg-p2` and `.dg-p3` drop out as the window narrows, in that order, and
        a per-grid "All columns" toggle brings them back for the rare occasion
        somebody needs them on a small screen.

     3. DENSITY IS SET ONCE. Cell padding, control height and font size live
        here, not in fourteen page-level <style> blocks each guessing.

   Nothing in this file knows what any column MEANS. It is layout only — no
   page's figures, rules or behaviour depend on it.
   =========================================================================== */

:root {
  /* How much of the window is furniture (topbar, page heading, card chrome)
     before a grid starts. Grids subtract it so they end at the fold instead of
     pushing the page down. */
  --dg-chrome: 300px;
  --dg-row-h: 34px;
  --dg-border: #e5e9f0;
  --dg-head-bg: #f6f8fb;
  --dg-stick-bg: #fff;
}

/* ---- The scroll box ------------------------------------------------------ */
.dg {
  position: relative;
  overflow: auto;
  max-height: calc(100vh - var(--dg-chrome));
  /* A grid shorter than its box must not stretch to fill it. */
  overscroll-behavior: contain;
  scrollbar-width: thin;
}
.dg-sm { --dg-chrome: 420px; }     /* a grid sharing the page with something else */
.dg-lg { --dg-chrome: 220px; }     /* a grid that IS the page */
.dg::-webkit-scrollbar { width: 10px; height: 10px; }
.dg::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 6px; }
.dg::-webkit-scrollbar-thumb:hover { background: #94a3b8; }

/* ---- The table ----------------------------------------------------------- */
.dg table { width: 100%; margin: 0; border-collapse: separate; border-spacing: 0; }

.dg thead th {
  position: sticky; top: 0; z-index: 3;
  background: var(--dg-head-bg);
  border-bottom: 1px solid var(--dg-border) !important;
  font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .02em;
  color: #475569; white-space: nowrap;
  padding: .4rem .5rem; vertical-align: middle;
}
/* Two-row headers (grouped columns) need the second row pinned under the first. */
.dg thead tr:nth-child(2) th { top: 30px; z-index: 2; }

.dg tbody td { padding: .3rem .5rem; vertical-align: middle; font-size: .84rem;
                border-bottom: 1px solid var(--dg-border); }
.dg tbody tr:hover > td { background: #fafcff; }
.dg tfoot td { padding: .4rem .5rem; background: var(--dg-head-bg); font-weight: 700;
                border-top: 2px solid var(--dg-border); position: sticky; bottom: 0; z-index: 2; }

/* Numbers line up only when they are tabular. */
.dg .num, .dg td.num, .dg th.num { text-align: right; font-variant-numeric: tabular-nums; }
.dg .ctr { text-align: center; }

/* ---- The pinned identity column -----------------------------------------
   Whatever names the row — the plate, the date — stays put while the rest
   scrolls. Without it a wide grid is a wall of numbers belonging to nobody. */
.dg .dg-stick { position: sticky; left: 0; z-index: 2; background: var(--dg-stick-bg); }
.dg thead .dg-stick { z-index: 4; background: var(--dg-head-bg); }
.dg tr:hover > .dg-stick { background: #fafcff; }
.dg .dg-stick::after {
  content: ""; position: absolute; top: 0; right: -6px; bottom: 0; width: 6px;
  background: linear-gradient(90deg, rgba(15,23,42,.07), transparent); pointer-events: none;
}

/* ---- Controls inside cells -----------------------------------------------
   Bootstrap's form-control-sm is still too tall for a thirty-row grid, and its
   focus ring overlaps the neighbouring cell. Both fixed here so no page has to. */
.dg :is(input, select, textarea).form-control,
.dg :is(input, select).form-select {
  height: auto; min-height: 0;
  padding: .18rem .4rem; font-size: .82rem; line-height: 1.35;
  border-color: #dde3ec; border-radius: .3rem;
}
.dg textarea.form-control { line-height: 1.3; }
.dg :is(input, select, textarea):focus {
  border-color: #7aa7f5; box-shadow: 0 0 0 .12rem rgba(37,99,235,.16); position: relative; z-index: 1;
}
.dg input[readonly], .dg input:disabled, .dg select:disabled, .dg textarea:disabled {
  background: #f1f5f9; color: #64748b;
}
/* A number box does not need arrows it is too short to show properly. */
.dg input[type="number"]::-webkit-outer-spin-button,
.dg input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.dg input[type="number"] { -moz-appearance: textfield; }

.dg .btn-sm, .dg .btn-group-sm > .btn { padding: .12rem .38rem; font-size: .76rem; border-radius: .3rem; }

/* ---- Column priority -----------------------------------------------------
   p2 goes first, then p3. `.dg-all` on the wrapper puts everything back — the
   toggle every grid header carries. */
@media (max-width: 1500px) { .dg:not(.dg-all) :is(th, td).dg-p3 { display: none; } }
@media (max-width: 1250px) { .dg:not(.dg-all) :is(th, td).dg-p2 { display: none; } }
@media (max-width: 1000px) { .dg:not(.dg-all) :is(th, td).dg-p1 { display: none; } }

/* ---- The detail row ------------------------------------------------------
   Where a record has more fields than a screen has width, the overflow goes on
   a second line UNDER the row rather than off the right-hand edge. Collapsed by
   default; the caret in the identity cell opens it. */
.dg tr.dg-detail > td { background: #f8fafc; padding: .5rem .75rem .65rem; }
.dg tr.dg-detail[hidden] { display: none; }
.dg-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: .5rem .75rem; }
.dg-field > label { display: block; font-size: .66rem; font-weight: 700; text-transform: uppercase;
                    letter-spacing: .03em; color: #64748b; margin-bottom: .1rem; }
.dg-caret { border: 0; background: none; padding: 0 .2rem; color: #94a3b8; line-height: 1; }
.dg-caret:hover { color: #2563eb; }
.dg-caret i { transition: transform .15s ease; display: inline-block; }
.dg-caret[aria-expanded="true"] i { transform: rotate(90deg); }

/* ---- Grid toolbar -------------------------------------------------------- */
.dg-bar { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
          padding: .5rem .75rem; border-bottom: 1px solid var(--dg-border); background: #fff; }
.dg-bar .dg-title { font-weight: 600; font-size: .95rem; margin-right: auto; }
.dg-bar .dg-sub { font-weight: 400; color: #64748b; font-size: .82rem; }
.dg-bar .form-control, .dg-bar .form-select { max-width: 240px; }

/* ---- Empty state --------------------------------------------------------- */
.dg-empty { text-align: center; color: #94a3b8; padding: 2.2rem 1rem; }
.dg-empty i { font-size: 1.8rem; display: block; margin-bottom: .4rem; opacity: .55; }

/* ---- Row states ----------------------------------------------------------
   Shared by every grid so "unsaved" and "frozen" look the same everywhere. */
.dg tr.is-dirty  > td { background: #fffbeb; }
.dg tr.is-dirty  > td.dg-stick { background: #fffbeb; }
.dg tr.is-saved  > td { background: #ecfdf5; transition: background .1s; }
.dg tr.is-locked > td { background: #f8fafc; color: #64748b; }
.dg tr.is-locked > td.dg-stick { background: #f8fafc; }
.dg tr.is-going  > td { opacity: .35; transition: opacity .25s; }

/* ---------------------------------------------------------------------------
   Narrow screens: the table stops being a table.

   Below 768px no amount of column-dropping makes a working grid usable, so each
   row becomes a stacked card with its header text as the label. Read-only
   grids benefit most; editable ones stay usable because the controls keep their
   full width instead of being squeezed into a 60px column.
   ------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
  .dg-cards { max-height: none; }
  .dg-cards table, .dg-cards tbody, .dg-cards tr, .dg-cards td { display: block; width: 100%; }
  .dg-cards thead, .dg-cards tfoot { display: none; }
  .dg-cards tbody tr {
    border: 1px solid var(--dg-border); border-radius: .5rem; margin-bottom: .6rem;
    padding: .35rem .1rem; background: #fff;
  }
  .dg-cards tbody td {
    display: flex; align-items: center; gap: .75rem; border: 0;
    padding: .28rem .7rem; text-align: left !important;
  }
  .dg-cards tbody td::before {
    content: attr(data-label); flex: 0 0 40%; max-width: 40%;
    font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
    color: #64748b;
  }
  .dg-cards tbody td:empty { display: none; }
  .dg-cards .dg-stick { position: static; }
  .dg-cards .dg-stick::after { display: none; }
  /* Everything is visible in card mode — there is no width to compete for. */
  .dg-cards :is(th, td):is(.dg-p1, .dg-p2, .dg-p3) { display: flex; }
}

/* ---------------------------------------------------------------------------
   Page furniture, so a screen reads the same wherever you land.
   ------------------------------------------------------------------------- */
.pg-head { display: flex; align-items: flex-start; justify-content: space-between;
           gap: 1rem; flex-wrap: wrap; margin-bottom: 1rem; }
.pg-head h1, .pg-head h2 { font-size: 1.22rem; font-weight: 700; margin: 0 0 .15rem; letter-spacing: -.01em; }
.pg-head p { margin: 0; color: #64748b; font-size: .86rem; max-width: 78ch; }
.pg-actions { display: flex; gap: .5rem; flex-wrap: wrap; }

/* A card that holds a grid should not add padding around it. */
.card > .dg:first-child { border-top-left-radius: inherit; border-top-right-radius: inherit; }
.card > .dg:last-child  { border-bottom-left-radius: inherit; border-bottom-right-radius: inherit; }

/* Filter/criteria strip above a grid — one row, wraps, never a wall of boxes. */
.pg-filters { display: flex; align-items: flex-end; gap: .6rem; flex-wrap: wrap; }
.pg-filters > * { flex: 0 1 auto; }
.pg-filters .form-label { font-size: .72rem; font-weight: 700; text-transform: uppercase;
                          letter-spacing: .03em; color: #64748b; margin-bottom: .15rem; }

@media print {
  .dg { max-height: none; overflow: visible; }
  .dg thead th, .dg .dg-stick, .dg tfoot td { position: static; }
  .dg:not(.dg-all) :is(th, td):is(.dg-p1, .dg-p2, .dg-p3) { display: table-cell; }
}
