/* filelist.css — The selected-file list (.file-list / .file-row) — item 63.
   Cascade position 4 of 18. Order is load-bearing and is asserted by
   verify-fixtures.mjs; index.html links these explicitly (never @import) because Caddy's
   try_files turns a missing file into a 200 text/html SPA shell.
   Owner after Step 1: section (k).

   ITEM 63, a P0, and the reason this file exists: `grep -rn "file-row\|file-list"
   apps/web` returned only the two JS emit sites — there was NO matching CSS rule
   anywhere in apps/web. The confirmation surface for the headline "1-8 files" feature
   rendered as one run-on sentence with a native UA button in the middle of it:

     benchy.stl10.8 MB · Uploaded · analyzed · 60x31x48 mm · 15.6 cm³ · 225,706
     triangles · 1 body [x]                     <- a white/grey rounded box, on black

   DESIGN-SPEC §4.19. Name, status and remove control get their own alignment rails; the
   invalid row is distinguishable in a screenshot WITHOUT reading it; and no native UA
   button styling survives anywhere in the list.

   REFERENCE-STUDY §14 (GitHub's repo file list + MakerWorld's profile row, both measured
   2026-09-05) supplies the three properties both references share and this file takes:
   a fixed row inset, ONE separator source, and a hover that changes FILL ONLY — no
   transform, no shadow growth, no outline (§18.4). §14's `opacity: 0` trailing control is
   the one thing deliberately NOT copied: ours is a destructive Remove, and hiding a
   destructive control until hover puts it out of reach of touch (items 77b, 138). */
.file-list {
  display: grid; gap: var(--sn-space-2);
  margin: var(--sn-space-2) 0 var(--sn-space-3);
}
.file-list:empty { display: none; }

/* The size column is a FIXED 64px rail, not DESIGN-SPEC §4.19's `auto`. Measured on the
   shipped `auto` at 1440 with benchy + bunny + snapmate loaded: the three size cells
   started at x = 1320.8 / 1325.3 / 1326.8 and were 44.2 / 39.7 / 38.2 wide, because
   `10.8 MB`, `3.3 MB` and `122 KB` set three different widths. `auto` therefore gives
   every row its OWN rail, which is exactly what item 63 says must stop. 64px holds
   `999.9 MB` at --sn-text-xs and costs the name column nothing it was using. */
.file-row {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr) 64px 32px;
  /* Row gap is 4px, not the 12px column gap: the status line belongs TO the name above
     it, and a 12px row gap reads as two unrelated lines and costs 8px of row height in
     a list that can hold eight rows. */
  column-gap: var(--sn-space-3); row-gap: var(--sn-space-1);
  align-items: center;
  min-height: 44px;
  padding: var(--sn-space-2) var(--sn-space-3);
  border: 1px solid var(--sn-line);
  border-radius: var(--sn-radius-sm);
  background: var(--sn-surface-2);
  /* Fill only, and only the fill is transitioned. --sn-dur-fast (120ms) rather than
     §14's measured 60ms: it is the token this app already uses for a hover and both
     numbers sit inside the 33-160ms band the house rules set. */
  transition: background-color var(--sn-dur-fast) var(--sn-ease-out);
}
/* One step up the surface ladder, and nothing else. Computed: --sn-surface-3 against
   --sn-surface-2 is 1.088:1, and GitHub's measured row hover (#151B23 on #0d1117,
   REFERENCE-STUDY §14.1) is 1.093:1 — the same step, from a token this app already has.
   The first attempt used --sn-surface-2-alt, which is DARKER than the row (1.017:1): a
   hover you cannot see, in the wrong direction. */
.file-row:hover { background: var(--sn-surface-3); }
/* Column 1 is the plate-index badge, and it is the SAME number AND the same hue the
   viewer draws over the model (item 130): js/viewer.js's setModelIdentity() puts
   --model-color on this row and on the viewer's .model-label, so row 2 and the model
   labelled "2" on the plate carry one colour. A multi-model user can tell which row is
   which object without reading either. */
.file-row__index {
  display: grid; place-items: center;
  width: 24px; height: 24px; border-radius: 50%;
  background: rgb(var(--sn-white-rgb) / .06);
  box-shadow: inset 0 0 0 1px var(--model-color, var(--sn-accent));
  color: var(--model-color, var(--sn-accent));
  font-size: var(--sn-text-2xs); font-weight: 700; line-height: 1;
  font-variant-numeric: normal;
}
/* Item 134, rows half: MIDDLE truncation, the same split js/topbar.js gives the header
   file chip. The head ellipsises; the tail — the last 13 characters, which is where
   `-v2-final.stl` lives — never does. js/filelist.js does the split and carries the full
   name in a `title`, so the row never overflows and the version suffix always survives. */
.file-row__name {
  display: flex; align-items: baseline; min-width: 0;
  font-size: var(--sn-text-sm); font-weight: 600; color: var(--sn-ink);
}
.file-row__head { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.file-row__tail { flex: 0 0 auto; white-space: nowrap; }
/* The analysed facts live on the STATUS line, inside the row, so removing one file
   cannot leave an aggregate stating numbers for a set that no longer exists (item 127).
   It spans to the end of the row rather than sitting under the name alone: measured at
   1440, the name column was 261.6-267.6px and the facts wrapped to two lines (36.3px)
   inside it, making every row 98.25px tall. Spanning the remaining columns gives the
   status 361.8px and one line (18.1px), and the row 72.13px — 26px off every row, in a
   list that holds eight. */
.file-row__status {
  display: block; grid-column: 2 / -1;
  font-size: var(--sn-text-xs); line-height: 1.45; color: var(--sn-ink-mute);
  /* Wraps rather than truncating. The status line carries the MEASURED facts, and an
     ellipsis in the middle of "15.6 c…" hides a number the user came here to read. */
  overflow-wrap: anywhere;
}
/* Every state js/filelist.js can put on the row has a rule here. The failure mode this
   file exists for (item 63) is a rendered class name with no rule, so a new state adds
   its colour in the same change as the JS that emits it. */
.file-row__state { font-weight: 600; color: var(--sn-ink-dim); }
.file-row[data-state="ready"] .file-row__state { color: var(--sn-ok); }
.file-row[data-state="unavailable"] .file-row__state { color: var(--sn-warn); }
.file-row[data-state="invalid"] .file-row__state { color: var(--sn-bad); }
/* NOT tabular-nums. mocks/NOTES.md B4, measured: Schibsted Grotesk gives the comma and
   the full stop a full digit advance, so `1,250` renders as `1 , 250` and `10.8 MB` as
   `10 . 8 MB`. Nothing in this list is a column being compared. */
.file-row__size {
  font-size: var(--sn-text-xs); color: var(--sn-ink-mute);
  white-space: nowrap; text-align: right;
}
/* 32 x 32, a real control, with a per-file accessible name written by the JS
   ("Remove benchy.stl") rather than a bare glyph. Always visible, never revealed on
   hover: §14's trailing control sits at `opacity: 0` until the row is hovered, and a
   destructive control that only appears on hover is unreachable by touch and invisible
   to a keyboard user until it already has focus (items 77b, 138). */
.file-row__remove {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; padding: 0;
  border: 1px solid transparent; border-radius: var(--sn-radius-sm);
  background: none; color: var(--sn-ink-mute);
  font: inherit; font-size: var(--sn-text-sm); line-height: 1; cursor: pointer;
  transition: background-color var(--sn-dur-fast) var(--sn-ease-out),
              border-color var(--sn-dur-fast) var(--sn-ease-out),
              color var(--sn-dur-fast) var(--sn-ease-out);
}
/* DESIGN-SPEC §4.19: --sn-ink-mute at rest, --sn-bad on hover. What it does is
   destructive and the colour says so before the click, not after. */
.file-row__remove:hover:not(:disabled) {
  background: rgb(var(--sn-bad-rgb) / .10);
  border-color: var(--sn-bad-edge); color: var(--sn-bad);
}
.file-row__remove:disabled { color: var(--sn-ink-mute); cursor: not-allowed; }

.file-row.is-invalid { border-color: var(--sn-bad-edge); background: var(--sn-bad-tint); }
/* The refused row keeps its own tint on hover and only deepens it: .10 -> .16 is
   1.116:1 over the rail, the same perceptual step the valid row gets. */
.file-row.is-invalid:hover { background: rgb(var(--sn-bad-rgb) / .16); }
.file-row.is-invalid .file-row__status { color: var(--sn-bad-ink); }
.file-row.is-invalid .file-row__index {
  background: var(--sn-bad-tint); box-shadow: inset 0 0 0 1px var(--sn-bad-edge);
  color: var(--sn-bad);
}

/* The aggregate line under the list. It states the size of the CURRENT selection and how
   far through measuring it we are — never a triangle total, which the four facts below
   it already carry once (item 128). It is recomputed from the live selection on every
   render, so it cannot describe a set that no longer exists (item 127). */
.file-aggregate { font-size: var(--sn-text-xs); color: var(--sn-ink-mute); }
.file-aggregate:empty { display: none; }

/* Item 149's second defect, and it is the same defect as item 63: the Remove button the
   JS generates for a SAVED CUSTOM PRINTER was a bare <button> too — measured
   62.9 x 23.5 px, rgb(239,239,239) fill, 2.23:1. Same primitive, so the list and the
   custom-printer row cannot drift apart again. */
.custom-printer-remove { /* see .file-row__remove; section (g) moves it onto the class */ }
