/* static/workflow/workflow-ui.css - NEW FILE (2026-10-19, the UI stage, U1).
   Destination: static/workflow/workflow-ui.css

   =========================================================================
   EVERY COLOUR IS A brand.css TOKEN AND NOT ONE HEX VALUE
   =========================================================================
   brand.css declares the palette on `:root` and redeclares it under
   `@media (prefers-color-scheme: dark)`, so a rule written against the tokens
   gets dark mode for nothing and a rule written against a hex value does not.
   This file introduces NO new token: the status colours reuse `--accent`,
   `--note` and `--error`, which brand.css has already measured for AA against
   `--surface`.

   =========================================================================
   EVERYTHING IS SCOPED UNDER #workflow
   =========================================================================
   capability-ui.css's rule, for its reason: this is a stylesheet loaded on
   every page view, and an unscoped `table` or `.chip` here would reach the
   chat transcript, the auth cards and the tool picker. The only exception is
   `#workflow-open`, the header button, which index.html owns and this file
   only sizes.
   ========================================================================= */

/* THE OVERLAY. Same shape as #capability: it sits over the page rather than
   pushing the composer around, because a run list is a destination and not a
   readout. `hidden` is index.html's; this adds nothing that would override
   it, which is why there is no `display` on the base rule. */
#workflow {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: var(--bg);
  overflow-y: auto;
  padding: 1.25rem;
}

#workflow[hidden] { display: none; }

#workflow .wf-head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}

#workflow .wf-title {
  font-size: 1.0625rem;
  font-weight: 600;
  color: var(--ink);
  margin: 0;
}

/* THE SCOPE SWITCH. A segmented pair rather than a checkbox, because the two
   states are not on/off - "my runs" and "all runs" are different questions and
   a cleared checkbox would not say which one it is showing. */
#workflow .wf-scope {
  display: inline-flex;
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
}

#workflow .wf-scope button {
  appearance: none;
  border: 0;
  background: var(--surface);
  color: var(--muted);
  font: inherit;
  font-size: 0.8125rem;
  padding: 0.3rem 0.7rem;
  cursor: pointer;
}

#workflow .wf-scope button[aria-pressed="true"] {
  background: var(--accent);
  color: var(--surface);
  font-weight: 600;
}

#workflow .wf-scope button[disabled] {
  cursor: not-allowed;
  opacity: 0.55;
}

#workflow .wf-spacer { flex: 1 1 auto; }

/* A LINE THE SERVER WROTE, shown verbatim. Used for `in_flight_scope` and for
   a refusal's `detail`. Never a sentence this file invented about either. */
#workflow .wf-note {
  font-size: 0.8125rem;
  color: var(--note);
  margin: 0 0 0.75rem;
}

#workflow .wf-error {
  font-size: 0.8125rem;
  color: var(--error);
  margin: 0 0 0.75rem;
}

#workflow .wf-empty {
  font-size: 0.875rem;
  color: var(--muted);
  padding: 1.5rem 0;
}

#workflow .wf-wrap {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  overflow-x: auto;
}

#workflow table {
  border-collapse: collapse;
  width: 100%;
  font-size: 0.8125rem;
}

#workflow th {
  text-align: left;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
  padding: 0.5rem 0.7rem;
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  background: var(--surface);
}

#workflow td {
  padding: 0.45rem 0.7rem;
  border-bottom: 1px solid var(--line);
  color: var(--ink);
  vertical-align: top;
}

#workflow tbody tr:last-child td { border-bottom: 0; }

/* A RUN ID IS A HEX STRING AND READS AS ONE. Monospace, and the cell carries
   the full id in a title attribute because the column shows the first eight
   characters - enough to tell 27 runs apart, short enough not to dominate. */
#workflow .wf-id {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  white-space: nowrap;
}

/* NUMBERS RIGHT-ALIGNED SO THEY COMPARE DOWN THE COLUMN, which is the only
   reason anyone reads a cost or a duration column at all. */
#workflow .wf-num {
  text-align: right;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* AN ABSENT FIGURE IS NOT A ZERO - §7.18 and §17.3. A tokenless run carries
   `tokens: {}` and no `total_cost_usd` at all, and this is what that renders
   as: a dash in --muted, visibly different from a measured 0. */
#workflow .wf-na {
  color: var(--muted);
  text-align: right;
}

#workflow .wf-req {
  max-width: 28rem;
  color: var(--muted);
}

#workflow .wf-owner {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.75rem;
  color: var(--muted);
  white-space: nowrap;
}

/* STATUS CHIPS. Three states the engine actually writes - `ok`, `degraded`,
   `failed` - plus a fallback for anything else, because a status this file
   does not recognise must still be legible rather than invisible. */
#workflow .wf-chip {
  display: inline-block;
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  padding: 0.1rem 0.4rem;
  border-radius: 999px;
  border: 1px solid currentColor;
  white-space: nowrap;
}

#workflow .wf-chip-ok { color: var(--accent); }
#workflow .wf-chip-degraded { color: var(--note); }
#workflow .wf-chip-failed { color: var(--error); }
#workflow .wf-chip-other { color: var(--muted); }

/* AN IN-FLIGHT ROW IS NOT A RECORD YET. It has no totals and no finish time,
   so it is marked rather than padded with blanks that look like absent data. */
#workflow tr.wf-live td { background: var(--code-bg); }

#workflow .wf-live-chip {
  display: inline-block;
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  padding: 0.1rem 0.4rem;
  border-radius: 999px;
  color: var(--note);
  border: 1px dashed currentColor;
  white-space: nowrap;
}

#workflow-open { white-space: nowrap; }

/* NARROW SCREENS: the request column is the one that can go, because the id,
   the status and the figures are what the table is for. */
@media (max-width: 720px) {
  #workflow .wf-req { display: none; }
  #workflow { padding: 0.75rem; }
}
/* ===========================================================================
   U3 - ONE RUN'S RECORD (2026-10-19)
   =========================================================================== */

/* THE RUN ID IN THE LIST IS A BUTTON, not a link: this app keeps its token in
   a JS variable and never a cookie, so a navigation would send no
   Authorization header. It is styled to read as a link anyway, because that is
   what it does. */
#workflow .wf-open {
  appearance: none;
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  color: var(--accent);
  cursor: pointer;
  text-decoration: underline;
}

#workflow .wf-summary {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.5rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 0.7rem 0.9rem;
  margin-bottom: 0.75rem;
}

#workflow .wf-kv {
  display: flex;
  gap: 0.4rem;
  align-items: baseline;
  font-size: 0.8125rem;
  margin-bottom: 0.2rem;
}

#workflow .wf-kv-k {
  color: var(--muted);
  font-weight: 600;
  white-space: nowrap;
}

#workflow .wf-kv-v { color: var(--ink); }

#workflow .wf-switches {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 0.7rem 0.9rem;
  margin-top: 0.75rem;
}

/* THE ORCHESTRATOR'S OWN ROW. Marked because it is not a domain node and is
   the reason nodes_run is N+1 on the model arm - §5f. */
#workflow tr.wf-parent td {
  background: var(--code-bg);
  font-style: italic;
}

/* "NOT MEASURED" IS NOT A NUMBER AND MUST NOT LOOK LIKE ONE. It is the third
   state - distinct from a measured value and from a blank, which means "not
   applicable". §7.19 and the module header carry the argument. Left-aligned
   on purpose: right-aligning it would put it in the column of figures it is
   explicitly not one of. */
#workflow .wf-unmeasured {
  color: var(--note);
  font-size: 0.6875rem;
  font-style: italic;
  white-space: nowrap;
  text-align: left;
  cursor: help;
}

/* A VIOLATION IS THE VALIDATOR'S OWN SENTENCE, spanning the table rather than
   squeezed into a cell, because it is prose and sometimes long. */
#workflow tr.wf-violations td {
  background: var(--surface);
  padding-top: 0.2rem;
}

#workflow .wf-violation {
  font-size: 0.75rem;
  color: var(--error);
  padding: 0.15rem 0 0.15rem 1rem;
  border-left: 2px solid var(--error);
  margin-bottom: 0.2rem;
}

/* ===========================================================================
   U4 - THE LIVE STREAM (2026-10-19)
   =========================================================================== */

/* A WAVE BOUNDARY IS A ROW, not a heading, so node rows stay in one table and
   one scroll. §7.12: they arrive as each node finishes, so a wave's rows are
   not contiguous in time even though they are contiguous here. */
#workflow tr.wf-waverow td {
  background: var(--code-bg);
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

#workflow .wf-live-foot {
  margin-top: 0.75rem;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}

/* ===========================================================================
   U5 - THE DAG REPORT (2026-10-19)
   =========================================================================== */

/* The counts table is one row of seven numbers, so its headers wrap rather
   than forcing a horizontal scroll on a narrow screen. */
#workflow .wf-wrap th.wf-num {
  white-space: normal;
  max-width: 7rem;
}

/* A chip row inside the summary block needs a little breathing room from the
   key/value pairs that follow it. */
#workflow .wf-summary .wf-chip,
#workflow .wf-summary .wf-live-chip {
  margin-right: 0.35rem;
}

/* ===========================================================================
   U6 - FIRING A RUN (2026-10-19)
   =========================================================================== */

#workflow .wf-form {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 0.9rem;
  margin-bottom: 0.75rem;
  display: grid;
  gap: 0.6rem;
}

#workflow .wf-field {
  display: grid;
  gap: 0.2rem;
}

#workflow .wf-field label {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--muted);
}

#workflow .wf-field input,
#workflow .wf-field select,
#workflow .wf-field textarea {
  font: inherit;
  font-size: 0.875rem;
  color: var(--ink);
  background: var(--field);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 0.4rem 0.55rem;
  width: 100%;
  box-sizing: border-box;
}

#workflow .wf-field textarea { resize: vertical; }

#workflow .wf-detail { margin-bottom: 0.75rem; }

/* THE BUTTONS THAT SPEND. Marked so they do not read as one more ghost
   control beside Refresh and Close - firing a run and publishing a corpus
   (U8) are the only two affordances in the whole panel that cost money, and
   they carry the same mark for that reason. */
#workflow .wf-fire {
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}

#workflow .wf-fire[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

/* ===========================================================================
   U7 - THE PLAN (2026-10-19)

   The left gutter under a slot line is 8rem, which is the time column's width
   plus its gap: a rationale and its cite chips hang under the TITLE rather
   than under the clock, so a day reads as a column of titles with their
   reasoning indented beneath. Below 720px the gutter collapses, because an
   8rem indent on a phone leaves no line length at all.
   =========================================================================== */

#workflow .wf-plan-summary {
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: 8px;
  padding: 0.6rem 0.8rem;
  margin: 0 0 0.75rem;
}

#workflow .wf-listline {
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--ink);
  padding: 0.1rem 0 0.1rem 0.9rem;
}

#workflow .wf-guidance,
#workflow .wf-conflicts {
  display: grid;
  gap: 0.3rem;
  margin-bottom: 0.6rem;
}

#workflow .wf-sub {
  border-left: 2px solid var(--line);
  padding-left: 0.6rem;
  margin: 0.25rem 0 0.25rem 0.9rem;
}

#workflow .wf-day {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  margin-bottom: 0.6rem;
  overflow: hidden;
}

#workflow .wf-day-head {
  background: var(--code-bg);
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 0.35rem 0.8rem;
}

#workflow .wf-slot {
  padding: 0.5rem 0.8rem;
  border-top: 1px solid var(--line);
}

#workflow .wf-day .wf-slot:first-of-type { border-top: 0; }

#workflow .wf-slot-head {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  flex-wrap: wrap;
}

/* THE TIME COLUMN IS FIXED-WIDTH AND TABULAR so a day's slots line up down
   the left edge, which is the only reason to print a clock time at all. A
   slot whose start_time is null falls back to the slot NAME - what the fan-in
   did commit to - and that is why the width is set here rather than left to
   the content. */
#workflow .wf-slot-when {
  flex: 0 0 auto;
  width: 7.5rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.75rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

#workflow .wf-slot-title {
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--ink);
}

#workflow .wf-tag {
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.05rem 0.35rem;
  white-space: nowrap;
}

#workflow .wf-rationale {
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--muted);
  margin: 0.2rem 0 0 8rem;
}

#workflow .wf-cites {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  flex-wrap: wrap;
  margin-top: 0.3rem;
}

#workflow .wf-slot > .wf-cites { margin-left: 8rem; }

/* A CITATION IS AN ID AND READS AS ONE. The tooltip resolves it to the
   upstream record's label; the id itself is what the postcondition checks and
   what a reader comparing two runs would quote. `cursor: help` is the honest
   signal that there is more here and that it is not a link - this app keeps
   its token in a JS variable, so nothing in this panel navigates. */
#workflow .wf-cite {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.6875rem;
  color: var(--accent);
  border: 1px solid currentColor;
  border-radius: 4px;
  padding: 0 0.25rem;
  cursor: help;
  white-space: nowrap;
}

/* AN UNRESOLVED CITE IS SHOWN, NOT DROPPED. cites_upstream_records would have
   refused the node over it, so one here means the record predates that check
   or an ancestor's records were not persisted. */
#workflow .wf-cite-missing {
  color: var(--error);
  text-decoration: line-through;
}

#workflow .wf-upstream {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 0.5rem 0.8rem;
  margin-bottom: 0.5rem;
}

#workflow .wf-upstream summary {
  cursor: pointer;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--ink);
}

#workflow .wf-upstream[open] summary { margin-bottom: 0.5rem; }

#workflow .wf-recs {
  display: grid;
  gap: 0.4rem;
  margin-top: 0.5rem;
}

#workflow .wf-rec {
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 0.4rem 0.6rem;
}

#workflow .wf-rec-head {
  display: flex;
  gap: 0.4rem;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 0.25rem;
}

/* THE ONE AFFORDANCE THAT LEADS TO THE PRODUCT rather than to the
   measurement, so it is tinted like `wf-fire` - without that one's weight,
   because unlike it this one costs nothing. */
#workflow .wf-plan-btn,
#workflow .wf-plan-link {
  border-color: var(--accent);
  color: var(--accent);
}

@media (max-width: 720px) {
  #workflow .wf-rationale,
  #workflow .wf-slot > .wf-cites { margin-left: 0; }
  #workflow .wf-slot-when { width: auto; }
}

/* ===========================================================================
   THE LIVE RUN'S "WORKING" INDICATOR - NEW 2026-10-19
   ===========================================================================
   An animated ellipsis beside the `live` chip, shown while frames are still
   arriving and hidden the moment the stream ends.

   CSS RATHER THAN A setInterval, DELIBERATELY. A JS timer on a page that can
   be closed mid-run is a timer somebody has to remember to clear - the same
   class of leak as an abandoned stream reader, which `stopStream()` already
   exists to avoid. An animation on an element dies with the element.

   THE WIDTH IS ANIMATED, NOT `content`. Animating `content` in keyframes is
   not reliable across engines; clipping a fixed "..." is ordinary CSS.

   `ch` AND A MONOSPACE DOT, FOR A REASON WORTH KEEPING. 1ch is exactly one
   character of the element's own font, so a clip at 1ch or 2ch lands on a
   glyph boundary. An `em`-based clip does not: the first version of this
   animated to 1.25em and rendered a HALF DOT at one step and three dots at
   two consecutive steps, because a period is not 1/3 of an em in any font
   you are likely to have. Caught by looking at it.

   THE END VALUE IS 4ch, NOT 3ch. `steps(4, end)` samples at 0%, 25%, 50%
   and 75% of the way, so an end of 4ch gives widths of exactly 0, 1, 2 and
   3 characters. Ending at 3ch would give 0, 0.75, 1.5 and 2.25 - the same
   half-glyph problem in a different disguise. */
#workflow .wf-live-working {
  color: var(--muted);
  font-size: 0.85rem;
  white-space: pre;
}

#workflow .wf-live-working::after {
  content: "...";
  display: inline-block;
  width: 0;
  overflow: hidden;
  vertical-align: bottom;
  /* The dots only. The label keeps the surrounding font. */
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  animation: wf-ellipsis 1.2s steps(4, end) infinite;
}

@keyframes wf-ellipsis {
  to { width: 4ch; }
}

/* A MOVING THING NOBODY ASKED FOR IS AN ACCESSIBILITY PROBLEM, and this one
   carries no information the `live` chip does not. Readers who have asked
   for less motion get the three dots standing still. */
@media (prefers-reduced-motion: reduce) {
  #workflow .wf-live-working::after {
    animation: none;
    width: 3ch;
  }
}

/* ===========================================================================
   U8 - THE PUBLIC CORPUS (2026-10-19)

   One card per registered corpus, on `.wf-form`'s surface because it is the
   same kind of thing as the new-run form: what is configured, read from the
   server, with the one control that acts on it at the bottom.
   =========================================================================== */

#workflow .wf-corpus-head {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-bottom: 0.5rem;
}

/* A CORPUS NAME IS A REGISTRY KEY AND READS AS ONE - the same monospace a run
   id gets, for the same reason: it is a string to be matched exactly against
   corpora.ini, not a label. */
#workflow .wf-corpus-name {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--ink);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* THE ACTION ROW. `.wf-live-foot`'s shape, under its own name because it
   holds two buttons and a status line rather than one control. */
#workflow .wf-corpus-act {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-top: 0.75rem;
}

#workflow .wf-corpus-act .wf-note {
  margin: 0;
}

/* A PUBLISH SPENDS TOO. `.wf-fire` carries the accent and the disabled
   treatment; this only sizes the button for a card footer, so the panel's two
   spending affordances read as the same kind of control rather than as a
   prominent one and an incidental one. */
#workflow .wf-pub {
  font-size: 0.8125rem;
  padding: 0.3rem 0.6rem;
}

/* THE RESULT BLOCK IS ABSENT UNTIL THERE IS A RESULT, rather than an empty
   bordered strip sitting under every card from the moment the panel opens. */
#workflow .wf-corpus-result:empty {
  display: none;
}

#workflow .wf-corpus-result {
  margin-top: 0.6rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--line);
}

#workflow .wf-corpus-done {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
  margin-bottom: 0.4rem;
}

/* A WARNING IS THE INGEST'S OWN SENTENCE AND IS NOT AN ERROR: a publish can
   complete and still have something to say. It borrows `.wf-violation`'s red
   gutter, which is the one visual this panel already has for "prose the
   server wrote about something that did not go to plan". */
#workflow .wf-corpus-warn {
  margin-top: 0.4rem;
}