/* --- Panels --- */
.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  /* So a table's square corners are clipped by the rounded border. */
  overflow: hidden;
}

/* A record card is a padded grid, so nothing inside it ever reaches the rounded
   corner and there is nothing to clip. Hiding overflow anyway cut the bottom off
   every hint bubble opening from a card's eyebrow — the bubble drops below the
   mark, and on the last card of a screen it had nowhere to go.

   Cards host tables now — the Kostenarten and Jahresspiegel `.datalist`, the
   Ablesetafel's reading grid — and none needs the card to clip for it: each is
   its own scroll container and stops at `--table-max`. */
.panel.record-card {
  overflow: visible;
}

.panel--pad {
  padding: 1.25rem;
}

/* The height is load-bearing, not decoration. `overflow-x` alone already made
   this a scroll container on both axes, so `.ledger th`'s `position: sticky`
   measures against this box — and against an `auto` height it never moved, so
   the sticky head held on no screen for the twenty days both were here.
   docs/issues/024. */
.panel__scroll {
  max-height: var(--table-max);
  overflow-x: auto;
  overflow-y: auto;
}

/* A statement embedded in a form is context beside the fields rather than the
   thing being read, so it stays shorter. `min` because on a short window 28rem
   is taller than the general cap. */
.panel__scroll--capped {
  max-height: min(28rem, var(--table-max));
}

/* --- Stat tiles --- */
.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(17rem, 100%), 21rem));
  gap: 0.7rem;
}

/*
  Tiles that split one total stretch to the width of the table below them, so the
  band reads as one figure broken apart rather than as loose chips. `auto-fit`
  rather than `auto-fill`: empty tracks collapse, so three tiles fill the row.
  Not the default — a lone tile would grow to the full width and look absurd.
*/
.tiles--fill {
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
}

.tile {
  padding: 0.95rem 1.1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  display: grid;
  gap: 0.35rem;
}

.tile__name {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.tile__value {
  font-size: 1.6rem;
  font-weight: 300;
  letter-spacing: -0.02em;
}

.tile__meta {
  font-size: 0.78rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

/* --- Data table: filter bar, sortable columns, pagination --- */
.dtable {
  display: grid;
  gap: 0.8rem;
}

.dtable__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
}

/* The Aktionen menu sits at the right edge whether or not the screen declared
   filters. space-between alone put it there only when something stood to its
   left, so a table with no search and no filters — Benachrichtigungen is the
   first — opened with its one command against the left margin, where every
   other screen has it against the right. */
.dtable__bar > :not(.dtable__filters) {
  margin-left: auto;
}

/* Wraps only when the row does not fit. Without it a table with two filters
   pushed its selects past the panel at 430 px and squeezed the search box to
   its icon behind them (#634). */
.dtable__filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  flex: 1 1 16rem;
  min-width: 0;
}

.search {
  position: relative;
  display: flex;
  align-items: center;
  flex: 1 1 14rem;
  min-width: 0;
}

.search__icon {
  position: absolute;
  left: 0.65rem;
  width: 1rem;
  height: 1rem;
  color: var(--text-faint);
  pointer-events: none;
}

.search__input {
  width: 100%;
  padding-left: 2.15rem;
}

.select--compact {
  --field-padding-block: 0.45rem;
  width: auto;
  max-width: 100%;
  flex: 0 1 auto;
  padding: var(--field-padding-block) 0.6rem;
  font-size: 0.9rem;
}

.th-sort {
  cursor: pointer;
  user-select: none;
  transition: color 120ms ease;
}

.th-sort:hover {
  color: var(--text-dim);
}

.th-sort::after {
  content: "";
  display: inline-block;
  width: 0.7em;
  opacity: 0.5;
}

.th-sort.is-sorted {
  color: var(--brass);
}

.th-sort.is-sorted[data-dir="asc"]::after {
  content: " ↑";
  opacity: 1;
}

.th-sort.is-sorted[data-dir="desc"]::after {
  content: " ↓";
  opacity: 1;
}

.num.th-sort::after {
  width: auto;
}

/* Left to right, with the pager pushed to the end. Not `space-between`: three
   of the four cells are conditional, and spreading whatever is left meant the
   count moved sideways when a filter emptied a page or a sum was declared. */
.dtable__foot {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

/* How big the table is, and which slice of it is on screen. The count wore the
   filter bar's register — uppercase, letterspaced, 600 — because that is where
   it used to sit; under the table it is a plain sentence and reads as one
   (#443). */
.dtable__count,
.dtable__range {
  font-size: 0.82rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

/* What a list left out, said under it. A retired building keeps every row it
   ever had, so a register that simply stops showing them is a count that shrank
   for no reason a reader can see (#488). The count's own register, because it is
   the same kind of sentence about the same table. */
.list-note {
  margin: 0.5rem 0 0;
  font-size: 0.82rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

/* The typed span, under the bar rather than in it: two date fields in a row of
   compact selects wrap the bar on a narrow screen and push the count off it.
   `.dtable__range` above is the pager's `1–10 von 27` and is a different thing
   wearing a similar name. */
.dtable__span {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin: 0 0 0.75rem;
}

.dtable__span .field {
  flex: 0 1 10rem;
  margin: 0;
}

.dtable__span-note {
  flex: 1 1 100%;
  margin: 0;
  font-size: 0.82rem;
  color: var(--neg);
}

/* What the filter in force is worth. On the same line as the count rather than
   under it: the count names the set the figure is true for, which is what keeps
   it from reading as a second opinion about the tiles above the table (#369).
   That job was the range's until the count moved down beside it (#443). */
.dtable__total {
  font-size: 0.82rem;
  font-weight: 550;
}

.dtable__pager {
  display: flex;
  gap: 0.4rem;
  margin-left: auto;
}

.dtable__pager .btn:disabled {
  opacity: 0.4;
  cursor: default;
}

/* --- Ledger table --- */
.ledger {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.ledger th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--sunk);
  text-align: left;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
  padding: 0.65rem 1rem;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

.ledger td {
  padding: 0.7rem 1rem;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}

/* `.linkish` carries the 1.02rem of the record card it was first written for.
   In a row of 0.9rem text that reads as a bigger word rather than as a link, so
   the cell's own size wins and the colour is what marks it (#493). */
.ledger .linkish {
  font-size: inherit;
}

.ledger tbody tr {
  transition: background 90ms ease;
}

.ledger tbody tr[data-clickable] {
  cursor: pointer;
}

.ledger tbody tr[data-clickable]:hover {
  background: var(--surface-2);
}

/* The button the keyboard lands on, stripped back to the text it wraps. A row
   that opens a record is a control, and this is the only part of it a browser
   will let hold focus — docs/issues/083-eine-zeile-die-die-tastatur-erreicht.md. */
.ledger__open {
  display: block;
  /* The cell's width and not the text's, so a lead column aligned `num` keeps
     its figures under their heading instead of hugging the left edge. */
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-variant-numeric: inherit;
  color: inherit;
  text-align: inherit;
  cursor: pointer;
}

/* The whole row answers, because the whole row is what opens. `:focus-visible`
   so a click leaves nothing behind, and the ring is the brass every other
   focused control in kontor wears — legible on the hover shade it sits on. */
.ledger tbody tr[data-clickable]:has(.ledger__open:focus-visible) {
  background: var(--surface-2);
}

.ledger__open:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  box-shadow: none;
}

.ledger tbody tr:last-child td {
  border-bottom: 0;
}

/* The Saldovortrag: the line the period opens on, not a movement within it. */
.ledger tbody tr[data-opening] td {
  background: var(--sunk);
  color: var(--text-dim);
}

.cell-date {
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
  white-space: nowrap;
}

.cell-strong {
  font-weight: 550;
}

.cell-dim {
  color: var(--text-dim);
  max-width: 34rem;
}

/* A second line under a figure, for the factor that qualifies it. Used where a
   Kostenart is split over two Umlageschlüssel and the weight alone would not
   produce the amount — rare enough that a column of its own would be empty on
   99 % of rows. */
.cell-note {
  font-size: 0.8125rem;
  color: var(--text-dim);
  white-space: nowrap;
}

/* Two lines, then an ellipsis. An incoming payment's purpose line is prose the
   tenant's bank wrote — `MIETE FUER BUERORAEUME LOTTER KIRCHWEG 28 GEM.
   MIETVERTRAG VOM 20.02.2019` — and shown whole it made one row five lines
   tall, so four payments filled the screen. The whole line is in the sheet,
   which is where somebody reading it is looking. */
.cell-clamp {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.num {
  text-align: right;
  white-space: nowrap;
}
