.requests-toolbar {
  margin-bottom: 1.25rem;
}

.requests-search {
  display: flex;
  gap: 0.75rem;
}

.requests-search input {
  flex: 1;
  padding: 0.6rem 0.8rem;
  border: 1px solid color-mix(in srgb, var(--text) 12%, transparent);
  border-radius: 8px;
  background: color-mix(in srgb, var(--surface) 60%, transparent);
  color: var(--text);
  font-size: 0.9rem;
}

.requests-search input:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--surface);
}

.requests-search .btn {
  width: auto;
  padding: 0.6rem 1.25rem;
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.data-table th {
  text-align: left;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  padding: 0.6rem 0.75rem;
  border-bottom: 1px solid var(--border);
}

.data-table td {
  padding: 0.75rem;
  border-bottom: 1px solid var(--border);
}

.data-table tbody tr:last-child td {
  border-bottom: none;
}

.data-table tbody tr:hover {
  background: color-mix(in srgb, var(--accent) 4%, transparent);
}

/* Dense table: full-width container with thin glass edge, tight rows.
   On the requests page (.main--fill), the toolbar/metrics above stay their
   natural height and this area grows to fill the rest of the viewport, with
   only the table body scrolling internally — pagination always stays
   in view below it instead of requiring a page-level scroll. */

.requests-table-area {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

.dense-table-wrap {
  background: color-mix(in srgb, var(--surface) 72%, transparent);
  border: 1px solid color-mix(in srgb, var(--text) 8%, transparent);
  border-radius: 4px 4px 0 0;
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  box-shadow: var(--shadow-sm);
  flex: 1;
  min-height: 0;
  overflow-y: auto;

  /* A flex column so the filler below the table (see ::after) can be told to
     take whatever height is left rather than collapsing to nothing. */
  display: flex;
  flex-direction: column;
}

/* The table ends part-way down a tall frame, so this filler takes the leftover
   height and carries the zebra banding to the bottom edge.
 *
 * Its phase cannot be hardcoded. Which colour the last row is depends on how
 * many rows the tbody actually holds, and that varies — so a filler that always
 * leads with grey (or always with transparent) matches the row above it half
 * the time and doubles up the other half. Instead the tbody itself is padded to
 * a fixed, EVEN row count by .row-blank (see _list_table.html), which pins the
 * last row's parity: with an even count the final row is always :nth-child(even)
 * and therefore always grey, so the filler leads with the transparent half. */
.dense-table-wrap::after {
  content: "";
  flex: 1;
  min-height: 0;
  background: repeating-linear-gradient(
    to bottom,
    transparent 0,
    transparent var(--row-h),
    color-mix(in srgb, var(--text) 3%, transparent) var(--row-h),
    color-mix(in srgb, var(--text) 3%, transparent) calc(var(--row-h) * 2)
  );
}

.requests-table-area .pagination {
  flex-shrink: 0;
  background: color-mix(in srgb, var(--surface) 72%, transparent);
  border: 1px solid color-mix(in srgb, var(--text) 8%, transparent);
  border-top: none;
  border-radius: 0 0 4px 4px;
}

.data-table--dense thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: color-mix(in srgb, var(--surface) 96%, var(--bg));
}

.data-table--dense th {
  padding: 0.4rem 0.75rem;
  font-size: 0.7rem;
}

/* One row height for every row, in every state.
 *
 * Row height used to come out of whatever each cell happened to contain: a
 * .status-pill carries its own vertical padding, so a loaded row was taller
 * than a pending row of em-dashes, and any cell that wrapped grew its row.
 * That is why the rows re-spaced as data arrived. --row-h is now the single
 * source of truth — the rows are that tall whether they hold a pill, a dash,
 * or nothing — and the zebra filler below the table reuses the same variable
 * so its bands line up with real rows instead of approximating them.
 *
 * It is declared on the WRAPPER, not the table: the filler is the wrapper's
 * own ::after, and a variable set on the descendant table would not be in
 * scope for it. The table inherits it from here. */
.dense-table-wrap {
  --row-h: 1.75rem;
}

.data-table--dense tbody tr {
  height: var(--row-h);
}

.data-table--dense td {
  padding: 0 0.75rem;
  height: var(--row-h);
  font-size: 0.85rem;
  line-height: 1.3;

  /* A cell that wraps to a second line would defeat the fixed height, so cells
     clip to one line instead. Combined with table-layout: fixed below, this
     makes the row geometry independent of the data entirely. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Column widths settled by these rules, not by the widest cell in the body.
   Without table-layout: fixed the browser re-measures every column on each
   swap — pending em-dashes are narrower than real codes and dates — so the
   columns visibly jumped when the rows landed. Fixed layout reads widths from
   the header row only, so they are declared here once and never move.

   Percentages, not rem: fixed widths sized to the content left the last column
   ending well short of the frame on a wide screen, with dead space after it.
   These sum to 100%, so the columns divide whatever width the table has and
   the row always reaches the right-hand edge. */
.requests-table-area .data-table--dense {
  table-layout: fixed;
  width: 100%;
}

.requests-table-area .data-table--dense th:nth-child(1) { width: 18%; }  /* Code */
.requests-table-area .data-table--dense th:nth-child(2) { width: 19%; }  /* Status */
.requests-table-area .data-table--dense th:nth-child(3) { width: 25%; }  /* Created */
.requests-table-area .data-table--dense th:nth-child(4) { width: 16%; }  /* Hub */
.requests-table-area .data-table--dense th:nth-child(5) { width: 16%; }  /* Attempts */
.requests-table-area .data-table--dense th:nth-child(6) { width: 6%; }   /* Comments */

/* The requests and orders tables stack two lines per cell (value, then a
   muted detail line — created time, DC, postcode), so their rows are taller
   than the other dense lists. Still one fixed --row-h for every row in every
   state; only the value changes. Cell macros: partials/_row_cells.html. */
.dense-table-wrap--stacked {
  --row-h: 2.5rem;
}

.cell-line {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
}

.cell-line--sub {
  font-size: 0.75rem;
  color: var(--text-muted);
  min-height: 1em;
}

.requests-table-area .requests-list-table th:nth-child(1) { width: 14%; }  /* Code */
.requests-table-area .requests-list-table th:nth-child(2) { width: 14%; }  /* Status */
.requests-table-area .requests-list-table th:nth-child(3) { width: 13%; }  /* Hub */
.requests-table-area .requests-list-table th:nth-child(4) { width: 19%; }  /* Collection */
.requests-table-area .requests-list-table th:nth-child(5) { width: 19%; }  /* Delivery */
.requests-table-area .requests-list-table th:nth-child(6) { width: 7%; }   /* Items */
.requests-table-area .requests-list-table th:nth-child(7) { width: 8%; }   /* Attempts */
.requests-table-area .requests-list-table th:nth-child(8) { width: 6%; }   /* Comments */

/* The comment icon sits at the row's right edge. `overflow: visible` overrides
   the clipping every other cell gets — the badge's count bubble is positioned
   outside the icon's own box and would otherwise be cut off. */
.data-table--dense .comment-cell {
  text-align: right;
  overflow: visible;
}

/* Orders has its own column set, so it needs its own split;
   the .orders-table class distinguishes it from the requests table, which
   shares every other rule in this block. Percentages sum to 100% for the same
   reason as above — the row must reach the right-hand edge on any width. */
.requests-table-area .orders-table th:nth-child(1) { width: 13%; }  /* Code */
.requests-table-area .orders-table th:nth-child(2) { width: 12%; }  /* Status */
.requests-table-area .orders-table th:nth-child(3) { width: 12%; }  /* Leg */
.requests-table-area .orders-table th:nth-child(4) { width: 11%; }  /* Hub */
.requests-table-area .orders-table th:nth-child(5) { width: 17%; }  /* Collection */
.requests-table-area .orders-table th:nth-child(6) { width: 17%; }  /* Delivery */
/* "Requests carried" is the longest header, so it keeps a wider share than
   its short numeric value needs — narrower and it ran into the comment icon. */
.requests-table-area .orders-table th:nth-child(7) { width: 12%; }  /* Requests */
.requests-table-area .orders-table th:nth-child(8) { width: 6%; }   /* Comments */

/* Zebra striping. Because the table always renders a full page of rows —
   real rows padded out with .row-blank — the stripes reach the bottom of the
   frame on a short or empty page just as they do on a full one. That is the
   whole reason the blank rows exist; without them the banding stopped wherever
   the data ended, part-way down a full-height table. */
.data-table--dense tbody tr:nth-child(even) {
  background: color-mix(in srgb, var(--text) 3%, transparent);
}

/* Value-less rows are excluded from the selector rather than having the hover
   undone afterwards: an override would have to restore whatever the zebra
   stripe put there, and no single value does that for both odd and even rows. */
.data-table--dense tbody tr:not(.row-pending):not(.row-blank):hover {
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}

.data-table--dense .status-pill,
.data-table--dense .attempts-badge {
  border-radius: 4px;
}

.data-table__empty {
  text-align: center;
  color: var(--text-muted);
  padding: 2rem 0;
}

/* --- Constant table frame --------------------------------------------------
 *
 * The table's chrome is part of the design, not a loading artefact: a full
 * page of rows and the pagination bar are drawn at first paint and stay put.
 * Two kinds of value-less row keep that box the same size:
 *
 *   .row-pending — first paint, before any data. Real rows with an em-dash in
 *                  every cell, so the row height comes from the same line box
 *                  as a loaded row rather than a guessed bar height.
 *   .row-blank   — a loaded page shorter than a full one. Structural padding
 *                  only, so the last page is the same height as a full one.
 *
 * Neither is data, so neither gets the hover affordance.
 */

/* .row-blank needs no line-box propping any more: every row is exactly
   --row-h tall regardless of what its cells contain. */

/* The pill slot is held open while pending so the status column's width is
   settled by the same rules before and after the swap. */
.status-pill--pending {
  background: color-mix(in srgb, var(--text) 6%, transparent);
  color: color-mix(in srgb, var(--text) 35%, transparent);
}

/* "No requests found." floats over the blank rows instead of replacing the
   table body, so an empty result is the same box as a full page. */
.dense-table-wrap {
  position: relative;
}

.data-table__empty-overlay {
  position: absolute;
  top: 25%;
  left: 0;
  right: 0;
  margin: 0;
  text-align: center;
  color: var(--text-muted);
  pointer-events: none;
}

.status-pill {
  display: inline-block;
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  background: color-mix(in srgb, var(--text-muted) 14%, transparent);
  color: var(--text-muted);
  /* One line always: "delivery-attempted" is long enough to wrap in a narrow
     column, which splits the pill into a two-line blob. A column too narrow
     for it truncates with an ellipsis instead. */
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: middle;
}

/* Inside the dense table the pill has to fit the fixed row height rather than
   set it, so its box is capped and centred instead of adding vertical padding
   on top of the row's own. Without this a pill row was taller than a dash row
   and the rows re-spaced as data arrived. */
.data-table--dense .status-pill {
  padding: 0 0.55rem;
  line-height: 1.35;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: middle;
}

.status-pill--delivered,
.status-pill--completed {
  background: var(--good-soft);
  color: var(--good);
}

.status-pill--out-for-delivery,
.status-pill--received,
.status-pill--accepted {
  background: var(--accent-soft);
  color: var(--accent);
}

.status-pill--delivery-attempted {
  background: var(--warn-soft);
  color: var(--warn);
  animation: status-pill-flash-amber 3.2s ease-in-out infinite;
}

.status-pill--unsuccessful {
  background: var(--bad-soft);
  color: var(--bad);
  animation: status-pill-flash-red 3.2s ease-in-out infinite;
}

@keyframes status-pill-flash-amber {
  0%, 100% {
    background: var(--warn-soft);
  }
  50% {
    background: color-mix(in srgb, var(--warn) 45%, var(--warn-soft));
  }
}

@keyframes status-pill-flash-red {
  0%, 100% {
    background: var(--bad-soft);
  }
  50% {
    background: color-mix(in srgb, var(--bad) 45%, var(--bad-soft));
  }
}

@media (prefers-reduced-motion: reduce) {
  .status-pill--delivery-attempted,
  .status-pill--unsuccessful {
    animation: none;
  }
}

.status-pill--canceled,
.status-pill--cancelled-charged,
.status-pill--fleetable-rejected,
.status-pill--not-received {
  background: var(--bad-soft);
  color: var(--bad);
}

.status-pill--in-progress {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--accent-soft);
  color: var(--accent);
  z-index: 0;
  animation: status-pill-glow-text 2.2s ease-in-out infinite;
}

.status-pill--in-progress::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: var(--accent);
  opacity: 0;
  animation: status-pill-glow-fill 2.2s ease-in-out infinite;
}

@keyframes status-pill-glow-fill {
  0%,
  100% {
    opacity: 0;
  }
  50% {
    opacity: 1;
  }
}

@keyframes status-pill-glow-text {
  0%,
  100% {
    color: var(--accent);
  }
  50% {
    color: var(--accent-contrast);
  }
}

@media (prefers-reduced-motion: reduce) {
  .status-pill--in-progress {
    animation: none;
    background: var(--accent-soft);
  }
}

.attempts-badge {
  display: inline-block;
  min-width: 1.4rem;
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 700;
  text-align: center;
}

.attempts-badge--warn {
  background: var(--warn-soft);
  color: var(--warn);
}

/* Notice on a reconciliation detail row: how long before a delivery window a
   cancellation arrived, or how long an order has waited unbatched. Sized like
   .attempts-badge but text-width rather than fixed, since it carries a phrase
   ("19 min late") and not a single digit.

   The sign is the meaning, so tone is not decoration here: --bad is a
   cancellation that landed after the window had already opened (no notice at
   all, nothing could be done), --warn is one that arrived with time in hand. */
.notice-badge {
  display: inline-block;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
  white-space: nowrap;
}

.notice-badge--bad {
  background: var(--bad-soft);
  color: var(--bad);
}

.notice-badge--warn {
  background: var(--warn-soft);
  color: var(--warn);
}

.notice-badge--ok {
  background: var(--good-soft);
  color: var(--good);
}

.data-table--dense .notice-badge {
  font-size: 0.75rem;
}

.leg-type-badge {
  display: inline-block;
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: capitalize;
  background: color-mix(in srgb, var(--text) 8%, transparent);
  color: var(--text-muted);
}

.leg-type-badge--final-mile {
  background: var(--accent-soft);
  color: var(--accent);
}
