/* SAB Portal — An owner's pages: the company row, the state bar, the tabs across the top,
 * the breadcrumb under them, the frame of the map page, and everything a
 * data table is made of — the box, the filter row, the column chooser, the
 * provisional row.
 *
 * One of the files listed in layers.css; read that first. Every rule below
 * is in the `app` cascade layer, so it beats Pico without needing to be
 * more specific than Pico. Sizes come from tokens.css and nowhere else. */

@layer app {

/* --- A company in a list ------------------------------------------------
 *
 * fleet/templates/fleet/partials/owner_row.html, on the overview. The line
 * is also drawn in the companies table, owner_table.html. */
.sab-owner-rows {
  display: flex;
  flex-direction: column;
}

.sab-owner-row {
  display: flex;
  align-items: center;
  gap: var(--sab-gap-loose);
  padding: var(--sab-gap) var(--sab-gap-tight);
  border-radius: var(--sab-radius);
  color: inherit;
  font-size: var(--sab-text-body);
  text-decoration: none;
}

.sab-owner-row + .sab-owner-row {
  border-top: var(--sab-hairline) solid var(--sab-rule);
  border-radius: 0;
}

a.sab-owner-row:hover,
a.sab-owner-row:focus-visible {
  background: var(--sab-quiet-bg);
}

/* A row that is not a link: a real company whose name happens to be unusable
 * in an address. Quieter, and no affordance it cannot honour. */
.sab-owner-row-flat .sab-owner-row-name {
  color: var(--pico-muted-color);
}

/* The name takes the row and gives up its tail before anything else does:
 * everything to its right is a number, and numbers do not truncate. */
.sab-owner-row-name {
  flex: 1 1 12rem;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--sab-ink);
  font-weight: var(--sab-weight-medium);
}

/* How the company is doing, as one line. Fixed width, so sixteen of them are
 * a column of comparable lines rather than sixteen different pictures. */
/* It gives way before the name does on a narrow panel. */
.sab-owner-row-line {
  flex: 1 1 10rem;
  width: 10rem;
  max-width: 18rem;
  min-width: 2.5rem;
  display: flex;
  height: var(--sab-bar-height);
  border-radius: var(--sab-bar-height);
  overflow: hidden;
  background: var(--sab-quiet-bg-hover);
}

.sab-owner-row-line i {
  display: block;
  height: 100%;
}

/* A seam in the card's own colour where one state gives way to the next. */
.sab-owner-row-line i + i {
  box-shadow: inset var(--sab-seam) 0 0 var(--sab-row);
}

.sab-owner-row-name span,
.sab-owner-row-name small {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sab-owner-row-name small {
  color: var(--pico-muted-color);
  font-size: var(--sab-text-meta);
  font-weight: var(--sab-weight-normal);
  font-variant-numeric: tabular-nums;
}

/* A fixed width, so the lines before it stay one column whatever it holds. */
.sab-owner-row-faults {
  flex: none;
  display: flex;
  justify-content: flex-end;
  gap: var(--sab-gap);
  width: 5.5rem;
  font-size: var(--sab-text-meta);
  font-variant-numeric: tabular-nums;
  color: var(--sab-ink);
}

.sab-owner-row-faults span {
  display: inline-flex;
  align-items: center;
  gap: var(--sab-gap-tight);
}

.sab-owner-row-faults i {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
}

.sab-owner-row-none {
  flex: none;
  color: var(--pico-muted-color);
  font-size: var(--sab-text-meta);
}

/* Said to a screen reader and to nothing else. It is how a row whose picture
 * is a line of colour still carries its counts in words — display rule 5. */
.sab-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- The state bar ------------------------------------------------------
 *
 * Three segments and three counts, saying the same thing twice on purpose. The
 * bar is the shape of the fleet at a glance; the legend under it is the answer,
 * and it is the half that survives greyscale, a screen reader and a printer.
 * Colour is never the only signal — display rule 5. */

.sab-state-bar {
  display: flex;
  height: var(--sab-bar-height);
  border-radius: var(--sab-radius);
  overflow: hidden;
  background: var(--sab-quiet-bg);
}

/* The fills take the lifted three — see the token block. A bar is read by
 * area and never has to pass as small text. */
.sab-state-bar-online {
  background: var(--sab-online-vivid);
}

.sab-state-bar-silent {
  background: var(--sab-silent-vivid);
}

.sab-state-bar-never {
  background: var(--sab-never-vivid);
}

.sab-state-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sab-gap-tight) var(--sab-gap-loose);
  margin-top: var(--sab-gap);
  font-size: var(--sab-text-meta);
  color: var(--pico-muted-color);
}

.sab-state-legend span {
  display: inline-flex;
  align-items: center;
  gap: var(--sab-gap-tight);
}

/* The dot in the legend carries the same colour as its segment, so the two
 * are read as one thing. It is beside the word, never instead of it. */
.sab-state-legend i {
  width: var(--sab-bar-height);
  height: var(--sab-bar-height);
  border-radius: 50%;
  flex: none;
}

.sab-state-legend b {
  font-variant-numeric: tabular-nums;
  color: var(--sab-ink);
}

/* The share, after the count and the word. Quieter than both: the number of
 * boxes is what a work order is written against, the share is the glance. */
.sab-state-legend small {
  font-variant-numeric: tabular-nums;
}

/* --- Tabs ---------------------------------------------------------------
 *
 * A row of links, one per page. Not buttons and not a script: each tab is a
 * URL, so the back button works, a tab can be bookmarked, and the page is
 * complete before anything runs.
 *
 * The current tab is marked with `aria-current="page"` in the markup as well
 * as tinted here, so it is announced and not only seen. */

/* `flex-start` against Pico, whose `nav` pushes the tabs to the two edges. */
.sab-tabs {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  gap: var(--sab-gap-tight);
  margin-bottom: var(--sab-block);
  border-bottom: var(--sab-hairline) solid var(--sab-rule);
}

.sab-tab {
  display: inline-flex;
  align-items: center;
  gap: var(--sab-gap);
  padding: var(--sab-gap) var(--sab-gap-loose);
  text-decoration: none;
  color: var(--pico-muted-color);
  font-size: var(--sab-text-label);
  border-bottom: 2px solid transparent;
}

.sab-tab:hover,
.sab-tab:focus-visible {
  color: var(--sab-ink);
  background: var(--sab-quiet-bg);
}

.sab-tab-current {
  color: var(--sab-ink);
  border-bottom-color: var(--pico-primary);
  font-weight: var(--sab-weight-strong);
}

/* The same grid and the same stroke weight as the download icon, so the two
 * read as one system. Beside the label, never instead of it. */
.sab-tab-icon {
  width: 1rem;
  height: 1rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex: none;
}

/* --- Where this page sits ------------------------------------------------
 *
 * The trail above a page deeper than the bar can reach: all companies, the
 * company, the list, the thing. Every step but the last is a link back to it,
 * because a page reached by clicking down needs a way up that is not the
 * browser's. Small and quiet — it is a signpost, read once, over the page's
 * own name. The separator is drawn here, not written in the markup, so a
 * screen reader hears four steps and not four steps and three arrows. */

/* `nav.` and not `.` alone: Pico pulls a nav's first list left by one link
 * pad (`nav ol:first-of-type`), and that rule outranks a bare class. */
nav.sab-breadcrumbs ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sab-gap-tight);
  margin: 0 0 var(--sab-gap-loose);
  padding: 0;
  list-style: none;
  font-size: var(--sab-text-meta);
  color: var(--pico-muted-color);
}

.sab-breadcrumbs li {
  display: inline-flex;
  align-items: center;
  gap: var(--sab-gap-tight);
  padding: 0;
}

/* Pico floats a zero-width character before every nav item. In a flex item
 * that is one more item and one more gap, so the first word starts a few
 * pixels late; it is switched off, and the separator drawn in its place. */
.sab-breadcrumbs li::before {
  float: none;
  content: none;
}

.sab-breadcrumbs li + li::before {
  content: "›";
  color: var(--pico-muted-color);
}

/* Pico gives a link in a `nav` a tap-sized pad and pulls it back out with a
 * negative margin, which drags the first word past the page's left edge.
 * Neither is wanted in a line of small words. */
.sab-breadcrumbs a {
  margin: 0;
  padding: 0;
  text-decoration: none;
}

.sab-breadcrumbs a:hover,
.sab-breadcrumbs a:focus-visible {
  text-decoration: underline;
}

/* The page itself, at the end of the trail: named, not linked, and in the
 * page's own colour so the eye stops there. */
.sab-breadcrumbs [aria-current="page"] {
  color: var(--sab-ink);
}

/* --- One owner's map page -----------------------------------------------
 *
 * The map square and to the left, the other pages as cards to the right, the
 * three state dials under the company's name. As few elements as the page can
 * carry: no frame around the map, no row of keys under it, no tab strip.
 *
 * The map's column is a fixed width and the map is a square inside it, so the
 * picture is the same shape for every owner. The cards share the map's height
 * between them, so the column reads as one block beside one picture rather
 * than tiles and a gap. Under 64rem there is no "beside": the cards go under
 * the map, in a row. */

/* The map on a company's overview: a panel like the others, and no longer
 * the page itself. The cards that used to sit beside it were the other tabs
 * said twice — the strip above the page already offers them. */
.sab-owner-map {
  margin-top: var(--sab-gap-loose);
}

.sab-owner-map .sab-map-canvas {
  height: 26rem;
}

/* The boxes to go and look at: one row each, and every row is a way into the
 * box's own page. Four things — which box, which site, what state, when it
 * was last heard from — because those four are what a work order needs and
 * nothing else on this page carries them. */
.sab-attention {
  margin: 0;
  padding: 0;
  list-style: none;
}

.sab-attention li {
  list-style: none;
  padding: 0;
}

.sab-attention li + li {
  border-top: var(--sab-hairline) solid var(--sab-rule);
}

.sab-attention a {
  display: flex;
  align-items: center;
  gap: var(--sab-gap-loose);
  padding: var(--sab-gap) var(--sab-gap-tight);
  border-radius: var(--sab-radius);
  color: inherit;
  font-size: var(--sab-text-small);
  text-decoration: none;
}

.sab-attention a:hover,
.sab-attention a:focus-visible {
  background: var(--sab-quiet-bg);
}

.sab-attention-unit {
  flex: none;
  color: var(--sab-ink);
  font-weight: var(--sab-weight-medium);
}

/* The site takes what is left and gives up its tail before the state and the
 * timestamp do: those two are why the row is in this panel. */
.sab-attention-site {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--pico-muted-color);
}

.sab-attention-when {
  flex: none;
  color: var(--pico-muted-color);
}

/* Pico's bottom margin on `details` lifts the time off the row's line. */
.sab-attention-when .sab-contact {
  margin: 0;
}

/* --- A row inside a panel -----------------------------------------------
 *
 * The hover runs to the card's edges, and the rules either side of the
 * hovered row step aside, so it reads as one band rather than a box in a
 * box. The rules stay inset at rest. */
.sab-panel .sab-attention a,
.sab-panel .sab-owner-row {
  margin-inline: calc(-1 * var(--sab-block));
  padding-inline: var(--sab-block);
  border-radius: 0;
  transition: background-color 120ms ease;
}

.sab-attention li:hover,
.sab-attention li:hover + li,
.sab-attention li:has(a:focus-visible),
.sab-attention li:has(a:focus-visible) + li {
  border-top-color: transparent;
}

.sab-panel .sab-owner-row + .sab-owner-row {
  border-top: 0;
  background-image: linear-gradient(var(--sab-rule), var(--sab-rule));
  background-size: calc(100% - 2 * var(--sab-block)) var(--sab-hairline);
  background-position: top center;
  background-repeat: no-repeat;
}

.sab-panel a.sab-owner-row:hover,
.sab-panel a.sab-owner-row:focus-visible,
.sab-panel a.sab-owner-row:hover + .sab-owner-row,
.sab-panel a.sab-owner-row:focus-visible + .sab-owner-row {
  background-image: none;
}

/* The name takes the accent under the pointer: the row is a way in. */
a.sab-owner-row:hover .sab-owner-row-name span,
a.sab-owner-row:focus-visible .sab-owner-row-name span,
.sab-attention a:hover .sab-attention-unit,
.sab-attention a:focus-visible .sab-attention-unit {
  color: var(--sab-action);
}

/* The age as a pill in its own freshness colour: five identical underlined
 * red phrases in a column were the loudest thing on the overview. */
.sab-attention-when .sab-contact-age {
  padding: 0.1rem var(--sab-gap);
  border: 0;
  border-radius: 999px;
  background: color-mix(in srgb, currentColor 12%, transparent);
  font-size: var(--sab-text-meta);
  font-weight: var(--sab-weight-medium);
}

/* --- Tables ------------------------------------------------------------- */

.sab-table-scroll {
  overflow-x: auto;
}

/* As wide as the page, with columns as narrow as their data.
 *
 * Those two want opposite things, and both are right. Pico gives every table
 * `width: 100%`, which spreads the slack across every column and leaves
 * `0001` sitting in the middle of four hundred pixels — the eye then travels
 * the width of the page to get from a path to its end unit. `width: auto`
 * fixed that and introduced the other half of the problem: the table stopped
 * where its data stopped, so a four-column listing was a narrow slab against
 * the left edge of a wide window.
 *
 * The slack has to go somewhere, so it goes somewhere that is not a column:
 * `.sab-table-spacer`, one empty cell at the end of every row, asking for
 * 100% of the table. In an auto-layout table a cell asking for everything
 * gets whatever the real columns did not take — so the columns stay at their
 * content width, side by side on the left, and the row itself reaches the
 * right edge of the window.
 *
 * Scrolling is unaffected. `white-space: nowrap` means the table's minimum
 * width is its content width, so a table with many columns still overflows
 * `.sab-table-scroll` and still scrolls sideways; the spacer is then the
 * first thing squeezed, down to nothing. */
.sab-table {
  width: 100%;
  font-size: var(--sab-text-body);
  white-space: nowrap;
}

/* The listing sits in a card like the overview's panels; the card clips the
 * table's corners and still scrolls sideways. */
.sab-table-scroll .sab-table {
  margin: 0;
}

.sab-table-scroll .sab-table tbody tr:last-child td {
  border-bottom: 0;
}

/* A row's identifier reads as a name, not as a hyperlink in a document: the
 * row is the link, and the hover says so. */
.sab-table-scroll tbody td a {
  text-decoration: none;
}

.sab-table-scroll tbody td:first-child a {
  color: var(--sab-ink);
  font-weight: var(--sab-weight-medium);
}

/* A timestamp's age in a listing is a dot and quiet words: a column of red
 * sentences reads as an alarm on every row. The colour stays on the dot. */
.sab-table-scroll [class^="sab-freshness-"]::before {
  content: "";
  display: inline-block;
  width: 0.5rem;
  height: 0.5rem;
  margin-right: var(--sab-gap);
  border-radius: 50%;
  vertical-align: 0.05em;
}

.sab-table-scroll .sab-freshness-fresh,
.sab-table-scroll .sab-freshness-ageing,
.sab-table-scroll .sab-freshness-stale {
  color: var(--pico-color);
}

.sab-table-scroll .sab-freshness-fresh::before { background: var(--sab-fresh); }
.sab-table-scroll .sab-freshness-ageing::before { background: var(--sab-ageing); }
.sab-table-scroll .sab-freshness-stale::before { background: var(--sab-stale); }
.sab-table-scroll .sab-freshness-future::before { background: var(--sab-future); }

.sab-table-spacer {
  width: 100%;
  padding: 0;
}

/* The gap after the text. Horizontal padding is what separates one column
 * from the next now that nothing else does, so it is the larger of the two. */
.sab-table th,
.sab-table td {
  padding: var(--sab-gap) var(--sab-gap-loose);
  height: var(--sab-table-row-height);
}

/* The listing — the search line, the meta line and the table — fills the page
 * it is on, whatever the data in it happens to be.
 *
 * It used to be `fit-content`: as wide as the table and no wider, so the
 * controls ended above the table's last column. That reads well on a table of
 * four narrow columns and badly on everything else — the page then changes
 * width when a column is turned on, and a wide table sat in a box narrower
 * than the window while scrolling sideways inside it. One width, always the
 * page's; a table wider than that still scrolls inside
 * `.sab-table-scroll`. */
.sab-listing {
  width: 100%;
}

/* Condensed, so the header row is told from the data by width rather than by
 * another rule or another tint — the two things a dense listing has least of.
 * It also buys back the space uppercase spends: a narrow column can carry its
 * own name instead of an abbreviation the reader has to learn. */
.sab-table thead th,
.sab-record th {
  font-family: var(--sab-font-condensed);
  font-weight: var(--sab-weight-strong);
  font-size: var(--sab-text-small);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--pico-muted-color);
  vertical-align: bottom;
}

/* A listing's header speaks plainly: sentence case, the body face, quiet. */
.sab-table-scroll .sab-table thead th {
  border-bottom: var(--sab-hairline) solid var(--sab-rule);
  font-family: var(--sab-font);
  font-size: var(--sab-text-meta);
  font-weight: var(--sab-weight-medium);
  letter-spacing: 0;
  text-transform: none;
}

.sab-table thead th a {
  text-decoration: none;
  color: inherit;
}

.sab-table thead th a:hover {
  color: var(--pico-primary);
}

/* Numbers line up so a column can be scanned rather than read. */
.sab-num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.sab-mono {
  font-family: var(--sab-mono);
  font-size: 0.9em;
}

.sab-sort-marker {
  color: var(--pico-primary);
}

/* The filter row, one input per filterable column. */
.sab-filter-row td {
  padding: var(--sab-gap-tight) 0.35rem;
}

.sab-filter-row input,
.sab-filter-row select {
  margin: 0;
  padding: 0.15rem 0.35rem;
  font-size: var(--sab-text-meta);
  height: auto;
  min-width: 6rem;
}

/* The filter box must not be the widest thing in its own column.
 *
 * An `<input>` with no `size` attribute has an intrinsic width of about twenty
 * characters, and in an auto-layout table it is the intrinsic width that sizes
 * the column. A column of four-digit unit numbers was therefore being held
 * open by an empty text box — 221px of column for `0001`.
 *
 * `width` is what the table measures, so it is small; `min-width: 100%` then
 * stretches the box back to whatever the column turned out to be, so a column
 * made wide by its data still gets a full-width input rather than a stub.
 *
 * `:only-child` leaves the min/max pair of a number range alone. Two boxes in
 * one cell each asking for the whole width would stack one under the other. */
.sab-filter-row td > input:only-child,
.sab-filter-row td > select:only-child {
  width: 6rem;
  min-width: 100%;
}

/* A number range is two boxes in one cell, and they have to share it.
 *
 * Pico gives every input `width: 100%`, which for a pair meant each one
 * claimed the whole cell and the two together claimed twice it. The result was
 * off the side of the table: `max` was pushed out of the cell entirely and
 * `min` sat under a neighbouring column's header, so a reader filtering on
 * diameter was typing into what looked like the capture-time column.
 *
 * Half each, less the gap between them. The floor is small enough that two
 * boxes fit a narrow column and large enough that `min` and `max` are still
 * readable in them. */
.sab-filter-row td > input:not(:only-child) {
  width: calc(50% - var(--sab-gap-tight));
  min-width: 3.5rem;
}

/* --- One box for the whole table ------------------------------------------
 *
 * Above the filter row, and wider than any of its inputs, because it does a
 * different job: a filter narrows a column the reader has already chosen, and
 * this finds the row when they do not know which column holds the thing they
 * remember. It is the first control on the page for the same reason.
 *
 * The working mark is a still dot that only appears while a request is out.
 * Nothing spins: the list under it stays readable and in place, and a reader
 * who types faster than the server answers is not shown a flicker for every
 * keystroke.
 *
 * The controls that act on the result — Filter, Columns, Clear — sit right
 * beside the box, at its height, so the line reads as one toolbar.
 */

.sab-search {
  display: flex;
  align-items: center;
  gap: var(--sab-gap);
  margin-bottom: var(--sab-gap-loose);
  flex-wrap: wrap;
}

/* The portal's one search field — components/search_box.html. Its width is
 * the length of what is typed into it, not the width of the page. */
.sab-search-box {
  display: flex;
  align-items: stretch;
  flex: 0 1 22rem;
  height: var(--sab-control-height);
  overflow: hidden;
  border: var(--sab-hairline) solid var(--sab-rule);
  border-radius: var(--sab-radius);
  background: var(--sab-row);
}

.sab-search-box:focus-within {
  border-color: var(--pico-primary);
}

.sab-search-box .sab-tab-icon {
  flex: none;
  width: 0.95rem;
  height: 0.95rem;
  margin-inline: var(--sab-gap-loose) 0;
  align-self: center;
  color: var(--pico-muted-color);
}

/* Two class names, so that this outranks Pico's `input:not([type=…])` rule.
 * No border and no ground of its own: the box around it is the field, and a
 * box inside a box is two controls. */
.sab-search-box .sab-search-input {
  flex: 1 1 auto;
  min-width: 0;
  height: auto;
  margin: 0;
  padding: var(--sab-gap) var(--sab-gap);
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  font-size: var(--sab-text-label);
}

/* Pico draws its own magnifier into a search field. Ours is an element, at
 * the size and colour every other icon in the portal is drawn at. */
.sab-search-box input[type="search"] {
  background-image: none;
  padding-inline-start: var(--sab-gap);
}

.sab-search-working {
  width: var(--sab-gap);
  height: var(--sab-gap);
  margin-inline-end: var(--sab-gap-loose);
  align-self: center;
  border-radius: 50%;
  background: var(--sab-action);
  opacity: 0;
  flex: none;
}

/* htmx marks the indicator while a request is in flight. */
.sab-search-working.htmx-request {
  opacity: 1;
}

.sab-filter-actions {
  display: flex;
  gap: var(--sab-gap);
  align-items: center;
  flex-wrap: wrap;
}

/* The search box's height, border and ground, so the line reads as one bar. */
.sab-filter-actions button,
.sab-filter-actions [role="button"] {
  display: inline-flex;
  align-items: center;
  height: var(--sab-control-height);
  margin: 0;
  width: auto;
  padding: 0 var(--sab-gap-loose);
  border: var(--sab-hairline) solid var(--sab-rule);
  border-radius: var(--sab-radius);
  background: var(--sab-row);
  color: var(--pico-muted-color);
  font-size: var(--sab-text-label);
}

.sab-filter-actions button:hover,
.sab-filter-actions [role="button"]:hover {
  background: var(--sab-quiet-bg);
  color: var(--sab-ink);
}

/* --- The filter row, folded away -----------------------------------------
 *
 * A label that looks like the buttons beside it, holding a checkbox the
 * reader never sees. The row of per-column inputs is shown while the box is
 * checked, and the box is checked by the server whenever a filter is set.
 *
 * `:has()` does the showing. A browser without it never matches the
 * `@supports` block, keeps the row visible at all times, and loses nothing
 * but the fold. */
.sab-filter-toggle {
  display: inline-flex;
  align-items: center;
  cursor: pointer;
}

.sab-filter-toggle input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}

.sab-filter-toggle:has(input:checked) {
  color: var(--sab-selected);
  background: var(--sab-selected-bg);
  border-color: var(--sab-selected-bg);
}

.sab-filter-toggle:has(input:focus-visible) {
  outline: 2px solid var(--pico-primary);
  outline-offset: 2px;
}

@supports selector(:has(a)) {
  form:not(:has(.sab-filter-toggle input:checked)) .sab-filter-row {
    display: none;
  }
}

/* The line between the controls and the table: the count when the list is
 * narrowed, at the left, and how many columns there are, at the right. One
 * line, muted, and empty at the left when nothing is narrowed. */
.sab-table-meta {
  display: flex;
  justify-content: space-between;
  gap: var(--sab-gap-loose);
  margin: 0 0 var(--sab-gap-tight);
  font-size: var(--sab-text-small);
  color: var(--pico-muted-color);
}

.sab-table-meta .sab-counts {
  font-size: inherit;
}

/* --- The reader's own choice of columns ---------------------------------- */

/* These tables are wider than any window, so every reader is looking at part
 * of one. The chooser is how they decide which part: twelve columns exist
 * because twelve jobs need them, and no reader has all twelve jobs.
 *
 * Drawn as one of the controls in the filter row rather than as a settings
 * screen somewhere else, because it is the same kind of act as filtering —
 * narrowing this table to today's question — and it is undone the same way. */
.sab-columns {
  margin: 0;
}

/* Pico sizes a dropdown summary like a form field, which is twice the height
 * this row can spare. Matched to the buttons beside it instead.
 *
 * `:not([role])` is here to match the specificity of the Pico rule being
 * overridden, not to select anything this one would otherwise catch. Without
 * it Pico's own rule is the more specific of the two and keeps the height. */
.sab-columns.dropdown > summary:not([role]) {
  display: inline-flex;
  align-items: center;
  gap: var(--sab-gap-tight);
  width: auto;
  height: var(--sab-control-height);
  margin: 0;
  padding: 0 var(--sab-gap-loose);
  border: var(--sab-hairline) solid var(--sab-rule);
  border-radius: var(--sab-radius);
  background: var(--sab-row);
  color: var(--pico-muted-color);
  font-size: var(--sab-text-label);
  line-height: 1.5;
}

.sab-columns.dropdown > summary:not([role]):hover,
.sab-columns.dropdown > summary:not([role]):focus-visible,
.sab-columns[open].dropdown > summary:not([role]) {
  background: var(--sab-quiet-bg);
  color: var(--sab-ink);
}

/* How many columns are switched off, on the control itself. Without it a
 * hidden column and a column the portal does not have look identical, and a
 * reader who tidied the table in March cannot tell in November which of the
 * two they are looking at. */
.sab-columns-count {
  color: var(--sab-absent);
  font-size: var(--sab-text-small);
}

.sab-columns.dropdown > summary:not([role]) + ul {
  z-index: var(--sab-layer-panel);
  /* The chooser sits at the right end of the search line, so a panel anchored
   * to its left edge — Pico's default — grows off the side of the window.
   * Anchored to the right edge it grows back across the page instead. */
  left: auto;
  right: 0;
  /* Grows to the longest column heading and never narrower than the summary —
   * the headings do not wrap, and a panel sized to the word "Columns" would
   * spill them past its own edge. */
  width: max-content;
  min-width: 100%;
  /* A dozen headings fit; the next table's might not, and a panel taller than
   * the window is a panel whose Apply button cannot be reached. */
  max-height: 60vh;
  overflow-y: auto;
  margin-top: var(--sab-gap-tight);
  padding: var(--sab-gap-tight);
  border-radius: var(--sab-radius);
  box-shadow: var(--sab-panel-shadow);
}

.sab-columns.dropdown > summary:not([role]) + ul li {
  padding: 0;
}

.sab-columns-hint {
  padding: var(--sab-gap-tight) var(--sab-gap);
  color: var(--pico-muted-color);
  font-size: var(--sab-text-micro);
}

.sab-columns-choice {
  display: flex;
  align-items: center;
  gap: var(--sab-gap);
  margin: 0;
  padding: var(--sab-gap-tight) var(--sab-gap);
  border-radius: var(--sab-radius);
  color: var(--sab-ink);
  font-size: var(--sab-text-meta);
  line-height: 1.5;
  white-space: nowrap;
  cursor: pointer;
}

.sab-columns-choice:hover {
  background: var(--sab-quiet-bg);
}

.sab-columns-choice input {
  margin: 0;
}

/* The identity column, offered so the list is the whole list, but not
 * switchable — see `Choice` in fleet/columns.py. */
.sab-columns-choice small {
  margin-left: auto;
  padding-left: var(--sab-gap);
  color: var(--sab-absent);
  font-size: var(--sab-text-micro);
}

.sab-columns-apply {
  width: 100%;
  margin: var(--sab-gap-tight) 0 0;
  padding: 0.3rem 0.9rem;
  font-size: var(--sab-text-label);
}

/* --- Provisional rows --------------------------------------------------- */

/* 530 of 546 pipes are a listing row and nothing more. Drawn at full weight
 * they would overstate what is known about them, so they are muted and marked
 * — and the legend says what the marking means, because a visual difference
 * nobody can decode is just noise. */
.sab-provisional td {
  color: var(--pico-muted-color);
}

.sab-provisional td:first-child {
  border-left: 2px dashed var(--sab-rule);
}

.sab-legend {
  color: var(--pico-muted-color);
  font-size: var(--sab-text-meta);
  margin-top: 0.75rem;
  display: flex;
  gap: var(--sab-gap-wide);
  flex-wrap: wrap;
}

.sab-legend .sab-legend-provisional::before {
  content: "";
  display: inline-block;
  width: 0.75rem;
  border-top: 2px dashed var(--sab-rule);
  margin-right: 0.4rem;
  vertical-align: middle;
}

}
