/* Reusable component styles. Uses only tokens.css variables. */

/* Utility: tabular figures for aligned numbers. */
.tabular-nums {
  font-variant-numeric: tabular-nums;
}

/* Utility: technical values (ids, timestamps, detail values) in a fixed pitch. */
.font-mono {
  font-family: var(--font-mono);
}

/* Navigation links of the header pills and the menus. */
.nav-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.nav-list a {
  display: block;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-full);
  color: var(--color-text);
  text-decoration: none;
}

.nav-list a:hover {
  background: var(--color-surface-sunken);
}

.nav-list a[aria-current="page"] {
  background: var(--color-nav-active);
  color: var(--color-primary);
  font-weight: 600;
}

/* Menu of the header band (macro menu): a <details data-menu> with the pill as summary and
   an absolute panel below it. The panel keeps the strong edge and casts no shadow. */
.menu {
  position: relative;
}

.menu-trigger {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-full);
  background: var(--color-header-nav);
  color: var(--color-header-text);
  font-size: var(--text-sm);
  white-space: nowrap;
}

.menu-trigger::-webkit-details-marker {
  display: none;
}

/* The account circle: initials in header blue on Sun. */
.menu-account .menu-trigger {
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  justify-content: center;
  background: var(--color-sun);
  color: var(--color-header-bg);
  font-weight: 700;
}

.menu-panel {
  position: absolute;
  top: calc(100% + var(--space-2));
  left: 0;
  z-index: 50;
  min-width: 12rem;
  max-width: 20rem;
  padding: var(--space-3);
  background: var(--color-surface);
  color: var(--color-text);
  border: var(--border-width) solid var(--color-border-strong);
  border-radius: var(--radius-md);
  overflow-wrap: anywhere;
}

/* A button or link as one row of a menu. */
.menu-item {
  display: block;
  width: 100%;
  padding: var(--space-2) var(--space-4);
  border: 0;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-text);
  font: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.menu-item:hover {
  background: var(--color-surface-sunken);
}

.menu-heading {
  margin: 0 0 var(--space-2) 0;
  font-weight: 700;
}

.menu-group-label {
  margin: var(--space-3) 0 var(--space-1) 0;
  color: var(--color-text-muted);
  font-size: var(--text-xs);
}

/* Page header. */
.page-header {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  margin-bottom: var(--space-7);
}

.page-header h1 {
  font-size: var(--text-2xl);
}

.page-header .subtitle {
  color: var(--color-text-muted);
  font-size: var(--text-lg);
  margin: 0;
}

/* The actions of the head: one row, wrapping on a narrow page and keeping the token gap
   in both directions; ``margin-left: auto`` keeps the row on the right. */
.page-header .actions {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-left: auto;
}

/* The breadcrumb above the head of a page (M9-7, A-44): one row of steps, wrapping on a
   narrow page; the separator between two steps comes from here, not from the markup. */
.breadcrumb ol {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0 0 var(--space-3) 0;
  padding: 0;
  list-style: none;
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

.breadcrumb li + li::before {
  content: "\203A";
  margin-inline-end: var(--space-2);
}

/* The head of the drill in the side panel (M9-7, A-44): badges, the asset name in the
   display font, the line location · org · asset type, the sentence and the buttons. */
.run-head {
  margin-bottom: var(--space-5);
}

.run-head p {
  margin: 0 0 var(--space-2) 0;
}

.run-head-name {
  font-family: var(--font-display);
  font-size: var(--text-xl);
}

.run-head-meta {
  color: var(--color-text-muted);
}

.run-head-sentence {
  font-size: var(--text-lg);
}

.run-head-actions {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* The head of the monitor page (M9-5, A-42): the day's sentence and the counter pills
   beside it, wrapping below it on a narrow page. Every axis of the row is set explicitly. */
.situation-head {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-3) var(--space-5);
  margin-bottom: var(--space-3);
}

/* The sentence is the h1 of the page: display face and weight 800 from `h1`. */
.situation-sentence {
  font-size: var(--text-2xl);
  margin: 0;
}

/* There is no global [hidden] rule: the list is hidden while every count is zero, so the
   display rule names the shown state. */
.situation-counts:not([hidden]) {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.situation-count {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-sm);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  border-radius: var(--radius-full);
  background: var(--color-surface);
}

.situation-count-glyph {
  margin-inline-end: var(--space-1);
  color: var(--color-text-muted);
}

.situation-count.is-running .situation-count-glyph {
  color: var(--color-info-text);
}

.situation-count.is-succeeded .situation-count-glyph {
  color: var(--color-success-text);
}

.situation-count.is-warning .situation-count-glyph {
  color: var(--color-warning-text);
}

.situation-count.is-failed .situation-count-glyph {
  color: var(--color-danger-text);
}

/* The pills of the agent page (M9-8): running and disturbed in their colour, off and
   unknown stay muted. */
.situation-count.is-agent-running .situation-count-glyph {
  color: var(--color-success-text);
}

.situation-count.is-agent-disturbed .situation-count-glyph {
  color: var(--color-danger-text);
}

/* The hint below the sentence of the agent page (M9-8, A-24): a line of its own. */
.situation-hint {
  flex-basis: 100%;
  margin: 0;
  color: var(--color-text-muted);
}

/* The live region of a changed sentence: read out, never seen. */
.situation-announce {
  position: absolute;
  width: var(--border-width);
  height: var(--border-width);
  margin: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The problem bar (M9-5, A-42): a row of its own below the head, up to three cards side by
   side, the rest as a list behind "+ n more". There is no global [hidden] rule, so every
   rule that sets display names the shown state, and the button hides itself against the
   display of `.button`. */
.problem-bar {
  margin-bottom: var(--space-3);
}

.problem-cards:not([hidden]),
.problem-rest:not([hidden]) {
  display: grid;
}

.problem-cards,
.problem-rest {
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.problem-cards {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.problem-rest {
  margin-top: var(--space-3);
}

.problem-more {
  margin-top: var(--space-2);
}

.problem-more[hidden] {
  display: none;
}

/* The whole card is the link (or the button): at least 44 px high, the focus ring is the
   global one. */
.problem-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  min-height: 2.75rem;
  padding: var(--space-3) var(--space-4);
  border: 0;
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
  text-align: start;
  text-decoration: none;
}

a.problem-card,
button.problem-card {
  cursor: pointer;
}

.problem-card-head {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.problem-card-line {
  font-size: var(--text-sm);
  overflow-wrap: anywhere;
}

/* The heading of the problem bar (M9-9, A-45): shown on a narrow viewport only, where the
   bar lists every card (the narrow block at the end of this file). */
.problem-bar-title {
  display: none;
  margin: 0 0 var(--space-2);
  font-size: var(--text-lg);
}

/* The overview of a narrow viewport (M9-9, A-45): one line per org with its note and its
   band as a strip over the period. Hidden on a wide viewport; the narrow block at the end
   of this file shows it and hides the gantt behind it while `data-view` is "overview". */
.org-strips {
  display: none;
  margin-bottom: var(--space-3);
}

.org-strips-head {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2) var(--space-3);
}

.org-strips-title {
  margin: 0;
  font-size: var(--text-lg);
}

.org-strips-window {
  margin: var(--space-1) 0 var(--space-3);
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

/* The hatch of "whereabouts unknown" lives in a drawing of its own that takes no room. */
.org-strip-defs {
  position: absolute;
  overflow: hidden;
}

.org-strip-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The whole line is the button: at least 44 px high, the focus ring is the global one. */
.org-strip {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-1);
  box-sizing: border-box;
  width: 100%;
  min-height: 2.75rem;
  padding: var(--space-3) var(--space-4);
  border: 0;
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.org-strip-head {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}

.org-strip-name {
  font-weight: 700;
  overflow-wrap: anywhere;
}

.org-strip-note {
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  overflow-wrap: anywhere;
}

.org-strip-note.is-disrupted {
  color: var(--color-danger-text);
}

.org-strip-band {
  display: block;
  width: 100%;
  height: 0.75rem;
}

/* Card. */
.card {
  background: var(--color-surface);
  border: 0;
  border-radius: var(--radius-md);
  padding: var(--space-6) var(--space-7);
  margin-bottom: var(--space-5);
}

.card > h2 {
  font-size: var(--text-xl);
}

/* The narrow, centred column of the masks before sign-in (auth_panel): login and setup. */
.auth-panel {
  max-inline-size: 28rem;
  margin-inline: auto;
  margin-block-start: var(--space-7);
}

/* Buttons. */
.button {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 2.5rem;
  padding: var(--space-2) var(--space-5);
  font: inherit;
  font-size: var(--text-base);
  font-weight: 600;
  line-height: var(--leading-tight);
  border: var(--border-width) solid transparent;
  border-radius: var(--radius-full);
  cursor: pointer;
  transition: background var(--motion-fast);
}

.button-primary {
  background: var(--color-primary);
  color: var(--color-on-primary);
}

.button-primary:hover {
  filter: brightness(1.1);
}

.button-secondary {
  background: var(--color-nav-active);
  color: var(--color-primary);
  border-color: transparent;
}

.button-danger {
  background: transparent;
  color: var(--color-danger-text);
  border: calc(2 * var(--border-width)) solid var(--color-danger-text);
}

.button-danger:hover {
  background: var(--color-danger-bg);
}

.button-quiet {
  background: transparent;
  color: var(--color-primary);
  border-color: transparent;
}

/* Link buttons: an anchor that carries an action, styled like a button. */
a.button {
  text-decoration: none;
}

/* A paging step that leads nowhere stays in place instead of disappearing. */
.button[aria-disabled="true"] {
  opacity: 0.5;
  pointer-events: none;
}

/* Status badges. */
.badge {
  display: inline-block;
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-sm);
  font-weight: 700;
  line-height: var(--leading-tight);
  white-space: nowrap;
  border-radius: var(--radius-full);
}

.badge-sign {
  margin-inline-end: var(--space-1);
}

.badge-success {
  color: var(--color-success-text);
  background: var(--color-success-bg);
}

.badge-warning {
  color: var(--color-warning-text);
  background: var(--color-warning-bg);
}

.badge-danger {
  color: var(--color-danger-text);
  background: var(--color-danger-bg);
}

.badge-info {
  color: var(--color-info-text);
  background: var(--color-info-bg);
}

.badge-production {
  color: var(--color-production-text);
  background: var(--color-production-bg);
}

.badge-neutral {
  color: var(--color-text-muted);
  background: var(--color-surface-sunken);
}

/* Messages. */
.messages {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.message {
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-sm);
  border: 0;
}

/* An action inside a message sits after the text in its natural width. */
.message .button {
  margin-inline-start: var(--space-3);
}

.message-success {
  color: var(--color-success-text);
  background: var(--color-success-bg);
}

.message-warning {
  color: var(--color-warning-text);
  background: var(--color-warning-bg);
}

.message-danger {
  color: var(--color-danger-text);
  background: var(--color-danger-bg);
}

.message-info {
  color: var(--color-info-text);
  background: var(--color-info-bg);
}

/* Tables: scroll the table horizontally, never the page. */
.table-scroll {
  overflow-x: auto;
  padding: var(--space-2) var(--space-5);
  background: var(--color-surface);
  border: 0;
  border-radius: var(--radius-md);
}

.card .table-scroll {
  padding: 0;
}

table {
  border-collapse: collapse;
  width: 100%;
}

th,
td {
  padding: var(--space-2) var(--space-3);
  border-bottom: var(--border-width) solid var(--color-border);
  text-align: left;
}

thead th {
  background: transparent;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-text-muted);
}

tbody tr:last-child td {
  border-bottom: 0;
}

/* Paging under a table: the range line and the two steps. */
.pagination {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

/* Key/value pairs inside a table cell (the audit details). */
.details-list {
  margin: 0;
}

.details-list dt {
  display: inline;
  color: var(--color-text-muted);
}

.details-list dd {
  display: inline;
  margin: 0 0 0 var(--space-1);
}

.details-list dd + dt::before {
  content: "";
  display: block;
}

/* A deactivated profile stays visible but steps back. */
.row-inactive {
  color: var(--color-text-muted);
}

/* Sits on a container inside the cell, never on the <td> itself: display: flex
   would take the cell out of its table-cell role (issue #79). */
.row-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* Forms. */
.form-grid {
  display: grid;
  gap: var(--space-4);
}

/* Filter bar above a list: the fields wrap, the buttons sit on the baseline. */
.filter-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: var(--space-3);
  align-items: end;
  margin-bottom: var(--space-5);
  padding: var(--space-5);
  background: var(--color-surface);
  border: 0;
  border-radius: var(--radius-md);
}

.filter-form .button {
  justify-self: start;
}

.form-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.form-field label {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-text);
}

.form-field input,
.form-field select,
.form-field textarea {
  min-height: 2.5rem;
  font: inherit;
  padding: var(--space-2) var(--space-4);
  border: var(--border-width) solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text);
}

.form-field textarea {
  resize: vertical;
}

/* A checkbox sits next to its label instead of below it. */
.checkbox-field label {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: 400;
  color: var(--color-text);
}

.checkbox-field input {
  min-height: 0;
  padding: 0;
}

/* A group of checkboxes under one legend; each box sits next to its own label. */
.checkbox-group {
  border: none;
  margin: 0;
  padding: 0;
}

.checkbox-group legend {
  padding: 0;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-text);
}

.checkbox-group label {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: 400;
  color: var(--color-text);
}

.checkbox-group input {
  min-height: 0;
  padding: 0;
}

/* A choice of one of a few options as adjoining segments (M4-3). The radios stay real and
   focusable but invisible; the segment after a radio carries its state -- the chosen one
   by colour and weight, the focused one by the focus ring. The row of segments is set
   explicitly, not inherited from a flex parent. */
.segmented-control {
  border: none;
  margin: 0;
  padding: 0;
}

.segmented-control legend {
  padding: 0;
  margin-bottom: var(--space-1);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-text);
}

.segmented-control-options {
  display: inline-flex;
  flex-direction: row;
  flex-wrap: nowrap;
  gap: calc(var(--space-1) / 2);
  padding: calc(var(--space-1) / 2);
  background: var(--color-surface-sunken);
  border-radius: var(--radius-full);
}

.segmented-option {
  position: relative;
  display: block;
}

.segmented-option input {
  position: absolute;
  inset: 0;
  margin: 0;
  padding: 0;
  opacity: 0;
  cursor: pointer;
}

.segmented-option span {
  display: block;
  padding: var(--space-1) var(--space-4);
  border: 0;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-text);
  cursor: pointer;
}

.segmented-option input:checked + span {
  background: var(--color-primary);
  color: var(--color-on-primary);
  font-weight: 600;
}

.segmented-option input:focus-visible + span {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

/* Explanatory text spanning a form grid cell. */
.form-hint {
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  margin: 0;
}

.form-field .field-error {
  color: var(--color-danger-text);
  font-size: var(--text-sm);
}

.form-field input[aria-invalid="true"],
.form-field select[aria-invalid="true"],
.form-field textarea[aria-invalid="true"] {
  border-color: var(--color-danger-text);
}

/* Empty state. */
.empty-state {
  text-align: center;
  padding: var(--space-8) var(--space-4);
  color: var(--color-text-muted);
  background: var(--color-surface);
  border-radius: var(--radius-md);
}

.card .empty-state {
  background: transparent;
}

.empty-state h2 {
  color: var(--color-text);
  font-size: var(--text-lg);
}

/* The actions of an empty state: a centred row, wrapping with the token gap in both
   directions. */
.empty-state-actions {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
}

/* Key/value header of one run's detail: label above value, spaced rows. */
.detail-list {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--space-2) var(--space-4);
  margin: 0 0 var(--space-6);
}

.detail-list dt {
  color: var(--color-text-muted);
}

/* A long value (a path, a key) breaks inside the column rather than widening the page on a
   narrow viewport (A-45). */
.detail-list dd {
  margin: 0;
  overflow-wrap: anywhere;
}

/* Progress of a run: the bar and its label on one line. */
.progress-line {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.progress {
  flex: 1;
  min-width: 0;
  accent-color: var(--color-primary);
}

.progress-label {
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

/* State line above the progress bar. */
.job-state {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

.state-reason {
  color: var(--color-text-muted);
}

/* Log pane: scrolls in itself (vertically and horizontally), the page never does. */
.log-pane {
  max-height: 30rem;
  overflow: auto;
  background: var(--color-surface-sunken);
  border: 0;
  border-radius: var(--radius-sm);
  padding: var(--space-3);
}

.log-truncated {
  margin: 0 0 var(--space-2);
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

.log-lines {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* A single log line keeps its own whitespace; the pane scrolls, the line does not wrap. */
.log-line {
  white-space: pre;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
}

.log-time {
  color: var(--color-text-muted);
  margin-right: var(--space-3);
}

.log-level-warning {
  color: var(--color-warning-text);
}

.log-level-error {
  color: var(--color-danger-text);
}

.log-empty {
  color: var(--color-text-muted);
}

/* The virtual log pane of the log view (M8-12, A-38): the script draws only the rows in
   view, each of a fixed height, into an absolutely placed list above a spacer as high as
   all rows. The gutter of the scroll bar is always kept, so the width the script wraps the
   lines to never depends on whether the vertical bar shows. Sizes come through the CSSOM. */
.log-pane.is-virtual {
  position: relative;
  max-height: none;
  height: 70vh;
  padding: 0;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
}

.log-pane.is-virtual .log-lines {
  position: absolute;
  top: 0;
  left: 0;
  min-width: 100%;
}

/* A fixed height per row: a fallback glyph must not make one row taller than the others.
   ``clip`` instead of ``hidden``, which would make the row a scroll container and break the
   sticky number cell. The row keeps ``white-space: pre`` -- the script wraps, never the
   browser. */
.log-pane.is-virtual .log-line {
  height: calc(var(--text-sm) * var(--leading-normal));
  line-height: var(--leading-normal);
  overflow-y: clip;
}

/* The line number stands at the left edge while the row scrolls sideways. */
.log-number {
  position: sticky;
  left: 0;
  display: inline-block;
  text-align: right;
  padding-inline: var(--space-2);
  color: var(--color-text-muted);
  background: var(--color-surface-sunken);
  user-select: none;
}

.log-hit {
  background: var(--color-log-hit-bg);
  color: var(--color-text);
  text-decoration: underline;
}

.log-hit.is-current {
  background: var(--color-primary);
  color: var(--color-on-primary);
}

/* The log view (M8-12): bar above, the pane below. ``.button`` sets ``display`` itself, so
   ``hidden`` needs a rule of its own inside the view. */
.log-view {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.log-view [hidden] {
  display: none;
}

.log-view-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-3);
}

.log-view-search {
  flex: 1 1 18rem;
}

.log-view-counter {
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

.log-view-actions {
  display: flex;
  gap: var(--space-2);
}

.log-view-message p {
  margin: 0;
}

.log-view-context {
  margin: var(--space-2) 0 0;
  white-space: pre-wrap;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
}

/* The modal dialog (M8-12): nearly the whole viewport with a margin of --space-4 all round,
   also in a narrow window. Head on top, the body takes the rest. */
.dialog {
  /* Percentages of the viewport the modal dialog stands in, not ``vw``/``vh``: those count
     the gutter the gantt page keeps for its scroll bar, and the margin would shrink. */
  width: min(72rem, calc(100% - 2 * var(--space-4)));
  height: calc(100% - 2 * var(--space-4));
  max-width: none;
  max-height: none;
  margin: auto;
  padding: 0;
  color: var(--color-text);
  background: var(--color-surface);
  border: 0;
  border-radius: var(--radius-md);
}

.dialog[open] {
  display: flex;
  flex-direction: column;
}

.dialog::backdrop {
  background: var(--color-backdrop);
}

/* Open but not modal: the catalog shows the dialog inline. */
.dialog:not(:modal) {
  position: static;
  width: auto;
  height: auto;
  margin: 0;
}

.dialog-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface);
}

.dialog-title {
  flex: 1 1 auto;
  margin: 0;
  font-size: var(--text-lg);
  overflow-wrap: anywhere;
}

.dialog-body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: var(--space-4);
  overflow: auto;
}

.dialog .log-view {
  flex: 1 1 auto;
  min-height: 0;
}

.dialog .log-pane.is-virtual {
  height: auto;
  flex: 1 1 auto;
  min-height: 10rem;
}

/* The context menu of a run (M12-2, A-47): fixed in the window, gantt.js sets its place and
   the largest height. There is no global rule for `hidden`, so every rule that sets
   `display` leaves a hidden element alone. The margin to the window is 0.5rem a side. */
.context-menu {
  position: fixed;
  z-index: 50;
  min-inline-size: 12rem;
  max-inline-size: min(20rem, 100vw - 1rem);
  overflow-y: auto;
  padding: var(--space-1);
  background: var(--color-surface);
  color: var(--color-text);
  border: var(--border-width) solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  overflow-wrap: anywhere;
}

/* In the catalog it stands open in the flow of the page. */
.context-menu.is-static {
  position: static;
}

.context-menu-item:not([hidden]) {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  inline-size: 100%;
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-text);
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.context-menu-item:hover {
  background: var(--color-surface-sunken);
}

.context-menu-item:focus-visible {
  outline: var(--focus-ring);
  outline-offset: calc(-1 * var(--focus-offset));
}

/* A locked entry stays in place, visibly out of use, and keeps its focus for the keys. */
.context-menu-item[aria-disabled="true"] {
  color: var(--color-text-muted);
  cursor: default;
}

.context-menu-note:not([hidden]) {
  display: block;
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

/* The gantt plot (M1-6, M1-7, A-10, A-11). Two columns: the tree on the left is HTML,
   the plot on the right an SVG drawn in CSS pixels one to one. Only the plot scrolls
   sideways; the tree column stands. The lengths that depend on the data -- the width of
   the plot, the height and the indent of every row -- are set by the page, so the
   stylesheet names none of them. Every colour comes from a token, so both themes carry
   the plot. Both columns stand on --color-surface, the ground the status colours are
   measured against; the frame sits around both, so the rows of the tree and the lanes
   of the plot start at the same height. */
.gantt {
  position: relative;
}

.gantt-body {
  display: grid;
  grid-template-columns: 28rem minmax(0, 1fr);
  /* The rows of the gantt, then the dock below the plot (M11-1). */
  grid-template-rows: minmax(0, 1fr) auto;
  /* In the frame a taller side panel beside it does not stretch the gantt (M3-9). */
  align-self: start;
  background: var(--color-surface);
  border: 0;
  border-radius: var(--radius-md);
  overflow: clip;
}

/* The plot scrolls sideways by touchpad, shifted wheel and keyboard, but shows no
   scrollbar of its own: the dock below draws it (M11-1, A-46). */
.gantt-scroll {
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}

/* The dock of the gantt (M11-1, A-46): below the plot column, sticking to the bottom edge
   of the window while the gantt reaches below it -- above a side panel docked there, whose
   height gantt.js sets as the inset (`fitDockInset`). Over the bars and their tooltip. */
.gantt-dock {
  grid-column: 2;
  grid-row: 2;
  position: sticky;
  inset-block-end: 0;
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: var(--space-3);
  row-gap: 0;
  min-inline-size: 0;
  padding-block: var(--space-1);
  /* Room for the focus ring of "+" at the right (A-46): the body clips at its edge, and at
     the corner the round ring fits inside the rounding of the body. */
  padding-inline-end: var(--space-2);
  background: var(--color-surface);
}

/* The scrollbar drawn by the page, always visible: a line of 0.5rem in a target of 1rem.
   A finger dragging it never scrolls the page. */
.gantt-dock-track {
  position: relative;
  flex: 1 1 8rem;
  min-inline-size: 0;
  block-size: 1rem;
  padding-block: 0.25rem;
  /* Sunken, not the hairline colour: that one stays with tables and lists (M9-2). */
  background: var(--color-surface-sunken);
  background-clip: content-box;
  border-radius: var(--space-1);
  cursor: pointer;
  touch-action: none;
}

.gantt-dock-track[aria-disabled="true"] {
  cursor: default;
}

/* Its thumb: gantt.js sets its size and place; without them it fills the track. */
.gantt-dock-thumb {
  position: absolute;
  inset-block: 0.25rem;
  inset-inline-start: 0;
  inline-size: 100%;
  background: var(--color-border-strong);
  border-radius: var(--space-1);
  cursor: grab;
}

.gantt-dock-thumb:hover {
  background: var(--color-primary);
}

.gantt-dock-track.is-dragging .gantt-dock-thumb {
  background: var(--color-primary);
}

.gantt-dock-controls {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  flex: 0 0 auto;
  min-inline-size: 0;
}

/* The label keeps its width over every scale, so the buttons never move. */
.gantt-dock-level {
  min-inline-size: 6rem;
  font-size: var(--text-sm);
  text-align: center;
}

/* A square button: every axis of the `.button` base is set here. */
.gantt-dock-button {
  justify-content: center;
  inline-size: 2rem;
  block-size: 2rem;
  min-block-size: 0;
  padding: 0;
}

/* The button at the end of the scales stays in place, visibly out of use. */
.gantt-dock-button:disabled {
  opacity: 0.5;
  cursor: default;
}

.gantt-plot {
  display: block;
}

/* The tree column ("tree grid", M1-7). Only the name gives way when the column runs out
   of room; the toggle, the production mark and the note keep their full width. The width
   of the column and the smaller type of the note leave the name of an org at least 8rem
   beside the production mark and the longest note ("fetch disrupted since ..."). */
.gantt-tree {
  min-width: 0;
  overflow: hidden;
  border-inline-end: var(--border-width) solid var(--color-border);
}

.gantt-tree-head {
  display: flex;
  align-items: center;
  padding-inline: var(--space-2);
}

.gantt-zone {
  color: var(--color-text);
  font-family: var(--font-ui);
  font-size: 0.625rem;
  font-variant-numeric: tabular-nums;
}

.gantt-tree-row {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  padding-inline: var(--space-2);
  border-top: var(--border-width) solid var(--color-border);
  font-size: var(--text-sm);
  line-height: 1;
  white-space: nowrap;
}

/* The one cell of a tree row in the treegrid (M4-7): it takes the room of the row and
   lays out the toggle, the name, the mark and the note like the row did; `min-width: 0`
   keeps the chain the name shortens along unbroken. */
.gantt-tree-cell {
  display: flex;
  flex: 1 1 auto;
  align-items: center;
  gap: var(--space-1);
  min-width: 0;
}

.gantt-tree-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--color-text);
}

.gantt-toggle,
.gantt-toggle-spacer {
  flex: 0 0 auto;
  width: 1.25em;
  height: 1.25em;
}

.gantt-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--space-1);
  background: transparent;
  color: var(--color-text-muted);
  font: inherit;
  line-height: 1;
  cursor: pointer;
}

.gantt-toggle:hover {
  color: var(--color-text);
}

.gantt-toggle:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

/* The production mark of an org: the colours of .badge-production, without the padding
   of .badge, so it fits into a lane. */
.gantt-mark {
  flex: 0 0 auto;
  display: inline-block;
  padding: 0.125rem var(--space-1);
  font-size: 0.6875rem;
  line-height: 1;
  border-radius: var(--space-1);
}

.gantt-note {
  flex: 0 0 auto;
  font-size: 0.6875rem;
  color: var(--color-text-muted);
}

.gantt-note.is-disrupted {
  color: var(--color-danger-text);
}

/* The note of a taskflow whose references stand in name order (A-33): quiet like every
   note -- no colour of its own, it is no error -- and the only note that gives way when the
   column runs out of room; the full text stands in its title. */
.gantt-note.is-flow {
  flex: 0 2 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A reference under a taskflow (M2-8, A-12): the row of one of its children, a second time
   in the tree. Its name steps back against the row at the place of the asset -- the same
   bars stand in both -- and the row itself can take the focus, because the jump of another
   reference leads to it. The place of the child stands only in the title of the row: the
   column is 28rem narrow, and the plot keeps its bars free of labels. */
.gantt-tree-row:focus-visible {
  outline: var(--focus-ring);
  outline-offset: calc(-1 * var(--focus-offset));
}

.gantt-tree-row.is-ref .gantt-tree-name {
  color: var(--color-text-muted);
}

/* The row under the pointer and the row of the run open in the side panel (A-43): the row of the tree column and its lane in the plot (.gantt-lane) carry the same
   ground, set by a class alone. */
.gantt-tree-row.is-hover,
.gantt-tree-row.is-chosen {
  background: var(--color-nav-active);
}

.gantt-tree-jump {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.25em;
  height: 1.25em;
  padding: 0;
  border: 0;
  border-radius: var(--space-1);
  background: transparent;
  color: var(--color-primary);
  font: inherit;
  line-height: 1;
  cursor: pointer;
}

.gantt-tree-jump:hover {
  color: var(--color-text);
}

.gantt-tree-jump:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

/* The link of an asset row into its expectation (M5-2): the shape of the jump, as a link. */
.gantt-tree-edit {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.25em;
  height: 1.25em;
  border-radius: var(--space-1);
  color: var(--color-primary);
  line-height: 1;
  text-decoration: none;
}

.gantt-tree-edit:hover {
  color: var(--color-text);
}

.gantt-tree-edit:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

.gantt-message {
  margin: 0 0 var(--space-3);
  color: var(--color-text-muted);
}

.gantt-message[role="alert"] {
  color: var(--color-danger-text);
}

/* The toolbar of the gantt (M9-5, A-42): the period, "last updated", the arrangement, the
   scale, "Change period" and "Expectations" in one row that wraps; the filter bar below
   is its second row. Every axis is set explicitly. Since M9-6 (A-43) it stands tighter and
   centred, so the gantt below gets the room. */
.gantt-toolbar {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-2) var(--space-3);
  margin-bottom: var(--space-2);
}

/* The period of the picture, in the text colour: it is what the sentence above speaks of. */
.gantt-window {
  color: var(--color-text);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* "Change period": the summary is the button; the unfolded form takes a row of its own. */
.gantt-period > summary {
  list-style: none;
}

.gantt-period > summary::-webkit-details-marker {
  display: none;
}

.gantt-period[open] {
  flex-basis: 100%;
}

.gantt-period[open] > .filter-form {
  margin-top: var(--space-3);
  margin-bottom: 0;
}

/* The state line of the refresh (M2-7, A-10): when the page last read successfully and,
   beside it, the hint that no worker answers. Small and quiet -- it stands above the plot
   and must not compete with it; the hint carries the warning colour, because it says
   something about the data below. Both parts are empty until the first successful read,
   so the line takes no room on a page that never got an answer. */
.gantt-refresh {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

/* Tabular figures, so the timestamp does not jump from one refresh to the next. */
.gantt-updated {
  font-variant-numeric: tabular-nums;
}

.gantt-worker-hint {
  color: var(--color-warning-text);
}

/* The row of switches above the filter bar (M4-3, M4-4): the arrangement and the scale
   stand side by side and wrap under one another on a narrow page. Every axis of the row
   is set explicitly, not inherited. */
.gantt-switches {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: 0;
}

.gantt-switches .segmented-option span {
  padding: var(--space-1) var(--space-3);
}

/* The legend of the arrangement switch and of the status boxes (A-43): read out, never seen --
   the options name what they choose, and the room goes to the gantt. */
.gantt-switches .segmented-control legend,
.gantt-filter-status .checkbox-group legend {
  position: absolute;
  width: var(--border-width);
  height: var(--border-width);
  margin: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The hint that the period is too long for the chosen scale (M4-4, A-10): small, in the
   warning colour like the worker hint, because it says the plot is coarser than asked. */
.gantt-zoom-hint {
  margin: 0 0 var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-warning-text);
}

/* The hint of the shared address (M4-5, A-10): the period of an opened link was refused,
   or parts of it cannot be shown. Small, in the warning colour like the scale hint. */
.gantt-link-hint {
  margin: 0 0 var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-warning-text);
}

/* The filter bar of the gantt (M4-2, A-13): the status boxes take a line of their own and
   stand side by side; below them the asset type, the free text and the two buttons stand in
   one row that wraps (A-43), the buttons next to each other on the baseline. Every axis is
   set explicitly. */
.gantt-filter {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-2) var(--space-3);
  margin-bottom: var(--space-3);
}

.gantt-filter-status {
  flex: 1 1 100%;
}

.gantt-filter > .form-field {
  flex: 0 1 14rem;
  min-width: 12rem;
}

.gantt-filter-status .checkbox-group {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-4);
}

.gantt-filter-actions {
  display: flex;
  flex: 0 0 auto;
  flex-direction: row;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* Axis: a hairline per tick through the whole plot, its label above it. */
.gantt-grid {
  stroke: var(--color-border);
  stroke-width: 1;
}

/* Centred on its tick; the page keeps a label near an edge inside the plot. */
.gantt-tick {
  fill: var(--color-text-muted);
  font-family: var(--font-ui);
  font-size: 0.625rem;
  text-anchor: middle;
}

/* One bar per run. The fill names the state, the glyph repeats it without colour, and
   the focus ring makes a run of seconds reachable by keyboard. */
.gantt-bar {
  cursor: default;
}

.gantt-bar:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

.gantt-bar-body,
.gantt-band-segment-body {
  stroke: var(--color-surface);
  stroke-width: 0.5;
}

.gantt-glyph {
  fill: var(--color-bg);
  font-family: var(--font-ui);
  font-size: 0.5625rem;
  text-anchor: middle;
  pointer-events: none;
}

/* The segments of a band are thinner pills than a bar (A-43), and so is their glyph. */
.gantt-band-segment .gantt-glyph {
  font-size: 0.5rem;
}

.gantt-bar.is-running .gantt-bar-body,
.gantt-band-segment.is-running .gantt-band-segment-body {
  fill: var(--color-status-running);
}

.gantt-bar.is-succeeded .gantt-bar-body,
.gantt-band-segment.is-succeeded .gantt-band-segment-body {
  fill: var(--color-status-succeeded);
}

.gantt-bar.is-warning .gantt-bar-body,
.gantt-band-segment.is-warning .gantt-band-segment-body {
  fill: var(--color-status-warning);
}

.gantt-bar.is-failed .gantt-bar-body,
.gantt-band-segment.is-failed .gantt-band-segment-body {
  fill: var(--color-status-failed);
}

.gantt-bar.is-stopped .gantt-bar-body,
.gantt-band-segment.is-stopped .gantt-band-segment-body {
  fill: var(--color-status-stopped);
}

.gantt-bar.is-unknown .gantt-bar-body,
.gantt-band-segment.is-unknown .gantt-band-segment-body {
  fill: var(--color-status-unknown);
}

/* "Whereabouts unknown" is hatched rather than filled: the bar is recognisable without
   any colour at all. The fill is a pattern the page hands in as an attribute -- only
   tokens.css may reference anything by address -- while the parts of the pattern take
   their colour here like everything else. */
.gantt-bar.is-unaccounted .gantt-bar-body,
.gantt-band-segment.is-unaccounted .gantt-band-segment-body {
  stroke: var(--color-status-unaccounted);
  stroke-width: 1;
}

/* A provisional run the living edge stopped reporting (M2-5, A-7): "not yet confirmed" is
   a dashed outline around the bar -- a shape, so the marking survives grey scale and the
   dark theme (A-10). It sits beside the status class instead of replacing it: the stage is
   a display state, the fill still names the stored status. */
.gantt-bar.is-unconfirmed .gantt-bar-body {
  stroke: var(--color-text);
  stroke-width: 1.5;
  stroke-dasharray: 3 2;
}

.gantt-hatch-ground {
  fill: var(--color-surface);
}

.gantt-hatch-line {
  stroke: var(--color-status-unaccounted);
  stroke-width: 2;
}

/* The compaction band of a folded node (M1-7): the segments wear the colours, glyphs and
   hatch of the bars above. The counters never lie over a segment -- they stand beside
   the band where its lane has room, or in a second lane below it -- and carry a halo in
   the colour of the ground, so a grid line or the now line does not cut through them. */
.gantt-band {
  display: block;
  background: var(--color-surface);
}

.gantt-count {
  fill: var(--color-text);
  font-family: var(--font-ui);
  font-size: 0.6875rem;
  font-variant-numeric: tabular-nums;
  paint-order: stroke;
  stroke: var(--color-surface);
  stroke-width: 0.25em;
  stroke-linejoin: round;
  pointer-events: none;
}

/* The plot half of a reference (M2-8): its bars only, without a label beside them. */
.gantt-ref {
  display: block;
  background: var(--color-surface);
}

/* The now line is its own layer, above every bar. At the axis it carries a pill with the
   clock time in the mono face (A-43). */
.gantt-now-line {
  stroke: var(--color-gantt-now);
  stroke-width: 1;
  stroke-dasharray: 3 3;
}

.gantt-now-pill {
  fill: var(--color-gantt-now);
}

.gantt-now-time {
  fill: var(--color-surface);
  font-family: var(--font-mono);
  font-size: 0.625rem;
  font-variant-numeric: tabular-nums;
  text-anchor: middle;
}

/* The lanes below every row (A-43): transparent but for the row under the pointer and the
   row of the run open in the side panel, which carry the ground of their tree row. */
.gantt-lane {
  fill: transparent;
}

.gantt-lane.is-hover,
.gantt-lane.is-chosen {
  fill: var(--color-nav-active);
}

/* "No fetch" (A-43): the running fetch disruption of an org, a hatch in the danger colour
   at a low opacity below its rows, and its word at the row of the org with the halo of a
   counter. The fill is a pattern the page hands in as an attribute, like the hatch of
   "whereabouts unknown". */
.gantt-no-fetch {
  opacity: 0.35;
}

.gantt-no-fetch-line {
  stroke: var(--color-danger-text);
  stroke-width: 1.5;
}

.gantt-no-fetch-label {
  fill: var(--color-danger-text);
  font-family: var(--font-ui);
  font-size: 0.6875rem;
  paint-order: stroke;
  stroke: var(--color-surface);
  stroke-width: 0.25em;
  stroke-linejoin: round;
}

/* The arrows of the flow (A-33) and the focus ring above them: layers of their own above
   the bars, which let every click and hover through to the bars below; so do the lanes and
   the layer "no fetch" below the rows (A-43). A path of an error is dashed and has a tone
   of its own -- told apart not only by colour. */
.gantt-lanes,
.gantt-no-fetch-layer,
.gantt-arrows,
.gantt-focus-layer {
  pointer-events: none;
}

.gantt-arrow-line {
  fill: none;
  stroke: var(--color-gantt-arrow);
  stroke-width: 1.25;
}

.gantt-arrow-head {
  fill: var(--color-gantt-arrow);
}

.gantt-arrow.is-error .gantt-arrow-line {
  stroke: var(--color-gantt-arrow-error);
  stroke-dasharray: 4 2;
}

.gantt-arrow.is-error .gantt-arrow-head {
  fill: var(--color-gantt-arrow-error);
}

/* The focus ring of a bar, where its outline stands, so the head of an arrow never
   covers it. */
.gantt-focus-ring {
  fill: none;
  stroke: var(--color-focus);
  stroke-width: 2;
}

/* "Done by" (M5-2): a finely dashed line across its own row with a glyph at the top --
   told apart from the now line by pattern, glyph and extent, not only by colour (A-10). */
.gantt-due-line {
  stroke: var(--color-primary);
  stroke-width: 1.5;
  stroke-dasharray: 1 2;
}

.gantt-due-glyph {
  fill: var(--color-primary);
  font-family: var(--font-ui);
  font-size: 0.5rem;
  text-anchor: middle;
  pointer-events: none;
}

/* The states of "done by" (M5-5, A-18): pattern and glyph tell them apart, the colour only
   adds to it (A-10). The glyph takes the colour of its line. */
.gantt-due.is-not-due .gantt-due-line {
  stroke: var(--color-primary);
  stroke-dasharray: 1 2;
}

.gantt-due.is-pending .gantt-due-line {
  stroke: var(--color-primary);
  stroke-dasharray: 4 2;
}

.gantt-due.is-not-rated .gantt-due-line {
  stroke: var(--color-text-muted);
  stroke-dasharray: 1 2;
}

.gantt-due.is-met .gantt-due-line {
  stroke: var(--color-status-succeeded);
  stroke-dasharray: none;
}

.gantt-due.is-violated .gantt-due-line {
  stroke: var(--color-status-failed);
  stroke-width: 2;
  stroke-dasharray: none;
}

.gantt-due.is-met-late .gantt-due-line {
  stroke: var(--color-status-warning);
  stroke-dasharray: 6 2;
}

.gantt-due.is-not-evaluable .gantt-due-line {
  stroke: var(--color-status-unknown);
  stroke-dasharray: 2 2;
}

.gantt-due.is-not-due .gantt-due-glyph,
.gantt-due.is-pending .gantt-due-glyph {
  fill: var(--color-primary);
}

.gantt-due.is-not-rated .gantt-due-glyph {
  fill: var(--color-text-muted);
}

.gantt-due.is-met .gantt-due-glyph {
  fill: var(--color-status-succeeded);
}

.gantt-due.is-violated .gantt-due-glyph {
  fill: var(--color-status-failed);
}

.gantt-due.is-met-late .gantt-due-glyph {
  fill: var(--color-status-warning);
}

.gantt-due.is-not-evaluable .gantt-due-glyph {
  fill: var(--color-status-unknown);
}

/* "At most" (M5-2): a solid notch with end strokes across the lane of its bar. */
.gantt-limit-mark {
  fill: none;
  stroke: var(--color-text);
  stroke-width: 1.5;
}

/* The states of the limit (M5-5): "met" stays the solid notch without addition, "pending"
   is dashed without a glyph, "not rated" dotted with its neutral glyph; "violated" and "not
   evaluable" carry a glyph of their own beside the notch (placed by
   limitGlyphPlacementOf), with the halo of the counters so it reads on any bar. */
.gantt-limit.is-pending .gantt-limit-mark {
  stroke: var(--color-text-muted);
  stroke-dasharray: 2 2;
}

.gantt-limit.is-not-rated .gantt-limit-mark {
  stroke: var(--color-text-muted);
  stroke-dasharray: 1 2;
}

.gantt-limit.is-met .gantt-limit-mark {
  stroke: var(--color-text);
}

.gantt-limit.is-violated .gantt-limit-mark {
  stroke: var(--color-status-failed);
}

.gantt-limit.is-not-evaluable .gantt-limit-mark {
  stroke: var(--color-status-unknown);
}

.gantt-limit-glyph {
  fill: var(--color-text);
  font-family: var(--font-ui);
  font-size: 0.5625rem;
  text-anchor: middle;
  paint-order: stroke;
  stroke: var(--color-surface);
  stroke-width: 0.25em;
  stroke-linejoin: round;
  pointer-events: none;
}

.gantt-limit.is-not-rated .gantt-limit-glyph {
  fill: var(--color-text-muted);
}

.gantt-limit.is-violated .gantt-limit-glyph {
  fill: var(--color-status-failed);
}

.gantt-limit.is-not-evaluable .gantt-limit-glyph {
  fill: var(--color-status-unknown);
}

/* The tooltip of a bar: shown on hover and on focus, positioned by the page. */
.gantt-tooltip {
  position: absolute;
  z-index: 1;
  max-width: 24rem;
  margin: 0;
  padding: var(--space-2) var(--space-3);
  background: var(--color-bg);
  border: var(--border-width) solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  pointer-events: none;
}

.gantt-tooltip p {
  margin: 0;
  display: flex;
  gap: var(--space-2);
}

.gantt-tooltip-label {
  color: var(--color-text-muted);
  min-width: 7rem;
}

/* The legend below the gantt (A-43): text, not a picture -- one pill per entry with its
   glyph in a field of the status colour, like the mark of `.run-status`, and its word.
   "Whereabouts unknown" shows its hatch as a glyph, "no fetch" as a hatch in the danger
   colour, "done by" and the flow in the tones of their marks. The row wraps. */
.gantt-legend {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-3) 0 0;
  padding: 0;
  list-style: none;
}

.gantt-legend-item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-3) var(--space-1) var(--space-1);
  border-radius: var(--radius-full);
  background: var(--color-surface);
  color: var(--color-text);
  font-size: var(--text-sm);
  white-space: nowrap;
}

.gantt-legend-glyph {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.25rem;
  height: 1.25rem;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-text);
  font-family: var(--font-ui);
  font-size: 0.6875rem;
  line-height: 1;
}

.gantt-legend-item.is-running .gantt-legend-glyph {
  background: var(--color-status-running);
  color: var(--color-bg);
}

.gantt-legend-item.is-succeeded .gantt-legend-glyph {
  background: var(--color-status-succeeded);
  color: var(--color-bg);
}

.gantt-legend-item.is-warning .gantt-legend-glyph {
  background: var(--color-status-warning);
  color: var(--color-bg);
}

.gantt-legend-item.is-failed .gantt-legend-glyph {
  background: var(--color-status-failed);
  color: var(--color-bg);
}

.gantt-legend-item.is-stopped .gantt-legend-glyph {
  background: var(--color-status-stopped);
  color: var(--color-bg);
}

.gantt-legend-item.is-unknown .gantt-legend-glyph {
  background: var(--color-status-unknown);
  color: var(--color-bg);
}

.gantt-legend-item.is-unaccounted .gantt-legend-glyph {
  color: var(--color-status-unaccounted);
}

.gantt-legend-item.is-due .gantt-legend-glyph {
  color: var(--color-primary);
}

.gantt-legend-item.is-no-fetch .gantt-legend-glyph {
  background: repeating-linear-gradient(
    -45deg,
    var(--color-danger-text) 0 0.09375rem,
    var(--color-surface) 0.09375rem 0.375rem
  );
}

.gantt-legend-item.is-flow .gantt-legend-glyph {
  color: var(--color-gantt-arrow);
}

/* The frame the gantt and its side panel share (M3-9, M8-3): the gantt takes what the
   panel leaves over, in three columns -- the gantt, the resize handle, the panel. A closed
   panel and its handle take no room; a covering one (.is-overlay) stands out of the flow,
   over the gantt. A panel below the gantt (.is-bottom) turns the frame into one column of
   three rows: the gantt, the handle, the panel. */
.gantt-frame {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  position: relative;
}

.gantt-frame:has(> .side-panel.is-bottom:not([hidden])) {
  grid-template-columns: minmax(0, 1fr);
}

/* A page with a gantt keeps the gutter of the page's scrollbar, whether the page scrolls
   or not (issue #48). The width of the frame then no longer depends on the page's height;
   otherwise an open panel beside the gantt could bring in the scrollbar, the narrower
   frame would cover the gantt, the page would shrink, the scrollbar would go and the
   panel would stand beside it again, in every frame. */
html:has(.gantt-frame) {
  scrollbar-gutter: stable;
}

/* The side panel (M3-9, A-15). Its width, its height and its scroll padding are set by
   the page; the max-height here is the limit without the script. A panel of its own
   display rule has to respect [hidden], so every rule that shows it names the open
   state. The head stays in place, only the rest scrolls: the panel has no padding at its
   top, the head carries it, so no strip of scrolled content shows above the head. The
   panel isolates its stacking, so the head's z-index never competes with the tooltip of
   the gantt. */
.side-panel:not([hidden]) {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  align-self: start;
  max-height: calc(100vh - 2 * var(--space-4));
  overflow-y: auto;
  isolation: isolate;
  padding: 0 var(--space-4) var(--space-3);
  background: var(--color-surface);
  border: 0;
  border-radius: var(--radius-md);
}

.side-panel.is-overlay {
  position: absolute;
  inset-inline-end: 0;
  inset-block-start: 0;
  z-index: 2;
  background: var(--color-surface);
  border: var(--border-width) solid var(--color-border-strong);
}

.side-panel:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

.side-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  position: sticky;
  inset-block-start: 0;
  z-index: 1;
  padding-block-start: var(--space-3);
  background: var(--color-surface);
}

.side-panel-head .side-panel-side {
  margin-inline-start: auto;
}

.side-panel-message {
  margin: 0;
  color: var(--color-danger-text);
}

/* A card inside the white panel needs no ground and no padding of its own. */
.side-panel .card {
  padding: 0;
}

/* The resize handle of the side panel (M8-3, A-35), at its inner edge: a column at the
   right, a row above a panel at the bottom. Its height at the right, its place over a
   covering panel and its offset above a docked one are set by the page; it takes no touch
   gesture of the browser, so a drag never scrolls the page. Every rule names one selector,
   so the contract tests find each of them. */
.side-panel-handle:not([hidden]) {
  display: block;
  inline-size: var(--panel-handle-size);
  min-block-size: var(--space-8);
  align-self: stretch;
  cursor: col-resize;
  touch-action: none;
  background: var(--color-border-strong);
  border-radius: var(--space-1);
}

.side-panel-handle:hover {
  background: var(--color-primary);
}

.side-panel-handle:active {
  background: var(--color-primary);
}

.side-panel-handle:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

.side-panel-handle.is-bottom:not([hidden]) {
  inline-size: auto;
  block-size: var(--panel-handle-size);
  min-block-size: 0;
  cursor: row-resize;
}

.side-panel-handle.is-overlay:not([hidden]) {
  position: absolute;
  inset-block-start: 0;
  z-index: 3;
}

/* A panel below the gantt sticks to the bottom of the window, its handle just above it
   (the offset is set by the page); the gantt scrolls with the page beneath them as
   before. Covering or not, it stays docked: every axis of the overlay rule is set
   again. */
.gantt-frame > .side-panel.is-bottom:not([hidden]) {
  position: sticky;
  inset-block-end: 0;
  z-index: 2;
}

.gantt-frame > .side-panel-handle.is-bottom:not([hidden]) {
  position: sticky;
  inset-block-end: 0;
  z-index: 2;
}

.gantt-frame > .side-panel.is-bottom.is-overlay {
  position: sticky;
  inset-inline-end: auto;
  inset-block-start: auto;
  inset-block-end: 0;
}

.gantt-frame > .side-panel-handle.is-bottom.is-overlay {
  position: sticky;
  inset-inline-end: auto;
  inset-block-start: auto;
  inset-block-end: 0;
}

/* The status of one run outside the plot (M3-8): a mark in the colour of the status with
   its glyph in the colour of the ground -- the same colours and glyphs as the bars -- and
   the label beside it. "Whereabouts unknown" is hatched like its bar; the hatch is a
   gradient of two tokens, so no pattern has to be referenced by address. */
.run-status {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.run-status-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.25rem;
  height: 1.25rem;
  border-radius: var(--radius-full);
  color: var(--color-bg);
  font-family: var(--font-ui);
  font-size: 0.75rem;
  line-height: 1;
}

.run-status.is-running .run-status-mark {
  background: var(--color-status-running);
}

.run-status.is-succeeded .run-status-mark {
  background: var(--color-status-succeeded);
}

.run-status.is-warning .run-status-mark {
  background: var(--color-status-warning);
}

.run-status.is-failed .run-status-mark {
  background: var(--color-status-failed);
}

.run-status.is-stopped .run-status-mark {
  background: var(--color-status-stopped);
}

.run-status.is-unknown .run-status-mark {
  background: var(--color-status-unknown);
}

.run-status.is-unaccounted .run-status-mark {
  background: repeating-linear-gradient(
    45deg,
    var(--color-status-unaccounted) 0 0.125rem,
    var(--color-surface) 0.125rem 0.3125rem
  );
}

.run-status-raw {
  color: var(--color-text-muted);
  font-family: var(--font-mono);
}

/* The small series of the drill (M3-8): the band P10-P90 behind the bars, one bar per
   predecessor and the median as a line across. No text in the drawing; every bar names
   its predecessor in its title. */
.duration-series {
  display: block;
  max-width: 100%;
  height: auto;
}

.duration-series-band {
  fill: var(--color-surface-sunken);
}

.duration-series-bar {
  fill: var(--color-status-succeeded);
}

.duration-series-median {
  stroke: var(--color-text);
  stroke-width: 1;
}

/* A value with a copy button (M8-5): the value selectable as a whole, an optional note and
   the button. The button only shows while static/js/copy.js has found a usable clipboard
   and marked <html> with data-clipboard="available"; without it the value stays
   selectable. The status region is read by screen readers, not shown. */
.copy-value {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
}

.copy-value-text {
  user-select: all;
  overflow-wrap: anywhere;
}

.copy-value-note {
  color: var(--color-text-muted);
}

.copy-value .copy-value-button {
  display: none;
}

:root[data-clipboard="available"] .copy-value .copy-value-button {
  display: inline-flex;
}

/* The label never breaks inside the button, not even where a parent such as
   `.detail-list dd` breaks long values anywhere; the button wraps as a whole. */
.copy-value .copy-value-button {
  overflow-wrap: normal;
  white-space: nowrap;
}

.copy-value-status {
  position: absolute;
  width: var(--border-width);
  height: var(--border-width);
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* htmx indicator styles (htmx does not inject them; includeIndicatorStyles is off). */
.htmx-indicator {
  opacity: 0;
  transition: opacity var(--motion-slow);
}

.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
  opacity: 1;
}

/* The state of an agent or service (M6-6): a mark in the colour of the state with its
   glyph in the colour of the ground -- the pattern of .run-status -- and the label beside
   it. */
.agent-state {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.agent-state-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.25rem;
  height: 1.25rem;
  border-radius: var(--radius-full);
  color: var(--color-bg);
  font-family: var(--font-ui);
  font-size: 0.75rem;
  line-height: 1;
}

.agent-state.is-running .agent-state-mark {
  background: var(--color-status-succeeded);
}

.agent-state.is-disturbed .agent-state-mark {
  background: var(--color-status-failed);
}

.agent-state.is-intentionally-off .agent-state-mark {
  background: var(--color-status-stopped);
}

.agent-state.is-unknown .agent-state-mark {
  background: var(--color-status-unknown);
}

/* The state tree of the agent page (M6-6): nested disclosure lists, indented per level.
   There is no global [hidden] rule, so every rule that sets display names the shown state. */
.agent-tree {
  padding: var(--space-5);
  background: var(--color-surface);
  border: 0;
  border-radius: var(--radius-md);
}

.agent-tree-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  margin: 0 0 var(--space-3);
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

.agent-tree-worker-hint:not([hidden]) {
  display: inline;
  color: var(--color-danger-text);
}

.agent-tree-list,
.agent-tree-children {
  margin: 0;
  padding: 0;
  list-style: none;
}

.agent-tree-children:not([hidden]) {
  display: block;
  padding-left: var(--space-6);
}

.agent-tree-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  padding: var(--space-1) 0;
  border-bottom: var(--border-width) solid var(--color-border);
}

.agent-tree-toggle,
.agent-tree-toggle-spacer {
  flex: 0 0 auto;
  width: 1.5em;
  height: 1.5em;
}

.agent-tree-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--space-1);
  background: transparent;
  color: var(--color-text-muted);
  font: inherit;
  line-height: 1;
  cursor: pointer;
}

.agent-tree-toggle:hover {
  color: var(--color-text);
}

.agent-tree-toggle:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

.agent-tree-name {
  font-weight: 600;
  overflow-wrap: anywhere;
}

.agent-tree-detail,
.agent-tree-time,
.agent-tree-note,
.agent-tree-count {
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

.agent-tree-rebuild {
  font-size: var(--text-sm);
  font-style: italic;
}

.agent-tree-note.is-disrupted {
  color: var(--color-danger-text);
}

.agent-tree-rollup:not([hidden]) {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

/* The tree with the card of the latest disturbance (M9-8): the card below the tree on a
   narrow screen, beside it from 80rem on. Every axis of the row is set explicitly, and the
   slot names its shown state (there is no global [hidden] rule). */
.agent-overview {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-5);
}

.agent-overview > .agent-tree {
  flex: 1 1 auto;
  min-width: 0;
}

.agent-incident-slot:not([hidden]) {
  display: block;
}

@media (min-width: 80rem) {
  .agent-overview {
    flex-direction: row;
    align-items: flex-start;
  }

  .agent-incident-slot:not([hidden]) {
    flex: 0 0 26rem;
  }
}

.agent-incident-head,
.agent-incident-actions {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}

.agent-incident-name {
  margin: var(--space-3) 0 0;
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 700;
  overflow-wrap: anywhere;
}

.agent-incident-meta,
.agent-incident-time,
.agent-incident-empty {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  overflow-wrap: anywhere;
}

.agent-incident-state {
  margin: var(--space-2) 0 0;
  overflow-wrap: anywhere;
}

.agent-incident-changes {
  margin-top: var(--space-4);
}

.agent-incident-actions {
  margin-top: var(--space-4);
}

/* Windows high contrast (forced-colors): a surface or a pill without a border would vanish,
   so every one of them gets a hairline in the system text colour. */
@media (forced-colors: active) {
  .card,
  .table-scroll,
  .filter-form,
  .empty-state,
  .message,
  .log-pane,
  .dialog,
  .side-panel:not([hidden]),
  .gantt-body,
  .agent-tree,
  .button,
  .badge,
  .situation-count,
  .problem-card,
  .org-strip,
  .segmented-option span,
  .nav-list a,
  .menu-trigger,
  .menu-panel,
  .menu-item,
  .form-field input,
  .form-field select,
  .form-field textarea {
    border: var(--border-width) solid CanvasText;
  }

  .segmented-option input:checked + span {
    background: Highlight;
    color: HighlightText;
  }

  .nav-list a[aria-current="page"],
  .menu-trigger[aria-current="true"] {
    background: Highlight;
    color: HighlightText;
  }

  .gantt-dock-track {
    background: GrayText;
  }

  .gantt-dock-thumb {
    background: CanvasText;
  }

  .context-menu-item[aria-disabled="true"] {
    color: GrayText;
  }
}

/* Narrow viewports (A-45, M9-9): below 40rem (640 px at 16 px) the monitor shows its
   overview, the cards in one column and a narrow tree column in the whole gantt; the side
   panel stands at the bottom (gantt.js), so the switch of its side is hidden. Every target
   a finger reaches is at least 2.75rem (44 px) in both directions -- except the rows, bars
   and band segments of the opened gantt, which keep their size (A-43, A-10), and links in
   running text. The query is `NARROW_QUERY` of static/js/monitor/narrow.js. */
@media (width < 40rem) {
  .org-strips {
    display: block;
  }

  /* In the overview only the toolbar and the plot of the gantt hide: its message (an
     expired session, a failed update), the "last updated" stamp and the worker hint stay
     visible and are read out, since the overview shows the same data. */
  .org-strips[data-view="overview"] ~ .gantt > :not(.gantt-message, .gantt-toolbar),
  .org-strips[data-view="overview"] ~ .gantt .gantt-toolbar > :not(.gantt-refresh),
  .org-strips[data-view="overview"] ~ .gantt .gantt-window {
    display: none;
  }

  .org-strips[data-view="gantt"] .org-strips-title,
  .org-strips[data-view="gantt"] .org-strips-window,
  .org-strips[data-view="gantt"] .org-strip-list {
    display: none;
  }

  .problem-bar-title {
    display: block;
  }

  .problem-cards {
    grid-template-columns: minmax(0, 1fr);
  }

  .gantt-body {
    grid-template-columns: 10rem minmax(0, 1fr);
  }

  .side-panel-head .side-panel-side {
    display: none;
  }

  .button,
  .menu-trigger {
    min-height: 2.75rem;
  }

  .menu-account .menu-trigger {
    width: 2.75rem;
    height: 2.75rem;
  }

  .nav-list a,
  .menu-item,
  .segmented-option span {
    display: flex;
    align-items: center;
    min-height: 2.75rem;
  }

  /* As a flex row the space between glyph and word of a theme entry collapses; the gap
     puts it back. */
  .menu-item {
    gap: var(--space-1);
  }

  .checkbox-group label,
  .form-field input,
  .form-field select {
    min-height: 2.75rem;
  }

  /* The handle grows to 44 px for a finger while its visible line stays as thin as on a
     wide viewport: the background is clipped to the content box between the paddings.
     The vertical radius reaches over the padding, so the line keeps its rounded ends. */
  .side-panel-handle.is-bottom:not([hidden]) {
    block-size: 2.75rem;
    padding-block: calc((2.75rem - var(--panel-handle-size)) / 2);
    background-clip: content-box;
    border-radius: var(--space-1) / calc(var(--space-1) + (2.75rem - var(--panel-handle-size)) / 2);
  }

  /* The dock: the scrollbar takes a row of its own over the whole plot column, its target
     44 px tall with the visible line as thin as on a wide viewport; below it "-", the
     label and "+", the buttons 44 px square and the label between them, broken into two
     lines where needed. With a panel at the bottom at its largest, about 104 px of gantt
     stay visible above the 96 px of the dock. */
  .gantt-dock-track {
    flex-basis: 100%;
    block-size: 2.75rem;
    padding-block: calc((2.75rem - 0.5rem) / 2);
  }

  .gantt-dock-thumb {
    inset-block: calc((2.75rem - 0.5rem) / 2);
  }

  .gantt-dock-controls {
    flex: 1 1 100%;
  }

  .gantt-dock-level {
    flex: 1 1 0;
    min-inline-size: 0;
  }

  .gantt-dock-button {
    inline-size: 2.75rem;
    block-size: 2.75rem;
    min-block-size: 2.75rem;
  }

  /* The entries of the context menu of a run, placed as on a wide viewport. */
  .context-menu-item:not([hidden]) {
    min-block-size: 2.75rem;
  }
}
