/* Drafts — the dashboard panel, the autosave indicator and the missing-preview
   card. Linked by index.html after styles.css, marked data-drafts-css, which
   receipts/drafts.js guards on before linking it as the fallback; it consumes that
   file's tokens and components and defines no colour of its own. Every value
   below is a var(): there is no hex literal in this file, which is what keeps
   all three themes correct without a single duplicated palette. */

/* ── The panel above the filed table ─────────────────────────────────────── */
#drafts-mount:empty { display: none; }

.drafts-panel { margin-bottom: var(--space-5); overflow: hidden; }

.drafts-head {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--rule);
}
.drafts-sub { flex: 1 0 100%; width: 100%; max-width: none; margin: 0; font-size: var(--text-xs); color: var(--ink-muted); }
.drafts-title {
  margin: 0;
  font-size: var(--text-md); font-weight: var(--weight-semibold);
  color: var(--ink-strong);
}
.drafts-head-actions { margin-left: auto; display: flex; gap: var(--space-2); flex-wrap: wrap; }

.drafts-list { display: flex; flex-direction: column; }

/* ── One draft ───────────────────────────────────────────────────────────── */
/* A grid rather than a table: the row is four facts and a pair of buttons, and
   a table would put a header over a list one line long. The two middle columns
   are fixed because a state chip and a timestamp have a known width; only the
   identity absorbs the remainder, which is what lets a long supplier name
   ellipsise instead of pushing the actions off the row. */
.draft-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 190px 190px max-content;
  align-items: center;
  gap: var(--space-3);
  padding: var(--row-pad-y) var(--row-pad-x);
  border-top: 1px solid var(--rule);
}
.draft-row:first-child { border-top: 0; }
.draft-row:hover { background: var(--surface-hover); }
/* The keyboard reaches this row through the two buttons at the end of it, so it
   takes the pointer's paint under :focus-within the way the shared table row does
   (styles.css). Without it the only list on the dashboard answered a mouse and
   said nothing to a tab. */
.draft-row:focus-within { background: var(--surface-hover); }
/* The row is no longer dimmed while it is busy: .6 took .draft-sub to 2.61:1, and
   a line the operator is waiting on is the last one to make faint. aria-busy is
   the semantics and the skeleton is the appearance. */

.draft-ident { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.draft-name {
  font-size: var(--text-base); font-weight: var(--weight-medium); color: var(--ink-strong);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.draft-sub {
  font-size: var(--text-xs); color: var(--ink-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.draft-state { min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-1); }
/* What a waiting draft waits for, under its chip: the count of products and
   units the catalogue still lacks - the dashboard's answer to "why is this
   still here", read without opening the receipt. */
.draft-waiting-detail { font-size: var(--text-xs); color: var(--ink-muted); line-height: var(--leading-tight); }
.count-pill--waiting { display: inline-flex; align-items: center; gap: var(--space-1); }
.count-pill--waiting svg { width: 12px; height: 12px; }
/* The wait is over. The row says so ahead of everything else: the done edge
   and ground, the detail in the done colour, and the one primary resume the
   other rows do not carry - what the eye lands on first in the morning. */
.draft-row--available { background: var(--state-done-bg); box-shadow: var(--edge) var(--state-done-edge); }
.draft-row--available:hover, .draft-row--available:focus-within { background: var(--state-done-bg); }
.draft-available-detail { color: var(--state-done-fg); font-weight: var(--weight-medium); }
.count-pill--available { background: var(--state-done-bg); color: var(--state-done-fg); border: 1px solid var(--state-done-rule); }
/* The chip wraps rather than clipping: a French label runs about a third longer
   than its English source and this column is a fixed width. */
.draft-state .state { white-space: normal; }

.draft-saved {
  display: flex; align-items: center; gap: var(--space-1);
  font-size: var(--text-xs); color: var(--ink-muted);
  font-variant-numeric: tabular-nums lining-nums;
}
.draft-saved svg { color: var(--ink-faint); }

.draft-actions { display: flex; gap: var(--space-2); align-items: center; }

/* An error belongs at the row that failed, in words, with a retry — never as a
   toast alone, which is gone in 4.2 s while the operator is out on a delivery. */
.draft-row-note {
  grid-column: 1 / -1;
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  margin: 0; padding-top: var(--space-1);
}

.draft-row.unreadable { background: var(--state-blocked-bg); }
.draft-row.unreadable .draft-name { color: var(--state-blocked-fg); }

/* Narrow: the four columns become two - the identity across the top, then
   state and time on the left with the actions stacked on the right. Nothing
   is hidden. (bp-mid is where the sidebar lies down into the app bar.) */
@media (max-width: 900px) {
  .draft-row { grid-template-columns: minmax(0, 1fr) max-content; grid-template-areas: "ident ident" "state actions" "saved actions"; gap: var(--space-2) var(--space-3); }
  .draft-ident { grid-area: ident; }
  .draft-state { grid-area: state; }
  .draft-saved { grid-area: saved; }
  .draft-actions { grid-area: actions; align-self: start; flex-direction: column; align-items: stretch; }
  .draft-row-note { grid-column: 1 / -1; }
  .drafts-head-actions { margin-left: 0; width: 100%; }
}

/* ── The autosave indicator, in the review bar ───────────────────────────── */
/* Deliberately not a spinner. A spinner in a toolbar reads as "wait for me",
   and the operator never has to: the save is already done or already failed by
   the time they look. The word carries the meaning, the icon repeats it, and
   the colour is third — colour alone never says which state this is. */
.save-state:empty { display: none; }
.save-state.saving { color: var(--ink-muted); }
.save-state.saved { color: var(--state-done-fg); }
.save-state.failed { color: var(--state-blocked-fg); font-weight: var(--weight-medium); }
.save-state .btn { margin-left: var(--space-1); }

/* A slow fade, not a rotation: it says "in flight" without asking anyone to
   watch it finish. It is the shared in-flight pulse at the chip's own pace
   (styles.css .state.processing), floored at .6 so the icon stays above 3:1 for
   the whole cycle. This sheet used to keep a copy of that keyframe under its own
   name and its own duration, which is how one meaning came to breathe at two
   speeds. */
.save-state.saving svg { animation: if-pulse 1.4s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
  .save-state.saving svg { animation: none; }
}


/* Standing conditions (a refused write, the draft cap, a second tab) sit on
   their own line under the bar, where they stay until they stop being true. */
.draft-bar-notes {
  flex-basis: 100%;
  display: flex; flex-direction: column; gap: var(--space-1);
}
.draft-bar-notes:empty { display: none; }
.draft-bar-notes p { margin: 0; }

/* ── The missing-preview card ────────────────────────────────────────────── */
/* Its own box beside the PDF preview rather than inside it: receipts/pdf.js
   rewrites that element on every selection change. */
.pdf-missing {
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2);
  padding: var(--space-3);
  border: 1px dashed var(--rule-strong);
  border-radius: var(--radius-md);
  background: var(--surface-sunken);
}
.pdf-missing.hidden { display: none; }
.pdf-missing p { margin: 0; }
/* The label IS the control here (the input before it is visually hidden), so it
   shows the ring the button it looks like would show. styles.css draws it for
   every `.file-input:focus-visible + label`; the copy that stood here is gone. */
.pdf-missing label.btn { cursor: pointer; }

/* ── The discard toast, which carries an Undo ────────────────────────────── */
.toast.draft-undo {
  display: flex; align-items: center; gap: var(--space-3);
}
.toast.draft-undo .btn {
  color: var(--ink-inverse);
  border-color: var(--ink-inverse);
  background: transparent;
}
.toast.draft-undo .btn:hover:not(:disabled):not([aria-disabled="true"]) { text-decoration: underline; }

/* ═══ À faire, the home (REDESIGN 4.1) ═════════════════════════════════════ */
/* Queue placement; shared component anatomy lives in styles.css. */

.todo-head { flex-wrap: wrap; align-items: flex-start; }
.todo-head-text { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; flex: 1 1 auto; }
.todo-summary { margin: 0; font-size: var(--text-sm); }
.todo-head-actions { display: flex; align-items: flex-end; gap: var(--space-3); flex-wrap: wrap; margin-left: auto; }
.todo-search { flex-direction: column; align-items: stretch; gap: var(--space-1); }
.todo-search .field-help { max-width: var(--prose-max); }

.todo-queue { display: flex; flex-direction: column; gap: var(--space-5); }
.todo-group[hidden] { display: none; }
.todo-group {
  min-width: 0;
}
.todo-group-head { align-items: flex-start; }
.todo-group-title { margin: 0; font-size: var(--text-section); font-weight: var(--weight-bold); }
/* The title is the fold: a real button that says whether its list is open. */
.todo-group-toggle {
  display: inline-flex; align-items: center; gap: var(--space-2);
  min-height: var(--tap-min); padding: 0 var(--space-2); margin-left: calc(-1 * var(--space-2));
  border: 0; border-radius: var(--radius-sm); background: transparent;
  color: var(--ink-strong); font: inherit; font-weight: var(--weight-semibold); cursor: pointer;
}
.todo-group-toggle:hover { background: var(--surface-hover); }
.todo-group-toggle svg { transition: transform var(--dur-2) var(--ease-out); }
.todo-group-toggle[aria-expanded="false"] svg { transform: rotate(-90deg); }
.todo-group-actions { margin-left: auto; display: flex; gap: var(--space-2); flex-wrap: wrap; }
.todo-group-sub { max-width: none; }
.todo-group-sub[hidden], .todo-list[hidden] { display: none; }

.todo-list { list-style: none; margin: 0; padding: 0; }
/* One row: who and what (the paper's facts), its state, the answer area when it
   is one answer away, and its one button with the "..." menu. A row's floor is
   the row token, so a row that changes state, is answered or leaves a note in
   its place keeps the height of the list under it (REDESIGN 4.3.2: nothing
   moves under the operator). */
.todo-row {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1.4fr) max-content;
  grid-template-areas: "main state act" "answer answer answer" "note note note";
  align-items: center; column-gap: var(--space-4); row-gap: var(--space-2);
  min-height: var(--row-h);
  padding: var(--row-pad-y) var(--row-pad-x);
  border-top: 1px solid var(--rule);
}
.todo-row:hover, .todo-row:focus-within { background: var(--surface-hover); }
.todo-row-main { grid-area: main; display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.todo-row-name {
  font-size: var(--text-base); font-weight: var(--weight-medium); color: var(--ink-strong);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: start;
}
.todo-row-sub { font-size: var(--text-xs); color: var(--ink-muted); font-variant-numeric: tabular-nums lining-nums; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.todo-row-state { grid-area: state; display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-1); min-width: 0; }
.todo-row-state .state { white-space: normal; max-width: 100%; }
.todo-row-detail { font-size: var(--text-xs); color: var(--ink-muted); line-height: var(--leading-tight); }
.todo-row-act { grid-area: act; display: flex; align-items: center; gap: var(--space-2); justify-content: flex-end; }
.todo-row-answer {
  grid-area: answer; display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  padding: var(--space-2) var(--space-3); border: 1px solid var(--rule); border-radius: var(--radius-sm);
  background: var(--surface-sunken);
}
.todo-row-question { font-size: var(--text-sm); color: var(--ink); flex: 1 1 auto; min-width: 0; }
.todo-row-note { grid-area: note; margin: 0; }
/* A row answered in place that is now ready: the ready edge says so before the
   words are read, and it stays where it was until the page is next opened. */
.todo-row--ready { box-shadow: var(--edge) var(--state-ready-edge); }
/* The note a received or abandoned row leaves where it was. */
.todo-row--ghost .todo-row-name { font-weight: var(--weight-regular); color: var(--ink-muted); white-space: normal; }
.todo-row--blocked { box-shadow: var(--edge) var(--state-blocked-edge); }

.todo-queue-foot { display: flex; flex-direction: column; gap: var(--space-2); }
.todo-queue-foot .save-state:empty { display: none; }

.todo-empty { margin: var(--space-6) 0; color: var(--ink-muted); }
.todo-empty[hidden] { display: none; }

.todo-done, .todo-setup { margin-top: var(--space-6); }
.todo-done[hidden], .todo-setup[hidden] { display: none; }
.todo-section-head { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-2); }
.todo-section-head h2, .todo-setup-title { margin: 0; font-size: var(--text-md); }
.todo-list--done { border: 1px solid var(--rule); border-radius: var(--radius-md); background: var(--surface-raised); }
.todo-list--done .todo-row:first-child, .todo-group .todo-row:first-child { border-top-color: var(--rule); }
.todo-row--done .row-open { padding: 0; border: 0; background: transparent; cursor: pointer; }
.todo-notices { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-2); }
#landing-dropzone { margin-top: var(--space-6); }

@media (max-width: 720px) {
  .todo-row { grid-template-columns: minmax(0, 1fr); grid-template-areas: "main" "state" "answer" "act" "note"; }
  .todo-row-act { justify-content: flex-start; }
}

/* ═══ Factures, every invoice (REDESIGN 4.4) ═══════════════════════════════ */
/* The toolbar is the missing lists' .mi-toolbar row (styles/data.css) with the
   filters and the table's own tools in it; it wraps on a narrow screen rather
   than scrolling. */
/* Layout only: the search and its help on the first line, then the filters,
   the table's tools at the end of the last line. Every control is the kit's. */
/* Two rows, each contained: the search beside the table's tools (density, the
   CSV, the trash), aligned on the search box; then every filter in one even row,
   "Effacer" closing it. (Two classes: data.css, linked later, makes every
   .mi-toolbar a flex row.) The flex row it replaced wrapped the filters onto a
   second ragged line and stranded the tools on a third, beside nothing. */
.ui-filter-bar.inv-tools {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "search tools" "filters filters";
  column-gap: var(--space-4); row-gap: var(--space-3); align-items: start;
}
.inv-tools > .inv-search { grid-area: search; margin: 0; }
.inv-tools > .inv-tools-end { grid-area: tools; align-self: center; display: flex; align-items: center; gap: var(--space-2); flex-wrap: nowrap; }
.inv-tools-end .field { margin: 0; }
/* The filters' disclosure (receipts/dashboard.js): the grid area, and a
   summary line drawn only on a phone, where the seven fields fold under it.
   Wider, the box is held open and is nothing but the filters' row. */
.inv-filters-box { grid-area: filters; min-width: 0; }
.inv-filters-summary { display: none; }
.inv-filters {
  display: grid; gap: var(--space-3); align-items: end;
  /* Every field as wide as "Tous les fournisseurs" needs with its chevron:
     the kit's 160px clipped it to "Tous les fourniss" in its own select from
     a phone to 1280. */
  grid-template-columns: repeat(auto-fill, minmax(min(100%, calc(var(--filter-select-min) + var(--space-12))), 1fr));
}
.inv-filters .field { margin: 0; min-width: 0; }
.inv-filters #inv-f-clear { justify-self: start; white-space: nowrap; }
/* Wide enough for one row: the state, a wider supplier, the two dates, the two
   amounts and the product, then "Tout effacer". At bp-xl and not before: the
   minimums of that row come to 1051px, and from 1024 to 1359 the content is
   908 to 1088 wide, so the row ran past the panel and took "Tout effacer" off
   the screen. Below, the fields fill even rows and "Tout effacer" wraps with
   them. */
@media (min-width: 1360px) {
  .inv-filters {
    grid-template-columns: minmax(7.5rem, 1fr) minmax(12rem, 1.4fr) repeat(2, minmax(8.25rem, 1fr))
                           repeat(2, minmax(6.5rem, 0.8fr)) minmax(7.5rem, 1fr) auto;
  }
}
@media (max-width: 900px) {
  .ui-filter-bar.inv-tools { grid-template-columns: minmax(0, 1fr); grid-template-areas: "search" "tools" "filters"; }
  .inv-tools > .inv-tools-end { flex-wrap: wrap; }
}
@media (max-width: 720px) {
  .inv-filters-summary {
    display: flex; align-items: center; gap: var(--space-2);
    min-height: var(--control-h); cursor: pointer; list-style: none;
    font-weight: var(--weight-medium); color: var(--ink-strong);
  }
  .inv-filters-summary::-webkit-details-marker { display: none; }
  .inv-filters-summary svg { transform: rotate(-90deg); transition: transform var(--dur-2) var(--ease-out); }
  .inv-filters-box[open] > .inv-filters-summary svg { transform: none; }
  .inv-filters-box[open] > .inv-filters { margin-top: var(--space-2); }
}
.inv-pos-note { margin: var(--space-2) 0 var(--space-3); }
.inv-pos-note:empty { display: none; }
/* A receiving Auphan holds that no receipt of this vault filed: no doc to open,
   so the row is not drawn as a link. */
.inv-row--outside td { color: var(--ink-muted); }
/* The tick column, as narrow as its box. */
.inv-table .col-pick { width: var(--control-h-sm); text-align: center; }
/* NINE COLUMNS WHERE THERE IS ROOM FOR NINE. The table held 1107px at every
   width, so at 1280 its row menu sat past the panel and at 768 the state was
   cut. Below bp-xl the two columns that are a count and a note ("Lignes",
   "Noté à la réception") step aside, the rest fit, and from 1360 all nine are
   back. The state chip breaks between words, never inside one: styles.css
   lets a chip wrap anywhere, which in a table sized by its content gave the
   state column one letter's width and a 500px row. A date and an invoice
   number are one piece each. */
.inv-table .inv-col-lines, .inv-table .inv-col-flags { display: none; }
@media (min-width: 1360px) {
  .inv-table .inv-col-lines, .inv-table .inv-col-flags { display: table-cell; }
}
.inv-table .inv-col-state .state { overflow-wrap: break-word; }
/* A receiving filed in Auphan alone: a quiet line in the state column. */
.inv-table .inv-state-note { color: var(--ink-muted); }
/* A state is one line at every width: a two-line chip doubled the row's
   height. The words are short (the chip leaves "Brouillon" out), the supplier's
   name is what wraps in a narrow table, and a card gives the chip a line of
   its own. */
.inv-table .inv-col-state .state, .inv-table .inv-state-note { white-space: nowrap; }
/* A column nothing on the page fills steps aside (receipts/dashboard.js
   emptyColumns): an empty tick box column, a column of dashes. */
.inv-table.inv-table--no-pick .col-pick,
.inv-table.inv-table--no-lines .inv-col-lines,
.inv-table.inv-table--no-flags .inv-col-flags { display: none; }
/* From the app bar's tablet down to the card, the seven columns close in on
   the compact rung of the row padding (as the learned-matches table does in
   data.css), which is what keeps the row's menu on the panel at 768 rather
   than 55px past its edge. */
@media (max-width: 900px) {
  table.grid.inv-table td, table.grid.inv-table thead th { padding-inline: var(--space-2); }
  /* A one-line state needs the room a header's words held on one line ("Date
     de la facture" and its arrow took 152px) and the chip's icon: the header
     breaks between its words and the chip says its state in words alone. */
  table.grid.inv-table thead th, .inv-table .th-sort { white-space: normal; text-align: left; }
  .inv-table .inv-col-state .state > svg { display: none; }
  table.grid.inv-table tr > td:first-child, table.grid.inv-table tr > th:first-child { padding-left: var(--space-3); }
  table.grid.inv-table tr > td:last-child, table.grid.inv-table tr > th:last-child { padding-right: var(--space-3); }
}
.inv-table .inv-col-date .row-open, .inv-table .inv-col-date > span { white-space: nowrap; }
/* Just above the card, a one-line state needs the date to give way too: the
   date breaks before its year, beside a supplier's name that breaks there
   already, rather than the row running past the panel. */
@media (max-width: 900px) {
  .inv-table .inv-col-date .row-open, .inv-table .inv-col-date > span { white-space: normal; text-align: left; }
}
/* Below 720px each row is a card (.table-stack, styles.css): the supplier,
   the draft's tick and its "..." menu on the first line, the rest under it
   with their column's names, and what was noted at filing back in the card,
   which has the room the table did not (an empty note leaves the card:
   is-blank). Each name holds a column of its own, so the values line up down
   the card whatever the name's length or the chip's width: the shared rule
   wraps a value under a long name ("Date de la facture") and beside a short
   one, and the menu took a line of its own on every card. */
@media (max-width: 720px) {
  .inv-table.table-stack tbody tr { grid-template-columns: minmax(0, 1fr) auto auto; column-gap: var(--space-3); }
  .inv-table.table-stack tbody td { grid-column: 1 / -1; }
  .inv-table.table-stack tbody td.inv-col-supplier { grid-row: 1; grid-column: 1; }
  .inv-table.table-stack tbody td.col-pick { grid-row: 1; grid-column: 2; width: auto; justify-content: flex-end; }
  .inv-table.table-stack tbody td.col-actions { grid-row: 1; grid-column: 3; padding-top: 0; align-self: start; }
  .inv-table.table-stack tbody td[data-label] {
    display: grid; grid-template-columns: 8rem minmax(0, 1fr); align-items: baseline; column-gap: var(--space-2);
  }
  .inv-table.table-stack tbody td[data-label]::before { grid-column: 1; min-width: 0; }
  .inv-table.table-stack tbody td[data-label] > * { grid-column: 2; justify-self: start; }
  /* The state chip says what it is: on its own line under the supplier, the
     card's subtitle, where the longest ("Brouillon prêt à recevoir") holds one
     line instead of two beside a name. */
  .inv-table.table-stack tbody td.inv-col-state { grid-row: 2; display: flex; }
  .inv-table.table-stack tbody td.inv-col-state::before { display: none; }
  /* After the grid, at its weight: a blank cell and a column the page left
     empty stay out of the card. */
  .inv-table.table-stack tbody td.is-blank, .inv-table.table-stack tbody td:empty,
  .inv-table.inv-table--no-lines.table-stack tbody td.inv-col-lines,
  .inv-table.inv-table--no-flags.table-stack tbody td.inv-col-flags { display: none; }
  .inv-table.table-stack tbody tr.inv-row--ghost td { grid-column: 1 / -1; grid-row: auto; }
}
/* The ticked drafts' bar (receipts/dashboard.js ensureBulk). Above 720px it
   holds no height: it sits right above the table and its bar is laid over the
   header row, from the end of the tick column to the table's edge, in the
   header's own colour and height, only while something is ticked; one layer
   over the header cells, which are positioned on the sticky layer and come
   later in the page. Nothing below it moves when the first box is ticked, and
   the 40px it kept empty under the filters on every visit is gone. At 720px
   and under the header row is not drawn (the rows are cards), so the bar is in
   the flow at its own height, as it always was. */
.inv-bulk { position: relative; z-index: var(--layer-sticky-head); height: 0; min-height: 0; margin: 0; }
.inv-bulk-bar {
  position: absolute; top: var(--kit-hairline); right: var(--kit-hairline);
  left: calc(var(--inv-pick-w, var(--control-h-sm)) + var(--kit-hairline));
  height: var(--table-head-h);
  padding-inline: var(--space-4);
  pointer-events: none;
}
.inv-bulk.is-on .inv-bulk-bar { background: var(--surface-hover); pointer-events: auto; border-top-right-radius: var(--radius-md); }
@media (max-width: 720px) {
  .inv-bulk { height: auto; min-height: var(--control-h-sm); margin-bottom: var(--space-2); }
  .inv-bulk-bar { position: static; height: auto; padding-inline: 0; }
  .inv-bulk.is-on .inv-bulk-bar { background: transparent; }
}
/* A sortable header is a button that looks like the header it is: the column's
   word and a chevron that shows only on the column in force, pointing the way
   the order runs. */
.th-sort { display: inline-flex; align-items: center; gap: var(--space-1); min-height: var(--tap-inline); padding: 0; border: 0; background: transparent; font: inherit; color: inherit; cursor: pointer; }
.th-sort-mark { visibility: hidden; }
[aria-sort] > .th-sort .th-sort-mark { visibility: visible; }
[aria-sort="ascending"] > .th-sort .th-sort-mark { transform: rotate(180deg); }
/* The ticked drafts' bar keeps its height whether anything is ticked or not,
   so the first tick moves nothing below it. */
.inv-bulk, .inv-bulk-bar { display: flex; align-items: center; gap: var(--space-3); }
.inv-bulk-acts { display: inline-flex; gap: var(--space-2); }
.inv-bulk-acts[hidden] { display: none; }
.inv-row--ghost td { color: var(--ink-muted); }
/* Opened under the toolbar, above the table (receipts/dashboard.js
   ensureTrash). */
.inv-trash { margin-block: var(--space-4); padding: var(--space-4); border: 1px solid var(--rule); border-radius: var(--radius-md); background: var(--surface-raised); }
.inv-trash-title:focus { outline: none; }
.inv-trash-title:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.inv-trash[hidden] { display: none; }
.inv-trash-title { margin: 0 0 var(--space-1); font-size: var(--text-md); }
.inv-trash-list { margin-top: var(--space-3); border: 1px solid var(--rule); border-radius: var(--radius-md); }
.inv-trash-list:empty { display: none; }
.inv-trash-empty[hidden] { display: none; }

/* UI kit proving ground: only placement and the home list's column layout. */
.todo-setup { margin: 0 0 var(--space-3); }
.todo-notices { margin-top: 0; }
#landing-dropzone.home-upload { margin-top: 0; }
.home-queue { gap: var(--space-3); }
.home-queue .todo-group { padding-bottom: var(--space-3); }
.home-queue .todo-list, .home-column-head { margin: 0 var(--space-3); border: var(--kit-hairline) solid var(--rule); border-radius: var(--radius-sm); }
.home-column-head { display: grid; grid-template-columns: minmax(0, 1fr) var(--home-state-w) var(--home-saved-w) var(--home-action-w); gap: var(--space-4); padding: var(--space-2) var(--row-pad-x); background: var(--surface-hover); font-size: var(--text-xs); color: var(--ink-strong); border-bottom: 0; border-radius: var(--radius-sm) var(--radius-sm) 0 0; }
.home-column-head + .todo-list { border-radius: 0 0 var(--radius-sm) var(--radius-sm); }
.home-queue .todo-row { grid-template-columns: minmax(0, 1fr) var(--home-state-w) var(--home-saved-w) var(--home-action-w); grid-template-areas: "main state saved act"; }
.home-queue .todo-row-answer, .home-queue .todo-row-note { grid-area: auto; grid-column: 1 / -1; }
.todo-row-saved { grid-area: saved; color: var(--ink-muted); font-size: var(--text-sm); font-variant-numeric: tabular-nums lining-nums; }
.home-queue .todo-row-name { line-height: var(--leading-tight); font-weight: var(--weight-semibold); font-size: var(--text-sm); }
.home-queue .todo-row-sub { line-height: var(--leading-tight); font-size: var(--text-sm); }
.home-queue .todo-row-act > .btn { min-width: var(--summary-min); white-space: nowrap; }
/* A receipt found already received offers "Supprimer" beside its own button
   (receipts/drafts.js queueRowHtml). The column holds one button and the menu,
   so the removal takes a line of its own above them, at its own width, rather
   than pushing the row's button over the saved time. */
.home-queue .todo-row--dup .todo-row-act { flex-wrap: wrap; row-gap: var(--space-1); }
.home-queue .todo-row-act > .todo-row-dup-remove { min-width: 0; }
.home-queue .todo-row--ready { box-shadow: none; }
.home-queue .todo-row-state .state { white-space: normal; max-width: 100%; }
.home-queue .todo-group-toggle { font-weight: var(--weight-bold); }
.home-queue .todo-group-toggle > svg { width: var(--icon-sm); }
.home-queue .todo-group-count { min-width: var(--disc); height: var(--disc); font-size: var(--text-base); background: var(--accent-surface); color: var(--ink-strong); }
.home-queue .todo-group-actions { align-items: center; gap: var(--space-3); }
.home-queue .todo-queue-foot { margin-top: calc(-1 * var(--space-2)); }
.home-queue .todo-queue-foot .save-state.saved { color: var(--ink-muted); }
.todo-done { margin-top: var(--space-3); }
.todo-done .ui-panel-actions { align-items: center; }
/* The search and "Toutes les factures" are one row beside the title. The
   search's width was only a min-width, which the row's own sizing does not
   count: the row came out narrower than its two controls and the button
   dropped to a line of its own, then (kept on the row) squeezed to two lines.
   A real width and a button that does not shrink; a phone stacks them (the
   720px rule below). */
.todo-done .ui-panel-actions { flex: 0 0 auto; flex-wrap: nowrap; }
.todo-done .todo-search { flex: 0 0 auto; width: calc(var(--filter-search-min) + var(--space-10)); }
.todo-done #todo-done-all { flex: none; }
/* Wide enough for its placeholder, "Fournisseur ou n° de facture": at the
   filters' 240px it read "Fournisseur ou n° de fact". */
.todo-done .todo-search { min-width: min(100%, calc(var(--filter-search-min) + var(--space-10))); }
.home-recent-table .btn--link { color: var(--ink-strong); font-size: var(--text-sm); font-weight: var(--weight-semibold); text-align: left; }
.home-recent-table td { font-size: var(--text-sm); }
.home-empty-line { margin: var(--space-4) 0; text-align: center; color: var(--ink-muted); }

/* Between the phone and the wide window the three thirds gave the saved date
   as much room as the supplier's name, which was cut ("Les Primeurs Jean-...")
   beside a date column half empty. The name now leads, the state follows,
   and the date takes only its own width. The group's title keeps a floor of
   its own beside the group's presses, which wrap under it rather than
   breaking "Factures à terminer" over two lines. */
@media (max-width: 1100px) {
  .home-queue .todo-row, .home-column-head { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1.2fr) max-content var(--home-action-w); }
}
.home-queue .todo-group-head .ui-panel-heading { flex: 1 1 18rem; }
@media (max-width: 720px) {
  .home-upload { flex-wrap: wrap; gap: var(--space-3); padding: var(--space-4); }
  .home-upload-copy { flex-basis: calc(100% - var(--disc-lg) - var(--space-3)); }
  .home-upload .dropzone-browse { width: 100%; }
  /* With invoices waiting, the zone is the one press: the waiting rows are what
     the phone opens on, and the zone's icon and two sentences (with the setup
     notice) put the first of them under the fold. Its words stay for a screen
     reader; the press names what it does, and the app bar's "Nouvelles
     factures" and a drop anywhere still import. */
  #view-todo:has(#todo-g-invoices:not([hidden])) .home-upload { padding: var(--space-2); border-style: solid; }
  #view-todo:has(#todo-g-invoices:not([hidden])) .home-upload > .dropzone-icon { display: none; }
  #view-todo:has(#todo-g-invoices:not([hidden])) .home-upload-copy {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  .home-column-head { display: none; }
  /* A row in two lines: who and what beside its press, then the state with
     the date it was saved at the line's end. One column of five lines made
     each row about 100px and the list a scroll of whitespace. The press takes
     at most 45% of the row, its words wrapping past that ("Voir ce qui
     manque"), so the name keeps the rest. The state has the whole second
     line less the date's room (its padding), not the name's column: a
     waiting row's long sentence was otherwise a word a line. */
  .home-queue .todo-row { grid-template-columns: minmax(0, 1fr) fit-content(45%); grid-template-areas: "main act" "state state"; column-gap: var(--space-3); }
  .home-queue .todo-row > .todo-row-main { grid-area: main; }
  .home-queue .todo-row > .todo-row-act { grid-area: act; justify-content: flex-end; }
  .home-queue .todo-row > .todo-row-state { grid-area: state; padding-inline-end: calc(var(--space-10) * 2); }
  .home-queue .todo-row > .todo-row-saved { grid-area: state; align-self: start; justify-self: end; font-size: var(--text-xs); }
  .home-queue .todo-row-act > .btn { min-width: 0; white-space: normal; text-align: center; }
  .home-queue .todo-row-state .state { white-space: normal; max-width: 100%; }
  /* The group's presses keep their words on one line each and wrap as whole
     buttons; shared out at half the row, both broke onto two lines. */
  .home-queue .todo-group-actions { margin-left: 0; width: 100%; }
  .home-queue .todo-group-actions > .btn { flex: 1 1 auto; white-space: nowrap; }
  .todo-done .ui-panel-actions { width: 100%; flex-wrap: wrap; }
  .todo-done .todo-search { flex: 1 1 100%; width: 100%; min-width: 0; }
  /* The recent receipts as cards (.table-stack, styles.css): the supplier,
     the row's title, leads each card. */
  .home-recent-table.table-stack tbody td:nth-child(2) { order: -1; }
  /* The kit's table puts its edge inset on the first and last cells, at a
     higher specificity than the stack's reset; stacked, every line of the card
     starts at the card's own padding. */
  .home-recent-table.table-stack tbody td { padding-inline: 0; }
}
