.pipeline-page {
  --pipeline-ok: #5bc54a;
  --pipeline-progress: #f4b942;
  --pipeline-failed: #c5221f;
  --pipeline-unobserved: #a8a8ad;
}

.pipeline-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.8rem 1.6rem;
  margin: 0 0 2rem;
  color: var(--muted-text);
  font-size: 1.1rem;
}

.pipeline-legend > span {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

.pipeline-legend-mark {
  display: inline-block;
  width: 2.4rem;
  height: 1rem;
}

.pipeline-legend-mark--pending {
  border: 1px solid var(--pipeline-unobserved);
}

.pipeline-legend-mark--partial {
  border: 1px solid var(--text-color);
  background: linear-gradient(
    to right,
    var(--pipeline-progress) 0 55%,
    transparent 55%
  );
}

.pipeline-legend-mark--unobserved {
  border: 1px dotted var(--pipeline-unobserved);
  background: repeating-linear-gradient(
    135deg,
    var(--pipeline-unobserved) 0 1px,
    transparent 1px 4px
  );
}

/* A migration notice, not a fault: dotted and muted so it reads as context
   rather than competing with the ribbon's live staleness warning. */
.pipeline-notice {
  margin: 0 0 1.6rem;
  padding: 0.8rem 1.2rem;
  border: 1px dotted var(--border-muted-color);
  color: var(--muted-text);
  font-size: 1.2rem;
}

.pipeline-ribbon:not([hidden]),
.pipeline-banner {
  margin-bottom: 1.5rem;
  padding: 0.8rem 1.2rem;
  border: 1px solid var(--pipeline-progress);
}

.pipeline-banner--error {
  border-color: var(--pipeline-failed);
}

.pipeline-advisories {
  margin: 1.5rem 0;
}

.pipeline-advisories p {
  margin: 0.4rem 0;
}

.pipeline-group {
  border-top: 1px solid var(--border-muted-color);
}

.pipeline-group:first-child {
  border-top: 0;
}

.pipeline-group > h3 {
  margin: 1.6rem 0 0.4rem;
  font-size: 1.5rem;
  scroll-margin-top: 2rem;
}

.pipeline-row {
  display: grid;
  grid-template-columns: 19rem minmax(0, 1fr) 16rem;
  align-items: start;
  gap: 2rem;
  padding: 1.6rem 0;
}

.pipeline-source-meta {
  overflow-wrap: anywhere;
  color: var(--muted-text);
  font-size: 1.2rem;
}

.pipeline-row-advisory {
  color: var(--pill-degraded-bg);
  font-weight: 700;
}

.pipeline-row-body {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  min-width: 0;
}

/* The field: a band per measurement, a square per run, time to the right.
   Clicking it cycles which dimension owns the rows. */

.pipeline-viz {
  flex: 1;
  min-width: 0;
}

.pipeline-viz[role="group"] {
  cursor: pointer;
}

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

.pipeline-field {
  display: flex;
  flex-direction: column;
  gap: var(--band-gap);
  min-width: 0;
}

.pipeline-rows {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--band-gap);
  justify-content: center;
  min-height: 0;
}

.pipeline-rows > .pipeline-band {
  flex: 1;
  max-height: calc(3 * var(--sq));
}

.pipeline-rows .pipeline-cells,
.pipeline-rows .pipeline-clump {
  align-self: stretch;
  height: 100%;
}

.pipeline-rows .pipeline-cell {
  height: auto;
}

.pipeline-field[data-compact] .pipeline-rows {
  flex: none;
}

.pipeline-field[data-compact] .pipeline-rows > .pipeline-band {
  flex: none;
  height: var(--sq);
}

.pipeline-field[data-compact] {
  gap: var(--lane-gap);
}

.pipeline-facet-lane {
  display: flex;
  flex-direction: column;
  gap: var(--band-gap);
}

.pipeline-band {
  display: grid;
  grid-template-columns: var(--band-gutter) minmax(0, max-content);
  align-items: center;
  gap: 0 0.6rem;
}

.pipeline-band-label {
  overflow: hidden;
  color: var(--muted-text);
  font-size: 1rem;
  line-height: 1;
  text-align: right;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pipeline-band[data-kind="lead"] .pipeline-band-label {
  color: var(--text-color);
}

.pipeline-cells {
  display: flex;
  gap: var(--run-gap);
}

/* a clumped band needs daylight between runs, since the squares within one run
   sit a single pixel apart */
.pipeline-cells[data-clumped] {
  gap: var(--clumped-run-gap);
}

.pipeline-clump {
  display: flex;
  flex: none;
  gap: var(--clump-gap);
}

.pipeline-cell {
  position: relative;
  flex: none;
  /* the lead dimension is proportional, so whichever axis it owns is set
     per cell; the other axis stays one cell */
  width: var(--cell-w, var(--sq));
  height: var(--cell-h, var(--sq));
  border: 1px solid var(--text-color);
}

.pipeline-cell-fill {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  height: var(--fill, 0%);
}

/* where lead time owns the horizontal axis, a cell fills from its left edge
   rather than its floor, so progress reads along the same axis as the leads */
.pipeline-field[data-fill="side"] .pipeline-cell-fill {
  top: 0;
  right: auto;
  width: var(--fill, 0%);
  height: auto;
}

.pipeline-cell.g-complete .pipeline-cell-fill {
  background: var(--pipeline-ok);
}

.pipeline-cell.g-complete[data-timing="delayed"] .pipeline-cell-fill,
.pipeline-cell.g-in_flight .pipeline-cell-fill {
  background: var(--pipeline-progress);
}

.pipeline-cell.g-in_flight[data-timing="on_time"] .pipeline-cell-fill {
  background: var(--pipeline-ok);
}

.pipeline-cell.g-failed .pipeline-cell-fill {
  height: 100%;
  background: var(--pipeline-failed);
}

.pipeline-field[data-fill="side"] .pipeline-cell.g-failed .pipeline-cell-fill {
  width: 100%;
}

/* an empty outline is "expected, nothing yet"; hatching is "no evidence either
   way" — the two must not read the same, since one is a measurement and the
   other is the absence of one */
.pipeline-cell.g-pending {
  border: 1px solid var(--pipeline-unobserved);
}

.pipeline-cell.g-pending[data-timing="delayed"] {
  border-color: var(--pipeline-progress);
}

.pipeline-cell.g-unobserved {
  border: 1px dotted var(--pipeline-unobserved);
  background: repeating-linear-gradient(
    135deg,
    var(--pipeline-unobserved) 0 1px,
    transparent 1px 4px
  );
}

.pipeline-cell.g-pending .pipeline-cell-fill,
.pipeline-cell.g-unobserved .pipeline-cell-fill {
  display: none;
}

/* The facet-row field: a run per block, lead groups as columns inside it */

.pipeline-column-label {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--cell-w, var(--sq));
  height: var(--label-h);
  font-size: 0.9rem;
  line-height: var(--label-h);
  text-align: center;
}

.pipeline-band--head {
  margin-bottom: 0.2rem;
}

.pipeline-band--foot {
  margin-top: 0.3rem;
}

/* one per run block, exactly as wide as the block it names */
.pipeline-run-label,
.pipeline-run-date {
  /* never shrink, or a label drifts out from under the blocks it names */
  flex: none;
  /* exactly one run block wide, so both tiers centre on the same axis */
  width: var(--run-width);
  height: var(--label-h);
  font-size: 0.9rem;
  line-height: var(--label-h);
  white-space: nowrap;
  text-align: center;
}

.pipeline-run-label {
  color: var(--muted-text);
}

.pipeline-stats {
  color: var(--muted-text);
  font-size: 1.2rem;
  text-align: right;
}

.pipeline-stats strong,
.pipeline-stats span {
  display: block;
}

.pipeline-stats strong {
  color: var(--text-color);
}

.pipeline-stats [data-timing="on_time"] {
  color: var(--pipeline-ok);
}

.pipeline-stats [data-timing="delayed"] {
  color: var(--pipeline-progress);
}

.pipeline-details-button {
  margin-top: 0.8rem;
  padding: 0;
  border: 0;
  color: var(--link-color);
  background: none;
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}

.pipeline-row-details {
  grid-column: 1 / -1;
  /* each table scrolls in its own container, so the row must not stretch the
     grid to fit one */
  min-width: 0;
}

.pipeline-row-details .table-container + .table-container {
  margin-top: 1.2rem;
}

.pipeline-row-details table {
  width: 100%;
  border-collapse: collapse;
  font-size: 1.2rem;
}

/* nine nowrap measurement columns need more room than the content column has;
   the facet table's five fit, so it is sized by its content */
.pipeline-row-details table:not(.pipeline-facets) {
  min-width: 90rem;
}

.pipeline-row-details th,
.pipeline-row-details td {
  padding: 0.4rem 1.2rem;
  border: 1px dotted var(--border-muted-color);
  text-align: right;
  white-space: nowrap;
}

.pipeline-row-details th:first-child,
.pipeline-row-details td:first-child {
  text-align: left;
}

/* "horizon" spans both header rows, so the sub-header row starts in column 2 —
   its first cell is a status column and belongs right with the rest */
.pipeline-row-details thead:has(th[rowspan]) tr + tr th:first-child {
  text-align: right;
}

/* a status wears the color its square wears on the grid: green once a run has
   landed on time, amber while it is running or running late, red when it failed.
   pending and unobserved stay uncolored, as their empty squares do. */
.pipeline-row-details [data-status="complete"],
.pipeline-row-details [data-status="in_flight"][data-timing="on_time"] {
  color: var(--pipeline-ok);
}

.pipeline-row-details [data-status="in_flight"],
.pipeline-row-details [data-timing="delayed"] {
  color: var(--pipeline-progress);
}

.pipeline-row-details [data-status="failed"] {
  color: var(--pipeline-failed);
}

/* the bar takes the row's leftover width, and the number beside it is always
   three characters wide, so every bar starts and ends in the same place */
.pipeline-facets td:last-child {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
}

.pipeline-facets progress {
  flex: 1;
  accent-color: var(--pipeline-ok);
  vertical-align: middle;
}

.pipeline-facet-pct {
  flex: none;
  font-variant-numeric: tabular-nums;
}

.pipeline-facet-num {
  display: inline-block;
  width: 3ch;
  text-align: right;
}

.pipeline-time-local .pipeline-time-utc,
body:not(.pipeline-time-local) .pipeline-time-local-only {
  display: none;
}

@media (max-width: 900px) {
  .pipeline-row {
    grid-template-columns: 1fr;
  }

  .pipeline-stats {
    text-align: left;
  }
}
