/* THE REGISTER. Every measurement the fit math reads arrives as a component-written custom property, since
   a second copy here is how band and rows fall out of line. One line only — the band capping the columns;
   whitespace divides the rows. The `RowFocusEntry` spans the row above in-flow content, so control-holding
   boxes are lifted over it on `--lotics-z-row-content`; the ordinal is text and stays under the door. */
@layer theme, base, lotics, components, utilities;
@layer lotics.tokens, lotics.reset, lotics.components;

@layer lotics.components {
  /* Fills its container; `min-width: 0` lets the columns absorb a squeeze. */
  .lotics-table {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    flex-shrink: 0;
    width: 100%;
    min-width: 0;
  }

  /* Invisible until measured, so the first visible frame is already register or stacked. */
  .lotics-table[data-unmeasured] {
    opacity: 0;
  }

  /* No padding (GAP-85): a borderless Table's container owns the top spacing, and band padding would
     double the gap. */
  .lotics-table__header {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: var(--lotics-table-column-gap);
    flex-shrink: 0;
    min-width: 0;
    padding-inline: var(--lotics-table-gutter);
    border: 0 solid var(--border);
    border-bottom-width: 1px;
  }

  /* The literal neutral, not the accent wash: a lid on the columns, never "you are here". It bleeds to the
     row wash's width so its edges meet a hovered row. */
  .lotics-table__header[data-filled] {
    background-color: var(--lotics-wash-selected);
    margin-inline: calc(var(--lotics-row-wash-bleed) * -1);
    padding-inline: calc(var(--lotics-table-gutter) + var(--lotics-row-wash-bleed));
    border-top-left-radius: calc(var(--lotics-radius-control) - var(--lotics-space-2));
    border-top-right-radius: calc(var(--lotics-radius-control) - var(--lotics-space-2));
  }

  /* One column box for heading and cells keeps a right-aligned heading over its digits. */
  .lotics-table__column,
  .lotics-table__cell {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    min-width: 0;
  }

  /* The basis is the fit's budget (`columnBasis`), never zero — from zero, `flex` share alone gives
     unequal needs equal widths. `--lotics-table-col-floor` is the fit's reservation, the shrink stop. */
  .lotics-table__column,
  .lotics-table__cell:not([data-stacked]) {
    flex: var(--lotics-table-col-flex) var(--lotics-table-col-shrink, 1) var(--lotics-table-col-basis, 0%);
    min-width: var(--lotics-table-col-floor, 0);
  }

  /* The row's name is capped (`IDENTITY_SHARE`), since measured off the longest row it would take a third
     of the register; frozen there, the free space goes to the other columns. `min-width` outranks
     `max-width`, so the fit's seat still wins. */
  .lotics-table__column[data-identity],
  .lotics-table__cell[data-identity]:not([data-stacked]) {
    max-width: var(--lotics-table-identity-cap, none);
  }

  .lotics-table__column[data-fixed],
  .lotics-table__cell[data-fixed] {
    flex: none;
    width: var(--lotics-table-col-width);
  }

  /* A PHONE'S NAME AND THE ONE READING BESIDE IT ARE ONE BOX, on the band and on every row, running to the
     row's edge: the name's slot takes what the reading leaves, never under its least (`phoneNameLeast`), and
     the name's line runs on under the reading inside the box. */
  .lotics-table__column[data-beside],
  .lotics-table__cell[data-beside]:not([data-stacked]) {
    flex: 1 1 0%;
    flex-direction: row;
    align-items: center;
    gap: var(--lotics-table-column-gap);
    width: auto;
    min-width: 0;
    max-width: none;
  }

  .lotics-table__subject {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    flex: 1 1 0%;
    min-width: var(--lotics-table-col-floor, 0);
  }

  /* The reading is its own words' width on each row, at the row's end — so neither is cut while the two fit
     the row; its seat is the most it takes. */
  .lotics-table__beside,
  .lotics-table__cell[data-seat]:not([data-stacked]) {
    display: flex;
    flex-direction: column;
    flex: 0 0 auto;
    width: auto;
    max-width: var(--lotics-table-col-width);
    min-width: 0;
  }

  /* Its reading fills the box it shrank to, so a cut one ends in its ellipsis inside it — aligned to an
     edge it would spill past the name instead. */
  .lotics-table__cell[data-seat]:not([data-stacked]) {
    align-items: stretch;
  }

  /* Its heading ends where the readings under it do. */
  .lotics-table__beside {
    align-items: flex-end;
  }

  .lotics-table__column[data-align="right"],
  .lotics-table__cell[data-align="right"] {
    align-items: flex-end;
  }

  /* The column's line budget held on every row (`TableColumn.lines`), top-set, so first lines share one
     edge even when captions are sparse. */
  .lotics-table__cell[data-lines="2"]:not([data-stacked]) {
    min-height: calc(var(--lotics-register-line) * 2);
  }

  /* A stacked cell keeping its column header over its value, both on the pile's right edge so positions
     land on their marks. */
  .lotics-table__cell[data-headed] {
    align-items: flex-end;
    gap: var(--lotics-space-2);
  }

  /* A phone's one reading whose chrome does not fit its room: its line form as one run of words, cut by an
     ellipsis where the room ends. */
  .lotics-table__compact {
    display: block;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    color: var(--lotics-ink-default);
  }

  .lotics-table__compact[data-align="right"] {
    text-align: end;
  }

  .lotics-table__compact [class*="lotics-"] {
    white-space: inherit;
  }

  /* THE FOLD, where the name's cell holds no row to seat it: the columns this width could not seat, each
     its label then its value, under the cell's content, wrapping whole. */
  .lotics-table__fold {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    min-width: 0;
    column-gap: var(--lotics-space-12);
    row-gap: var(--lotics-space-2);
    /* The separator hangs in the gap before its reading, so one the width wrapped to a new line takes its
       separator past the box's start edge, where this clips it: a line never opens on a `·`. */
    clip-path: inset(-100% -100% -100% 0);
  }

  .lotics-table__folded {
    position: relative;
    display: flex;
    flex-direction: row;
    align-items: center;
    min-width: 0;
    gap: var(--lotics-space-4);
  }

  /* The label keeps its words; the value is what wraps. */
  .lotics-table__fold-label {
    flex-shrink: 0;
  }

  .lotics-table__folded ~ .lotics-table__folded::before {
    content: "·";
    position: absolute;
    inset-inline-start: calc(var(--lotics-space-12) * -1);
    width: var(--lotics-space-12);
    text-align: center;
    color: var(--lotics-ink-muted);
  }

  /* The header's reservation for the trailing gutter, so the last heading ends where the last value does. */
  .lotics-table__trailing-spacer {
    flex: none;
    width: var(--lotics-table-trailing-width);
  }

  /* Right-aligned so 9 and 100 share an edge. */
  .lotics-table__ordinal {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    flex: none;
    width: var(--lotics-table-ordinal-width);
  }

  .lotics-table__lead {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    flex: none;
    width: var(--lotics-table-leading-width);
    position: relative;
    z-index: var(--lotics-z-row-content);
  }

  /* The stacked bulk-select and its words as one press target; the gutter keeps its width to hold the mark
     over the rows' boxes. Side by side the headings hold this space. */
  .lotics-table__select-all {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: var(--lotics-table-column-gap);
    flex: 1;
    min-width: 0;
  }

  .lotics-table__select-all-label {
    min-width: 0;
  }

  /* Just enough to keep two washes apart; the row height carries the air. Shared with a group's rows. */
  .lotics-table__body,
  .lotics-table__group-body {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    flex-shrink: 0;
    min-width: 0;
    gap: var(--lotics-space-4);
  }

  /* Two classes deep so it beats the row surface's gap whatever sheet order a bundler emits. */
  .lotics-table .lotics-table__row {
    gap: var(--lotics-table-column-gap);
  }

  /* A read-only row keeps the gutter and layout to align with the header, without the press surface. */
  .lotics-table__row[data-static] {
    display: flex;
    flex-direction: row;
    align-items: center;
    flex-shrink: 0;
    min-width: 0;
    padding-inline: var(--lotics-table-gutter);
  }

  /* The pressable row's hold and fade, on the row's own box. */
  .lotics-table__row[data-static][data-arrived] {
    animation-name: lotics-table-static-arrival;
    animation-duration: calc(var(--lotics-duration-slow) * 3);
    animation-delay: calc(var(--lotics-duration-slow) * 4);
    animation-timing-function: var(--lotics-ease-exit);
    animation-fill-mode: both;
  }

  @keyframes lotics-table-static-arrival {
    from {
      background-color: var(--lotics-accent-wash);
    }
    to {
      background-color: transparent;
    }
  }

  .lotics-table__row[data-static][data-stacked] {
    align-items: stretch;
  }

  /* One box for headings and each row's cells between the same gutters, so a column's share resolves
     alike. Columns at their width leave the slack as equal gaps between them, so the readings spread
     across the row and the last ends on the register's edge; a growing column takes it all instead. */
  .lotics-table__columns,
  .lotics-table__cells {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    flex: 1;
    gap: var(--lotics-table-column-gap);
    min-width: 0;
  }

  /* A phone row of two lines: the reading beside the name keeps to the title's line, the top of the
     cells, so the line of readings under it passes clear and no row's status drifts between. The row
     stands the floor and air itself, and centres its one line of tick, cells and ⋯ in them — the lines
     centred whatever the floor, their tops one edge. The cells shrink, so the line never wraps. */
  .lotics-table__row:has(> .lotics-table__cells[data-reach]) {
    flex-wrap: wrap;
    align-content: center;
    align-items: flex-start;
    min-height: var(--lotics-table-row-height, var(--lotics-row-beat));
    padding-block: var(--lotics-table-row-inset, var(--lotics-row-inset));
  }

  /* Deep enough to beat a top-aligned register's padding, which the row now pays. */
  .lotics-table .lotics-table__row > .lotics-table__cells[data-reach] {
    min-height: 0;
    padding-block: 0;
  }

  .lotics-table__cells[data-reach] > .lotics-table__cell[data-beside] {
    align-items: flex-start;
  }

  /* The row's tick centres on that same title line; its ⋯, about the whole row, centres on the row. */
  .lotics-table__row:has(> .lotics-table__cells[data-reach]) > .lotics-table__lead {
    align-self: flex-start;
    align-items: center;
    height: var(--lotics-leading-tight-sm);
  }

  .lotics-table__row:has(> .lotics-table__cells[data-reach]) > .lotics-table__trailing {
    align-self: center;
  }

  /* The page's beat, a floor; `TableRow`'s `minHeight` is the one override. A register row carries its own
     air (`--lotics-row-inset`) because its cells are cells; a denser floor states its own, capped at that
     floor less the pressable cell, halved (`TableRow`). */
  .lotics-table__cells {
    min-height: var(--lotics-table-row-height, var(--lotics-row-beat));
    padding-block: var(--lotics-table-row-inset, var(--lotics-row-inset));
    position: relative;
    z-index: var(--lotics-z-row-content);
  }

  .lotics-table__trailing {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-end;
    gap: var(--lotics-space-8);
    flex: none;
    width: var(--lotics-table-trailing-width);
    position: relative;
    z-index: var(--lotics-z-row-content);
  }

  /* THE DOOR TAKES THE POINTER, the press the accessibility tree names (`RowFocusEntry`): the boxes lifted over
     it let the pointer through, and only the row's own controls — a tick, a link, a menu, an act — take it
     back. Lifted whole, a row's words took every press, and a click on them was never the door's. */
  .lotics-table__row:not([data-static]) > :is(.lotics-table__lead, .lotics-table__cells, .lotics-table__trailing, .lotics-table__stack) {
    pointer-events: none;
  }

  .lotics-table__row:not([data-static]) :is(a[href], button, input, select, textarea, label, summary, [role="button"], [role="checkbox"], [role="switch"], [role="radio"], [role="link"], [role="menuitem"], [role="combobox"], [role="slider"], [role="tab"], [contenteditable="true"]) {
    pointer-events: auto;
  }

  /* Stacked, the overflow is a plain slot: no columns to reserve a gutter for. */
  .lotics-table__trailing[data-stacked] {
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    width: auto;
  }

  /* Top alignment pays in padding so a one-line row matches the centred default, measured from the beat so
     a dense row does not shift up. */
  .lotics-table[data-align="top"] .lotics-table__row,
  .lotics-table[data-align="top"] .lotics-table__cells,
  .lotics-table[data-align="top"] .lotics-table__cell[data-beside],
  .lotics-table[data-align="top"] .lotics-table__trailing:not([data-stacked]) {
    align-items: flex-start;
  }

  /* Paid once, on the content boxes; charging the row too would double it. */
  .lotics-table[data-align="top"] .lotics-table__cells,
  .lotics-table[data-align="top"] .lotics-table__trailing:not([data-stacked]) {
    padding-block: calc((var(--lotics-row-beat) - var(--lotics-control-height)) / 2);
  }

  .lotics-table__block {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    flex-shrink: 0;
    min-width: 0;
  }

  /* Indented to the identity's text edge; vertical padding measured from the selected wash, the edge the
     eye reads. */
  .lotics-table__detail {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    flex-shrink: 0;
    min-width: 0;
    padding-inline: var(--lotics-table-gutter);
    padding-block: var(--lotics-space-16);
  }

  /* Held then faded: a wash starting mid-fade reads as an artefact. */
  .lotics-table__arrival {
    position: absolute;
    inset: 0;
    border-radius: var(--lotics-radius-control);
    background-color: var(--lotics-accent-wash);
    pointer-events: none;
    animation-name: lotics-table-arrival;
    animation-duration: calc(var(--lotics-duration-slow) * 3);
    animation-delay: calc(var(--lotics-duration-slow) * 4);
    animation-timing-function: var(--lotics-ease-exit);
    animation-fill-mode: both;
  }

  @keyframes lotics-table-arrival {
    from {
      opacity: 1;
    }
    to {
      opacity: 0;
    }
  }

  /* The top line keeps the lead/trail slots beside the first cell; the rest are label-over-value blocks,
     padded instead of centred. */
  .lotics-table__stack {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    flex: 1;
    min-width: 0;
    gap: var(--lotics-space-10);
    padding-block: var(--lotics-space-12);
    position: relative;
    z-index: var(--lotics-z-row-content);
  }

  .lotics-table__stack-top {
    display: flex;
    flex-direction: row;
    align-items: center;
    flex-shrink: 0;
    min-width: 0;
    gap: var(--lotics-table-column-gap);
    min-height: var(--lotics-control-height);
  }

  .lotics-table__stack-fields {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    flex-shrink: 0;
    min-width: 0;
    gap: var(--lotics-space-8);
  }

  .lotics-table__stack-fields[data-indented] {
    padding-left: calc(
      var(--lotics-table-leading-width) + var(--lotics-table-ordinal-width) +
        var(--lotics-table-column-gap)
    );
  }

  /* The CTA closes the content, right-aligned — content scale, not chrome; the overflow stays on the top
     line. */
  .lotics-table__stack-action {
    display: flex;
    flex-direction: row;
    align-items: stretch;
    flex-shrink: 0;
    min-width: 0;
    justify-content: flex-end;
  }

  /* A verb that stood beside the ⋯ leaves this empty and gapless. */
  .lotics-table__stack-action:empty {
    display: none;
  }

  /* The identity cell is the top line's flexible box, heading the pile. */
  .lotics-table__cell[data-stacked][data-primary] {
    flex: 1;
    min-width: 0;
  }

  /* No label to spread against, so the content pins right. */
  .lotics-table__cell[data-bare] {
    flex-direction: row;
    align-items: center;
    justify-content: flex-end;
    min-height: var(--lotics-control-content-height);
  }

  /* Air, not a second rule competing with the band's, as margin outside the heading so the wash does not
     cover it. */
  .lotics-table__group {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    flex-shrink: 0;
    min-width: 0;
    margin-top: var(--lotics-space-16);
    /* The lead slot before every label, which the description's indent repeats. */
    --lotics-table-group-mark: 16px;
  }

  .lotics-table__group-heading {
    display: flex;
    flex-direction: row;
    align-items: center;
    flex-shrink: 0;
    min-width: 0;
    gap: var(--lotics-space-8);
    min-height: var(--lotics-register-row);
    padding-inline: var(--lotics-table-gutter);
    border: 0 solid var(--border);
    border-radius: calc(var(--lotics-radius-control) - var(--lotics-space-2));
    background-color: transparent;
    background-image: none;
    font-family: var(--font-sans);
    font-size: var(--lotics-text-sm);
    line-height: var(--lotics-leading-sm);
    letter-spacing: var(--lotics-tracking-sm);
    color: var(--lotics-ink-default);
    appearance: none;
    cursor: auto;
    text-align: start;
    touch-action: manipulation;
    user-select: none;
    transition: background-color var(--lotics-duration-fast) var(--lotics-ease-standard);
  }

  /* A record heading its band stands at the rows' edge, as a plain heading does. */
  .lotics-table__group-head {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    flex-shrink: 0;
    min-width: 0;
    padding-inline: var(--lotics-table-gutter);
  }

  /* The title and its count on one line, as the plain heading sets them. */
  .lotics-table__group-title {
    display: flex;
    flex-direction: row;
    align-items: baseline;
    column-gap: var(--lotics-space-8);
    min-width: 0;
  }

  .lotics-table__group-heading[data-pressable]:hover {
    background-color: var(--lotics-wash-hover);
  }

  .lotics-table__group-heading:focus-visible {
    outline: var(--lotics-ring-width) solid var(--ring);
    outline-offset: 0;
  }

  /* Starts where the label does: the gutter, the mark slot and the gap between them. Air below as
     margin, like the band's own, since a register composes no band height. */
  .lotics-table__group-description {
    padding-inline: calc(var(--lotics-table-gutter) + var(--lotics-table-group-mark) + var(--lotics-space-8)) var(--lotics-table-gutter);
    margin-bottom: var(--lotics-space-8);
  }

  /* An unmarked band has no lead slot: its words start at the rows' edge. */
  .lotics-table__group-description:not([data-marked]) {
    padding-inline-start: var(--lotics-table-gutter);
  }

  /* A fixed slot so labels start at one x on every band. */
  .lotics-table__group-mark {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: var(--lotics-table-group-mark);
  }

  .lotics-table__group-dot {
    flex: none;
    width: 6px;
    height: 6px;
    border-radius: var(--lotics-radius-full);
    background-color: var(--lotics-table-group-dot);
  }
}
