/* --- CI run page --------------------------------------------------- */
.ci-run { margin: 1.5rem 0; }
.ci-run-title { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 1rem; }
.ci-run-facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.4rem 1.25rem;
  margin: 0 0 1.5rem;
}
.ci-run-facts dt { color: var(--fg-muted); }
.ci-run-facts dd { margin: 0; }
/* The two lists are one block visually — identity, then chronology —
   so only the last one carries the gap to whatever follows. */
.ci-run-facts-head { margin-bottom: 0.4rem; }
/* The run's wall clock, in the headline rather than the list, because
   it is the second thing a reader wants after pass/fail and cannot be
   derived from the step times below (steps run in sequence). */
.ci-run-elapsed {
  margin-left: auto;
  color: var(--fg-muted);
  /* Tabular figures so a duration ticking once a second does not make
     the headline jitter as digit widths change. */
  font-variant-numeric: tabular-nums;
}
/* A pending run has no server-side elapsed until ci_run.js fills it in,
   and an empty box must not open a gap in the flex row. */
.ci-run-elapsed:empty { display: none; }

/* The state dot breathes while the run can still change, and stops dead
   the moment it cannot. This is the page's only always-visible sign of
   life: the poll is every few seconds and states are MINUTES apart, so
   a working page otherwise sits perfectly still and reads as hung. It
   is driven by data-state, which ci_run.js rewrites on every verdict,
   so the animation ends without anything else having to remember to
   stop it. */
@keyframes ci-run-breathe {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}
.ci-run[data-state="queued"] .ci-run-title .ci-dot,
.ci-run[data-state="running"] .ci-run-title .ci-dot {
  animation: ci-run-breathe 1.6s ease-in-out infinite;
}
/* Honouring reduced-motion costs nothing here because the dot is not
   the only sign of life: the headline duration ticks once a second, and
   that is content rather than decoration. A reader who has asked for
   less movement still sees the run getting longer. */
@media (prefers-reduced-motion: reduce) {
  .ci-run[data-state="queued"] .ci-run-title .ci-dot,
  .ci-run[data-state="running"] .ci-run-title .ci-dot {
    animation: none;
  }
}
.ci-run-committer { color: var(--fg-muted); margin-left: 0.5rem; }
/* discussion #9: the re-run button — its own form, so it sits apart
   from the facts/log it acts on rather than crowding either. */
.ci-rerun-form { margin: 1.25rem 0 0; }
.ci-rerun-button:disabled { cursor: not-allowed; opacity: 0.6; }
.ci-steps-note { margin: 0 0 0.5rem; font-size: 0.9em; }
/* Same treatment as the collapsed .ci-log pre this page expands on, so
   a log reads identically wherever you meet it. */
.ci-run-log {
  overflow-x: auto;
  background: var(--bg-inset);
  border: 1px solid var(--border);
  border-radius: var(--radius-ctl);
  padding: 0.75rem;
}
.ci-run-log-empty { margin: 0.5rem 0; }

/* --- the run's workflow -------------------------------------------- */
ol.ci-steps { list-style: none; margin: 0 0 1.5rem; padding: 0; }
.ci-step { border: 1px solid var(--border); border-radius: var(--radius-ctl); margin-bottom: 0.4rem; }
.ci-step-head {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.5rem 0.75rem;
  cursor: pointer;
  list-style: none;
}
.ci-step-head::-webkit-details-marker { display: none; }
.ci-step-head:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.ci-step-name { flex: 1 1 auto; font-weight: 500; }
.ci-step-state { flex: 0 0 auto; font-size: 0.85rem; }
.ci-step-elapsed { flex: 0 0 auto; color: var(--fg-muted); font-size: 0.85rem; }
/* A skipped step never ran; it should not look like a result. */
.ci-step-skipped .ci-step-name { color: var(--fg-muted); }
.ci-step-log {
  overflow-x: auto;
  margin: 0;
  padding: 0.75rem;
  border-top: 1px solid var(--border);
  background: var(--bg-inset);
}
.ci-step-log-empty { margin: 0; padding: 0.75rem; border-top: 1px solid var(--border); }
.ci-whole-log summary { cursor: pointer; color: var(--fg-muted); font-size: 0.9rem; }

/* A sharded step's children. Same row language as a step — dot, name,
   verdict, time — one level in, so the eye reads them as parts of the
   step above rather than as more steps. The count rides on the step's
   own collapsed summary ("2 of 10 shards failed"), which is the whole
   point: a reader should never open eight green shards to find the two
   red ones. */
.ci-step-shards { flex: 0 0 auto; font-size: 0.85rem; }
ol.ci-shards {
  list-style: none;
  margin: 0;
  padding: 0.4rem 0.5rem 0.5rem 1.5rem;
  border-top: 1px solid var(--border);
  background: var(--bg-inset);
}
.ci-shard {
  border: 1px solid var(--border);
  border-radius: var(--radius-ctl);
  margin-bottom: 0.3rem;
  background: var(--bg);
}
.ci-shard:last-child { margin-bottom: 0; }
.ci-shard-head { padding: 0.35rem 0.6rem; }
/* Tabular figures so "shard 2 of 10" and "shard 10 of 10" line up down
   the column rather than jittering. */
.ci-shard-name { font-weight: 400; font-variant-numeric: tabular-nums; }
/* The one row anybody opened the page for. */
.ci-shard-failure { border-color: var(--danger); }
.ci-shard-cancelled .ci-shard-name,
.ci-shard-skipped .ci-shard-name { color: var(--fg-muted); }

/* --- branch CI tab ------------------------------------------------- */
ul.ci-runs { list-style: none; padding: 0; margin: 0; }
.ci-run-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--border);
}
.ci-run-state { flex: 0 0 12rem; }
.ci-run-row .sha-link { flex: 0 0 auto; }
.ci-run-runner-cell { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.ci-run-elapsed { flex: 0 0 auto; color: var(--fg-muted); font-size: 0.85rem; }
/* Queue wait, shown only when there was one. Deliberately quieter than
   the run's own duration beside it: the wait is the fleet's fact, not
   the commit's, and a reader scanning for "how long did my build take"
   must not read this as part of that answer. */
.ci-run-waited {
  flex: 0 0 auto;
  color: var(--fg-muted);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  opacity: 0.8;
}
.ci-run-row .when { flex: 0 0 auto; color: var(--fg-muted); font-size: 0.85rem; }
.merge-ci-more { margin-top: 0.5rem; font-size: 0.85rem; }
/* The repo CI tab adds a branch column the branch tab does not need. */
.ci-run-branch { flex: 0 0 10rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 40rem) {
  .ci-run-row { flex-wrap: wrap; gap: 0.4rem 0.75rem; }
  .ci-run-state { flex-basis: auto; }
  .ci-run-branch { flex-basis: auto; }
  .ci-run-runner-cell { flex-basis: 100%; }
}
.ci-run-live { margin-top: 1rem; font-size: 0.9rem; }

/* Jobs that died before they could run. Set apart from the run list
   above on purpose: these are not builds of a commit, and looking like
   one is exactly how a dead job read as a failing branch. */
.ci-dead-jobs { margin-top: 2rem; padding-top: 1rem; border-top: 1px solid var(--border); }
.ci-dead-jobs > p { margin: 0 0 0.75rem; font-size: 0.9rem; }
ul.ci-dead-job-list { list-style: none; padding: 0; margin: 0; }
.ci-dead-job {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--border);
}
.ci-dead-job .when { flex: 0 0 auto; color: var(--fg-muted); font-size: 0.85rem; }
@media (max-width: 40rem) {
  .ci-dead-job { flex-wrap: wrap; gap: 0.4rem 0.75rem; }
}

/* --- running now, and the pipeline runs list ------------------------ */
/* "Running now" spans both results tables: it is the only place the
   page says two unrelated pipelines are executing at the same moment.
   Boxed rather than merely listed, because it is a statement about NOW
   and everything around it is history. */
.ci-inflight {
  margin: 1rem 0 1.5rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-inset);
}
.ci-inflight > h3 { margin: 0 0 0.5rem; font-size: 0.95rem; }
ul.ci-inflight-list { list-style: none; padding: 0; margin: 0; }
.ci-inflight-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.35rem 0;
}
.ci-inflight-row .when { flex: 0 0 auto; color: var(--fg-muted); font-size: 0.85rem; }

.ci-pipelines { margin-top: 2rem; padding-top: 1rem; border-top: 1px solid var(--border); }
.ci-pipelines > p { margin: 0 0 0.75rem; font-size: 0.9rem; }
ul.ci-pipeline-runs { list-style: none; padding: 0; margin: 0; }
.ci-pipeline-run {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--border);
}
.ci-pipeline-name { flex: 0 0 9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ci-pipeline-trigger { flex: 0 0 11rem; font-size: 0.85rem; }
.ci-pipeline-commit { flex: 1 1 auto; font-size: 0.85rem; color: var(--fg-muted); }
.ci-pipeline-run .when { flex: 0 0 auto; color: var(--fg-muted); font-size: 0.85rem; }
/* Colour is never the only signal (the same rule .ci-dot follows): the
   dot carries a title, the state is spelled out in words beside it, and
   this left edge is a third, redundant marker for a run that did not
   deliver. */
.ci-pipeline-undelivered { border-left: 3px solid var(--danger); padding-left: 0.5rem; }
.ci-pipelines-unknown { margin: 0 0 0.5rem; }
@media (max-width: 40rem) {
  .ci-pipeline-run, .ci-inflight-row { flex-wrap: wrap; gap: 0.4rem 0.75rem; }
  .ci-pipeline-name, .ci-pipeline-trigger { flex-basis: auto; }
}

ul.commits li.commit > .ci-section { flex-basis: 100%; }
.ci-section { margin: 1.25rem 0; }
.ci-summary { display: flex; align-items: center; gap: 0.5rem; font-weight: 500; }
.ci-summary .ci-dot { width: 0.7rem; height: 0.7rem; }
.ci-log { margin-top: 0.5rem; }
.ci-log summary { cursor: pointer; color: var(--fg-muted); font-size: 0.9rem; }
.ci-log pre {
  margin: 0.5rem 0 0;
  background: var(--bg-inset);
  border: 1px solid var(--border);
  border-radius: var(--radius-ctl);
  padding: 0.75rem;
  font-size: 0.85rem;
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 24rem;
  overflow-y: auto;
  overflow-x: auto;
}
.commit .sha {
  color: var(--accent);
  background: var(--code-bg);
  padding: 0 0.35rem;
  border-radius: 4px;
  font-size: 0.8rem;
}
.commit .msg { flex: 1; min-width: 10rem; font-weight: 500; }
.commit .author, .commit .when { color: var(--fg-muted); font-size: 0.85rem; }
.commit .when { font-variant-numeric: tabular-nums; }
/* Phones: the single wrapping row clipped the author at the right
   edge — instead, message first at full width, then one quiet
   sha · author · time meta row. */
@media (max-width: 720px) {
  ul.commits li.commit { gap: 0.3rem 0.6rem; }
  .commit .msg { order: -1; flex: 1 1 100%; min-width: 0; }
}

/* --- Shared CI (/settings/ci) --------------------------------------
   Each action is its own <form> because each is a distinct CSRF-checked
   POST — which makes them block-level, so Accept and Decline stacked
   under the namespace instead of sitting beside it. Laying the row out
   as a flex line puts the decision and its two answers on one line,
   where they read as a pair rather than a sequence. */
.ci-requests, .ci-offerings, .ci-arrangements { list-style: none; padding: 0; }
.ci-requests li, .ci-offerings li, .ci-arrangements li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--border);
}
.ci-requests form, .ci-offerings form, .ci-arrangements form { margin: 0; }
/* The agent list under an offering is a second line, not a third
   column — it is detail about the row above it. Scoped to the div, so
   the note SPAN still sits beside the name where it belongs. */
.ci-offerings li > div.muted { flex-basis: 100%; }
/* Push the actions to the end so a row of any length still lines its
   buttons up with every other row's. */
.ci-requests li form:first-of-type,
.ci-offerings li form:first-of-type,
.ci-arrangements li form:first-of-type { margin-left: auto; }

.ci-offer-form { margin: 1.5rem 0; display: grid; gap: 0.75rem; max-width: 40rem; }
.ci-offer-form label { display: inline-flex; align-items: center; gap: 0.4rem; }
.ci-offer-form fieldset { border: 1px solid var(--border); border-radius: 6px; padding: 0.75rem 1rem; }
.ci-offer-form fieldset label { margin-right: 1rem; }
.ci-offer-form button { justify-self: start; }
.warning-note { margin: 0.5rem 0 1rem; }
/* Namespace switcher in the header (2026-08-23). MOBILE ONLY: above
   720px the home rail already lists your namespaces, and showing them
   in two places on one page is worse than one copy in the right place.
   Below it the rail stacks far under the feed, which is the whole
   reason this exists.

   The switcher wears a real Lucide icon (users), per DESIGN.md's Drawn
   Mark Rule — only the seal and the "+" stay CSS-drawn. That needed the
   icon sprite, which used to be included per-page by the repo/branch
   templates and so was absent from home.html; it now ships from
   "siteHeader" itself, so every page carrying the header can use an
   icon. */
.ns-menu { display: none; }
.ns-menu .menu-panel { max-height: 70vh; overflow-y: auto; }

@media (max-width: 720px) {
  .ns-menu { display: block; }
  /* The list moved to the header here — don't repeat it below. */
  .home-rail .rail-namespaces { display: none; }
}
