/* ============================================================================
   Tables
   ========================================================================== */

.table-wrap { overflow-x: auto; }
.data-table { width: 100%; }

/* === Fitting a list inside its card ===
   A table sized by its content is sized by the longest string in it, and one address or clone URL
   is enough to push the last two columns past the right edge, where .table-wrap hides them behind
   a scrollbar nobody goes looking for. Allowing a break inside an otherwise unbreakable string
   drops the table's minimum width to something the card can hold. A table that already fits is
   unaffected: the layout algorithm only reaches for the minimum when there is not enough room.
   The horizontal scroll above stays as the last resort, for the tablet widths where ten columns
   genuinely do not fit. */
.data-table th, .data-table td { overflow-wrap: anywhere; }

/* Where the string is a name, an ellipsis reads better than a word cut in half. .cell-clip needs
   a width to clip against, so pair it with a max-width on the cell and put the whole value in the
   cell's title. .cell-line keeps a marker beside the clipped text, rather than alone on a second
   line: the asterisk on an unresolved identity is the case it was written for. */
.cell-line { display: flex; align-items: baseline; gap: var(--space-1); }
.cell-clip { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Heads never wrap. The break-anywhere rule above is there so a clone URL or an
   address in a CELL can fold; applied to a head it breaks a two-word label mid
   syllable ("CRE ATE D BY"), which is unreadable and, being uppercase and
   letter-spaced, unmistakably wrong. A head that genuinely does not fit is what
   .table-wrap's horizontal scroll is for. */
.data-table thead th {
    text-align: left;
    white-space: nowrap;
    padding: var(--space-5) var(--space-8);
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wider);
    color: var(--text-muted);
    background: var(--surface-alt);
    border-bottom: var(--border-width) solid var(--border);
}
.data-table thead th.text-right { text-align: right; }

/* A header that ranks the table on its column. It is a button because it is a
   control, and it inherits every letter of the th's typography because it must
   not read as one: the only thing that marks it is the caret it grows once the
   table is ranked on it, and the colour coming up to full strength. A th that
   sorts and a th that does not have to line up to the pixel, or a table with one
   sortable column looks broken. */
.table-sort {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    color: inherit;
    cursor: pointer;
}
.data-table thead th.text-right .table-sort { flex-direction: row-reverse; }
.table-sort:hover { color: var(--text-body); }
.table-sort.is-sorted { color: var(--text); }
.data-table tbody td {
    padding: var(--space-6) var(--space-8);
    font-size: var(--text-md);
    border-bottom: var(--border-width) solid var(--border-subtle);
    color: var(--text-body);
}
.data-table tbody td.text-right { text-align: right; }
.data-table tbody tr:hover { background: var(--surface-hover); }
.data-table tbody tr.clickable { cursor: pointer; }
/* A disabled developer: still listed on the screen that manages them, and nowhere else. */
.data-table tbody tr.row-disabled td { opacity: 0.5; }
.data-table tbody tr.row-disabled td:last-child { opacity: 1; }
.text-nowrap { white-space: nowrap; }

/* A rank or ordinal column: as narrow as its digits. width: 1% is the auto-layout
   idiom for "shrink to content" — the algorithm treats it as a minimum and hands
   the slack to the columns that asked for room. */
.data-table .col-rank { width: 1%; white-space: nowrap; }

/* Architecture violations: fixed layout so long paths/symbols wrap inside the tile, no h-scroll. */
.arch-violations { table-layout: fixed; }
.arch-violations td.col-wrap {
    white-space: normal;
    overflow-wrap: anywhere;
    word-break: break-word;
}
.arch-violations td .status-badge { white-space: normal; overflow-wrap: anywhere; }

/* === Repositories list ===
   The row is name-over-URL rather than two columns: the URL is the widest thing on the page and
   truncating it is what keeps the table inside the viewport. */
.repo-table .repo-cell { max-width: 380px; }
.repo-name {
    display: block;
    font-weight: var(--weight-semibold);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.repo-url {
    display: block;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    color: var(--text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.repo-table .repo-status-cell { max-width: 260px; }
.repo-error {
    display: block;
    margin-top: var(--space-2);
    font-size: var(--text-xs);
    color: var(--danger-on-soft);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The architecture report's seven columns. Rule and symbol names are short and
   fixed, the file path is the only column that wants the room, and the three
   numeric columns are narrow on purpose so the eye reads down them. */
.arch-violations col:nth-child(1) { width: 13%; }
.arch-violations col:nth-child(2) { width: 30%; }
.arch-violations col:nth-child(3) { width: 6%; }
.arch-violations col:nth-child(4) { width: 21%; }
.arch-violations col:nth-child(5) { width: 9%; }
.arch-violations col:nth-child(6) { width: 7%; }
.arch-violations col:nth-child(7) { width: 14%; }

/* The row that unfolds under a violation to hold its AI explanation. */
.explain-row > td { padding: 0 var(--space-9) var(--space-8); background: var(--surface-alt); }

/* === Developers list ===
   Name over address in one cell, for the reason the repositories list stacks name over URL: half
   the display names on a real installation ARE addresses, so a separate Email column printed the
   same string a second time and pushed the row's own Disable button past the right edge of the
   card. Both stacked columns are capped, because the widest alias set decided the table's width. */
.dev-table .dev-cell { max-width: 280px; }
.dev-table .dev-alias-cell { max-width: 220px; }
/* An alias is an address, and an address has no break opportunity in it: without this the
   tag keeps its own width and hangs over the Commits column the cell was capped to protect. */
.dev-table .dev-alias-cell .tag { max-width: 100%; overflow-wrap: anywhere; }
.dev-name {
    display: block;
    font-weight: var(--weight-semibold);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.dev-email {
    display: block;
    font-size: var(--text-xs);
    color: var(--text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* === Discover repositories ===
   Name over namespace-and-address, for the reason the roster stacks name over URL: the clone URL is
   the widest string on the page and capping it here is what keeps the table inside its card. The
   cap is wider than the roster's because this list is the only place the namespace is shown beside
   the address, and an Azure project path in front of a repository name is what tells two called
   "common" apart.

   A tick column is as narrow as its box. width: 1% is the auto-layout idiom for "shrink to
   content" — the algorithm treats it as a minimum and hands the slack to the columns that asked
   for room. */
.data-table .col-tick { width: 1%; }
.discover-table .discover-repo-cell { max-width: 460px; }

/* Ghost left, primary right, with the primary written first in the markup. A form submitted with
   Enter fires its first submit button, and "Add all 133" is not what somebody who tabbed to a
   checkbox and pressed Enter meant. Source order is the safety rule; this reverses the row so the
   screen still reads the way every other header does. */
.discover-actions { flex-direction: row-reverse; }

/* === Pull requests list ===
   Seven columns now, the other four folded into the panel the row opens. The author column is
   still capped: on an installation where the display names ARE addresses it spent 260px on a
   string the reader scans rather than reads, and the title is what wants that room. Capped and
   clipped, with the full value on the cell's title. */
.pr-table .pr-ref-cell { max-width: 120px; }
.pr-table .pr-person-cell { max-width: 130px; }

/* ============================================================================
   Expanding rows
   ==========================================================================

   Ten columns is not a row, it is a paragraph set sideways. The fix is to pick
   the handful of facts a reader scans and fold the rest away, so the table can
   be read down a column again.

   The contract is three attributes and no page-local script:
     <tr class="row-parent" data-fold="ID">   the row that opens, caret first
     <tr class="row-fold" id="ID" hidden>     what it opens, one colspan cell
   ckwm-rows.js flips the hidden attribute and the is-open class; CSS turns the
   caret. A click on a link, button or form control inside the parent row is
   left alone, so the row can still carry real links.

   Two shapes, one component:
     .row-fold     a panel under the row — free-form, its own columns
     .row-subrow   the same columns again, indented: a breakdown of the row
                   above it, which is what the contributions summary needs

   Both need the explicit [hidden] rule. A <tr> takes display: table-row from
   the UA sheet, and that wins over the UA's own [hidden] { display: none }. */

.data-table tbody tr.row-parent { cursor: pointer; }
.row-caret {
    width: var(--space-5);
    color: var(--text-muted);
    transition: transform var(--transition);
}
.row-parent.is-open .row-caret { transform: rotate(90deg); }
/* Holds the caret's column open on a row that has nothing to unfold, so the
   names in the cell beside it still line up down the table. */
.row-caret-spacer { display: inline-block; width: var(--space-7); }

.row-fold[hidden], .row-subrow[hidden] { display: none; }
/* The opened panel is one surface, not a row: no cell padding, no hover tint,
   and a full-strength border under it so the pair reads as one block. */
.data-table tbody tr.row-total td {
    border-top: 1px solid var(--border-strong);
    background: var(--surface-alt);
}
/* The average row is not a competitor in the ranking above it, so it does not light up under the
   pointer the way a rankable row does. */
.data-table tbody tr.row-total:hover td { background: var(--surface-alt); }

.data-table tbody tr.row-fold > td {
    padding: 0;
    background: var(--surface-alt);
    border-bottom: var(--border-width) solid var(--border);
}
.data-table tbody tr.row-fold:hover, .data-table tbody tr.row-subrow:hover {
    background: var(--surface-alt);
}
.row-fold-body {
    padding: var(--space-8) var(--space-10) var(--space-10) var(--space-14);
}
/* Auto-fit rather than a fixed count: the same panel holds four facts on a
   phone-width card and eight on a desktop without a breakpoint of its own. */
.row-fold-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--space-8) var(--space-10);
}
.row-fold-item { min-width: 0; }
/* For the one fact that is a sentence rather than a value. */
.row-fold-wide { grid-column: 1 / -1; }
.row-fold-label {
    display: block;
    margin-bottom: var(--space-2);
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wider);
    color: var(--text-muted);
}
.row-fold-value {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
    font-size: var(--text-md);
    color: var(--text-body);
    overflow-wrap: anywhere;
}
.row-fold-list { display: flex; flex-direction: column; gap: var(--space-3); }
.row-fold-actions { margin-top: var(--space-8); }

/* A panel with more than about six facts in it needs grouping, or the reader is
   scanning a wall of labels. The heading is sentence case and full-strength on
   purpose: the labels under it are already uppercase, letter-spaced and muted,
   so a second uppercase level would read as the same level twice. */
.row-fold-section + .row-fold-section { margin-top: var(--space-10); }
.row-fold-heading {
    margin: 0 0 var(--space-6);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    color: var(--text);
}

.data-table tbody tr.row-subrow td { background: var(--surface-alt); border-top: 0; }
.data-table tbody td.row-subrow-cell { padding-left: var(--space-14); }

/* ============================================================================
   Pager
   ==========================================================================

   The count sentence is half the component. "Showing 1-25 of 363" is the only
   thing on the screen that tells a reader how much is behind the page they are
   looking at; a bare row of numbers leaves them counting clicks.

   Every control is a link with the whole query string on it, so a page is an
   address: it survives a refresh, a bookmark and the back button, and it needs
   no script at all. */

.pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-6);
    padding: var(--space-6) var(--space-8);
    border-top: var(--border-width) solid var(--border-subtle);
}
.pager-count { margin: 0; font-size: var(--text-sm); color: var(--text-muted); }
.pager-pages { display: flex; align-items: center; gap: var(--space-2); }
.pager-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: var(--space-18);
    padding: var(--space-3) var(--space-5);
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text-body);
    font-size: var(--text-sm);
    text-decoration: none;
}
.pager-link:hover { background: var(--surface-hover); color: var(--text); }
.pager-link.is-current {
    background: var(--brand);
    border-color: var(--brand);
    color: var(--text-inverse);
    font-weight: var(--weight-semibold);
}
/* Rendered as a span, not a disabled link: there is no page to point it at. */
.pager-link.is-disabled {
    background: var(--surface-sunken);
    color: var(--text-subtle);
    cursor: default;
}
.pager-gap { padding: 0 var(--space-2); color: var(--text-muted); }

/* === A fact list ===========================================================
   A term and its value, repeated. Deliberately not a two-column .data-table,
   for three reasons that a table cannot be talked out of.

   A table asserts a column axis. A customer's VAT number and the label "VAT
   number" do not sit on one, so a table makes a structural claim the content
   does not support.

   A screen reader announces a table with its row and column counts and offers
   table navigation for it. A detail page carrying four fact blocks becomes
   four tables somebody has to navigate past to read eight facts.

   .data-table lives inside .table-wrap, which is overflow-x: auto. A fact list
   has to collapse to stacked pairs on a narrow screen; inside a table wrap it
   scrolls sideways instead, which is a real failure on a phone.

   dt and dd are styled bare inside the class, the way .data-table styles bare
   th and td, so this is one class name rather than three. overflow-wrap is
   anywhere because these values are VAT numbers, licence serials and key ids,
   which have no break opportunity and would otherwise push the grid wider than
   its card. */
.data-list {
    display: grid;
    grid-template-columns: minmax(0, 12rem) minmax(0, 1fr);
    gap: var(--space-3) var(--space-8);
    margin: 0;
}
.data-list dt {
    color: var(--text-muted);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
}
.data-list dd {
    margin: 0;
    min-width: 0;
    color: var(--text-body);
    font-size: var(--text-sm);
    overflow-wrap: anywhere;
}
