/* Administrative interface styling.
   Specification SPEC_ADMIN_UI_UX_IMPROVEMENTS.md: section 3 (tokens), section 4
   (fluid layout), sections 6-14 (components), section 17 (accessibility).
   Every colour, spacing and radius comes from a token in :root, so a dark
   palette can be added without touching component rules. */

:root {
  /* -- spacing and density (UI-4.3) -- */
  --gutter: clamp(1rem, 2vw, 3rem);
  --space-1: clamp(0.25rem, 0.2rem + 0.1vw, 0.4rem);
  --space-2: clamp(0.5rem, 0.4rem + 0.2vw, 0.8rem);
  --space-3: clamp(0.75rem, 0.6rem + 0.3vw, 1.2rem);
  --space-4: clamp(1rem, 0.8rem + 0.4vw, 1.6rem);
  --space-6: clamp(1.5rem, 1.2rem + 0.6vw, 2.4rem);
  --panel-padding: clamp(1rem, 0.8rem + 0.6vw, 2rem);
  --cell-padding-block: clamp(0.5rem, 0.4rem + 0.2vw, 0.9rem);
  --cell-padding-inline: clamp(0.6rem, 0.5rem + 0.3vw, 1.1rem);

  /* -- shape -- */
  --radius-1: 0.4rem;
  --radius-2: 0.75rem;
  --radius-pill: 999px;
  --border-width: 1px;
  --shadow-1: 0 0.25rem 1rem rgb(30 42 67 / 8%);
  --shadow-2: 0 0.5rem 1.5rem rgb(30 42 67 / 18%);

  /* -- type -- */
  --font-body: system-ui, sans-serif;
  --font-mono: ui-monospace, "DejaVu Sans Mono", monospace;
  --font-small: 0.875rem;
  --font-large: 1.125rem;
  --measure: 75ch;

  /* -- palette -- */
  --color-text: #172033;
  --color-text-muted: #526078;
  --color-background: #f3f5f8;
  --color-surface: #fff;
  --color-surface-alt: #f9fafb;
  --color-border: #d7dce5;
  --color-border-strong: #98a2b3;
  --color-accent: #174ea6;
  --color-accent-contrast: #fff;
  --color-focus: #174ea6;
  --color-danger: #b42318;
  --color-danger-soft: #fef3f2;
  --color-danger-text: #7a271a;
  --color-success: #05603a;
  --color-success-soft: #ecfdf3;
  --color-success-border: #75e0a7;
  --color-warning-text: #7a2e0e;
  --color-warning-soft: #ffead5;
  --color-info-text: #175cd3;
  --color-info-soft: #dbeafe;
  --color-neutral-text: #475467;
  --color-neutral-soft: #eaecf0;
  --color-nav: #172033;
  --color-nav-text: #fff;
  --color-highlight: #fef7c3;

  /* -- widths (UI-4.1) -- */
  --width-narrow: 34rem;
  --width-regular: 80rem;
  --width-wide: 110rem;

  color-scheme: light;
  font-family: var(--font-body);
  /* UI-4.2: 1280 -> 16px, 1920 -> 17px, 2560 -> 18px, 3840 -> 20px */
  font-size: clamp(16px, 14px + 0.156vw, 20px);
  line-height: 1.5;
  color: var(--color-text);
  background: var(--color-background);
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --color-text: #e6eaf2;
  --color-text-muted: #a6b0c3;
  --color-background: #0f141f;
  --color-surface: #172033;
  --color-surface-alt: #1c2740;
  --color-border: #2b3752;
  --color-border-strong: #47536e;
  --color-accent: #7aa7ef;
  --color-accent-contrast: #0f141f;
  --color-nav: #0a0e17;
  --color-focus: #7aa7ef;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  overflow-x: hidden;
}

p,
li,
dd,
.field-help {
  max-width: var(--measure);
}

h1,
h2,
h3 {
  line-height: 1.2;
}

h1 {
  margin: 0;
  font-size: 1.6rem;
}

h2 {
  margin-top: 0;
  font-size: 1.25rem;
}

h3 {
  margin-top: 0;
  font-size: 1.05rem;
}

a {
  color: var(--color-accent);
}

code,
.mono {
  font-family: var(--font-mono);
}

.muted {
  color: var(--color-text-muted);
  font-size: var(--font-small);
}

/* Keeps the line breaks of a catalogue string; long lines still wrap. */
.multiline {
  white-space: pre-line;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* -- shell and widths (UI-4.1) -- */

.shell {
  margin-inline: auto;
  padding: var(--space-4) 0 var(--space-6);
}

.shell-narrow {
  width: min(var(--width-narrow), 100% - 2 * var(--gutter));
}

.shell-regular {
  width: min(var(--width-regular), 100% - 2 * var(--gutter));
}

.shell-wide {
  width: min(var(--width-wide), 100% - 2 * var(--gutter));
}

.page-header {
  margin-block: var(--space-4) var(--space-3);
}

.page-content > * + * {
  margin-top: var(--space-3);
}

.page-footer {
  margin-top: var(--space-6);
  color: var(--color-text-muted);
  font-size: var(--font-small);
  text-align: center;
}

.skip-link {
  position: absolute;
  left: -100vw;
}

.skip-link:focus-visible {
  position: static;
  display: inline-block;
  margin: var(--space-2);
  padding: var(--space-2);
  background: var(--color-surface);
}

/* -- login screen (UI-5.1, UI-5.2) -- */

.login-shell {
  position: relative;
  width: min(28rem, 100% - 2 * var(--gutter));
  min-height: 100dvh;
  margin-inline: auto;
}

.login-block {
  position: relative;
  top: 33.333dvh;
  transform: translateY(-50%);
}

@media (max-height: 40rem) {
  .login-block {
    top: 0;
    transform: none;
    padding-top: 2rem;
  }

  .login-shell .page-footer {
    top: 0;
  }
}

.login-brand,
.product-header {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  color: var(--color-accent);
}

.product-mark {
  height: 2rem;
  width: auto;
}

.product-name {
  margin: 0;
  color: var(--color-text-muted);
}

.login-shell .page-footer {
  position: relative;
  top: 33.333dvh;
}

/* -- navigation (UI-6.1, UI-6.6) -- */

.main-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2) var(--space-4);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-1);
  color: var(--color-nav-text);
  background: var(--color-nav);
}

.nav-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-4);
}

.nav-group-service {
  padding-left: var(--space-4);
  border-left: var(--border-width) solid rgb(255 255 255 / 25%);
}

.main-nav a,
.link-button {
  color: var(--color-nav-text);
  text-decoration: underline;
}

.main-nav a[aria-current="page"] {
  font-weight: 700;
  text-decoration: none;
}

.logout-form {
  display: inline;
}

.link-button {
  min-height: auto;
  padding: 0;
  border: 0;
  font: inherit;
  background: transparent;
  cursor: pointer;
}

/* -- status tiles (UI-6.4) -- */

.status-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.status-tile {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-2) var(--space-3);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-1);
  color: inherit;
  background: var(--color-surface);
  text-decoration: none;
}

a.status-tile:hover {
  border-color: var(--color-accent);
}

/* A tile that is not a link still carries a tooltip: the cursor has to say
   so, otherwise the tile looks inert next to its clickable neighbours. */
div.status-tile.has-hint {
  cursor: help;
}

.tile-label {
  color: var(--color-text-muted);
  font-size: var(--font-small);
}

/* Gap between caption text and hint marker inside tiles — .tile-label is not
   a flex container, so .has-hint gap does not reach the marker here. */
.tile-label > .hint-marker {
  margin-inline-start: var(--space-2);
}

.tile-value {
  font-size: var(--font-large);
  font-weight: 700;
}

/* -- panels -- */

.panel {
  padding: var(--panel-padding);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-2);
  background: var(--color-surface);
  box-shadow: var(--shadow-1);
}

.panel + .panel {
  margin-top: var(--space-3);
}

.panel-subtitle {
  margin-top: calc(-1 * var(--space-2));
  color: var(--color-text-muted);
  font-size: var(--font-small);
}

.subpanel {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: var(--border-width) solid var(--color-border);
}

.section-heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}

.counter {
  min-width: 2rem;
  padding: 0 var(--space-2);
  border-radius: var(--radius-pill);
  color: var(--color-neutral-text);
  background: var(--color-neutral-soft);
  text-align: center;
}

/* SG-1: the panels of a row share its height. Left to their own
   heights they ended at different lines and the row read as a set of
   unrelated cards with holes between them. */
.settings-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 26rem), 1fr));
  gap: var(--space-3);
  align-items: stretch;
}

.settings-grid > .panel + .panel {
  margin-top: 0;
}

/* SG-10: the main action of a panel stands at the bottom left, on the line
   shared by every panel of the row. The panel and its last form are columns
   that fill the height of the row, and the row of actions takes the free
   space above itself. */
.settings-grid > .panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* A flex container does not collapse the margins of its items, so the own
   margins of the headings and the paragraphs would add up to the gap and
   spread the content of the panel. One gap answers for the spacing. */
.settings-grid > .panel > * {
  margin-block: 0;
}

.settings-grid > .panel > form:last-child {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  gap: var(--space-3);
  margin: 0;
}

.settings-grid > .panel > form:last-child > .form-actions,
.settings-grid > .panel > .form-actions:last-child {
  margin-top: auto;
}

.panel-wide {
  grid-column: 1 / -1;
}

@media (min-width: 100rem) {
  .settings-grid {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr));
  }
}

/* -- forms -- */

form {
  display: grid;
  gap: var(--space-3);
}

.field {
  display: grid;
  gap: var(--space-1);
  min-width: 0;
}

/* A grid item may not shrink below its min-content width by default, so a
   long option such as "Команда не отправлялась" would push the control out
   of the panel. Controls follow the column instead. */
.field > input,
.field > select {
  min-width: 0;
  max-width: 100%;
}

.field-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
  gap: var(--space-3);
  align-items: end;
}

/* UI-FIX-27: fields of unequal height (a recommendation under one, a row of
   presets under the other) line up by their rows, not by their bottom edges.
   The base rule is the top edge; where subgrid is available every field
   shares the rows of the grid, so the labels keep one line and the controls
   another even when a label wraps. */
.field-grid-rows {
  align-items: start;
}

@supports (grid-template-rows: subgrid) {
  /* A subgridded axis takes its gutter from the parent grid, so the row gap
     here is the distance between the label, the control and the text under
     it — it has to stay the gap of a plain field, not the gap between the
     columns of the form. */
  .field-grid-rows {
    row-gap: var(--space-1);
  }

  .field-grid-rows > .field {
    grid-row: span 3;
    grid-template-rows: subgrid;
  }
}

/* UI-FIX-25: a row of filters where one cell is taller than the others (the
   group picker carries the list of chosen groups under it) lines up by the
   top edge, so every control keeps the same line. */
.field-grid-top {
  align-items: start;
}

/* UI-FIX-28: a field standing alone in its grid would stretch across the
   whole panel; a control holding a version number does not need that width. */
.field-compact {
  max-width: 28rem;
}

/* SW-FIX-07.4: the field that caused the error carries the colour of the
   message standing above the form. */
.field-invalid > input,
.field-invalid select,
.field-invalid .password-field > input {
  border-color: var(--color-danger);
}

.field-help,
.field-note {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--font-small);
}

.field-note {
  font-style: italic;
}

input,
select,
button,
.button-link {
  min-height: 2.75rem;
  padding: var(--space-2) var(--space-3);
  border: var(--border-width) solid var(--color-border-strong);
  border-radius: var(--radius-1);
  font: inherit;
  color: inherit;
  background: var(--color-surface);
}

input[type="checkbox"],
input[type="radio"] {
  min-height: auto;
  padding: 0;
}

input:disabled,
select:disabled {
  color: var(--color-text-muted);
  background: var(--color-neutral-soft);
  cursor: not-allowed;
}

button,
.button-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: fit-content;
  border-color: var(--color-accent);
  color: var(--color-accent-contrast);
  background: var(--color-accent);
  text-decoration: none;
  cursor: pointer;
}

button.secondary,
.button-link.secondary {
  color: var(--color-accent);
  background: var(--color-surface);
}

button.danger {
  border-color: var(--color-danger);
  background: var(--color-danger);
  color: #fff;
}

button.small,
.button-link.small {
  min-height: 2.25rem;
  padding: var(--space-1) var(--space-2);
  font-size: var(--font-small);
}

button[disabled] {
  border-color: var(--color-border-strong);
  color: var(--color-text-muted);
  background: var(--color-neutral-soft);
  cursor: not-allowed;
}

.choice {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-2);
  align-items: center;
}

.form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
}

.filter-form .form-actions,
.mailing-form .form-actions,
.build-form .form-actions {
  grid-column: 1 / -1;
  justify-content: flex-end;
}

/* UI-8.1: filter fields fill the row, the buttons live on their own row. */
.code-form {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
  align-items: end;
}

/* Flex instead of a track grid: the fields grow to eat the whole row, so the
   last one always ends flush with the panel edge — and with the actions row
   below it — instead of leaving empty tracks behind. */
.filter-form:not(.compact) {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
}

.filter-form:not(.compact) > .field {
  flex: 1 1 8rem;
}

.filter-form:not(.compact) > .field-wide {
  flex: 2 1 16rem;
}

.filter-form:not(.compact) > .form-actions {
  flex: 1 0 100%;
}

.filter-form.compact {
  grid-template-columns: minmax(12rem, 20rem) auto;
}

.filter-form.compact .form-actions {
  grid-column: auto;
  justify-content: flex-start;
}

.field-wide {
  grid-column: span 2;
}

.build-form {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
}

.build-form .choice {
  grid-column: 1 / -1;
}

.form-block {
  margin: 0;
  padding: var(--space-3);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-1);
}

.form-block legend,
.form-block > summary {
  padding-inline: var(--space-2);
  font-weight: 700;
}

/* Blocks stand next to each other inside a panel, which is plain flow and
   has no gap of its own, so the borders would touch. The submit row after a
   block keeps the same air the form uses between its own elements. */
.form-block + .form-block,
.form-block + .form-actions {
  margin-top: var(--space-3);
}

.input-with-suffix {
  display: flex;
  align-items: stretch;
}

/* The input and its unit read as one control, so the focus ring outlines
   the pair instead of cutting along the seam between them. */
.input-with-suffix:focus-within {
  border-radius: var(--radius-1);
  outline: 3px solid var(--color-focus);
  outline-offset: 2px;
}

.input-with-suffix input:focus-visible {
  outline: none;
}

.input-with-suffix input {
  flex: 1 1 auto;
  min-width: 0;
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

.input-suffix {
  display: flex;
  align-items: center;
  padding-inline: var(--space-2);
  border: var(--border-width) solid var(--color-border-strong);
  border-left: 0;
  border-radius: 0 var(--radius-1) var(--radius-1) 0;
  color: var(--color-text-muted);
  background: var(--color-surface-alt);
  font-size: var(--font-small);
  white-space: nowrap;
}

.password-field {
  display: flex;
  /* The gap must survive the focus ring of the input (outline 3px plus a
     2px offset), so the toggle button never touches it. */
  gap: var(--space-2);
}

.password-field input {
  flex: 1 1 auto;
  min-width: 0;
}

.password-toggle {
  color: var(--color-accent);
  background: var(--color-surface);
  border-color: var(--color-border-strong);
}

.password-strength {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--font-small);
}

.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-top: var(--space-1);
}

.chip {
  min-height: 2rem;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-pill);
  border-color: var(--color-border-strong);
  color: var(--color-accent);
  background: var(--color-surface);
  font-size: var(--font-small);
}

/* UI-FIX-25: the group picker and the list of the groups already chosen
   share one cell of the field grid, so the list always stands directly
   under the control it belongs to. */
.field-groups {
  display: grid;
  gap: var(--space-1);
  align-content: start;
  min-width: 0;
}

.selected-groups {
  list-style: none;
  margin: 0;
  padding: 0;
}

.selected-groups > li {
  display: flex;
}

/* UI-7.13: only the fields of the selected TLS mode are visible. */
.tls-modes {
  display: grid;
  gap: var(--space-2);
  margin: 0;
  padding: var(--space-3);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-1);
}

.mode-fields {
  display: none;
  gap: var(--space-2);
  padding-left: var(--space-4);
}

.choice:has(input:checked) + .mode-fields {
  display: grid;
}

/* -- messages -- */

.message {
  margin: var(--space-3) 0;
  padding: var(--space-2) var(--space-3);
  border: var(--border-width) solid transparent;
  border-radius: var(--radius-1);
}

.error,
.error-panel {
  border-color: var(--color-danger);
  color: var(--color-danger-text);
  background: var(--color-danger-soft);
}

.success {
  border-color: var(--color-success-border);
  color: var(--color-success);
  background: var(--color-success-soft);
}

.warning {
  border-color: var(--color-warning-soft);
  color: var(--color-warning-text);
  background: var(--color-warning-soft);
  font-weight: 600;
}

p.warning {
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-1);
}

/* -- tables (UI-8.2, UI-8.3) -- */

.table-panel {
  padding-inline: 0;
}

.table-panel > h2,
.table-panel > h3,
.table-panel > .section-heading,
.table-panel > form,
.table-panel > .panel-subtitle,
.table-panel > .pagination,
.table-panel > p {
  padding-inline: var(--panel-padding);
}

/* The heading and the table keep exactly the same air around the filter row,
   so the panel reads as three evenly spaced blocks. */
.table-panel > .section-heading,
.table-panel > h2,
.table-panel > h3 {
  margin-bottom: var(--space-4);
}

.table-panel > .section-heading > h2,
.table-panel > .section-heading > h3 {
  margin-bottom: 0;
}

.table-panel > form + .table-scroll,
.table-panel > form + .pagination-top,
.table-panel > p + .table-scroll,
.table-panel > p + .pagination-top,
.panel > form + .table-scroll,
.panel > .form-actions + .table-scroll,
.panel > form + details,
.panel > .form-actions + details {
  margin-top: var(--space-4);
}

.panel > form + form,
.panel > .subpanel + form {
  margin-top: var(--space-4);
}

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

/* A scroll container clips both axes, so a tooltip opened in the last rows
   would be cut off (UI-14.1). While one is open the container reserves a
   strip and compensates it with a negative margin, so nothing below moves.
   Browsers without :has() simply keep the previous behaviour. */
.table-scroll:has(.has-hint:hover),
.table-scroll:has(.has-hint:focus-within) {
  padding-bottom: 6rem;
  margin-bottom: -6rem;
}

.table-panel > .table-scroll:last-child {
  border-bottom-right-radius: var(--radius-2);
  border-bottom-left-radius: var(--radius-2);
}

table {
  width: 100%;
  border-collapse: collapse;
}

th,
td {
  padding: var(--cell-padding-block) var(--cell-padding-inline);
  border-top: var(--border-width) solid var(--color-neutral-soft);
  text-align: left;
  vertical-align: top;
}

thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  color: var(--color-neutral-text);
  background: var(--color-surface-alt);
  white-space: nowrap;
}

tbody tr:nth-child(even) {
  background: var(--color-surface-alt);
}

tbody tr:hover {
  background: var(--color-info-soft);
}

tbody tr.row-highlight {
  background: var(--color-highlight);
}

.sort-link {
  color: inherit;
  text-decoration: none;
}

.sort-marker {
  margin-left: var(--space-1);
}

/* UI-FIX-36: a column caption is never underlined — the dotted underline
   stays the mark of a form label. The tooltip marker next to it is what
   says the explanation is there. */
th .has-hint-label {
  text-decoration: none;
}

.empty-cell {
  color: var(--color-text-muted);
  text-align: center;
}

/* A multi-line hint reads as a paragraph, not as a table placeholder. */
.empty-cell.multiline {
  text-align: left;
}

.col-narrow {
  width: 1%;
  white-space: nowrap;
}

/* Optional columns collapse before the table starts scrolling (UI-8.2). */
@media (max-width: 75rem) {
  .col-optional {
    display: none;
  }
}

.value-with-copy {
  display: inline-flex;
  gap: var(--space-1);
  align-items: center;
}

.breakable {
  overflow-wrap: anywhere;
}

/* UI-FIX-33: the controls of an inline form are glued together by string
   concatenation, so the gap has to come from the layout. Inline-flex keeps
   the form inside its table cell instead of stretching it. */
.inline-form {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.inline-link {
  margin-left: var(--space-2);
  font-size: var(--font-small);
}

/* -- summaries -- */

.details-grid,
.summary {
  display: grid;
  grid-template-columns: minmax(10rem, max-content) minmax(0, 1fr);
  gap: var(--space-2) var(--space-3);
}

.details-grid dt,
.summary dt {
  font-weight: 700;
}

.details-grid dd,
.summary dd {
  min-width: 0;
  margin: 0;
}

.change-link {
  font-size: var(--font-small);
}

/* -- badges -- */

.badge {
  display: inline-block;
  padding: 0 var(--space-2);
  border: var(--border-width) solid transparent;
  border-radius: var(--radius-pill);
  font-size: var(--font-small);
  font-weight: 700;
  white-space: nowrap;
}

.badge.online,
.badge.active,
.badge.status-active,
.badge.mailing-status-done,
.badge.send-status-sent,
.badge.project-status-ok {
  color: var(--color-success);
  background: var(--color-success-soft);
  border-color: var(--color-success-border);
}

.badge.offline,
.badge.inactive,
.badge.mailing-status-queued,
.badge.send-status-pending,
.badge.mailing-status-cancelled,
.badge.send-status-skipped,
.badge.project-status-not_configured,
.badge.project-status-unknown {
  color: var(--color-neutral-text);
  background: var(--color-neutral-soft);
  border-color: var(--color-border-strong);
}

.badge.status-used,
.badge.mailing-status-running,
.badge.send-status-sending {
  color: var(--color-info-text);
  background: var(--color-info-soft);
  border-color: var(--color-info-text);
}

.badge.mailing-status-paused,
.badge.build-type-unsupported,
.badge.project-status-warning {
  color: var(--color-warning-text);
  background: var(--color-warning-soft);
  border-color: var(--color-warning-text);
}

.badge.status-revoked,
.badge.send-status-invalid_target,
.badge.send-status-failed,
.badge.project-status-error {
  color: var(--color-danger);
  background: var(--color-danger-soft);
  border-color: var(--color-danger);
}

/* -- tooltips (section 14) -- */

.has-hint {
  position: relative;
  display: inline-flex;
  /* Air between the caption and the "?" marker. */
  gap: var(--space-2);
  align-items: baseline;
}

.has-hint-label {
  text-decoration: underline dotted;
  text-underline-offset: 0.2em;
  cursor: help;
}

.hint-marker {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.05em;
  min-height: 0;
  height: 1.05em;
  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  color: var(--color-accent-contrast);
  background: var(--color-border-strong);
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 1;
  cursor: help;
}

button.hint-marker:hover {
  background: var(--color-accent);
}

.hint {
  position: absolute;
  top: calc(100% + 0.35rem);
  left: 0;
  z-index: 20;
  display: none;
  width: max-content;
  max-width: 22rem;
  padding: var(--space-2) var(--space-3);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-1);
  color: var(--color-text);
  background: var(--color-surface);
  box-shadow: var(--shadow-2);
  font-size: var(--font-small);
  font-weight: 400;
  font-style: normal;
  line-height: 1.4;
  white-space: normal;
  /* SW-FIX-03.4: the bubble never intercepts the pointer, so it cannot
     flicker between the marker and its own body. */
  pointer-events: none;
}

/* SW-FIX-03.1: only the "?" marker and the caption open a tooltip. Focus
   inside the field must not: the bubble is anchored under the caption, which
   is exactly where the control the operator is typing into stands. */
.has-hint:hover .hint,
.has-hint:focus-within .hint,
.has-hint.hint-open .hint {
  display: block;
  /* UI-14.1: 400 ms before the tooltip appears, instant on disappearance. */
  animation: hint-delay 0.4s step-end;
}

/* SW-FIX-03.3: inside a form field the caption stands directly above the
   control, so a tooltip hanging below it would cover the control. */
.field > label .hint {
  top: auto;
  bottom: calc(100% + 0.35rem);
}

@keyframes hint-delay {
  from {
    visibility: hidden;
  }

  to {
    visibility: hidden;
  }
}

/* Near the right edge the tooltip aligns to the right of its anchor. */
th:last-child .hint,
td:last-child .hint,
.nav-group-service .hint {
  right: 0;
  left: auto;
}

.sort-cell {
  gap: var(--space-2);
  align-items: center;
}

.sort-link {
  display: inline-flex;
  gap: var(--space-1);
  align-items: center;
  color: inherit;
}

/* -- progress (UI-11.11) -- */

.progress {
  display: inline-flex;
  gap: var(--space-2);
  align-items: center;
}

progress {
  width: 6rem;
  height: 0.6rem;
}

.progress-label {
  font-size: var(--font-small);
  white-space: nowrap;
}

.live-note {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  margin-top: 0;
  color: var(--color-text-muted);
  font-size: var(--font-small);
}

.live-dot {
  width: 0.6rem;
  height: 0.6rem;
  border-radius: var(--radius-pill);
  background: var(--color-info-text);
}

/* -- disclosures -- */

details > summary {
  cursor: pointer;
}

.confirm-details > summary,
.danger-summary {
  color: var(--color-danger);
  font-weight: 600;
}

.confirm-details[open] {
  padding: var(--space-2);
  border: var(--border-width) solid var(--color-danger);
  border-radius: var(--radius-1);
  background: var(--color-danger-soft);
}

/* The warning must not touch the button that carries out the deletion. */
.confirm-details > .field-help {
  margin-bottom: var(--space-2);
}

/* A narrow cell forbids wrapping so that its own controls stay on one
   line; the opened confirmation needs the opposite and gets a width of
   its own instead of stretching the table. */
.col-narrow .confirm-details[open] {
  width: max-content;
  max-width: 16rem;
  white-space: normal;
}

.recipients {
  margin-top: var(--space-3);
}

.recipients > summary {
  padding: var(--space-2) 0;
  font-weight: 600;
}

.history-entry {
  padding-block: var(--space-2);
  border-top: var(--border-width) solid var(--color-neutral-soft);
}

.history-entry summary {
  font-weight: 600;
}

pre {
  max-height: 32rem;
  overflow: auto;
  padding: var(--space-3);
  border-radius: var(--radius-1);
  color: #d0d5dd;
  background: #101828;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* -- setup wizard (UI-7.5, UI-7.6) -- */

/* SW-FIX-01: the wizard owns its page shell. The sign-in shell centres its
   block vertically, which turned the gap above the stepper into a function
   of the height of the step and pushed the top of the tall steps out of the
   viewport. */
.setup-shell {
  display: flex;
  flex-direction: column;
  width: min(40rem, 100% - 2 * var(--gutter));
  min-height: 100dvh;
  margin-inline: auto;
  padding-block: var(--space-6);
}

.setup-main {
  flex: 1 0 auto;
}

/* SW-FIX-08: the mark of the product and the path of the wizard stand on
   the middle line of the panel, not at its left edge. */
.setup-shell .product-header {
  justify-content: center;
}

.setup-shell .page-footer {
  margin-top: var(--space-6);
}

.setup-intro {
  margin-top: 0;
  max-width: var(--measure);
}

/* SW-FIX-05.5: the back link keeps the left edge of the row, so the main
   button of every step stands in the same place. */
.setup-shell .form-actions {
  justify-content: flex-end;
}

/* SW-FIX-05.6: inside a form the grid gap spaces the row of actions; the
   passed step 1 shows its action without a form and needs the same gap. */
.setup-shell .panel > .form-actions {
  margin-top: var(--space-3);
}

.setup-shell .form-actions .setup-back {
  margin-inline-end: auto;
}

/* SW-FIX-02: one row, "1 › 2 › … › 6". The marker keeps a fixed size in
   every state, so switching a number for a tick moves nothing. */
.setup-progress {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--space-2);
  align-items: center;
  /* SW-FIX-08: centred under the mark of the product. */
  justify-content: center;
  margin: 0 0 var(--space-4);
  padding: 0;
  list-style: none;
}

.setup-progress .step {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  min-width: 0;
  color: var(--color-text-muted);
  font-size: var(--font-small);
}

.setup-progress .step + .step::before {
  content: "›";
  color: var(--color-text-muted);
  font-weight: 400;
}

.setup-progress .step-index,
.setup-progress .step-mark {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border: var(--border-width) solid currentcolor;
  border-radius: var(--radius-pill);
  font-size: var(--font-small);
  font-weight: 700;
  line-height: 1;
}

/* The tick replaces the number of a passed step; both markers are always in
   the markup, so the row keeps its height. */
.setup-progress .step-mark,
.setup-progress .step-done .step-index {
  display: none;
}

.setup-progress .step-done .step-mark {
  display: inline-flex;
  /* The glyph reads lighter than a digit at the same size. */
  font-size: 1rem;
}

/* SW-FIX-02.2: six names never fit one row of a 40 rem shell, and the step
   is named by the heading right below. The stepper therefore shows the
   schema "1 › 2 › … › 6" and keeps the names for assistive technology. */
.setup-progress .step-name {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.setup-progress .step-done,
.setup-progress .step-done a {
  color: var(--color-success);
}

.setup-progress .step-current,
.setup-progress .step-current a {
  color: var(--color-accent);
  font-weight: 700;
}

/* SW-FIX-02.5: the current step is told apart by shape as well as colour. */
.setup-progress .step-current .step-index,
.setup-progress .step-current .step-mark {
  border-color: var(--color-accent);
  color: var(--color-accent-contrast);
  background: var(--color-accent);
}

/* SW-FIX-08: a marker of the path is a schema, not a word: the underline of
   a link would be drawn right under its circle. */
.setup-progress a {
  display: inline-flex;
  gap: var(--space-2);
  align-items: center;
  min-width: 0;
  color: inherit;
  text-decoration: none;
}


/* -- incoming builds -- */

.incoming-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 26rem), 1fr));
  gap: var(--space-3);
}

.incoming-build {
  padding: var(--space-3);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-1);
  background: var(--color-surface-alt);
}

.incoming-build h3 {
  margin-top: 0;
}

.build-group + .build-group {
  margin-top: var(--space-3);
}

/* -- codes -- */

.activation-code {
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  white-space: nowrap;
}

.qr-details summary {
  color: var(--color-accent);
  text-decoration: underline;
}

.qr-details img {
  display: block;
  width: min(20rem, 80vw);
  height: auto;
  margin-top: var(--space-2);
  border: var(--border-width) solid var(--color-border);
  background: #fff;
}

/* The address of the QR shown as text (QR-8): it is long, so it wraps
   instead of stretching the narrow QR column. */
.qr-url {
  margin-top: var(--space-2);
  font-size: var(--font-small);
  overflow-wrap: anywhere;
}

.qr-url-label {
  color: var(--color-text-muted);
}

.print-url {
  overflow-wrap: anywhere;
}

.print-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr));
  gap: var(--space-3);
}

.print-card {
  padding: var(--space-3);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-1);
  background: var(--color-surface);
  text-align: center;
}

.print-card img {
  width: 100%;
  max-width: 15rem;
  height: auto;
}

.print-note {
  color: var(--color-text-muted);
  font-size: var(--font-small);
}

.page-actions,
.back-link {
  margin: var(--space-3) 0;
}

/* Page navigation of a listing: the range of the page and the way out of it
   (specification SPEC_FLEET_CONVERGENCE, FC-8.1).
   Three fixed cells (UI-FIX-21.3): the side ones hold the buttons and keep
   their width even while empty, so hiding a direction never moves the range
   away from the centre. */
.pagination {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--font-small);
  container-type: inline-size;
}

.pagination-top {
  margin-bottom: var(--space-3);
}

.pagination-bottom {
  margin-top: var(--space-3);
}

.pagination-side {
  display: flex;
}

.pagination-end {
  justify-content: flex-end;
}

.pagination-range {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-align: center;
}

/* UI-FIX-21.10: on a screen too narrow for both buttons and the range, the
   way out of the page wins — the counter is dropped instead of pushing the
   navigation past the edge. Browsers without container queries clip it. */
@container (max-width: 22rem) {
  .pagination-range {
    display: none;
  }
}

.pagination .page-link {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-2);
  padding: var(--space-1) var(--space-3);
  text-decoration: none;
  white-space: nowrap;
}

.danger-panel {
  border-color: var(--color-danger);
}

.js-only {
  display: none;
}

:root.js .js-only {
  display: inline-flex;
}

:root.js .chip-row.js-only {
  display: flex;
}

:root.js p.password-strength.js-only {
  display: block;
}

/* The counterpart of .js-only: a control the script has made unnecessary. */
:root.js .js-hidden {
  display: none;
}

/* -- accessibility (UI-17.1) -- */

:focus-visible {
  outline: 3px solid var(--color-focus);
  outline-offset: 2px;
}

@media print {
  .no-print,
  .main-nav,
  .status-tiles,
  .pagination,
  .page-footer {
    display: none;
  }

  .print-card {
    break-inside: avoid;
  }
}

/* -- narrow screens -- */

@media (max-width: 40rem) {
  .field-grid,
  .filter-form,
  .code-form,
  .build-form,
  .details-grid,
  .summary,
  .filter-form.compact {
    grid-template-columns: 1fr;
  }

  /* The non-compact filter form is flex, so the track override above does
     nothing for it: stack its items by hand. */
  .filter-form:not(.compact) {
    flex-direction: column;
    align-items: stretch;
  }

  /* Column direction turns flex-grow and the 100% basis into height, so the
     items keep their natural size here. */
  .filter-form:not(.compact) > .field,
  .filter-form:not(.compact) > .field-wide,
  .filter-form:not(.compact) > .form-actions {
    flex: 0 0 auto;
  }

  .field-wide {
    grid-column: auto;
  }

  .main-nav {
    justify-content: flex-start;
  }

  .nav-group-service {
    padding-left: 0;
    border-left: 0;
  }

  .table-panel > h2,
  .table-panel > h3,
  .table-panel > .section-heading,
  .table-panel > .pagination,
  .table-panel > form {
    padding-inline: var(--space-3);
  }

  /* UI-FIX-21.10: the buttons keep their size here and the range gives up
     the space it lacks, so the navigation never widens the page. */
  .pagination {
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: var(--space-2);
  }

  /* One column again: the fields follow each other and the rows of the grid
     separate whole fields, so the compact gap of the subgrid would glue them
     together. */
  .field-grid-rows {
    row-gap: var(--space-3);
  }

  .field-grid-rows > .field {
    grid-row: auto;
    grid-template-rows: none;
  }
}
