/* ---------------------------------------------------------------------
   Machina design system.

   Own compact CSS, no framework — see CLAUDE.md and the Phase 1 plan
   ("Django + HTMX + own compact CSS"). 13px base, tight 4/8/12px spacing,
   dark theme only (decided 5 Sep 2026 — see CLAUDE.md §1). Tokens are
   derived from the login screen's reference palette (static/css/login.css
   `--lg-*`): near-black ground, a surface one step lighter, and the same
   purple accent family (#7c74e8 / #534ab7) carried through links, the
   active loop-bar tab and buttons. Token NAMES are unchanged from the
   light-theme version so nothing downstream had to be renamed — only the
   hex/rgba values moved.
   --------------------------------------------------------------------- */

:root {
  /* Spacing scale */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;

  /* Accent (purple) — same family as the login screen's --lg-accent /
     --lg-accent-dim, so the one dark screen and the rest of the app read
     as one palette. */
  --accent: #7c74e8;
  --accent-dark: #534ab7;
  --accent-light: #d6d1ff;
  --accent-contrast: #ffffff;

  /* Neutral scale. Numbering is kept from the light-theme file (950 = the
     strongest text, 50 = the subtlest fill) but the values now run the
     other way round: 950 is near-white text, 50 is a hair off the page
     background. Border-weight steps (200/300/400) are translucent white
     overlays, the standard dark-UI hairline idiom (see login.css
     --lg-line), rather than solid greys, so they read against either
     --bg or --surface without a second set of tokens. */
  --grey-950: #f4f5fa;
  --grey-900: #e8e9f2;
  --grey-700: #aeb2c4;
  --grey-600: #8b8fa6;
  --grey-500: #6d7184;
  --grey-400: #6e717e;
  --grey-300: rgba(255, 255, 255, 0.16);
  --grey-200: rgba(255, 255, 255, 0.09);
  --grey-100: #1c1e27;
  --grey-50: #0d0e13;
  --white: #ffffff;

  /* Semantic tokens */
  --bg: #0a0b0f;
  --surface: #15161d;
  --border: var(--grey-200);
  --text: var(--grey-900);
  --text-muted: var(--grey-600);
  --text-faint: var(--grey-400);
  --danger: var(--c-danger);
  --warning: var(--c-warning);
  --success: var(--c-success);

  --ring-track: var(--grey-300);
  --chevron-color: var(--grey-500);

  /* -------------------------------------------------------------------
   * Colour system (7 Sep 2026, colour worker) — see docs/ui-colours.md.
   *
   * Ignas, 7 Sep: "Whole UI is extremely dark blue — let's work on
   * putting colours into statuses, states, buttons — colours should help
   * identifying things." Everything below is additive to the palette
   * above: `--accent` (brand purple, the login screen and the loop
   * ring) is untouched; `--c-accent` is a SEPARATE warm-gold token for
   * primary ACTIONS only, so a button never reads as "the brand mark"
   * and vice versa. `--danger`/`--warning`/`--success` above now alias
   * the new `--c-*` tokens so every file already written against those
   * three names (approvals.css, library.css, inspector.css, briefs.css)
   * improves tonight with no edits of its own.
   * ------------------------------------------------------------------- */

  /* Core semantic — primary action, feedback, neutral. */
  --c-accent: #edba45;
  --c-accent-ink: #1a1a1a;
  --c-success: #4dcb8c;
  --c-warning: #e7aa40;
  --c-danger: #f2675a;
  --c-info: #67a0e4;
  --c-muted: var(--grey-600);

  /* Approvals states (AdFormatState / Ad.state) — one hue per state so a
     queue reads at a glance instead of everything being the same purple
     tint. `--state-failed` covers tagging/QA failure states outside the
     approval state machine itself (e.g. CodingProposal.State.FAILED). */
  --state-draft: #a0a4b1;
  --state-in-review: #67a0e4;
  --state-amends: #e67d33;
  --state-finalized: #37beac;
  --state-final-review: #ac8be4;
  --state-approved: #4dcb8c;
  --state-archived: #656c81;
  --state-failed: #f2675a;
  /* "Return to Production" (Ignas, 12 Sep 2026, ad 87) added Ad.State.
     RETURNED without a matching token — the badge silently fell back to
     .state-pill's plain grey default, indistinguishable from a neutral
     "nothing to report" chip even though it means "sent back, the vendor
     must act" (found in the 13 Sep 2026 review). Same family as
     --state-amends (also "needs vendor action") but a distinct hue since
     the doc's rule is one hue per state. */
  --state-returned: #d9634f;

  /* Tagging (coding_state) and Inspector score bands. */
  --tag-proposed: #e7aa40;
  --tag-confirmed: #4dcb8c;
  --score-good: #4dcb8c;
  --score-warn: #e7aa40;
  --score-bad: #f2675a;

  /* Brief statuses — a coherent progression: cool/neutral while the idea
     is unformed, warming up through production, landing on green at
     "done", archived falling back to slate. */
  --brief-research: #878fa1;
  --brief-draft: #a0a4b1;
  --brief-shared: #5ac0e2;
  --brief-in-production: #37beac;
  --brief-assets-ready: #e7aa40;
  --brief-published: #eb7e47;
  --brief-done: #4dcb8c;
  --brief-archived: #656c81;

  /* Production board statuses — same cool-to-warm-to-green logic, "dropped"
     reads as a dusty, terminal red-brown rather than the full alarm of
     --c-danger (a dropped idea isn't a system failure). */
  --prod-idea: #878fa1;
  --prod-shortlisted: #67a0e4;
  --prod-in-production: #37beac;
  --prod-inspecting: #ac8be4;
  --prod-ready: #37beac;
  --prod-sent: #4dcb8c;
  --prod-in-library: #7fd9a8;
  --prod-dropped: #a96a60;

  /* One hue per loop stage (rainbow around the ring, in loop order) for
     the loop bar's active icon colour + underline — see .loopbar-stage
     rules below and templates/components/loopbar.html's data-stage-key. */
  --stage-insights: #6eafdd;
  --stage-briefs: #706edd;
  --stage-production: #b36edd;
  --stage-approvals: #dd6ec5;
  --stage-library: #dd6e83;
  --stage-publishing: #dd9d6e;
  --stage-performance: #c5c76b;
  --stage-tagging: #8ec76b;
  --stage-reporting: #6edd86;
  --stage-analysis: #6eddc9;

  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
    Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";

  --radius-sm: 3px;
  --radius-md: 6px;

  --loopbar-height: 40px;
  --utility-height: 28px;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  color-scheme: dark;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

a {
  color: var(--accent);
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}

h1, h2, h3 {
  font-weight: 600;
  margin: 0 0 var(--sp-3);
  color: var(--grey-950);
}
h1 { font-size: 18px; }
h2 { font-size: 15px; }
h3 { font-size: 13px; }

p { margin: 0 0 var(--sp-3); }

/* ------------------------------------------------------------------- *
 * Loop bar — wordmark + ring, ten stage tabs, chevrons, return arrow.
 * Nothing else lives in this bar. See core/context_processors.py for
 * loop_stages and templates/components/loopbar.html.
 * ------------------------------------------------------------------- */

.loopbar {
  height: var(--loopbar-height);
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: 0 var(--sp-4);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.loopbar-brand {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex: 0 0 auto;
  color: var(--grey-950);
}
.loopbar-brand:hover {
  text-decoration: none;
}

.loop-ring {
  display: block;
  overflow: visible;
}
.ring-track {
  fill: none;
  stroke: var(--ring-track);
  stroke-width: 3.5;
}
.ring-arc {
  fill: none;
  stroke: var(--accent);
  stroke-width: 3.5;
  stroke-linecap: round;
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
  animation: ring-sweep 2s cubic-bezier(0.22, 0.61, 0.36, 1) 1 both;
}
.ring-arc-b {
  animation-delay: 0.15s;
}
@keyframes ring-sweep {
  from { stroke-dashoffset: 100; }
  to { stroke-dashoffset: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .ring-arc {
    animation: none;
    stroke-dashoffset: 0;
    opacity: 1;
  }
}

.loopbar-word {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
}

.loopbar-stages {
  list-style: none;
  display: flex;
  align-items: stretch;
  margin: 0;
  padding: 0;
  gap: 0;
  overflow-x: auto;
  flex: 1 1 auto;
}

.loopbar-chevron {
  display: flex;
  align-items: center;
  color: var(--chevron-color);
  flex: 0 0 auto;
}
.loopbar-chevron .icon {
  width: 14px;
  height: 14px;
  stroke: var(--chevron-color);
}

.loopbar-stage {
  display: flex;
  flex: 0 0 auto;
}
.loopbar-stage > a {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  padding: 0 var(--sp-2);
  height: var(--loopbar-height);
  color: var(--grey-700);
  border-bottom: 2px solid transparent;
  white-space: nowrap;
}
.loopbar-stage > a:hover {
  color: var(--grey-950);
  text-decoration: none;
}
.loopbar-stage .icon {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
}
.loopbar-stage.is-inactive > a {
  color: var(--text-faint);
}
.loopbar-stage.is-current > a {
  color: var(--accent);
  border-bottom-color: var(--accent);
  font-weight: 600;
}

/* Per-stage colour (7 Sep, colour worker) — one hue per loop stage, in
   loop order, so the active tab's icon + underline is identifiable at a
   glance rather than every stage sharing one flat purple. The ring graphic
   itself keeps --accent (brand identity, unchanged) — this is scoped to
   the active tab only via data-stage-key (set in loopbar.html). */
.loopbar-stage.is-current[data-stage-key="insights"] > a { color: var(--stage-insights); border-bottom-color: var(--stage-insights); }
.loopbar-stage.is-current[data-stage-key="briefs"] > a { color: var(--stage-briefs); border-bottom-color: var(--stage-briefs); }
.loopbar-stage.is-current[data-stage-key="production"] > a { color: var(--stage-production); border-bottom-color: var(--stage-production); }
.loopbar-stage.is-current[data-stage-key="approvals"] > a { color: var(--stage-approvals); border-bottom-color: var(--stage-approvals); }
.loopbar-stage.is-current[data-stage-key="library"] > a { color: var(--stage-library); border-bottom-color: var(--stage-library); }
.loopbar-stage.is-current[data-stage-key="publishing"] > a { color: var(--stage-publishing); border-bottom-color: var(--stage-publishing); }
.loopbar-stage.is-current[data-stage-key="performance"] > a { color: var(--stage-performance); border-bottom-color: var(--stage-performance); }
.loopbar-stage.is-current[data-stage-key="tagging"] > a { color: var(--stage-tagging); border-bottom-color: var(--stage-tagging); }
.loopbar-stage.is-current[data-stage-key="reporting"] > a { color: var(--stage-reporting); border-bottom-color: var(--stage-reporting); }
.loopbar-stage.is-current[data-stage-key="analysis"] > a { color: var(--stage-analysis); border-bottom-color: var(--stage-analysis); }

.stage-badge,
.count-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-contrast);
  font-size: 10px;
  font-weight: 600;
  line-height: 1;
}

.loopbar-return {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  margin-left: var(--sp-1);
}
.loopbar-return a {
  display: flex;
  align-items: center;
  padding: 0 var(--sp-2);
  color: var(--accent);
}
.loopbar-return .icon {
  width: 16px;
  height: 16px;
}

/* Collapse inactive stages to icon-only under ~1100px; active stages keep
   their labels so the two live stages stay legible on a narrower window. */
@media (max-width: 1100px) {
  .loopbar-stage.is-inactive .stage-label {
    display: none;
  }
  .loopbar-word {
    display: none;
  }
}

/* ------------------------------------------------------------------- *
 * Utility row — section title left, user/role/notifications/admin/logout
 * right. Thin, quiet, no borders competing with the loop bar above it.
 * ------------------------------------------------------------------- */

.utility-row {
  height: var(--utility-height);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--sp-4);
  background: var(--grey-50);
  border-bottom: 1px solid var(--border);
  font-size: 12px;
  color: var(--text-muted);
}

.utility-title {
  font-weight: 600;
  color: var(--grey-900);
}

.utility-right {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}

.utility-user {
  color: var(--grey-900);
  font-weight: 500;
}

/* accent-dark/accent-light swap from the light theme: on dark, the pill
   needs a dark-purple fill with light-lavender text, not the other way
   round. */
.utility-role {
  padding: 1px 6px;
  border-radius: var(--radius-sm);
  background: var(--accent-dark);
  color: var(--accent-light);
  font-size: 11px;
  text-transform: capitalize;
}

.utility-link {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  color: var(--text-muted);
}
.utility-link:hover {
  color: var(--accent);
}
.utility-icon {
  width: 14px;
  height: 14px;
}

.utility-logout-form {
  margin: 0;
  display: inline-flex;
}
.link-button {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--text-muted);
  cursor: pointer;
}
.link-button:hover {
  color: var(--accent);
  text-decoration: underline;
}

/* ------------------------------------------------------------------- *
 * Page shell
 * ------------------------------------------------------------------- */

.page {
  padding: var(--sp-5);
  max-width: 1200px;
  margin: 0 auto;
}

/* Library grid opts out of the centred container: it needs the full
   viewport width (12px gutters) and the full height below the loop bar +
   utility row, with the AG Grid doing its own scrolling instead of the
   page. Both bars above are border-box and a fixed height (--loopbar-height,
   --utility-height), so a flex column filling 100vh naturally hands
   `.page` exactly the remaining space — no calc() to keep in sync if either
   bar's height ever changes. Scoped to `body.page-wide` (set only by
   library/templates/library/index.html) so every other page keeps the
   plain `.page` rule above untouched. */
body.page-wide {
  height: 100vh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
body.page-wide > .loopbar,
body.page-wide > .utility-row {
  flex: 0 0 auto;
}
body.page-wide > .page {
  flex: 1 1 auto;
  min-height: 0;
  max-width: none;
  width: 100%;
  margin: 0;
  padding: 12px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.placeholder-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--sp-5);
  max-width: 640px;
}
.placeholder-blurb {
  color: var(--text-muted);
}
.placeholder-note {
  margin-top: var(--sp-4);
  font-size: 12px;
  color: var(--text-faint);
}

.form-error {
  color: var(--danger);
  font-size: 12px;
}

/* ------------------------------------------------------------------- *
 * Forms, buttons, inputs, badges, tables
 * ------------------------------------------------------------------- */

label {
  display: block;
  font-size: 12px;
  color: var(--text-muted);
  margin-bottom: var(--sp-1);
}

input[type="text"],
input[type="email"],
input[type="password"],
input[type="search"],
input[type="number"],
select,
textarea {
  width: 100%;
  padding: 6px var(--sp-2);
  font-size: 13px;
  font-family: inherit;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--grey-300);
  border-radius: var(--radius-sm);
}
input:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-light);
}

p:has(> input),
form p {
  margin-bottom: var(--sp-3);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-1);
  padding: 6px var(--sp-3);
  font-size: 13px;
  font-weight: 500;
  border-radius: var(--radius-sm);
  border: 1px solid var(--grey-300);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
}
.btn:hover {
  border-color: var(--grey-400);
  text-decoration: none;
}
/* Ignas, 10 Sep 2026 (ad 76 resubmit guard): a disabled .btn/.btn-primary/
   etc. had no visual difference from an enabled one — same solid fill,
   only the cursor and a title= tooltip told them apart. Dims any disabled
   button app-wide (not just Resubmit) so "why can't I click this" reads
   at a glance instead of requiring a hover-and-wait for the tooltip. */
.btn:disabled,
.btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}
.btn:disabled:hover,
.btn[disabled]:hover {
  border-color: var(--grey-300);
}
/* Primary action = warm gold (--c-accent), deliberately distinct from the
   brand-purple --accent used by the loop ring/links/focus rings — a button
   should never read as "the Machina mark" and vice versa (7 Sep colour
   pass). Gold is light, so ink text is dark, not white. */
.btn-primary {
  background: var(--c-accent);
  border-color: var(--c-accent);
  color: var(--c-accent-ink);
}
.btn-primary:hover {
  background: #f5c869;
  border-color: #f5c869;
}

.btn-success {
  background: var(--c-success);
  border-color: var(--c-success);
  color: var(--c-accent-ink);
}
.btn-success:hover {
  background: #67d9a2;
  border-color: #67d9a2;
}

.btn-warning {
  background: var(--c-warning);
  border-color: var(--c-warning);
  color: var(--c-accent-ink);
}
.btn-warning:hover {
  background: #edbb63;
  border-color: #edbb63;
}

.btn-danger {
  background: var(--c-danger);
  border-color: var(--c-danger);
  color: var(--white);
}
.btn-danger:hover {
  background: #f58579;
  border-color: #f58579;
}

/* Ghost = no fill, quiet by default, only the border/text signal intent —
   used where a page wants a de-emphasised action next to a primary one
   (Cancel, secondary bulk actions) without a second loud colour. */
.btn-ghost {
  background: transparent;
  border-color: var(--grey-300);
  color: var(--text-muted);
}
.btn-ghost:hover {
  border-color: var(--c-accent);
  color: var(--text);
}

.badge {
  display: inline-flex;
  align-items: center;
  padding: 1px 6px;
  border-radius: var(--radius-sm);
  background: var(--grey-100);
  color: var(--grey-700);
  font-size: 11px;
}

/* `.badge-<semantic>` — same shape as `.badge`, tinted by meaning (counts
   on the loop bar / dashboard cards that need to read as good/bad/neutral
   rather than plain grey). */
.badge-success { background: color-mix(in srgb, var(--c-success) 20%, transparent); color: var(--c-success); }
.badge-warning { background: color-mix(in srgb, var(--c-warning) 20%, transparent); color: var(--c-warning); }
.badge-danger { background: color-mix(in srgb, var(--c-danger) 20%, transparent); color: var(--c-danger); }
.badge-info { background: color-mix(in srgb, var(--c-info) 20%, transparent); color: var(--c-info); }
.badge-accent { background: color-mix(in srgb, var(--c-accent) 20%, transparent); color: var(--c-accent); }
.badge-muted { background: var(--grey-100); color: var(--text-muted); }

/* -------------------------------------------------------------------
 * Pills — one shared component for every status/state chip in the app
 * (approvals states, tagging, brief/production statuses, inspector
 * score). Naming: `.pill.state-<value>` where `<value>` is the literal
 * status/state string a model uses (e.g. `state-in_review`,
 * `state-research`, `state-idea`) — apps share a class whenever they
 * share a word (both Briefs and Production have an `in_production`
 * status, and both mean the same thing, so both get the same colour).
 * `.state-pill.state-<value>` (Approvals' existing markup, predating this
 * pass) is aliased to the identical colours below so that page improves
 * tonight with no template edit — the two-class selector outweighs
 * approvals.css's single-class `.state-draft` etc regardless of link
 * order, so this always wins.
 * ------------------------------------------------------------------- */

.pill {
  display: inline-flex;
  align-items: center;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.6;
  white-space: nowrap;
  background: color-mix(in srgb, var(--c-muted) 18%, transparent);
  color: var(--c-muted);
  border: 1px solid color-mix(in srgb, var(--c-muted) 40%, transparent);
}

/* Approvals states + shared "failed" */
.pill.state-draft, .state-pill.state-draft { background: color-mix(in srgb, var(--state-draft) 18%, transparent); color: var(--state-draft); border-color: color-mix(in srgb, var(--state-draft) 40%, transparent); }
.pill.state-in_review, .state-pill.state-in_review { background: color-mix(in srgb, var(--state-in-review) 18%, transparent); color: var(--state-in-review); border-color: color-mix(in srgb, var(--state-in-review) 40%, transparent); }
.pill.state-amends_requested, .state-pill.state-amends_requested { background: color-mix(in srgb, var(--state-amends) 18%, transparent); color: var(--state-amends); border-color: color-mix(in srgb, var(--state-amends) 40%, transparent); }
.pill.state-finalized, .state-pill.state-finalized { background: color-mix(in srgb, var(--state-finalized) 18%, transparent); color: var(--state-finalized); border-color: color-mix(in srgb, var(--state-finalized) 40%, transparent); }
.pill.state-final_review, .state-pill.state-final_review { background: color-mix(in srgb, var(--state-final-review) 18%, transparent); color: var(--state-final-review); border-color: color-mix(in srgb, var(--state-final-review) 40%, transparent); }
.pill.state-approved, .state-pill.state-approved { background: color-mix(in srgb, var(--state-approved) 18%, transparent); color: var(--state-approved); border-color: color-mix(in srgb, var(--state-approved) 40%, transparent); }
.pill.state-archived, .state-pill.state-archived { background: color-mix(in srgb, var(--state-archived) 18%, transparent); color: var(--state-archived); border-color: color-mix(in srgb, var(--state-archived) 40%, transparent); }
.pill.state-failed, .state-pill.state-failed { background: color-mix(in srgb, var(--state-failed) 18%, transparent); color: var(--state-failed); border-color: color-mix(in srgb, var(--state-failed) 40%, transparent); }
.pill.state-returned, .state-pill.state-returned { background: color-mix(in srgb, var(--state-returned) 18%, transparent); color: var(--state-returned); border-color: color-mix(in srgb, var(--state-returned) 40%, transparent); }

/* Tagging (coding_state) */
.pill.state-proposed, .state-pill.state-proposed { background: color-mix(in srgb, var(--tag-proposed) 18%, transparent); color: var(--tag-proposed); border-color: color-mix(in srgb, var(--tag-proposed) 40%, transparent); }
.pill.state-confirmed, .state-pill.state-confirmed { background: color-mix(in srgb, var(--tag-confirmed) 18%, transparent); color: var(--tag-confirmed); border-color: color-mix(in srgb, var(--tag-confirmed) 40%, transparent); }

/* Brief statuses */
.pill.state-research, .state-pill.state-research { background: color-mix(in srgb, var(--brief-research) 18%, transparent); color: var(--brief-research); border-color: color-mix(in srgb, var(--brief-research) 40%, transparent); }
.pill.state-shared, .state-pill.state-shared { background: color-mix(in srgb, var(--brief-shared) 18%, transparent); color: var(--brief-shared); border-color: color-mix(in srgb, var(--brief-shared) 40%, transparent); }
.pill.state-in_production, .state-pill.state-in_production { background: color-mix(in srgb, var(--brief-in-production) 18%, transparent); color: var(--brief-in-production); border-color: color-mix(in srgb, var(--brief-in-production) 40%, transparent); }
.pill.state-assets_ready, .state-pill.state-assets_ready { background: color-mix(in srgb, var(--brief-assets-ready) 18%, transparent); color: var(--brief-assets-ready); border-color: color-mix(in srgb, var(--brief-assets-ready) 40%, transparent); }
.pill.state-published, .state-pill.state-published { background: color-mix(in srgb, var(--brief-published) 18%, transparent); color: var(--brief-published); border-color: color-mix(in srgb, var(--brief-published) 40%, transparent); }
.pill.state-done, .state-pill.state-done { background: color-mix(in srgb, var(--brief-done) 18%, transparent); color: var(--brief-done); border-color: color-mix(in srgb, var(--brief-done) 40%, transparent); }

/* Production board statuses (idea/shortlisted/inspecting/ready/sent/dropped
   not shared with any other domain's vocabulary, so each gets its own
   rule even where the resulting colour matches another key above). */
.pill.state-idea, .state-pill.state-idea { background: color-mix(in srgb, var(--prod-idea) 18%, transparent); color: var(--prod-idea); border-color: color-mix(in srgb, var(--prod-idea) 40%, transparent); }
.pill.state-shortlisted, .state-pill.state-shortlisted { background: color-mix(in srgb, var(--prod-shortlisted) 18%, transparent); color: var(--prod-shortlisted); border-color: color-mix(in srgb, var(--prod-shortlisted) 40%, transparent); }
.pill.state-inspecting, .state-pill.state-inspecting { background: color-mix(in srgb, var(--prod-inspecting) 18%, transparent); color: var(--prod-inspecting); border-color: color-mix(in srgb, var(--prod-inspecting) 40%, transparent); }
.pill.state-ready, .state-pill.state-ready { background: color-mix(in srgb, var(--prod-ready) 18%, transparent); color: var(--prod-ready); border-color: color-mix(in srgb, var(--prod-ready) 40%, transparent); }
.pill.state-sent, .state-pill.state-sent { background: color-mix(in srgb, var(--prod-sent) 18%, transparent); color: var(--prod-sent); border-color: color-mix(in srgb, var(--prod-sent) 40%, transparent); }
.pill.state-in_library, .state-pill.state-in_library { background: color-mix(in srgb, var(--prod-in-library) 18%, transparent); color: var(--prod-in-library); border-color: color-mix(in srgb, var(--prod-in-library) 40%, transparent); }
.pill.state-dropped, .state-pill.state-dropped { background: color-mix(in srgb, var(--prod-dropped) 18%, transparent); color: var(--prod-dropped); border-color: color-mix(in srgb, var(--prod-dropped) 40%, transparent); }

/* Inspector score bands — same shape, used on a plain span (not
   necessarily `.pill`) wherever a good/warn/bad read is needed. */
.score-good { color: var(--score-good); }
.score-warn { color: var(--score-warn); }
.score-bad { color: var(--score-bad); }

table.table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
}
table.table th,
table.table td {
  padding: var(--sp-1) var(--sp-2);
  border-bottom: 1px solid var(--border);
  text-align: left;
}
table.table th {
  color: var(--text-muted);
  font-weight: 600;
  background: var(--grey-50);
}
table.table tr.is-unread td {
  font-weight: 600;
  color: var(--grey-950);
}

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

/* ------------------------------------------------------------------- *
 * Framework messages (django.contrib.messages) — plain list, no toast
 * library. Library's own grid uses a separate JS-driven toast for inline
 * edit failures (see static/css/library.css); this is for classic
 * form-post pages (vocab add/toggle).
 * ------------------------------------------------------------------- */

.messages {
  list-style: none;
  margin: 0 0 var(--sp-3);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}
.message {
  padding: 6px var(--sp-3);
  border-radius: var(--radius-sm);
  font-size: 12px;
  background: var(--grey-100);
  color: var(--text);
}
/* Tinted washes over the dark ground, same idiom as login.css .login-errors
   (rgba(179, 69, 47, ...)) rather than the light theme's solid pastels. */
.message-error {
  background: color-mix(in srgb, var(--c-danger) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--c-danger) 40%, transparent);
  color: var(--danger);
}
.message-success {
  background: color-mix(in srgb, var(--c-success) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--c-success) 40%, transparent);
  color: var(--success);
}

/* The `hidden` attribute must always win over component display rules
   (display:grid/flex on a panel otherwise keeps it visible — this is why
   the Library Filters panel would not collapse). */
[hidden] { display: none !important; }

/* Click-to-sort headers (static/js/table-sort.js). */
th.sortable-th { cursor: pointer; user-select: none; white-space: nowrap; }
th.sortable-th:hover { color: var(--text); }
th.sortable-th:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
th.sortable-th::after { content: " ↕"; opacity: 0.3; font-size: 0.85em; }
th.sortable-th.sort-asc::after { content: " ▲"; opacity: 0.9; color: var(--accent); }
th.sortable-th.sort-desc::after { content: " ▼"; opacity: 0.9; color: var(--accent); }

/* Queue tables (Approvals, Production): sortable headers, static/js/queue-sort.js. */
.queue-table th.sortable-th { cursor: pointer; user-select: none; }
.queue-table th.sortable-th:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.queue-table th.sortable-th:not(.sorted-asc):not(.sorted-desc)::after { content: " ↕"; opacity: 0.3; font-size: 10px; }
.queue-table th.sorted-asc::after { content: " ▲"; color: var(--accent); font-size: 10px; }
.queue-table th.sorted-desc::after { content: " ▼"; color: var(--accent); font-size: 10px; }

/* Sortable list header strip (Insights — not a table, static/js/
   table-sort.js's initList): the same arrow affordance as
   th.sortable-th above, generalised to any element (a plain <span>
   here) since a reddit-style feed has no <th>. */
.sort-header-list { display: flex; align-items: center; gap: var(--sp-4); padding: var(--sp-1) var(--sp-2); border-bottom: 1px solid var(--border); margin-bottom: var(--sp-2); font-size: 11px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.02em; }
.sort-header-list [data-sort-key].sortable-th { cursor: pointer; user-select: none; white-space: nowrap; }
.sort-header-list [data-sort-key].sortable-th:hover { color: var(--text); }
.sort-header-list [data-sort-key].sortable-th:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.sort-header-list [data-sort-key].sortable-th::after { content: " ↕"; opacity: 0.3; font-size: 0.9em; }
.sort-header-list [data-sort-key].sortable-th.sort-asc::after { content: " ▲"; opacity: 0.9; color: var(--accent); }
.sort-header-list [data-sort-key].sortable-th.sort-desc::after { content: " ▼"; opacity: 0.9; color: var(--accent); }

/* ---------------------------------------------------------------------
   Shared "Filters ▾" panel (static/js/page-filters.js, Ignas 17 Sep 2026)
   — same look as the Library/Approvals/Production filter panels
   (library.css's .lib-filters-*/.lib-filter-*, approvals.css's
   .appr-filters-*/.appr-filter-*), generalised with pf- names into
   machina.css since this version is shared by every plain page instead
   of being copied per app. Only the DOM built by page-filters.js uses
   these classes — see that file's own header comment for the markup
   contract a page opts in with.
   --------------------------------------------------------------------- */
.pf-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-2);
}
.pf-wrap { position: relative; flex: 0 0 auto; }
.pf-btn[aria-expanded="true"] {
  background: var(--accent-dark);
  border-color: var(--accent);
  color: var(--accent-light);
}
.pf-chips { display: flex; gap: var(--sp-1); flex-wrap: wrap; flex: 1 1 auto; min-width: 0; }
.pf-count { font-size: 12px; white-space: nowrap; flex: 0 0 auto; margin-left: auto; }

.pf-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: var(--accent-dark);
  color: var(--accent-light);
  border-radius: 999px;
  padding: 2px 4px 2px 10px;
  font-size: 12px;
  white-space: nowrap;
}
.pf-chip-remove {
  border: none;
  background: none;
  color: inherit;
  font: inherit;
  font-size: 11px;
  line-height: 1;
  padding: 2px 4px;
  cursor: pointer;
}
.pf-chip-remove:hover { opacity: 0.65; }

.pf-panel {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 20;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--sp-3);
  width: max-content;
  max-width: min(920px, 90vw);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  padding: var(--sp-3);
  max-height: 45vh;
  overflow: auto;
}
.pf-panel-head {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 2px;
}
.pf-panel-title {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.pf-panel-actions { display: flex; align-items: center; gap: var(--sp-2); }
.pf-close {
  border: none;
  background: none;
  color: var(--text-muted);
  font-size: 14px;
  line-height: 1;
  padding: 2px 6px;
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.pf-close:hover { color: var(--text); background: rgba(255, 255, 255, 0.07); }

.pf-box { display: flex; flex-direction: column; min-width: 0; }
.pf-box-head {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  margin-bottom: 4px;
}
.pf-box-search { width: 100%; padding: 3px 6px; font-size: 12px; margin-bottom: 4px; }
.pf-box-list {
  max-height: 176px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 2px 6px;
  background: var(--grey-50);
}
.pf-box-compact .pf-box-list { max-height: 88px; }
.pf-opt { display: flex; align-items: center; gap: 6px; font-size: 12px; padding: 2px 0; margin: 0; color: var(--text); cursor: pointer; }
.pf-opt input { margin: 0; width: auto; }
.pf-opt-label { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pf-opt-count { color: var(--text-faint); font-size: 11px; flex: 0 0 auto; }
.pf-opt.is-zero .pf-opt-label, .pf-opt.is-zero .pf-opt-count { color: var(--text-faint); }

@media (max-width: 700px) {
  .pf-panel { left: auto; right: 0; max-width: 92vw; }
}

/* ------------------------------------------------------------------- *
 * "Open on your phone" (CLAUDE.md §7c, Ignas 29 Sep 2026) — QR + a
 * copyable signed preview link, shared markup across Production/
 * Approvals landing-page version cards (static/css/landing.css hosts
 * those cards themselves), the Library ad page, and the Brief Files
 * panel (briefs.css). Lives here, not landing.css, so briefs.css (which
 * never loads landing.css) gets it too without a second stylesheet
 * include. See templates/components/phone_preview.html and
 * static/js/phone-preview.js.
 * ------------------------------------------------------------------- */

.phone-preview {
  display: flex;
  flex-wrap: wrap; /* the link row drops under the QR when the card is narrow */
  gap: var(--sp-3);
  align-items: flex-start;
  padding: var(--sp-2) 0;
}
.phone-preview-qr {
  flex: 0 0 auto;
  width: 96px;
  height: 96px;
  border-radius: var(--radius-sm);
  background: var(--white); /* QR codes need a true white quiet zone to scan */
  padding: 4px;
}
.phone-preview-link {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: min(320px, 100%); /* the link must stay readable, not "https://machina.deadlift." */
  flex: 1 1 320px;
}
.phone-preview-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.phone-preview-copy-row {
  display: flex;
  gap: 6px;
  min-width: 0;
}
.phone-preview-input {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 12px;
  padding: 3px 6px;
  background: var(--grey-50);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
}
.phone-preview-expiry {
  font-size: 11px;
}

/* ------------------------------------------------------------------- *
 * Approvals "Handoff" tab (Ignas, 24 Oct 2026) - a landing-page ad's
 * whole CMS handoff package read-only, inline on its own Approvals page
 * (approvals/templates/approvals/_handoff_tab.html). Standard Alpine
 * x-cloak rule (not previously needed - this is the first x-show use in
 * the codebase) so the Handoff pane never flashes visible for an instant
 * before Alpine initialises.
 * ------------------------------------------------------------------- */

[x-cloak] {
  display: none !important;
}

.handoff-status-card .handoff-status-list {
  list-style: none;
  margin: 0 0 var(--sp-2) 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
}
.handoff-format-html {
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--border);
}
.handoff-format-html:last-child {
  border-bottom: none;
}
.handoff-format-html-head {
  font-size: 12px;
  margin-bottom: 4px;
}
.handoff-copy-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
}
.handoff-table th {
  text-align: left;
  width: 220px;
  color: var(--text-muted);
  font-weight: 500;
  vertical-align: top;
}
.handoff-table td {
  word-break: break-word;
}

/* Handoff tab "Send handover and instructions" panel (Ignas, 24 Oct
   2026) - approvals/templates/approvals/_handoff_tab.html. */
.handover-panel {
  margin-top: var(--sp-2);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}
.handover-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
  color: var(--text-muted);
}
.handover-field input,
.handover-field textarea {
  font-size: 13px;
  color: var(--text);
}
.handover-actions {
  display: flex;
  gap: var(--sp-2);
}
.handover-preview-wrap {
  margin-top: var(--sp-2);
  border: 1px solid var(--border);
  border-radius: 4px;
  overflow: hidden;
}
.handover-preview-iframe {
  width: 100%;
  height: 480px;
  border: none;
  background: var(--white); /* the email preview is a light document */
}
.handover-log {
  margin-top: var(--sp-2);
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.handover-log-row {
  font-size: 12px;
}

/* 25 Oct 2026 Boss UI pass on the Handoff tab's "Send handover and
   instructions" panel, from live screenshots - appended after the 24 Oct
   2026 rules above rather than editing them in place, so later rules of
   equal specificity win the cascade instead of this file's own earlier
   declarations being touched. */
.handover-field input,
.handover-field textarea {
  /* The To/Cc text inputs were sizing to the browser default (a narrow
     ~20-character box) instead of the panel's own width - every other
     field in this panel (the note textarea) already stretched full width
     because a <textarea> defaults differently from an <input>. */
  width: 100%;
  box-sizing: border-box;
}
.handover-preview-iframe {
  /* Overrides the 24 Oct 2026 fixed height above (later same-specificity
     rule wins) - the iframe now grows to the email's own content height
     (set from JS on load, _handoff_tab.html), so the page scrolls instead
     of a clipped frame with its own inner scrollbar. */
  height: auto;
  min-height: 900px;
}

/* 25 Oct 2026, Boss, second live look: `.action-form` lays its children out
   in a ROW, which kept To / Cc / Note side by side and narrow whatever the
   inputs' own width was; the handover form stacks its fields full width.
   The preview iframe cannot measure its sandboxed document, so it gets a
   tall fixed height and an "open in a new tab" link instead. */
.action-form.handover-form {
  flex-direction: column;
  align-items: stretch;
}
.action-form.handover-form .handover-field {
  width: 100%;
}
.handover-preview-iframe {
  min-height: 1800px;
  height: 1800px;
}
.handover-preview-link {
  font-size: 12px;
  padding: 6px 8px;
}

/* ------------------------------------------------------------------- *
 * Utility row dashboard picker (Performance, 28 Oct 2026) - the compact
 * dropdown in the thin band right of the stage title. `.utility-left`
 * wraps the title + this picker as ONE flex item so `.utility-row`'s
 * existing space-between layout (title/left group vs. user/right group)
 * keeps working with no change to any rule above. Any stage can reuse
 * `.utility-picker-select` via base.html's `{% block utility_extra %}`.
 * ------------------------------------------------------------------- */
.utility-left {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-width: 0;
}
.utility-picker-select {
  font-size: 12px;
  padding: 1px 6px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--grey-100, var(--grey-50));
  color: var(--grey-900);
  max-width: 220px;
}
.utility-picker-select:hover,
.utility-picker-select:focus {
  border-color: var(--accent);
  outline: none;
}

/* Minimal shell for a dashboard page (performance/dashboard.html) and its
   "coming soon" placeholder - performance.css (owned by the Performance
   app's own worker) is where a real dashboard's own layout belongs; this
   is only the generic wrapper every dashboard, filled-in or not, shares. */
.perf-dashboard {
  padding: var(--sp-4);
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}
.perf-dashboard-header h1 {
  margin: 0 0 4px;
}
.perf-dashboard-placeholder {
  padding: var(--sp-5);
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  text-align: center;
}

/* ------------------------------------------------------------------- *
 * Flow Ribbon (Ignas, 2 Nov 2026) - third horizontal band under the loop
 * bar and the utility row: where this item is in its journey, whose move
 * it is, the forward/back step. Fed by core.flow_ribbon.Ribbon, rendered
 * by templates/components/flow_ribbon.html, included from base.html right
 * after .utility-row. Phase 1 is Approvals (approvals/ribbon.py);
 * Production/Library/Briefs reuse this same CSS (docs/flow-ribbon.md).
 *
 * Sticky under band 2, matching --loopbar-height/--utility-height exactly
 * so it sits directly below band 2 however far the page scrolls (bands 1-2
 * are plain, non-sticky flow today - see docs/flow-ribbon.md for the note
 * on what that means until they themselves become sticky).
 * ------------------------------------------------------------------- */

.flow-ribbon-wrap {
  position: sticky;
  /* bands 1-2 are not sticky, so this one sticks at the very top (review 2 Nov 2026) */
  top: 0;
  z-index: 5;
  flex: 0 0 auto;
}

.flow-ribbon {
  min-height: 52px;
  display: flex;
  align-items: stretch;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  font-size: 12px;
}

.flow-ribbon-flow {
  display: flex;
  align-items: center;
  gap: 0;
  padding: 0 var(--sp-2) 0 var(--sp-4);
  flex: 1 1 auto;
  overflow-x: auto;
  scrollbar-width: none;
}
.flow-ribbon-flow::-webkit-scrollbar {
  display: none;
}

.flow-ribbon-stage {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
}
.flow-ribbon-stage.open {
  background: var(--grey-50);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin: 8px 2px;
  padding: 0 var(--sp-2);
  height: 36px;
}
.flow-ribbon-lbl {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
  color: var(--text-faint);
  margin-right: var(--sp-2);
  white-space: nowrap;
}
.flow-ribbon-stage.open > .flow-ribbon-lbl {
  color: var(--accent-light);
}

.flow-ribbon-collapsed {
  display: flex;
  align-items: center;
  color: inherit;
  text-decoration: none;
}
.flow-ribbon-collapsed:hover {
  text-decoration: none;
}
a.flow-ribbon-collapsed:hover .flow-ribbon-lbl {
  color: var(--accent-light);
}

.flow-ribbon-seg {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: 0 var(--sp-2);
  height: 52px;
  color: var(--text-faint);
  white-space: nowrap;
}
.flow-ribbon-stage.open .flow-ribbon-seg {
  height: 34px;
}
.flow-ribbon-seg small {
  font-weight: 400;
  color: var(--text-faint);
  margin-left: 2px;
}
.flow-ribbon-seg[data-flow-anchor] {
  cursor: pointer;
}
.flow-ribbon-seg[data-flow-anchor]:hover {
  color: var(--text);
}

.flow-ribbon-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 2px solid var(--text-faint);
  box-sizing: border-box;
  flex: none;
  position: relative;
}

/* done: filled green with a check mark */
.flow-ribbon-done {
  color: var(--text-muted);
}
.flow-ribbon-done .flow-ribbon-dot,
.flow-ribbon-fmt-done .flow-ribbon-fmt-dot {
  background: var(--c-success);
  border-color: var(--c-success);
}
.flow-ribbon-done .flow-ribbon-dot::after {
  content: "";
  position: absolute;
  left: 2px;
  top: 1px;
  width: 3px;
  height: 6px;
  border: solid var(--bg);
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}

/* now: filled accent with a soft halo, pulsing (motion-safe) */
.flow-ribbon-now {
  color: var(--text);
  font-weight: 600;
}
.flow-ribbon-now .flow-ribbon-dot,
.flow-ribbon-fmt-now .flow-ribbon-fmt-dot {
  background: var(--accent);
  border-color: var(--accent-light);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 22%, transparent);
}
@media (prefers-reduced-motion: no-preference) {
  .flow-ribbon-now .flow-ribbon-dot,
  .flow-ribbon-fmt-now .flow-ribbon-fmt-dot {
    animation: flow-ribbon-pulse 2.2s ease-in-out infinite;
  }
}
@keyframes flow-ribbon-pulse {
  0%,
  100% {
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 22%, transparent);
  }
  50% {
    box-shadow: 0 0 0 7px color-mix(in srgb, var(--accent) 10%, transparent);
  }
}

/* blocked: dashed amber, cannot happen yet - hover/focus for the reason */
.flow-ribbon-blocked {
  color: var(--c-warning);
}
.flow-ribbon-blocked .flow-ribbon-dot,
.flow-ribbon-fmt-blocked .flow-ribbon-fmt-dot {
  border-color: var(--c-warning);
  border-style: dashed;
  background: transparent;
}
.flow-ribbon-fmt-blocked .flow-ribbon-fmt-dot {
  border: 2px dashed var(--c-warning);
  background: transparent;
}

/* next: hollow, ahead */
.flow-ribbon-next .flow-ribbon-dot,
.flow-ribbon-fmt-next .flow-ribbon-fmt-dot {
  border-color: var(--grey-300);
  background: transparent;
}

.flow-ribbon-seg-link {
  width: 18px;
  height: 1px;
  background: var(--grey-300);
  flex: none;
}
.flow-ribbon-seg-link-done {
  background: var(--c-success);
}

.flow-ribbon-stage-link {
  width: 18px;
  height: 1px;
  background: var(--grey-300);
  flex: none;
  margin: 0 2px;
}
.flow-ribbon-stage-link.is-done {
  background: var(--c-success);
}

/* Second row (Ignas, 2 Nov 2026: "move the Waiting/Resubmit part to another
   line, below"): formats at the left, whose-move + buttons at the right. */
.flow-ribbon-below {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  padding: 4px var(--sp-4);
  background: var(--grey-50);
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
  font-size: 12px;
}
.flow-ribbon-side {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-left: auto;
  white-space: nowrap;
  flex: 0 0 auto;
}

.flow-ribbon-turn {
  color: var(--text-muted);
}
.flow-ribbon-turn-yours {
  color: var(--text);
  font-weight: 600;
}
.flow-ribbon-turn-waiting {
  color: var(--text-muted);
}
.flow-ribbon-turn-none {
  color: var(--text-faint);
}

.flow-ribbon-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  border: 1px solid var(--grey-300);
  border-radius: var(--radius-sm);
  padding: 4px var(--sp-2);
  font-size: 12px;
  color: var(--text);
  background: var(--grey-100);
  line-height: 1.3;
  text-decoration: none;
}
.flow-ribbon-btn:hover {
  text-decoration: none;
  border-color: var(--grey-400, var(--grey-300));
}
.flow-ribbon-btn-primary {
  background: var(--c-accent);
  border-color: var(--c-accent);
  color: var(--c-accent-ink);
  font-weight: 600;
}
.flow-ribbon-btn-ok {
  background: var(--c-success);
  border-color: var(--c-success);
  color: var(--c-accent-ink);
  font-weight: 600;
}
.flow-ribbon-btn-ghost {
  background: transparent;
  color: var(--text-muted);
}
.flow-ribbon-btn.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}


.flow-ribbon-fmts {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: 11px;
  color: var(--text-muted);
  flex-wrap: wrap;
  flex: 1 1 auto;
}
.flow-ribbon-fmt {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 1px var(--sp-2);
}
.flow-ribbon-fmt[data-flow-anchor] {
  cursor: pointer;
}
.flow-ribbon-fmt[data-flow-anchor]:hover {
  border-color: var(--grey-400, var(--grey-300));
}
.flow-ribbon-fmt-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--text-faint);
  flex: none;
}

/* Brief highlight on the real control a ribbon dot/button scrolled to
   (never a hidden action - see components/flow_ribbon.html). */
.flow-ribbon-target {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

body.page-wide > .flow-ribbon-wrap {
  flex: 0 0 auto;
  position: static;
}

@media (max-width: 640px) {
  .flow-ribbon-stage:not(.open) {
    display: none;
  }
  .flow-ribbon-stage-link {
    display: none;
  }
  .flow-ribbon-side {
    width: 100%;
    flex: 1 1 100%;
    margin-left: 0;
  }
}

/* 2 Nov 2026, first live look: a long refusal text made the side panel so
   wide it clipped the Library/Live stages - cap the side panel and
   ellipsise the reason (the full text stays in the button's tooltip). */

/* side panel on row 1 (no formats row, e.g. a list page): keep the divider */
.flow-ribbon > .flow-ribbon-side { border-left: 1px solid var(--border); padding: 0 var(--sp-4); }
