/* table.css — the data tables (table.data in a .table-wrap): the frame that scrolls sideways
 * only, the head the script holds under the navigation, sortable heads, column lines, column
 * walls with their resize zone, cell copy, the selection column, the filter row and the
 * selection summary (src/shared/components/connect-table.php, selection-summary.php;
 * js/shared/app.js). Loaded on every page after base.css. */

/* tables: compact cells on one line each (no wrapping — the frame scrolls sideways), a head
   that is its own band, zebra, hover; numbers tabular and right-aligned (.num), identity (ids,
   IPs, SteamIDs, times) in mono (.mono); long texts over .peek.
   The frame scrolls SIDEWAYS only (1.10.0, operator 09.10.2026: "bitte ich das vertikale
   Scrollen innerhalb der Tabelle zu entfernen. Stattdessen soll über die ganze Seite gescrollt
   werden"): no max-height anywhere, the table is as tall as its rows and the PAGE scrolls;
   overflow-x keeps the one-line rows from pushing the page wide. */
.table-wrap { overflow-x: auto; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); }
table.data { border-collapse: collapse; width: 100%; font-size: var(--fs-13); line-height: var(--lh-13); font-variant-numeric: tabular-nums; }
table.data th, table.data td { padding: var(--s1) var(--s2); border-bottom: 1px solid var(--line); text-align: left; vertical-align: top; white-space: nowrap; }
/* the head (operator 09.10.2026: "better set off from the two alternating row stylings" — reading
   the table at a glance is a function): its own surface --head-bg, darker than page and rows,
   text in --ink (not muted) 600, and a 2 px rule in --control at its lower edge. The rule is an
   inset shadow, not a border: in a collapsed-border table the borders are painted by the table
   and would stay behind when the head is moved. Column names stay lower-case — they are raw
   identifiers.
   The head stays visible while the page scrolls (operator 1.3.1: "ja!") — but not by
   position: sticky any more (1.10.0): an overflow-x frame is a scroll container, and a sticky
   head inside it sticks to the frame, not to the page. app.js holds it instead: on every
   scroll and resize each head cell gets transform: translateY(d), d = how far the frame's top
   has gone under the navigation (clamped to the table), so the head stays in the frame (it
   scrolls sideways with the table) and under the navigation. position: relative keeps the
   z-index (the moved head paints over the rows) and anchors the grip. */
table.data th {
  position: relative; z-index: 1;
  background: var(--head-bg); color: var(--ink); font-weight: 600; font-size: var(--fs-12); line-height: var(--lh-12);
  padding-top: 6px; padding-bottom: 6px; border-bottom: 0;
  box-shadow: inset -1px 0 0 var(--line-soft), inset 0 -2px 0 var(--control);
}
/* while the script holds the head (table.head-held), the cells are promised to move; the
   promise is withdrawn as soon as the head is back in place */
table.data.head-held th { will-change: transform; }
/* sortable heads (1.10.0, operator 09.10.2026: "Die angezeigten Spalten sollen sortierbar
   sein"): every head with data-sort takes a click (app.js sorts the loaded rows: ascending,
   descending, back to the page's order); the pointer says so, the sorted head carries the
   direction as an arrow in the accent — the one colour for "something acted on" */
table.data th[data-sort] { cursor: pointer; }
table.data th[data-sort]:hover { color: var(--accent); }
/* the arrow sits at the right edge of the cell, left of the grip, and the cell makes room for
   it (padding) — inline after the name it would be the first thing the ellipsis of a fixed
   column cuts off */
table.data th.sort-asc, table.data th.sort-desc { padding-right: 22px; }
table.data th.sort-asc::after, table.data th.sort-desc::after {
  position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
  color: var(--accent); font-size: 9px; line-height: 1;
}
table.data th.sort-asc::after { content: "\25B2"; }
table.data th.sort-desc::after { content: "\25BC"; }
/* column lines (operator 09.10.2026, 1.6.0: "a lightly visible line between the columns"): a
   1 px line at the right of every cell but the last, head and body — an orientation line that
   lets the eye follow a column through the whole height of the cut-out, not a grid. --line-soft
   is translucent ink, so the line keeps the same low contrast (about 1.2:1) on both zebra tones,
   the hovered and the open row and the head. The head draws it as the first inset shadow above
   (next to its 2 px rule), for the rule's reason: collapsed borders stay behind the sticky head. */
table.data td { border-right: 1px solid var(--line-soft); }
table.data td:last-child { border-right: 0; }
table.data th:last-child { box-shadow: inset 0 -2px 0 var(--control); }
table.data tbody tr:nth-child(even) td { background: var(--surface-2); }
/* zebra by VISIBLE position while a column filter hides rows (1.8.0, app.js sets .za/.zb on
   the rows that remain — nth-child would count the hidden ones and put two of a tone together) */
table.data tbody tr.za td { background: var(--surface); }
table.data tbody tr.zb td { background: var(--surface-2); }
table.data tbody tr.open td { background: var(--open-bg); }
table.data tbody tr:hover td { background: var(--hover); }
table.data tbody tr.status-expired td { color: var(--muted); }
table.data tr:last-child td { border-bottom: 0; }
table.data .num { text-align: right; }
table.data td.wrap { white-space: normal; min-width: 180px; max-width: 320px; }

/* the long lists (/bans/*, /connects, /servers) carry .page-sticky — since 1.10.0 a marker
   only: the cut-out of 1.4.0 (a frame no taller than the window, scrolling inside both ways)
   is gone at the operator's word, the page scrolls and the head is held by the script. No
   height rule here, on purpose. */

/* column walls (app.js, table.data[data-table]): once the script has fixed the layout, a cell
   cuts its content instead of pushing the column (the full text stays in title/hover where the
   page offers it); the grip sits at the right edge of each head cell, 8 px wide, and shows a
   1 px line while the head is hovered — the accent while it is dragged. */
table.data.cols-fixed th, table.data.cols-fixed td { overflow: hidden; text-overflow: ellipsis; }
table.data.cols-fixed td.wrap { white-space: nowrap; min-width: 0; max-width: none; }
.col-resize { position: absolute; top: 0; right: 0; width: 8px; height: 100%; cursor: col-resize; touch-action: none; user-select: none; }
.col-resize::after { content: ""; position: absolute; top: 5px; bottom: 5px; right: 3px; width: 1px; background: transparent; }
table.data th:hover .col-resize::after { background: var(--control); }
.col-resize:hover::after, .col-resize.dragging::after { background: var(--accent); }
/* the resize zone (1.6.0, app.js): within 6 px of a column edge — in the head or in a body
   cell — the script puts .col-hover on the table and the pointer shows the wall; a press there
   drags it. Links keep their own pointer (the press on a link is a click, not a drag). During a
   drag the wall cursor holds everywhere, whatever the pointer passes (!important: the cells'
   own cursors below are more specific). */
table.data.col-hover, table.data.col-hover th, table.data.col-hover td { cursor: col-resize; }
body.col-dragging { cursor: col-resize; user-select: none; -webkit-user-select: none; }
body.col-dragging * { cursor: col-resize !important; }

/* cell copy (operator 09.10.2026, 1.6.0, app.js): in the connect tables (data-copy="cells",
   src/shared/components/connect-table.php) a click on a body cell copies its value; the cursor says
   so — except in the resize zone, where the wall takes the click (the rule above, later in the
   cascade at the same specificity, wins). The "copied" label sits fixed at the pointer, out of
   the flow (nothing moves), 900 ms, and fades. Inverted greys, not a colour: a transient note. */
table.data[data-copy="cells"] td { cursor: copy; }
table.data.col-hover[data-copy="cells"] td { cursor: col-resize; }
.copied {
  position: fixed; z-index: 60; pointer-events: none;
  padding: 0 var(--s2); border-radius: var(--radius);
  background: var(--ink); color: var(--bg);
  font-size: var(--fs-11); line-height: var(--lh-11); font-weight: 600; white-space: nowrap;
  animation: copied 900ms ease-out forwards;
}
/* there at once (feedback must not lag the click), held, then gone upwards */
@keyframes copied {
  0% { opacity: 1; transform: translateY(0); }
  70% { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(-6px); }
}

/* the selection column (1.8.0, operator 09.10.2026: "bestimmte Connects auszuwählen: alle
   angezeigten anwählen sowie einzeln an- und abwählen"): the first column of the connect tables,
   28 px, one box per row and the "all shown rows" box in the head; nothing to copy there, no
   text to select on a shift-click. A selected row sits on --selected-bg — the second blue of
   the table, deeper than --hover — and keeps it under the pointer (the rule comes after the
   hover rule): a selection is a state the admin reads the sums from, a hover a moment. Without
   scripts the column is pointless and hidden (html:not(.js), theme.js sets the class). */
/* (the 7 px either side of the 14 px box ARE the 28 px: in an auto-layout table wider than its
   frame Firefox shrinks a cell to its min-content and ignores the width — the padding holds) */
table.data th.sel, table.data td.sel { width: 28px; padding-left: 7px; padding-right: 7px; text-align: center; user-select: none; -webkit-user-select: none; }
table.data .sel input { margin: 0; vertical-align: middle; cursor: pointer; }
table.data[data-copy="cells"] td.sel { cursor: default; }
table.data.col-hover[data-copy="cells"] td.sel { cursor: col-resize; }
table.data tbody tr.selected td { background: var(--selected-bg); }
html:not(.js) table.data .sel { display: none; }

/* the filter row (1.8.0, operator: "Spalten sollten außerdem gefiltert werden können"): a second
   head row, one search field per column, under the name row (the script moves both rows
   together, 1.10.0); the fields filter the loaded rows of the page in the browser — no query —
   and a field with a filter in it shows the accent frame. Hidden by the "hide filters" link
   ([hidden]) and, without scripts, by html:not(.js). The column line runs through it like
   through the head; its lower edge is a hairline, the 2 px rule stays where it is: under the
   names. */
table.data thead tr.filters th {
  padding: var(--s1); font-weight: 400;
  box-shadow: inset -1px 0 0 var(--line-soft), inset 0 -1px 0 var(--line);
}
table.data thead tr.filters th:last-child { box-shadow: inset 0 -1px 0 var(--line); }
table.data thead tr.filters th.sel { padding: var(--s1) 0; }
table.data tr.filters[hidden] { display: none; }
table.data tr.filters input[type=search] {
  display: block; width: 100%; min-width: 0;
  font-size: var(--fs-12); line-height: var(--lh-12); padding: 2px var(--s2); border-color: var(--line);
}
table.data tr.filters input[type=search].active { border-color: var(--accent); }
table.data tr.filters input[type=search]:focus-visible { border-color: var(--accent); }
table.data.col-hover tr.filters input { cursor: auto; }
html:not(.js) table.data tr.filters { display: none; }
/* "· x of y rows match" in the panel meta with its "clear filters" button after it; the
   "hide filters" link of the panel actions is spaced by the panel-actions rule above */
.flt-meta { margin-left: var(--s2); }
.flt-meta a { margin-left: var(--s1); }

/* the selection summary (1.8.0): the frame above the table in the same panel, shown while two
   or more rows are selected (app.js fills it from the rows' data-s). One line of head
   ("Selection: 3 of 25 visits", the rows a filter hides, "clear"), then two small tables side by
   side — kills / deaths per group with K/D, and the times as totals and per visit — numbers
   right-aligned and tabular like everywhere, the total row in 600, the row labels muted, the
   column labels in the 11 px label style of the profile card. */
.selection-summary { padding: var(--s3) var(--s4) var(--s2); border-bottom: 1px solid var(--line); }
.selection-summary[hidden] { display: none; }
.sum-head { display: flex; align-items: baseline; flex-wrap: wrap; margin: 0 0 var(--s2); font-size: var(--fs-13); line-height: var(--lh-13); font-variant-numeric: tabular-nums; }
.sum-head strong { margin-right: var(--s2); }
.sum-head .dim { margin-right: var(--s2); }
.sum-head a { margin-left: auto; }
.sum-grid { display: flex; flex-wrap: wrap; align-items: flex-start; }
.sums { border-collapse: collapse; margin: 0 var(--s6) var(--s2) 0; font-size: var(--fs-13); line-height: var(--lh-13); font-variant-numeric: tabular-nums; }
.sums th, .sums td { padding: 2px var(--s2); text-align: left; white-space: nowrap; vertical-align: baseline; }
.sums th:first-child, .sums td:first-child { padding-left: 0; }
.sums thead th { font-size: var(--fs-11); line-height: var(--lh-11); font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); border-bottom: 1px solid var(--line); }
.sums tbody th { font-weight: 400; color: var(--muted); }
.sums .num { text-align: right; }
.sums tr.total td { font-weight: 600; }
/* the two population averages (1.9.0): coloured by the rule, 600 like a status word; pop-na
   (no value on one side) stays in the text colour — "n/a" is not a status */
.sums .pop { font-weight: 600; }
.sums .pop-level { color: var(--pop-level); }
.sums .pop-higher-join { color: var(--pop-join); }
.sums .pop-higher-disc { color: var(--pop-disc); }

@media (max-width: 800px) {
  table.data td.wrap { min-width: 160px; max-width: 240px; }
}
