/*
 * Fuscatcher design system — "Mission Control for QA".
 *
 * Two-tone shell: fixed dark sidebar (#101014) + light content plane
 * (#f7f7f8) with white cards, hairline borders, layered subtle shadows.
 * The tenant's white-label --primary / --accent (injected per-org by the
 * layout) drive every accent: buttons, focus rings, active nav, progress,
 * coverage ring.
 *
 * Type: Instrument Sans (UI) · Geist Mono (data, tabular-nums).
 * Plain hand-written CSS (Propshaft). No frameworks, no JS dependencies.
 */

:root {
  --bg: #f7f7f8;
  --surface: #ffffff;
  --surface-2: #fafafa;
  --border: #e4e4e7;
  --border-strong: #d4d4d8;
  --text: #18181b;
  --text-2: #52525b;
  /* Tertiary text carries real content (hints, meta, table headers) — kept at
     4.5:1+ on every plane it sits on (#fff / #fafafa / #f7f7f8) for WCAG AA. */
  --text-3: #71717a;

  /* Dark sidebar palette */
  --shell: #101014;
  --shell-2: #1b1b21;
  --shell-border: #26262c;
  --shell-text: #a1a1aa;
  --shell-text-hi: #f4f4f5;

  --ok: #16a34a;    --ok-bg: #f0fdf4;    --ok-bd: #bbf7d0;
  --bad: #dc2626;   --bad-bg: #fef2f2;   --bad-bd: #fecaca;
  --warn: #d97706;  --warn-bg: #fffbeb;  --warn-bd: #fde68a;
  --idle: #71717a;  --idle-bg: #f4f4f5;  --idle-bd: #e4e4e7;
  /* In-flight work (live Jira statuses: In Progress / In Review / …) */
  --info: #2563eb;  --info-bg: #eff6ff;  --info-bd: #bfdbfe;

  --ui: "Instrument Sans", -apple-system, "Segoe UI", sans-serif;
  --mono: "Geist Mono", ui-monospace, "SF Mono", monospace;

  --space-1: 0.25rem;  /* 4px  — icon gaps, pill padding      */
  --space-2: 0.5rem;   /* 8px  — chip rows, inline clusters   */
  --space-3: 0.75rem;  /* 12px — intra-card element gaps      */
  --space-4: 1rem;     /* 16px — field groups, list rows      */
  --space-5: 1.5rem;   /* 24px — card padding, card stacks    */
  --space-6: 2rem;     /* 32px — sub-section gaps             */
  --space-7: 3rem;     /* 48px — page-level section gaps      */

  --r: 10px;
  --r-lg: 14px;
  --topbar-h: 52px;
  --shadow-xs: 0 1px 2px rgba(24, 24, 27, 0.05);
  --shadow-sm: 0 1px 3px rgba(24, 24, 27, 0.06), 0 1px 2px rgba(24, 24, 27, 0.04);
  --shadow-md: 0 4px 12px -2px rgba(24, 24, 27, 0.08), 0 2px 4px rgba(24, 24, 27, 0.04);
  --ring: 0 0 0 3px color-mix(in srgb, var(--primary) 18%, transparent);
}

/* ---------- Base ---------- */

* { box-sizing: border-box; }
html { height: 100%; }

body {
  margin: 0;
  min-height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: var(--ui);
  font-size: 14px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

main.container {
  max-width: 1080px;
  margin: 0 auto;
  padding: 1.75rem 1.5rem 5rem;
}
.container--wide { max-width: 1280px; }
.container--form { max-width: 44rem; }

h1 {
  font-size: 1.45rem;
  font-weight: 650;
  letter-spacing: -0.022em;
  margin: 0.25rem 0 1.25rem;
}

h2 {
  font-size: 0.8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-2);
  margin: 2rem 0 0.6rem;
}
/* Tenant-colored tick in front of section headers. Card/modal headings that
   restyle h2 as a plain title opt back out below. */
h2::before {
  content: "";
  display: inline-block;
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 2.5px;
  background: linear-gradient(135deg, var(--primary), var(--accent));
  margin-right: 0.5rem;
  vertical-align: 0.02em;
}
.feature-card__head h2::before,
.org-card__head h2::before,
.modal__title::before,
#error_explanation h2::before { content: none; }

h3 { font-size: 0.95rem; font-weight: 600; margin: 1rem 0 0.4rem; }

p { color: var(--text-2); }
p:has(> a:only-child) { margin: 0.5rem 0; }

a { color: var(--text); text-decoration: none; font-weight: 500; }
main a { color: color-mix(in srgb, var(--primary) 85%, var(--text)); }
main a:hover { text-decoration: underline; text-underline-offset: 3px; }

code, .mono { font-family: var(--mono); font-size: 0.85em; }

::selection { background: color-mix(in srgb, var(--primary) 22%, transparent); }

/* ---------- Keyboard focus ---------- */

/* Buttons and fields get the soft box-shadow ring (defined with each control);
   plain links, summaries, and toggle inputs get a crisp outline ring here so
   every interactive element shows keyboard focus. */
a:focus-visible,
summary:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: 4px;
}
a.btn:focus-visible, a.btn-secondary:focus-visible {
  outline: none;
  box-shadow: var(--ring);
}
input[type="checkbox"]:focus-visible,
input[type="color"]:focus-visible,
input[type="file"]:focus-visible {
  outline: none;
  box-shadow: var(--ring);
}
/* Stretched-link cards: the anchor's own outline would hug the invisible
   pseudo-element, so light the whole card up instead. */
.nav-card:has(a.nav-card__title:focus-visible) {
  border-color: color-mix(in srgb, var(--primary) 45%, var(--border));
  box-shadow: var(--ring), var(--shadow-sm);
}
.nav-card a.nav-card__title:focus-visible { outline: none; }

/* ---------- Screen-reader-only text ---------- */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------- App shell: dark top bar + light plane ---------- */

.top-bar {
  position: sticky;
  top: 0;
  /* Persistent global chrome — deliberately well above ordinary in-page
     z-index usage (matrix sticky columns/headers: 3-7, local dropdowns: 25,
     the one legitimate tooltip escape hatch: 70 — see the Tooltips section),
     so a future addition anywhere in normal content can't quietly repeat the
     bug where a chip ended up floating over this bar mid-scroll. Stays below
     true modal-level overlays (the slide-over drawer's backdrop/panel: 60/61,
     and native <dialog>s, which always paint above everything regardless of
     z-index) — those are SUPPOSED to cover the bar. */
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  height: var(--topbar-h);
  padding: 0 1rem;
  background: var(--shell);
  color: var(--border);
}

.top-bar__brand { display: flex; align-items: center; color: inherit; }
.top-bar__brand strong {
  color: var(--shell-text-hi);
  font-size: 0.95rem;
  font-weight: 650;
  letter-spacing: -0.01em;
}

.top-bar__search {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font: 500 0.8rem var(--ui);
  color: var(--shell-text);
  background: var(--shell-2);
  border: 1px solid var(--shell-border);
  border-radius: 8px;
  padding: 0.4rem 0.65rem;
  cursor: pointer;
  max-width: 260px;
}
.top-bar__search:hover { color: var(--shell-text-hi); border-color: #52525b; }
.top-bar__search:focus-visible { outline: none; box-shadow: var(--ring); }
.top-bar__search-label { flex: 1; text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.top-bar__search kbd {
  font: 550 0.68rem var(--mono);
  color: var(--shell-text);
  background: var(--shell);
  border: 1px solid var(--shell-border);
  border-radius: 4px;
  padding: 0.05rem 0.35rem;
}

.top-bar__spacer { flex: 1; }

.user-menu { position: relative; flex: none; }
.user-menu__trigger {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 50%;
  background: color-mix(in srgb, var(--primary) 55%, var(--shell));
  color: #fff;
  font: 650 0.78rem var(--ui);
  border: 1px solid var(--shell-border);
  cursor: pointer;
}
.user-menu__trigger:hover,
.user-menu__trigger[aria-expanded="true"] { background: color-mix(in srgb, var(--primary) 70%, var(--shell)); }
.user-menu__trigger:focus-visible { outline: none; box-shadow: var(--ring); }

.user-menu__panel {
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  z-index: 25;
  min-width: 12rem;
  display: flex;
  flex-direction: column;
  gap: 1px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: var(--shadow-md);
  padding: 0.3rem;
}
.user-menu__panel[hidden] { display: none; }
.user-menu__email {
  font-size: 0.72rem;
  color: var(--text-3);
  padding: 0.42rem 0.6rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  border-bottom: 1px solid var(--border);
  margin-bottom: 0.2rem;
}
/* High-specificity prefix beats the global button[type=submit] / main a rules
   — same reason .kebab__menu .kebab__item needs it. */
.user-menu__panel .user-menu__item {
  display: flex;
  align-items: center;
  width: 100%;
  font: 550 0.82rem var(--ui);
  color: var(--text);
  background: none;
  border: none;
  border-radius: 7px;
  padding: 0.42rem 0.6rem;
  cursor: pointer;
  text-align: left;
  box-shadow: none;
  text-decoration: none;
}
.user-menu__panel .user-menu__item:hover { background: var(--surface-2); text-decoration: none; }

.plane { background: var(--bg); min-height: calc(100vh - var(--topbar-h)); }

/* Authless pages (sign in, reset, invitation accept): no top bar, centered card */
.authless .flash-stack { top: 1.25rem; } /* no topbar on sign-in pages */
.authless main.container { padding-top: 0; }

@media (max-width: 640px) {
  .top-bar { gap: 0.5rem; padding: 0 0.6rem; }
  .top-bar__search { max-width: none; padding: 0.4rem; }
  .top-bar__search-label, .top-bar__search kbd { display: none; }
}

/* ---------- Breadcrumbs ---------- */

.breadcrumbs { margin-bottom: 0.5rem; }
.breadcrumbs ol {
  list-style: none;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin: 0;
  padding: 0;
  font-size: 0.78rem;
  letter-spacing: 0.01em;
}
.breadcrumbs li { display: inline-flex; align-items: center; color: var(--text-3); }
.breadcrumbs li a { color: var(--text-3); font-weight: 500; }
.breadcrumbs li a:hover { color: var(--text); text-decoration: none; }
.breadcrumbs li[aria-current="page"] { color: var(--text-2); font-weight: 550; }
.breadcrumbs__sep { color: var(--text-3); line-height: 0; }

/* ---------- Page header ---------- */

.page-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
  margin-bottom: 1.5rem;
}
.page-header h1 { margin: 0; }
.page-subtitle { font-size: 0.85rem; color: var(--text-2); margin: 0.1rem 0 0; max-width: 60ch; }
.page-header__actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex: none;
  padding-top: 0.15rem;
}

/* ---------- Flash ---------- */

/* Fixed toast stack: feedback floats over the viewport instead of flowing at
   the top of the page, so it is visible regardless of scroll position.
   pointer-events split so the (usually empty) stack never blocks clicks.
   z-index 80: above the drawer (60) and tooltips (70); <dialog> elements
   (palette, shortcuts) live in the browser top layer and still cover it. */
.flash-stack {
  position: fixed;
  top: calc(var(--topbar-h) + 0.75rem);
  left: 50%;
  translate: -50% 0;
  z-index: 80;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  pointer-events: none;
  width: max-content;
  max-width: min(48rem, calc(100vw - 2rem));
}

.flash {
  pointer-events: auto;
  box-shadow: 0 10px 28px rgb(20 40 25 / 0.16);
  padding: 0.6rem 0.9rem;
  font-size: 0.85rem;
  font-weight: 500;
  border-radius: var(--r);
  border: 1px solid;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.flash::before { font-size: 0.7rem; }
.flash.notice { background: var(--ok-bg); border-color: var(--ok-bd); color: #166534; }
.flash.notice::before { content: "●"; color: var(--ok); }
.flash.alert  { background: var(--bad-bg); border-color: var(--bad-bd); color: #991b1b; }
.flash.alert::before { content: "●"; color: var(--bad); }

.flash__text { flex: 1; }
.flash__close {
  flex: none;
  font: 500 1.05rem/1 var(--ui);
  color: inherit;
  opacity: 0.7; /* >=3:1 against the flash tint (WCAG non-text contrast) */
  background: none;
  border: none;
  border-radius: 5px;
  padding: 0.05rem 0.3rem;
  cursor: pointer;
}
.flash__close:hover { opacity: 1; }
.flash__close:focus-visible { outline: none; box-shadow: var(--ring); }
.flash--leaving { opacity: 0; translate: 0 -6px; transition: opacity 0.18s ease, translate 0.18s ease; }

/* ---------- Buttons ---------- */

input[type="submit"], button[type="submit"], .btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font: 600 0.85rem var(--ui);
  color: #fff;
  background: var(--primary);
  border: 1px solid color-mix(in srgb, var(--primary) 85%, #000);
  border-radius: var(--r);
  padding: 0.5rem 0.95rem;
  cursor: pointer;
  box-shadow: var(--shadow-xs), inset 0 1px 0 rgba(255, 255, 255, 0.12);
  transition: filter 0.12s ease, box-shadow 0.12s ease;
}
main a.btn { color: #fff; }
a.btn:hover { text-decoration: none; }
input[type="submit"]:hover, button[type="submit"]:hover, .btn:hover { filter: brightness(1.06); box-shadow: var(--shadow-sm); }
input[type="submit"]:focus-visible, button:focus-visible, .btn:focus-visible { outline: none; box-shadow: var(--ring); }
input[type="submit"]:active, button[type="submit"]:active { filter: brightness(0.97); }

.btn-secondary, main a.btn-secondary, input[type="submit"].btn-secondary, button.btn-secondary {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font: 600 0.85rem var(--ui);
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 0.5rem 0.95rem;
  cursor: pointer;
  box-shadow: var(--shadow-xs);
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
a.btn-secondary:hover { text-decoration: none; }
.btn-secondary:hover { filter: none; border-color: color-mix(in srgb, var(--primary) 55%, var(--border)); box-shadow: var(--shadow-sm); }

.btn-ghost-danger, button.btn-ghost-danger, input[type="submit"].btn-ghost-danger {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font: 600 0.8rem var(--ui);
  color: var(--bad);
  background: none;
  border: 1px solid transparent;
  border-radius: var(--r);
  padding: 0.45rem 0.7rem;
  cursor: pointer;
  box-shadow: none;
  transition: background 0.12s ease, border-color 0.12s ease;
}
.btn-ghost-danger:hover { filter: none; background: var(--bad-bg); border-color: var(--bad-bd); box-shadow: none; }

/* ---------- Forms ---------- */

label {
  display: block;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text);
  margin: 1rem 0 0.3rem;
}

input[type="text"], input[type="email"], input[type="password"], input[type="file"],
input[type="number"], textarea, select {
  font: 400 0.9rem var(--ui);
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 0.5rem 0.7rem;
  width: 100%;
  max-width: 26rem;
  box-shadow: var(--shadow-xs);
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
textarea { min-height: 6.5rem; max-width: 34rem; resize: vertical; }
input:focus, textarea:focus, select:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: var(--ring);
}
input::placeholder, textarea::placeholder { color: var(--text-3); }

form div { margin-bottom: var(--space-4); }
.form-card form > div:first-child label { margin-top: 0; }

input[type="checkbox"] { accent-color: var(--primary); width: 1rem; height: 1rem; }
input[type="color"] { border: 1px solid var(--border); border-radius: var(--r); padding: 0.15rem; height: 2.2rem; width: 4rem; background: var(--surface); cursor: pointer; }

.field-hint { font-size: 0.78rem; color: var(--text-3); margin: 0.25rem 0 0; }
.field-hint.is-met { color: var(--ok); }

/* Platform applicability — real checkboxes styled as pill toggles (CSS-only) */
fieldset.platforms { border: none; margin: 1rem 0 0; padding: 0; }
fieldset.platforms legend { font-size: 0.8rem; font-weight: 600; color: var(--text); padding: 0; }
.platform-pills { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.6rem; }
.platform-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0;
  font: 600 0.82rem var(--ui);
  color: var(--text-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.38rem 0.9rem;
  cursor: pointer;
  position: relative;
  transition: border-color 0.12s ease, background 0.12s ease, color 0.12s ease;
}
.platform-pill input[type="checkbox"] { position: absolute; opacity: 0; pointer-events: none; }
.platform-pill::before { content: "+"; font-weight: 600; color: var(--text-3); }
.platform-pill:hover { border-color: var(--border-strong); }
.platform-pill:has(input:checked) {
  color: var(--text);
  background: color-mix(in srgb, var(--primary) 8%, var(--surface));
  border-color: color-mix(in srgb, var(--primary) 55%, var(--border));
}
.platform-pill:has(input:checked)::before { content: "✓"; color: var(--primary); }
.platform-pill:has(input:focus-visible) { box-shadow: var(--ring); }

label.check-inline { display: inline-flex; align-items: center; gap: 0.45rem; margin: 1rem 0 0.1rem; }
.check-inline input[type="checkbox"] { margin: 0; }

.form-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: 1.5rem;
  max-width: 40rem;
  margin-bottom: 1.5rem;
}
.form-card label:first-child, .form-card h2:first-child { margin-top: 0; }
.form-card h2 { margin-top: 0; }
.form-card__title { font-size: 1rem; font-weight: 650; letter-spacing: -0.01em; margin: 0 0 0.35rem; }

/* A standing condition worth reading before acting, as opposed to a flash,
   which reports something that just happened and then goes away. Built from the
   same --warn / --bad token trios the flashes and #error_explanation already
   use, so a warning looks like a warning everywhere in the product. */
.callout {
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 0.7rem 0.9rem;
  margin: 0 0 1rem;
  font-size: 0.85rem;
  line-height: 1.5;
}
.callout strong { display: block; margin-bottom: 0.15rem; }
.callout--warn { background: var(--warn-bg); border-color: var(--warn-bd); color: #92400e; }
.callout--bad { background: var(--bad-bg); border-color: var(--bad-bd); color: #991b1b; }

.form-actions { display: flex; align-items: center; gap: 0.6rem; margin-top: 1.25rem; }

#error_explanation {
  background: var(--bad-bg);
  border: 1px solid var(--bad-bd);
  border-radius: var(--r);
  padding: 0.65rem 0.9rem;
  margin: 1rem 0;
  font-size: 0.85rem;
  color: #991b1b;
}
#error_explanation ul { margin: 0; padding-left: 1.1rem; }

/* ---------- Cards & tables ---------- */

table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  font-size: 0.875rem;
  margin: 0.5rem 0 1.5rem;
  overflow: hidden;
}

thead th {
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-3);
  text-align: left;
  padding: 0.6rem 0.9rem;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
}

tbody td {
  padding: 0.6rem 0.9rem;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
tbody tr:last-child td { border-bottom: none; }
tbody tr { transition: background 0.1s ease; }
tbody tr:hover { background: var(--surface-2); }

tr.needs-review { background: var(--warn-bg); }
tr.needs-review td:first-child { box-shadow: inset 3px 0 0 var(--warn); }

.detail-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: 1.25rem 1.5rem;
  margin-bottom: var(--space-4);
}
.detail-card h2:first-child { margin-top: 0; }
.prose { font-size: 0.9rem; line-height: 1.7; color: var(--text); max-width: 65ch; }
.prose p { color: inherit; }

/* ---------- Nav cards (projects / apps grids) ---------- */

.card-grid {
  list-style: none;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 0.9rem;
}
.card-grid > li { margin: 0; }

.nav-card, main .nav-card {
  display: block;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: 1.15rem 2.4rem 1.15rem 1.25rem;
  color: var(--text);
  position: relative;
  transition: border-color 0.12s ease, box-shadow 0.12s ease, transform 0.12s ease;
}
.nav-card::after {
  content: "→";
  position: absolute;
  right: 1.1rem;
  top: 50%;
  translate: 0 -50%;
  color: var(--text-3);
  transition: translate 0.12s ease, color 0.12s ease;
}
.nav-card:hover {
  text-decoration: none;
  border-color: color-mix(in srgb, var(--primary) 45%, var(--border));
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}
.nav-card:hover::after { translate: 3px -50%; color: var(--primary); }
.nav-card__title { display: block; font-size: 1rem; font-weight: 600; letter-spacing: -0.01em; }
/* The title is the anchor; a stretched pseudo makes the whole card clickable
   while meta text stays OUTSIDE the <a> (tests assert exact link text). */
main a.nav-card__title { color: var(--text); }
main a.nav-card__title:hover { text-decoration: none; }
a.nav-card__title::before { content: ""; position: absolute; inset: 0; }
.nav-card__meta { display: block; font-size: 0.75rem; color: var(--text-3); margin-top: 0.15rem; }

/* ---------- Per-app coverage rollup (apps grid, BRD Req 8) ---------- */

/* Project-level glance line above the grid: the least-covered verdict plus
   every app's latest-cycle run share side by side. */
.rollup-glance {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-4);
  margin: 0 0 var(--space-4);
  padding: 0.55rem 0.9rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  font-size: 0.8rem;
  color: var(--text-2);
}
.rollup-glance__verdict b { color: var(--text); }
.rollup-glance__list { display: inline-flex; flex-wrap: wrap; gap: var(--space-3); margin-left: auto; }
.rollup-glance__app { white-space: nowrap; color: var(--text-3); }
.rollup-glance__app .mono { color: var(--text-2); }

/* Slim coverage bar on each app card — share of the latest cycle's checks
   run; tinted red while the cycle has open failures. */
.cov-bar {
  height: 5px;
  margin-top: 0.6rem;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  overflow: hidden;
}
.cov-bar__fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--ok);
  transition: width 0.3s ease;
}
.cov-bar__fill--failing { background: var(--bad); }

.app-card__stats { font-size: 0.75rem; color: var(--text-3); margin: 0.4rem 0 0; }
.app-card__stats .mono { color: var(--text-2); }
.app-card__stats--empty { margin-top: 0.5rem; }
/* The stretched title link covers the card; inline links must sit above it. */
.app-card__stats a { position: relative; z-index: 2; font-weight: 600; }
.app-card__failing { color: var(--bad); font-weight: 600; }

/* Relative timestamps share one voice; the absolute moment rides on hover. */
time.reltime { white-space: nowrap; cursor: default; }

/* ---------- Empty states ---------- */

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.4rem;
  background: var(--surface);
  border: 1px dashed color-mix(in srgb, var(--primary) 30%, var(--border-strong));
  border-radius: var(--r-lg);
  padding: 3rem 2rem;
  max-width: 34rem;
  margin: 2rem auto;
}
.empty-state .icon { color: var(--text-3); }
.empty-state .glyph { color: var(--primary); }
.empty-state h3 { font-size: 1rem; font-weight: 600; margin: 0.4rem 0 0; }
.empty-state p { font-size: 0.85rem; color: var(--text-2); margin: 0; }
.empty-state .btn { margin-top: 0.9rem; }

/* ---------- Stat tiles & coverage ring ---------- */

.stat-tile {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: 0.9rem 1.1rem;
  min-width: 8.5rem;
}
.stat-tile__label {
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-3);
}
.stat-tile__value {
  font: 600 1.6rem var(--mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  color: var(--text);
  line-height: 1.25;
}
.stat-tile__sub { font-size: 0.72rem; color: var(--text-3); }
.stat-tile--ok .stat-tile__value { color: var(--ok); }
.stat-tile--bad .stat-tile__value { color: var(--bad); }
.stat-tile--warn .stat-tile__value { color: var(--warn); }

.coverage-ring {
  width: 96px;
  aspect-ratio: 1;
  border-radius: 50%;
  display: grid;
  place-content: center;
  text-align: center;
  flex: none;
  background:
    radial-gradient(closest-side, var(--surface) 79%, transparent 80% 100%),
    conic-gradient(var(--primary) calc(var(--pct) * 1%), var(--idle-bg) 0);
}
/* Was margin: 0 auto in an inline style attribute on the hub's overall ring.
   It is a fixed value, so it belongs here — only the per-record --pct is
   genuinely dynamic, and that arrives through the css-value controller. */
.coverage-ring--centered { margin: 0 auto; }

/* --pct is set from JavaScript, so a ring rendered before Stimulus connects —
   or with JS unavailable — would inherit nothing and paint a full circle in the
   primary colour, reading as 100%. Zero is the honest starting point: an empty
   ring that fills in. */
.coverage-ring { --pct: 0; }

.coverage-ring__num { font: 600 1.15rem var(--mono); font-variant-numeric: tabular-nums; }
.coverage-ring__label {
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-3);
}

.cycle-dash {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.9rem;
  margin-bottom: var(--space-6);
}
.cycle-dash__platforms {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.3rem;
}

/* ---------- Project dashboard (Overview / Apps tabs) ---------- */

.tab-bar {
  display: flex;
  gap: 0.25rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 1.5rem;
}
.tab-bar__tab {
  appearance: none;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  padding: 0.55rem 0.9rem;
  font: 600 0.9rem var(--ui);
  color: var(--text-3);
  cursor: pointer;
  transition: color 0.12s ease, border-color 0.12s ease;
}
.tab-bar__tab:hover { color: var(--text); }
.tab-bar__tab[aria-selected="true"] {
  color: var(--text);
  border-bottom-color: var(--primary);
}
.tab-bar__tab:focus-visible {
  outline: none;
  box-shadow: var(--ring);
  border-radius: var(--r) var(--r) 0 0;
}

.dash-section { margin-bottom: var(--space-7); }

.cycle-card-grid {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 0.9rem;
}
.cycle-card {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.35rem 1.1rem;
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: 1.15rem 1.25rem 1rem;
}
.cycle-card__name {
  margin: 0.45rem 0 0.05rem;
  font-size: 1.05rem;
  font-weight: 650;
  letter-spacing: -0.015em;
}
.cycle-card__name a { color: var(--text); }
.cycle-card__meta { font-size: 0.74rem; color: var(--text-3); }
.cycle-card__stats {
  display: flex;
  gap: 0.9rem;
  margin-top: 0.55rem;
  font-size: 0.82rem;
  font-variant-numeric: tabular-nums;
  color: var(--text-2);
}
.cycle-card__failed { color: var(--bad); font-weight: 600; }
.cycle-card__actions {
  grid-column: 1 / -1;
  border-top: 1px solid var(--border);
  margin-top: 0.6rem;
  padding-top: 0.85rem;
  display: flex;
  gap: 0.6rem;
}

.failure-list {
  list-style: none;
  padding: 0;
  margin: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.failure-row {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  padding: 0.7rem 1rem;
}
.failure-row + .failure-row { border-top: 1px solid var(--border); }
/* Doubled selector outranks the later base .evidence-thumb size */
.failure-row .evidence-thumb.failure-row__thumb { width: 46px; height: 34px; }
.failure-row__text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.failure-row__title { font-weight: 600; color: var(--text); }
a.failure-row__title:hover { color: var(--primary); text-decoration: none; }
.failure-row__meta {
  font-size: 0.74rem;
  color: var(--text-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Failures strip (cycle page, beneath the matrix) — the same failure-row
   language as the dashboard, extended with thumbnails, notes and bug state. */
.failures-strip h2 { font-size: 1rem; margin: 0 0 0.6rem; }
.req-board h2 { font-size: 1rem; text-transform: none; letter-spacing: -0.01em; margin: 0 0 0.6rem; }
.failure-row--strip { align-items: flex-start; }
.failure-row--strip .chip { margin-top: 0.15rem; }
.failure-row__thumbs { display: flex; gap: 0.35rem; flex-shrink: 0; }
.failure-row__notes {
  margin: 0.2rem 0 0;
  font-size: 0.78rem;
  color: var(--text-2);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.failure-row__jira { flex-shrink: 0; display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 0.4rem; min-height: 34px; }
.failure-row__jira-key { font-size: 0.78rem; font-weight: 600; }
.failure-row__jira-muted { font-size: 0.74rem; color: var(--text-3); white-space: nowrap; }
.failure-row__report { padding: 0.28rem 0.7rem; font-size: 0.78rem; white-space: nowrap; }

/* Cycles index: per-row run entry */
.cycles-table__run { text-align: right; width: 1%; }
.cycles-table__run-link { padding: 0.28rem 0.7rem; font-size: 0.78rem; white-space: nowrap; }

/* ---------- Badges & chips ----------
   One pill language for the whole app.
   - .badge          status pill with a leading dot (drawer, evidence page)
   - .chip           neutral metadata pill; .chip--active is the shared
                     primary-tinted "selected" formula (filter chips today)
   - .req-chip / .hist-pill  compact status pills (requirements board,
                     execution history) — same shape, same tone map
   - the status tone map below is the ONE place passed/failed/blocked
     pills get their colors; new pill variants join these selector lists
     instead of re-rolling tones. */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font: 600 0.72rem var(--ui);
  padding: 0.18rem 0.6rem;
  border: 1px solid;
  border-radius: 999px;
  white-space: nowrap;
}
.badge::before { content: "●"; font-size: 0.6rem; }

/* Compact status pills share one shape… */
.req-chip, .hist-pill {
  font: 500 0.7rem var(--mono);
  padding: 0.1rem 0.45rem;
  border: 1px solid var(--idle-bd);
  border-radius: 999px;
  background: var(--idle-bg);
  color: var(--idle);
  white-space: nowrap;
}
/* …and every status-tinted pill resolves to one tone map. */
.badge--passed,  .req-chip--passed,  .hist-pill--passed  { color: var(--ok);   background: var(--ok-bg);   border-color: var(--ok-bd); }
.badge--failed,  .req-chip--failed,  .hist-pill--failed  { color: var(--bad);  background: var(--bad-bg);  border-color: var(--bad-bd); }
.badge--blocked, .req-chip--blocked, .hist-pill--blocked { color: var(--warn); background: var(--warn-bg); border-color: var(--warn-bd); }
.badge--not_run { color: var(--idle); background: var(--idle-bg); border-color: var(--idle-bd); border-style: dashed; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font: 500 0.74rem var(--mono);
  padding: 0.2rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--text-2);
}
/* The shared "active/selected" chip formula — tenant --primary tinted. */
.chip--active {
  background: color-mix(in srgb, var(--primary) 12%, var(--surface));
  border-color: color-mix(in srgb, var(--primary) 45%, var(--border));
  color: color-mix(in srgb, var(--primary) 80%, var(--text));
}

.prio {
  display: inline-flex;
  align-items: center;
  font: 500 0.66rem var(--mono);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 0.12rem 0.45rem;
  border: 1px solid var(--idle-bd);
  border-radius: 999px;
  background: var(--idle-bg);
  color: var(--idle);
  white-space: nowrap;
}
.prio--critical { color: var(--bad);  background: var(--bad-bg);  border-color: var(--bad-bd); }
.prio--high     { color: var(--warn); background: var(--warn-bg); border-color: var(--warn-bd); }

.review-flag {
  display: inline-flex;
  align-items: center;
  font: 600 0.68rem var(--ui);
  padding: 0.12rem 0.5rem;
  border: 1px solid var(--warn-bd);
  border-radius: 999px;
  background: var(--warn-bg);
  color: var(--warn);
  white-space: nowrap;
}

.mark--yes { color: var(--ok); }
.mark--no { color: var(--text-3); }

.row-action { font-size: 0.78rem; color: var(--text-2); }

/* Chip row under a test-case title (priority, platforms, review flag) */
.tc-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; margin: 0 0 var(--space-4); }

/* ---------- Tooltips ---------- */

/* position: relative is universal — every [data-tooltip] trigger needs it so
   its ::after bubble (below) can anchor to it. z-index is deliberately NOT
   universal: a past fix put z-index:70 here for every trigger, to solve one
   specific case (a trigger whose OWN styling — opacity < 1 — creates a local
   stacking context that traps its ::after below unrelated sticky/fixed page
   chrome, e.g. .dash-grid__rail). That blanket rule over-corrected: it also
   promotes perfectly ordinary, non-trapped elements (e.g. a plain kind chip)
   into the SAME page-root z-index:70 layer, so any of them can end up
   floating on top of chrome they were never meant to compete with — this is
   exactly how a .chip badge once ended up rendering over .top-bar mid-scroll.
   Scope the escape hatch to the specific trigger(s) that actually need it —
   e.g. .cycle-card__app-remove--disabled's own rule below adds its z-index:70
   right alongside the opacity that necessitates it — instead of every
   [data-tooltip] element. If you hit this same bug for a NEW trigger, give
   THAT trigger's own rule a z-index:70 — don't widen this one. */
[data-tooltip] { position: relative; }
[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute; left: 50%; bottom: calc(100% + 6px);
  translate: -50% 0; z-index: 70;
  max-width: 240px; width: max-content;
  font: 500 0.72rem var(--ui); line-height: 1.4; text-transform: none; letter-spacing: 0;
  color: var(--shell-text-hi); background: var(--shell);
  border-radius: 6px; padding: 0.3rem 0.55rem;
  box-shadow: var(--shadow-md);
  opacity: 0; pointer-events: none; transition: opacity 0.12s ease 0.15s;
}
[data-tooltip]:hover::after, [data-tooltip]:focus-visible::after { opacity: 1; }
@media (hover: none) { [data-tooltip]::after { display: none; } }

/* ---------- Cycles index ---------- */

.cycles-table__name a { color: var(--text); font-weight: 600; }

/* ---------- Matrix ---------- */

/* Sticky header: the generic table overflow:hidden (rounded-corner clipping)
   would defeat position:sticky, so the matrix opts out and rounds its own
   corner cells instead. */
table.matrix { overflow: visible; }
.matrix thead th { position: sticky; top: 0; z-index: 5; }
.matrix thead th:first-child { border-top-left-radius: var(--r-lg); }
.matrix thead th:last-child { border-top-right-radius: var(--r-lg); }
.matrix tbody tr:last-child td:first-child { border-bottom-left-radius: var(--r-lg); }
.matrix tbody tr:last-child td:last-child { border-bottom-right-radius: var(--r-lg); }

.matrix th.col-platform { width: 13%; }
.matrix td.matrix__test { min-width: 220px; }
.matrix td.cell-td { padding: 0.45rem 0.55rem; }
.matrix__test .prio { margin-left: 0.4rem; vertical-align: 0.08em; }

.matrix td.na {
  background:
    repeating-linear-gradient(-45deg, transparent 0 6px, rgba(24, 24, 27, 0.03) 6px 8px),
    var(--surface-2);
  color: var(--text-3);
  text-align: center;
  font-family: var(--mono);
  font-size: 0.8rem;
}

.matrix__group td {
  background: var(--surface-2);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-2);
  padding: 0.45rem 0.9rem;
}
.matrix__title { font-weight: 550; }
main a.matrix__title { color: var(--text); }
.matrix__steps summary {
  font-size: 0.72rem;
  font-weight: 600;
  color: color-mix(in srgb, var(--primary) 85%, var(--text));
  cursor: pointer;
}
.matrix__steps div { font-size: 0.8rem; color: var(--text-2); padding: 0.35rem 0 0.15rem; }
.matrix__legend { font-size: 0.75rem; color: var(--text-3); margin: var(--space-2) 0 var(--space-4); }

.cell { padding: 0; }
.cell form { margin: 0; }

.cell select {
  width: 100%;
  min-width: 6.8rem;
  max-width: none;
  font: 600 0.78rem var(--ui);
  text-align: center;
  border-radius: 999px;
  padding: 0.32rem 0.5rem;
  cursor: pointer;
  box-shadow: none;
  appearance: none;
  transition: box-shadow 0.12s ease, border-color 0.12s ease;
}
.cell select:focus { box-shadow: var(--ring); }
.cell select:disabled { opacity: 0.6; cursor: default; }

.cell.status-not_run select { color: var(--idle); background: var(--idle-bg); border-color: var(--idle-bd); border-style: dashed; }
.cell.status-passed  select { color: var(--ok);   background: var(--ok-bg);   border-color: var(--ok-bd); }
.cell.status-failed  select { color: var(--bad);  background: var(--bad-bg);  border-color: var(--bad-bd); }
.cell.status-blocked select { color: var(--warn); background: var(--warn-bg); border-color: var(--warn-bd); }

.cell a {
  display: block;
  font-size: 0.72rem;
  font-weight: 600;
  text-align: center;
  margin-top: 0.3rem;
  color: var(--bad) !important;
}
.cell a.cell__note-link { color: var(--warn) !important; }

/* ---------- Matrix power tools (filter bar, selection, keyboard grid) ---------- */

/* Filter & search bar — one row: search + facet selects, then chips, the
   N-of-M count, and the clear affordance pushed to the right. */
.matrix-filters-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin: var(--space-4) 0 var(--space-3);
}
.matrix-filters { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin: 0; }
.matrix-filters__search {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding-left: 0.65rem;
  color: var(--text-3);
}
.matrix-filters__search:focus-within { box-shadow: var(--ring); }
.matrix-filters__search input {
  border: none;
  background: transparent;
  width: 13rem;
  font-size: 0.82rem;
  padding: 0.38rem 0.65rem 0.38rem 0.15rem;
}
.matrix-filters__search input:focus, .matrix-filters__search input:focus-visible { outline: none; box-shadow: none; }
.matrix-filters select { width: auto; max-width: 11rem; font-size: 0.8rem; padding: 0.34rem 1.7rem 0.34rem 0.6rem; }
.matrix-filters__meta { display: inline-flex; align-items: center; gap: var(--space-2); margin-left: auto; }
/* Filter chips take their active state from the shared .chip--active formula. */
.matrix-count { font-size: 0.75rem; color: var(--text-2); white-space: nowrap; }
.matrix-filters__clear { font-size: 0.78rem; font-weight: 600; white-space: nowrap; }

/* Scroll box: horizontal room for four platform columns AND a vertically
   sticky header — the box owns both axes, the header sticks to its top and
   the test column to its left, so context survives scrolling a big matrix. */
.matrix-scroll {
  overflow: auto;
  overscroll-behavior: contain;
  max-height: calc(100dvh - 10rem - var(--topbar-h));
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  margin: 0.5rem 0 var(--space-3);
}
.matrix-scroll table.matrix { margin: 0; box-shadow: none; }

/* Sticky first column (feature/test context). The group rows span the full
   width, so only their label needs to stick. */
.matrix th:first-child, .matrix td:first-child { position: sticky; left: 0; z-index: 3; }
.matrix tbody tr:not(.matrix__group) td:first-child { background: var(--surface); border-right: 1px solid var(--border); }
.matrix tbody tr:hover td:first-child { background: var(--surface-2); }
.matrix thead th:first-child { z-index: 7; border-right: 1px solid var(--border); }
.matrix__group-label { position: sticky; left: var(--space-4); display: inline-block; }

/* Roving keyboard focus rides the td (never the select — matrix-guard only
   protects an OPEN control, so a merely-focused cell still takes broadcasts). */
.matrix td[tabindex] { outline: none; }
.matrix td[tabindex]:focus-visible {
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--primary) 55%, transparent);
  border-radius: 4px;
}

/* Row selection */
.matrix__check { accent-color: var(--primary); margin: 0 0.45rem 0 0; vertical-align: -0.12em; cursor: pointer; }

/* Sticky bulk bar — dark pill floating over the content plane, visible only
   while ≥1 row is selected. */
.bulk-bar {
  position: fixed;
  bottom: 1.1rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 60;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  background: var(--shell);
  color: var(--shell-text-hi);
  border: 1px solid var(--shell-border);
  border-radius: 999px;
  padding: 0.5rem 1rem;
  box-shadow: var(--shadow-md);
}
@media (prefers-reduced-motion: no-preference) {
  .bulk-bar:not([hidden]) { animation: bulk-rise 0.16s ease-out; }
}
@keyframes bulk-rise { from { opacity: 0; translate: 0 10px; } }
.bulk-bar__count { font-size: 0.8rem; color: var(--shell-text); white-space: nowrap; }
.bulk-bar__count strong { color: var(--shell-text-hi); font-family: var(--mono); }
.bulk-bar__form { display: flex; align-items: center; gap: var(--space-2); margin: 0; }
.bulk-bar__btn { font-size: 0.78rem; padding: 0.35rem 0.8rem; white-space: nowrap; }
.bulk-bar__why {
  font-size: 0.72rem;
  color: var(--shell-text);
  border-bottom: 1px dashed var(--shell-border);
  cursor: help;
  white-space: nowrap;
}
.bulk-bar__clear {
  background: none;
  border: none;
  padding: 0;
  color: var(--shell-text);
  font: 600 0.78rem var(--ui);
  cursor: pointer;
}
.bulk-bar__clear:hover { color: var(--shell-text-hi); }

/* Narrow screens: give the row back to the content — unfreeze the first
   column, let the page own vertical scroll, recentre the bar (the sidebar
   collapses under 900px). */
@media (max-width: 900px) {
  .bulk-bar {
    left: 50%;
    max-width: calc(100vw - 2rem);
    flex-wrap: wrap;
    justify-content: center;
    border-radius: var(--r-lg);
  }
}
@media (max-width: 760px) {
  .matrix th:first-child, .matrix td:first-child { position: static; }
  .matrix tbody tr:not(.matrix__group) td:first-child { border-right: none; }
  .matrix-scroll { max-height: none; }
  .matrix-filters__search { flex: 1; }
  .matrix-filters__search input { width: 100%; }
  .matrix-filters__meta { margin-left: 0; }
}
@media print {
  .matrix-scroll { overflow: visible; max-height: none; box-shadow: none; }
}

/* ---------- Evidence page (results#edit) ---------- */

.result-layout {
  display: grid;
  grid-template-columns: 340px minmax(0, 1fr);
  gap: 1.25rem;
  align-items: start;
}
.result-layout__side { position: sticky; top: calc(var(--topbar-h) + 1rem); }
@media (max-width: 900px) {
  .result-layout { grid-template-columns: 1fr; }
  .result-layout__side { position: static; }
}
.result-layout .form-card { max-width: none; }

.tc-context h3 {
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-3);
  margin: 1.1rem 0 0.25rem;
}
.tc-context__meta { font-size: 0.75rem; margin: -0.3rem 0 0.6rem; }

.evidence-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}
main a.evidence-item {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  color: var(--text);
}
main a.evidence-item:hover { text-decoration: none; }
main a.evidence-item:hover .evidence-item__name { text-decoration: underline; text-underline-offset: 3px; }
.evidence-thumb {
  display: block;
  width: 56px;
  height: 40px;
  object-fit: cover;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  flex: none;
}
.evidence-thumb--file { display: inline-flex; align-items: center; justify-content: center; color: var(--text-3); }
.evidence-item__name { font-size: 0.78rem; }

/* ---------- Sections / catalogue ---------- */

section { margin-bottom: var(--space-7); }

.feature-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: 0;
  overflow: hidden;
  margin-bottom: var(--space-5);
}
.feature-card__head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.8rem 1rem;
  border-bottom: 1px solid var(--border);
}
.feature-card__head h2 {
  font-size: 0.95rem;
  font-weight: 600;
  text-transform: none;
  letter-spacing: -0.01em;
  color: var(--text);
  margin: 0;
  flex: 1;
}
/* Author view wraps the title in a turbo-frame (inline rename target). */
.feature-card__name { display: block; flex: 1; min-width: 0; }
.feature-card table { border: none; box-shadow: none; border-radius: 0; margin: 0; }
.feature-card__foot {
  padding: 0.6rem 1rem;
  background: var(--surface-2);
  border-top: 1px solid var(--border);
}
.feature-card__empty { font-size: 0.85rem; color: var(--text-3); padding: var(--space-4) 1.25rem var(--space-5); margin: 0; }

/* Catalogue table inside a feature card */
.catalog th.col-platform, .catalog td.mark-cell { text-align: center; width: 7%; }
.catalog__title { color: var(--text); font-weight: 550; }
.catalog td .review-flag { margin-left: 0.4rem; }
.catalog__edit { text-align: right; }
.mark-cell .icon { vertical-align: -0.12em; }

/* ---------- Org settings ---------- */

.color-row,
.invite-project-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-1) var(--space-4);
  max-width: 26rem;
}
.color-row > div, .invite-project-row > div { margin: 0; }

/* ---------- Admin console (platform host) ---------- */

.org-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  margin-bottom: 1.25rem;
  max-width: 44rem;
}
.org-card__head {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  padding: 0.85rem 1.1rem;
  border-bottom: 1px solid var(--border);
}
.org-card__head h2 {
  font-size: 1rem;
  font-weight: 600;
  text-transform: none;
  letter-spacing: -0.01em;
  color: var(--text);
  margin: 0;
}
.org-card__head .mono { font-size: 0.75rem; }
.org-card__head .kebab { margin-left: auto; }
/* This trigger sits ~14px below org-card's own top edge — the shared
   tooltip's default (bottom: calc(100% + 6px), opening upward) needs ~22px
   of clearance above the trigger, which doesn't exist: the whole bubble
   renders above org-card's top boundary and org-card's overflow:hidden (for
   its rounded corners) clips it entirely, so it never paints at all. Same
   root cause as the domain-row trash-icon fix above (an edge trigger inside
   an overflow:hidden card), different edge — here it's the top, not the
   right — so the fix opens the bubble downward and right-aligns it instead
   of centering, keeping it inside the card on both axes. */
.org-card__head .kebab__btn::after {
  left: auto; right: 0; bottom: auto; top: calc(100% + 6px); translate: 0 0;
}
.org-card__domains { padding: 0.35rem 1.1rem; }
.org-card__none { font-size: 0.85rem; padding: 0.55rem 0; margin: 0; }

.domain-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--border);
}
.domain-row:last-child { border-bottom: none; }
.domain-row__host { flex: 1; font-size: 0.85rem; }
.domain-row form { margin: 0; }
.domain-row .btn-secondary { padding: 0.28rem 0.7rem; font-size: 0.78rem; }
.domain-row .btn-ghost-danger { padding: 0.28rem 0.4rem; }
/* This trigger sits flush against .org-card's right edge (overflow:hidden,
   there to clip content to the card's rounded corners) — the shared
   tooltip's centered anchor (left: 50%; translate: -50% 0) pushes roughly
   half the bubble past that edge, where org-card's overflow:hidden then
   clips the text. This is a plain overflow-clipping bug, unrelated to the
   z-index/stacking-context one documented above [data-tooltip] — don't
   "fix" it by touching z-index or org-card's overflow. Anchoring the bubble
   to the trigger's right edge instead of centering keeps it entirely left
   of the trigger, safely inside the card. */
.domain-row .btn-ghost-danger::after { left: auto; right: 0; translate: 0 0; }

form.domain-add {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.75rem 1.1rem;
  margin: 0;
  background: var(--surface-2);
  border-top: 1px solid var(--border);
}
.domain-add input[type="text"] { margin: 0; max-width: 20rem; }
.domain-add div { margin: 0; }

/* ---------- Auth ---------- */

.auth-card {
  max-width: 23rem;
  margin: 8vh auto 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-md);
  padding: 2rem 2rem 1.6rem;
}
.auth-card__brand { margin-bottom: 0.9rem; }
.auth-card__brand strong { font-size: 1rem; font-weight: 650; letter-spacing: -0.01em; }
.auth-card h1 { font-size: 1.25rem; margin: 0 0 0.15rem; }
.auth-card .auth-sub { color: var(--text-3); font-size: 0.85rem; margin: 0 0 0.9rem; }
.auth-card .auth-note { font-size: 0.8rem; color: var(--text-2); margin: 0 0 0.5rem; }
.auth-card input { max-width: none; }
.auth-card input[type="submit"] { width: 100%; margin-top: 1.1rem; justify-content: center; }
.auth-card .auth-foot { margin: 1rem 0 0; font-size: 0.82rem; text-align: center; }

/* ---------- Recovery codes ---------- */

/* The one screen in the product that cannot be revisited — it says so itself.
   The hierarchy is therefore the whole design: keeping the codes has to outrank
   destroying them, which is the opposite of how this page originally read. */
.recovery-codes { margin: 0 0 0.75rem; padding-left: 1.15rem; line-height: 1.7; }

/* Full width so it reads as the natural companion to the list above it rather
   than an afterthought floating beside it. */
.recovery-codes__copy { width: 100%; justify-content: center; }

/* Two full-width actions one under the other. A row is the wrong shape inside
   an auth-card: it is 23rem across and these labels are sentences, so side by
   side they wrap into three-line stacks that read as broken. */
.stacked-actions { display: flex; flex-direction: column; gap: 0.6rem; margin-top: 1.2rem; }
.stacked-actions .btn,
.stacked-actions input[type="submit"] { width: 100%; justify-content: center; }
.auth-card .stacked-actions input[type="submit"] { margin-top: 0; }

.recovery-codes__actions { margin-top: 1.1rem; }
.recovery-codes__actions .btn { width: 100%; justify-content: center; }

/* Regeneration sits below the fold of the decision, separated by a rule: it is
   a different kind of act from everything above it, and on this page it undoes
   what the page just asked for. auth-card overrides input[type=submit] to full
   width, so the button is pulled back to its own size here. */
.recovery-codes__regenerate {
  margin-top: 1.4rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
  text-align: center;
}
.auth-card .recovery-codes__regenerate input[type="submit"] {
  width: auto;
  margin-top: 0;
  font-size: 0.82rem;
}

/* ---------- Pagy ---------- */

nav.pagy {
  display: flex;
  gap: 0.25rem;
  font-family: var(--mono);
  font-size: 0.8rem;
  margin: 1rem 0;
}
nav.pagy a, nav.pagy span {
  padding: 0.28rem 0.62rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text-2);
}
nav.pagy a:hover { text-decoration: none; border-color: var(--border-strong); color: var(--text); }
nav.pagy a[aria-current="page"], nav.pagy .current {
  background: var(--primary);
  color: #fff;
  border-color: var(--primary);
}

/* ---------- Interaction layer: drag & drop ---------- */

.drag-grip {
  position: absolute;
  left: 0.3rem;
  top: 50%;
  translate: 0 -50%;
  z-index: 2;
  display: inline-flex;
  padding: 0.25rem 0.1rem;
  color: var(--text-3);
  opacity: 0;
  cursor: grab;
  transition: opacity 0.12s ease, color 0.12s ease;
}
.drag-grip:active { cursor: grabbing; }
.drag-grip:hover { color: var(--text-2); }
.app-card:hover .drag-grip,
.feature-card:hover .drag-grip,
.drag-grip:focus-visible { opacity: 1; }
/* Feature cards keep the grip in the header flow, not floated over content */
.feature-card .drag-grip { position: static; translate: none; flex: none; margin-left: -0.3rem; }

.dragging {
  opacity: 0.55;
  box-shadow: var(--shadow-md), 0 14px 28px -10px rgba(24, 24, 27, 0.28);
}
.drag-over { border-color: color-mix(in srgb, var(--primary) 55%, var(--border)); }

/* The card title sits in a turbo-frame so the kebab's Rename can swap it */
.app-card__name { display: block; }

/* ---------- Kebab (⋯) menus ---------- */

.kebab { position: relative; flex: none; z-index: 3; }
.app-card .kebab { position: absolute; top: 0.5rem; right: 0.5rem; }

.kebab__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.7rem;
  height: 1.7rem;
  color: var(--text-3);
  background: var(--surface);
  border: 1px solid transparent;
  border-radius: 7px;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease, background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.app-card:hover .kebab__btn,
.feature-card:hover .kebab__btn,
.org-card:hover .kebab__btn,
.kebab__btn:focus-visible,
.kebab__btn[aria-expanded="true"] { opacity: 1; }
.kebab__btn:hover,
.kebab__btn[aria-expanded="true"] { background: var(--surface-2); color: var(--text); border-color: var(--border); }
.kebab__btn:focus-visible { outline: none; box-shadow: var(--ring); }

.kebab__menu {
  position: absolute;
  right: 0;
  top: calc(100% + 4px);
  z-index: 25;
  min-width: 10.5rem;
  display: flex;
  flex-direction: column;
  gap: 1px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: var(--shadow-md);
  padding: 0.3rem;
}
.kebab__menu[hidden] { display: none; }
.kebab__form { margin: 0; display: block; }
/* High-specificity prefix beats the global button[type=submit] / main a rules */
.kebab__menu .kebab__item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  font: 550 0.82rem var(--ui);
  color: var(--text);
  background: none;
  border: none;
  border-radius: 7px;
  padding: 0.42rem 0.6rem;
  cursor: pointer;
  text-align: left;
  box-shadow: none;
}
.kebab__menu .kebab__item:hover { background: var(--surface-2); text-decoration: none; filter: none; }
.kebab__menu .kebab__item .icon { color: var(--text-3); flex: none; }
.kebab__menu .kebab__item--danger { color: var(--bad); }
.kebab__menu .kebab__item--danger .icon { color: inherit; }
.kebab__menu .kebab__item--danger:hover { background: var(--bad-bg); }

/* ---------- Ghost cards (quick create) ---------- */

.ghost-card-wrap { display: flex; margin: 0; }
.ghost-card {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  flex: 1;
  font: 600 0.9rem var(--ui);
  color: var(--text-2);
  background: transparent;
  border: 1.5px dashed var(--border-strong);
  border-radius: var(--r-lg);
  padding: 1.15rem 1.25rem;
  cursor: pointer;
  transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease, transform 0.12s ease;
}
.ghost-card:hover {
  color: color-mix(in srgb, var(--primary) 80%, var(--text));
  border-color: color-mix(in srgb, var(--primary) 55%, var(--border-strong));
  background: color-mix(in srgb, var(--primary) 4%, transparent);
  transform: translateY(-1px);
}
.ghost-card:focus-visible { outline: none; box-shadow: var(--ring); }
.ghost-card--row { flex: none; padding: 0.6rem 1.1rem; }

/* ---------- Quick-create dialog ---------- */

dialog.modal {
  width: min(26rem, calc(100vw - 2rem));
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: 0 24px 60px -12px rgba(16, 16, 20, 0.3), var(--shadow-md);
  padding: 1.4rem 1.5rem 1.5rem;
}
dialog.modal::backdrop { background: rgba(16, 16, 20, 0.45); }
.modal__title {
  font-size: 1.05rem;
  font-weight: 650;
  text-transform: none;
  letter-spacing: -0.01em;
  color: var(--text);
  margin: 0 0 0.2rem;
}
.modal__hint { font-size: 0.82rem; color: var(--text-2); margin: 0 0 0.75rem; }
dialog.modal input[type="text"] { max-width: none; }
dialog.modal label { margin-top: 0.5rem; }
.modal__actions { display: flex; justify-content: flex-end; gap: 0.6rem; margin-top: 1.1rem; }

/* ---------- Inline rename (turbo-frame swap) ---------- */

.inline-rename { display: flex; align-items: center; gap: 0.45rem; margin: 0; }
.inline-rename input[type="text"] {
  margin: 0;
  flex: 1;
  min-width: 0;
  max-width: none;
  padding: 0.32rem 0.55rem;
  font-size: 0.9rem;
}
.inline-rename input[type="submit"] { padding: 0.34rem 0.75rem; font-size: 0.8rem; }
/* Inside an app card the form floats above the stretched card link */
.app-card .inline-rename { position: relative; z-index: 2; }

/* ---------- Guided hints (first-use surfaces) ---------- */

.guide-hint {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  max-width: 44rem;
  font-size: 0.85rem;
  color: var(--text-2);
  background: color-mix(in srgb, var(--primary) 6%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--primary) 28%, var(--border));
  border-radius: var(--r-lg);
  padding: 0.65rem 0.9rem;
  margin: -0.5rem 0 1.25rem;
}
.guide-hint .icon { color: var(--primary); flex: none; }

/* ---------- Slide-over drawer ---------- */

/* Persistent right-hand panel around <turbo-frame id="drawer">. The stimulus
   drawer controller toggles .drawer-root--open when frame content arrives or
   is cleared; everything else is CSS. Durations collapse to 0.01ms under the
   prefers-reduced-motion rule below, like every other transition. */
.drawer-root__backdrop {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(16, 16, 20, 0.35);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease;
}
.drawer-root--open .drawer-root__backdrop { opacity: 1; pointer-events: auto; }

turbo-frame#drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 61;
  width: min(540px, 94vw);
  display: block;
  background: var(--surface);
  border-left: 1px solid var(--border);
  overflow-y: auto;
  transform: translateX(110%);
  transition: transform 0.22s cubic-bezier(0.32, 0.72, 0.16, 1);
}
.drawer-root--open turbo-frame#drawer {
  transform: translateX(0);
  box-shadow: -20px 0 48px -16px rgba(16, 16, 20, 0.28);
}

.drawer-shell {
  min-height: 100%;
  display: flex;
  flex-direction: column;
  outline: none;
}
.drawer-shell__head {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 0.9rem 1.25rem;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.drawer-shell__heading { flex: 1; min-width: 0; }
.drawer-shell__title {
  font-size: 1rem;
  font-weight: 650;
  text-transform: none;
  letter-spacing: -0.015em;
  color: var(--text);
  margin: 0;
}
.drawer-shell__title::before { content: none; }
.drawer-shell__subtitle { margin: 0.15rem 0 0; font-size: 0.72rem; color: var(--text-3); }
.drawer-shell__close {
  flex: none;
  width: 1.9rem;
  height: 1.9rem;
  border: 1px solid transparent;
  border-radius: var(--r);
  background: none;
  font-size: 1.15rem;
  line-height: 1;
  color: var(--text-3);
  cursor: pointer;
  padding: 0;
}
.drawer-shell__close:hover { color: var(--text); background: var(--surface-2); border-color: var(--border); }
.drawer-shell__body { padding: 1.1rem 1.25rem 2.5rem; }
.drawer-shell__body .form-card { max-width: none; }
.drawer-shell__note { font-size: 0.82rem; color: var(--text-2); margin: 0 0 0.9rem; }
.drawer-actions { margin: 0 0 1rem; }

/* Amber nudge shown when a Failed mark bounced for missing evidence. */
.drawer-hint {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.85rem;
  color: var(--text-2);
  background: color-mix(in srgb, var(--warn) 8%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--warn) 38%, var(--border));
  border-radius: var(--r-lg);
  padding: 0.65rem 0.9rem;
  margin: 0 0 1rem;
}
.drawer-hint .icon { color: var(--warn); flex: none; }

/* Collapsed steps/expected context so the form stays front and centre. */
.drawer-context {
  margin: 0 0 1rem;
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface-2);
  padding: 0.55rem 0.8rem;
  font-size: 0.85rem;
}
.drawer-context summary { cursor: pointer; font-weight: 600; color: var(--text-2); }
.drawer-context[open] summary { margin-bottom: 0.35rem; }

/* ---------- Run mode (guided testing) ---------- */

/* Minimal chrome: no sidebar, one centered column. The whole viewport belongs
   to the current check; the top bar carries the only way out. */
.run-plane {
  max-width: 720px;
  margin: 0 auto;
  padding: 1.4rem 1.5rem 4rem;
}
.run-plane .breadcrumbs { margin-bottom: 0.9rem; }

.run-topbar {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.4rem;
}
.run-topbar__exit {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-2);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 0.35rem 0.7rem;
  background: var(--surface);
}
main .run-topbar__exit { color: var(--text-2); }
.run-topbar__exit:hover { text-decoration: none; border-color: var(--border-strong); color: var(--text); }
.run-topbar__context {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 0.55rem;
}
.run-topbar__cycle {
  font-weight: 650;
  letter-spacing: -0.015em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
main a.run-topbar__cycle { color: var(--text); }
.run-progress {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.3rem;
}
.run-progress__count { font-size: 0.72rem; color: var(--text-2); font-variant-numeric: tabular-nums; }
.run-progress__track {
  width: 170px;
  height: 6px;
  border-radius: 999px;
  background: var(--idle-bg);
  border: 1px solid var(--border);
  overflow: hidden;
}
.run-progress__fill {
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--primary), var(--accent));
  transition: width 0.2s ease;
}

.run-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-md);
  padding: 1.6rem 1.9rem 1.5rem;
}
.run-card__feature {
  margin: 0;
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-3);
}
.run-card__heading {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-bottom: 0.4rem;
}
.run-card__title {
  margin: 0.15rem 0 0;
  font-size: 1.3rem;
  letter-spacing: -0.02em;
}
.run-card__recorded { color: var(--text-2); }
.run-card__label { margin-top: 1.3rem; }
.run-card__steps {
  margin: 0;
  padding-left: 1.4rem;
  display: grid;
  gap: 0.35rem;
  font-size: 0.92rem;
  color: var(--text);
}
.run-card__steps li::marker {
  font-family: var(--mono);
  font-size: 0.8em;
  color: var(--text-3);
}
.run-card__expected {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-left: 3px solid var(--primary);
  border-radius: var(--r);
  padding: 0.6rem 0.9rem;
  font-size: 0.92rem;
}
.run-card__expected p { margin: 0; color: var(--text); }
.run-card__expected p + p { margin-top: 0.4rem; }

kbd {
  font: 500 0.72rem var(--mono);
  color: var(--text-2);
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-bottom-width: 2px;
  border-radius: 5px;
  padding: 0.05rem 0.35rem;
}

.run-actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-top: 1.5rem;
  border-top: 1px solid var(--border);
  padding-top: 1.25rem;
}
.run-actions__form { display: contents; }
.run-btn, button.run-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font: 600 0.92rem var(--ui);
  border-radius: var(--r);
  padding: 0.6rem 1.05rem;
  cursor: pointer;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text);
  box-shadow: var(--shadow-xs);
  transition: background 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
}
.run-btn:hover, button.run-btn:hover { filter: none; box-shadow: var(--shadow-sm); text-decoration: none; }
.run-btn:focus-visible, button.run-btn:focus-visible { outline: none; box-shadow: var(--ring); }
.run-btn--pass, button.run-btn--pass { color: var(--ok); border-color: var(--ok-bd); background: var(--ok-bg); }
.run-btn--pass:hover, button.run-btn--pass:hover { border-color: var(--ok); }
.run-btn--fail, button.run-btn--fail { color: var(--bad); border-color: var(--bad-bd); background: var(--bad-bg); }
.run-btn--fail:hover, button.run-btn--fail:hover { border-color: var(--bad); }
.run-btn--block, button.run-btn--block { color: var(--warn); border-color: var(--warn-bd); background: var(--warn-bg); }
.run-btn--block:hover, button.run-btn--block:hover { border-color: var(--warn); }
main a.run-btn--skip, .run-btn--skip {
  margin-left: auto;
  color: var(--text-3);
  background: none;
  border-color: transparent;
  box-shadow: none;
}
main a.run-btn--skip:hover { color: var(--text); border-color: var(--border); text-decoration: none; }
.run-btn kbd { margin-left: 0.15rem; }

.run-fail {
  margin-top: 1.25rem;
  border-top: 1px dashed var(--border-strong);
  padding-top: 1.1rem;
}
.run-fail__form label:first-of-type { margin-top: 0.25rem; }

.run-card__evidence {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin: 1rem 0 0;
  font-size: 0.75rem;
  color: var(--text-3);
}

.run-legend {
  text-align: center;
  margin-top: 1.2rem;
  font-size: 0.78rem;
  color: var(--text-3);
}

/* Completion: celebratory but tasteful — a pop-in check badge, the numbers,
   and the ways onward. CSS only; reduced-motion collapses the animation. */
.run-complete {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-md);
  padding: 2.6rem 2rem 2.2rem;
  text-align: center;
}
.run-complete__badge {
  width: 64px;
  height: 64px;
  margin: 0 auto 1rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  color: var(--ok);
  background: var(--ok-bg);
  border: 1px solid var(--ok-bd);
  box-shadow: 0 0 0 8px color-mix(in srgb, var(--ok) 7%, transparent);
}
.run-complete h1 { margin: 0 0 0.3rem; }
.run-complete__note { margin: 0 auto; max-width: 34rem; }
.run-complete__tiles {
  display: flex;
  justify-content: center;
  align-items: stretch;
  gap: 0.9rem;
  flex-wrap: wrap;
  margin: 1.6rem 0 1.8rem;
}
.run-complete__tiles .stat-tile { min-width: 110px; }
.run-complete__tiles .coverage-ring { width: 96px; }
.run-complete__actions {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0.7rem;
  flex-wrap: wrap;
}
.run-complete__back { font-size: 0.85rem; color: var(--text-2); }
.run-complete__failures {
  margin: 2rem auto 0;
  max-width: 640px;
  text-align: left;
}
.run-complete__failures > h2 { font-size: 1rem; margin-bottom: 0.75rem; }
.run-failure {
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 0.9rem 1rem;
  margin-bottom: 0.9rem;
}
.run-failure__title { display: flex; align-items: center; gap: 0.5rem; font-weight: 600; }
.run-failure__notes { font-size: 0.85rem; }

@media (prefers-reduced-motion: no-preference) {
  .run-card { animation: run-card-in 0.18s ease; }
  .run-complete__badge { animation: run-pop 0.45s cubic-bezier(0.34, 1.56, 0.64, 1); }
}
@keyframes run-card-in { from { opacity: 0; transform: translateY(5px); } }
@keyframes run-pop { from { transform: scale(0.4); opacity: 0; } 60% { transform: scale(1.08); } }

/* ---------- Command palette & shortcuts ---------- */

dialog.palette {
  width: min(34rem, calc(100vw - 2rem));
  margin-top: 12vh;
  padding: 0;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: 0 24px 60px -12px rgba(16, 16, 20, 0.3);
}
dialog.palette::backdrop { background: rgba(16, 16, 20, 0.45); }
.palette__field {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.8rem 1rem;
  border-bottom: 1px solid var(--border);
}
.palette__field .icon { flex: none; color: var(--text-3); }
.palette__input {
  flex: 1;
  border: none;
  box-shadow: none;
  max-width: none;
  font-size: 0.95rem;
  padding: 0;
}
.palette__input:focus { box-shadow: none; border: none; outline: none; }
.palette__results {
  list-style: none;
  margin: 0;
  padding: 0.4rem;
  max-height: 50vh;
  overflow-y: auto;
}
.palette__group-label {
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-3);
  padding: 0.55rem 0.7rem 0.25rem;
}
.palette__results [role="option"] {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  border-radius: 8px;
  padding: 0.5rem 0.7rem;
  cursor: pointer;
}
.palette__results [role="option"][aria-selected="true"] {
  background: color-mix(in srgb, var(--primary) 10%, var(--surface));
}
.palette__title { font-size: 0.9rem; color: var(--text); }
.palette__sub {
  flex: 1;
  font-size: 0.78rem;
  color: var(--text-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.palette__aside { flex: none; }
.palette__run {
  font: 600 0.72rem var(--ui);
  color: var(--primary);
  background: none;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.15rem 0.5rem;
  cursor: pointer;
}
.palette__run:hover { border-color: var(--primary); }
.palette__note { padding: 0.5rem 0.7rem; font-size: 0.85rem; color: var(--text-3); }
.palette__empty {
  margin: 0;
  padding: 0.9rem 1rem;
  font-size: 0.85rem;
  color: var(--text-3);
}
.palette__foot {
  display: flex;
  gap: var(--space-4);
  padding: 0.55rem 1rem;
  border-top: 1px solid var(--border);
  font-size: 0.72rem;
  color: var(--text-3);
}
.palette__foot span { display: inline-flex; align-items: center; gap: 0.3rem; }

/* ? overlay — reuses dialog.modal visuals via class="modal shortcuts". */
dialog.shortcuts { width: min(34rem, calc(100vw - 2rem)); }
.shortcuts__cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 var(--space-6);
  margin-top: var(--space-3);
}
.shortcuts__head {
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-3);
  margin: var(--space-3) 0 var(--space-2);
}
.shortcuts__head:first-child { margin-top: 0; }
.shortcuts__note { font-weight: 500; text-transform: none; letter-spacing: 0; }
.shortcuts__list {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-2) var(--space-3);
  align-items: center;
  margin: 0;
}
.shortcuts__list dt { display: flex; gap: var(--space-1); }
.shortcuts__list dd { margin: 0; font-size: 0.85rem; color: var(--text-2); }

/* ---------- Motion (respects prefers-reduced-motion) ---------- */

.section-hint { font-size: 0.82rem; color: var(--text-3); margin: -0.35rem 0 0.75rem; }

@media (prefers-reduced-motion: no-preference) {
  .flash { animation: flash-in 0.22s ease; }
  .kebab__menu { animation: pop-in 0.12s ease; }
  dialog.modal[open] { animation: modal-in 0.16s ease; }
  dialog.modal[open]::backdrop { animation: backdrop-in 0.16s ease; }
  dialog.palette[open] { animation: modal-in 0.16s ease; }
  dialog.palette[open]::backdrop { animation: backdrop-in 0.16s ease; }
}
@keyframes flash-in { from { opacity: 0; translate: 0 -6px; } }
@keyframes pop-in { from { opacity: 0; transform: translateY(-3px) scale(0.98); } }
@keyframes modal-in { from { opacity: 0; transform: translateY(6px) scale(0.97); } }
@keyframes backdrop-in { from { opacity: 0; } }

/* Hard stop for users who ask for less motion: kill transition/animation time
   and the hover lifts that would otherwise still jump into place. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
  .nav-card:hover, .ghost-card:hover { transform: none; }
  .nav-card:hover::after { translate: 0 -50%; }
}

/* ---------- Requirement traceability ---------- */

/* Linked Jira issues on the test-case detail (drawer + standalone show):
   mono key pills with an author-only unlink, plus the inline add form. */
.tc-links__list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.4rem; }
.tc-links__pill {
  display: inline-flex;
  align-items: center;
  gap: 0.15rem;
  padding: 0.2rem 0.35rem 0.2rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface-2);
}
.tc-links__pill .mono { font-size: 0.78rem; font-weight: 600; color: var(--text-2); }
.tc-links__pill:has(> .mono:last-child) { padding-right: 0.6rem; }
.tc-links__detach-form { display: inline-flex; }
.tc-links__detach {
  border: 0;
  background: none;
  color: var(--text-3);
  font-size: 0.9rem;
  line-height: 1;
  padding: 0.1rem 0.3rem;
  border-radius: 999px;
  cursor: pointer;
}
.tc-links__detach:hover { color: var(--bad); background: var(--bad-bg); }
.tc-links__empty { font-size: 0.82rem; color: var(--text-3); margin: 0; }
.tc-links__error { font-size: 0.8rem; color: var(--bad); margin: 0.5rem 0 0; }
.tc-links__form { display: flex; gap: 0.5rem; margin-top: 0.75rem; align-items: center; }
.tc-links__input { max-width: 10rem; text-transform: uppercase; }
.tc-links__input::placeholder { text-transform: none; }

/* Requirements coverage board on the cycle page: one card per Jira issue,
   verdict-colored left border, status rollup chips. */
.req-cards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 0.75rem;
}
.req-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--idle);
  border-radius: var(--r);
  box-shadow: var(--shadow-xs);
  padding: 0.7rem 0.85rem;
}
.req-card--untested { border-left-color: var(--idle-bd); }
.req-card--passing  { border-left-color: var(--ok); }
.req-card--failing  { border-left-color: var(--bad); }
.req-card--covered  { border-left-color: var(--primary); }
.req-card__head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; }
.req-card__key { font-size: 0.82rem; font-weight: 600; }
.req-card__verdict {
  font: 500 0.66rem var(--mono);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  white-space: nowrap;
  color: var(--text-3);
}
.req-card__verdict--passing { color: var(--ok); }
.req-card__verdict--failing { color: var(--bad); }
.req-card__verdict--covered { color: color-mix(in srgb, var(--primary) 85%, var(--text)); }
.req-card__summary {
  margin: 0.3rem 0 0;
  font-size: 0.82rem;
  color: var(--text-2);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.req-card__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.45rem;
  margin-top: 0.5rem;
}
.req-card__count { font-size: 0.74rem; color: var(--text-3); white-space: nowrap; }
.req-card__chips { display: inline-flex; flex-wrap: wrap; gap: 0.3rem; }
/* .req-chip base + tones live in the unified pill block ("Badges & chips"). */
.req-board__unconfigured { font-size: 0.78rem; color: var(--text-3); margin: 0.6rem 0 0; }

/* ---------- Live Jira defect status (cached by SyncJiraStatusesJob) ----------
   A toned pill beside the reported jira key — done-ish statuses green,
   in-flight ones blue, anything unrecognized neutral — plus a muted
   synced-ago note. Shared by the failures strip and the evidence drawer. */

.jira-status {
  font: 500 0.66rem var(--mono);
  padding: 0.1rem 0.45rem;
  border: 1px solid var(--idle-bd);
  border-radius: 999px;
  background: var(--idle-bg);
  color: var(--idle);
  white-space: nowrap;
}
.jira-status--done     { color: var(--ok);   background: var(--ok-bg);   border-color: var(--ok-bd); }
.jira-status--progress { color: var(--info); background: var(--info-bg); border-color: var(--info-bd); }
.jira-status__synced { font-size: 0.7rem; color: var(--text-3); white-space: nowrap; }
.jira-status-line { display: inline-flex; align-items: center; gap: 0.4rem; }

/* ---------- Execution history (test-case drawer + standalone show) ----------
   Timeline of cycles this case was actually run in, newest activity first.
   Per-platform pills reuse the same status tones as the matrix cell / chip. */

.exec-history__list { list-style: none; margin: 0.6rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.6rem; }
.exec-history__row { border: 1px solid var(--border); border-radius: var(--r); padding: 0.55rem 0.75rem; background: var(--surface); }
.exec-history__main { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.exec-history__cycle { font-weight: 600; }
.exec-history__date { font-size: 0.78rem; color: var(--text-3); margin-left: auto; }
.exec-history__pills { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-top: 0.45rem; }
/* .hist-pill base + tones live in the unified pill block ("Badges & chips"). */

/* ---------- Release readiness card (cycle dash) ----------
   A wider sibling of the stat tiles inside #cycle_progress: requirement
   coverage %, open defects, and a pass-rate sparkline across the product's
   recent cycles. Same surfaces/tokens as .stat-tile — tenant --primary
   drives the sparkline stroke. */

.readiness {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: 0.9rem 1.1rem;
}
.readiness__label {
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-3);
}
.readiness__body {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 1.25rem;
}
.readiness__stat { display: flex; flex-direction: column; }
.readiness__num {
  font: 600 1.6rem var(--mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  line-height: 1.25;
}
.readiness__stat--bad .readiness__num { color: var(--bad); }
.readiness__sub { font-size: 0.72rem; color: var(--text-3); }
.readiness__trend { display: flex; flex-direction: column; gap: 0.15rem; }

.sparkline { width: 130px; height: 30px; display: block; overflow: visible; }
.sparkline__line {
  stroke: var(--primary);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.sparkline__dot { fill: var(--primary); }

/* ---------- Turbo 8: progress bar, view transitions, skeletons ---------- */

/* Turbo Drive progress bar in the tenant's white-label color. */
.turbo-progress-bar {
  height: 3px;
  background: var(--primary);
  box-shadow: 0 0 8px color-mix(in srgb, var(--primary) 45%, transparent);
}

/* Same-origin view transitions (the <meta name="view-transition"> opt-in in
   the layouts). The page header, cycle dashboard, and matrix carry names so
   cycle → report → run navigation reads as the same surfaces moving, not a
   repaint. Everything here sits out under prefers-reduced-motion. */
@media (prefers-reduced-motion: no-preference) {
  /* Cross-document fallback too — the report layout ships no Turbo, so
     leaving it back into the app is a real navigation. */
  @view-transition { navigation: auto; }

  /* One element per name per page: each renders at most once. */
  .page-header { view-transition-name: page-header; }
  #cycle_progress { view-transition-name: cycle-progress; }
  table.matrix { view-transition-name: matrix; }

  ::view-transition-old(root), ::view-transition-new(root) { animation-duration: 0.18s; }
  ::view-transition-group(page-header),
  ::view-transition-group(cycle-progress),
  ::view-transition-group(matrix) {
    animation-duration: 0.22s;
    animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
  }
}
/* The global reduced-motion hard stop above cannot reach the
   ::view-transition tree (it hangs off the root, not off elements), so view
   transitions get their own kill switch. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-image-pair(*),
  ::view-transition-old(*), ::view-transition-new(*) {
    animation: none !important;
  }
}

/* Shimmer skeleton shown inside a lazy turbo-frame while its section loads
   (shared/_skeleton). Static grey bars under reduced motion — the shimmer is
   an animated background sweep, opted into below like every other motion. */
.skeleton { display: grid; gap: var(--space-3); margin-bottom: var(--space-7); }
.skeleton__bar {
  display: block;
  height: 0.9rem;
  border-radius: var(--r);
  background: color-mix(in srgb, var(--border) 55%, var(--surface-2));
}
.skeleton__bar--heading { width: 160px; height: 1.1rem; margin-bottom: var(--space-2); }
.skeleton__bar--row { height: 3.2rem; border-radius: var(--r-lg); }
@media (prefers-reduced-motion: no-preference) {
  .skeleton__bar {
    background: linear-gradient(90deg,
      color-mix(in srgb, var(--border) 55%, var(--surface-2)) 25%,
      var(--surface-2) 45%,
      color-mix(in srgb, var(--border) 55%, var(--surface-2)) 65%);
    background-size: 300% 100%;
    animation: skeleton-shimmer 1.4s ease-in-out infinite;
  }
}
@keyframes skeleton-shimmer {
  from { background-position: 100% 0; }
  to   { background-position: 0 0; }
}

/* ---------- Live collaboration ---------- */

/* Presence chip row under the cycle page header: stacked initials + a live
   label ("You + Maria testing now"). Tenant --primary drives the accents. */
.presence-root { margin: calc(-1 * var(--space-4)) 0 var(--space-5); }
.presence {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 1.9rem;
}
.presence__stack { display: inline-flex; }
.presence__chip {
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font: 600 0.66rem var(--mono);
  letter-spacing: 0.02em;
  color: var(--primary);
  background: color-mix(in srgb, var(--primary) 12%, var(--surface));
  border: 2px solid var(--surface);
  box-shadow: 0 0 0 1px var(--border), var(--shadow-xs);
}
.presence__chip + .presence__chip { margin-left: -0.5rem; }
.presence__chip--self {
  color: #fff;
  background: var(--primary);
  z-index: 1;
}
.presence__dot {
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  background: var(--ok);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok) 18%, transparent);
  margin-left: var(--space-1);
  flex: none;
}
.presence__label { font-size: 0.82rem; color: var(--text-2); }

/* Compact latest-activity feed on the cycle page. */
.activity {
  margin: var(--space-6) 0;
  padding: var(--space-4) var(--space-5);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-xs);
}
.activity__head {
  margin: 0 0 var(--space-3);
  font-size: 0.8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-3);
}
.activity__list { list-style: none; margin: 0; padding: 0; }
.activity__row {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: 0.3rem 0;
  font-size: 0.82rem;
  color: var(--text-2);
}
.activity__row + .activity__row { border-top: 1px dashed var(--border); }
.activity__dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--idle);
  flex: none;
  align-self: center;
}
.activity__dot--passed  { background: var(--ok); }
.activity__dot--failed  { background: var(--bad); }
.activity__dot--blocked { background: var(--warn); }
.activity__who { font-size: 0.76rem; color: var(--text); flex: none; }
.activity__text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.activity__what { font-weight: 600; color: var(--text); }
.activity__status--passed  { color: var(--ok); }
.activity__status--failed  { color: var(--bad); }
.activity__status--blocked { color: var(--warn); }
.activity__status--not_run { color: var(--idle); }
.activity__when {
  margin-left: auto;
  flex: none;
  font: 400 0.72rem var(--mono);
  color: var(--text-3);
}
.activity__empty { margin: 0; font-size: 0.82rem; }

/* Run mode: subtle "also here" indicator in the topbar. */
.run-presence { display: flex; align-items: center; }
/* Composes the shared .chip; only the live dot and quiet tone are local. */
.run-presence__text { color: var(--text-3); white-space: nowrap; }
.run-presence__text::before {
  content: "●";
  color: var(--ok);
  font-size: 0.6rem;
  margin-right: 0.35rem;
}

/* ---------- Unified project dashboard (Cycles + Apps, no tabs) ---------- */

.panel-title {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: var(--space-3);
}
.panel-title h3 {
  margin: 0;
  font-size: 0.8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-2);
}
.panel-title__count { font-size: 0.75rem; color: var(--text-3); }

.dash-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: var(--space-6);
  align-items: start;
}
/* Same sticky-sidebar pattern as .result-layout__side: the rail pins in
   place as the taller main column (cycles + failures) scrolls past it,
   instead of scrolling away with the page. No overflow/max-height here —
   that would clip .kebab__menu's absolute-positioned dropdown for any
   rail card near the bottom edge. */
.dash-grid__rail { position: sticky; top: calc(var(--topbar-h) + 1rem); }
@media (max-width: 980px) {
  .dash-grid { grid-template-columns: 1fr; }
  .dash-grid__rail { position: static; }
}

/* Grouped cycle card: per-app sub-rows replace the old single-product layout. */
.cycle-card--grouped {
  display: block;
  padding: 1.1rem 1.25rem 0.9rem;
}
.cycle-card__head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-3);
}
.cycle-card__name-link { color: var(--text); font-size: 1.02rem; font-weight: 650; letter-spacing: -0.015em; }
main a.cycle-card__name-link:hover { text-decoration: none; color: color-mix(in srgb, var(--primary) 80%, var(--text)); }

.cycle-card__app-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.45rem 0;
  font-size: 0.82rem;
}
.cycle-card__app-row + .cycle-card__app-row { border-top: 1px dashed var(--border); }
/* Big enough for "100%" to sit inside without crowding the band around it.
   Specificity 0,3,0, deliberately one level deeper than ".cycle-card
   .coverage-ring" (0,2,0) above — a tie would leave the winner up to source
   order, which is exactly the fragility that bit the sibling
   ".cycle-card__app-remove" rule earlier in this file. The .hub-card branch
   covers the identical .cycle-card__app-row markup reused on the cycle detail
   hub page, kept at the same (0,3,0) specificity for the same reason. */
.cycle-card .cycle-card__app-row .coverage-ring--sm,
.hub-card .cycle-card__app-row .coverage-ring--sm {
  width: 42px;
  height: 42px;
  background:
    radial-gradient(closest-side, var(--surface) 70%, transparent 71% 100%),
    conic-gradient(var(--primary) calc(var(--pct) * 1%), var(--idle-bg) 0);
}
.coverage-ring--sm .coverage-ring__num { font-size: 0.68rem; }
/* .cycle-card__actions is reused here (once per app-row) for the Run mode /
   Continue testing links; it was originally styled for "once per card" atop a
   CSS grid (border-top separator + top padding + spanning grid-column). Nested
   in this flex row that produced a floating border segment above the buttons
   on every row. The row already has its own dashed top border, so strip the
   card-level framing when actions live inside a row. */
.cycle-card__app-row .cycle-card__actions { border-top: none; margin-top: 0; padding-top: 0; }
main a.cycle-card__app-name { flex: 1; min-width: 0; color: var(--text); font-weight: 550; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cycle-card__app-stat { color: var(--text-3); flex: none; }
.cycle-card__failed { color: var(--bad); font-weight: 600; font-size: 0.76rem; flex: none; }
/* Shared layout for both variants: the removable case is a real <button>
   (button_to), the non-removable case is a plain <span> (see _cycle_card.html.erb). */
.cycle-card__app-remove {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.4rem;
  height: 1.4rem;
  color: var(--text-3);
  border-radius: 6px;
  cursor: pointer;
}
/* .cycle-card__app-row button.cycle-card__app-remove (class + element + class, matching the
   real DOM nesting): button_to renders type="submit", and button[type=submit] carries the
   primary .btn treatment (filled background, white text, padding, border, box-shadow) at
   specificity (0,1,1) — same fix pattern as .kebab__menu .kebab__item above. A bare
   button.cycle-card__app-remove is ALSO (0,1,1), only a tie, so it only wins today because this
   block happens to be declared later in the file; reordering stylesheet sections would silently
   reintroduce the bug (it rendered as a solid blue pill with a white-on-white invisible icon).
   Prefixing with the row selector (0,2,1) wins unconditionally, regardless of source order. The
   <span> variant never matches button[type=submit] so it only needs the shared rule above. */
.cycle-card__app-row button.cycle-card__app-remove { padding: 0; background: none; border: none; box-shadow: none; color: var(--text-3); }
/* Same row-scoped prefix on :hover: the base rule above is now (0,2,1), which already
   outranks a bare .cycle-card__app-remove:hover (0,2,0) — that would've silently no-op'd
   the hover state (background/color never changing) if left unscoped. */
.cycle-card__app-row button.cycle-card__app-remove:hover { background: var(--bad-bg); color: var(--bad); filter: none; box-shadow: none; }
/* z-index:70 here (matching the tooltip ::after's own z-index — see the
   "Tooltips" section) is load-bearing: opacity < 1 creates a local stacking
   context that would otherwise trap this element's tooltip below unrelated
   page chrome. Don't copy this onto other tooltip triggers "just in case" —
   see the comment above [data-tooltip] in the Tooltips section for why. */
.cycle-card__app-remove--disabled { cursor: help; opacity: 0.4; position: relative; z-index: 70; }
.cycle-card__app-remove--disabled:hover { background: none; color: var(--text-3); }
/* .hub-card branch: the cycle detail hub reuses this same "Add app" ghost
   button under .hub-card, not .cycle-card--grouped, so it needs the same
   full-width row treatment (otherwise flex: none plus the button's default
   shrink-to-fit collapses it into a narrow pill with no top margin). */
.cycle-card--grouped .ghost-card--row,
.hub-card .ghost-card--row {
  margin-top: 0.6rem;
  width: 100%;
}

.cycle-card-grid--grouped { grid-template-columns: 1fr; max-width: 640px; }

/* Drop zone (dragging an app card onto a cycle card, or the "new cycle" strip) */
.cycle-card[data-cycle-assign-target="dropzone"].drag-over,
.new-cycle-drop.drag-over {
  outline: 2px dashed color-mix(in srgb, var(--primary) 55%, var(--border));
  outline-offset: 2px;
  background: color-mix(in srgb, var(--primary) 4%, var(--surface));
}
.new-cycle-drop {
  border: 1.5px dashed var(--border-strong);
  border-radius: var(--r-lg);
  padding: 0.85rem 1rem;
  text-align: center;
  font-size: 0.8rem;
  color: var(--text-3);
  margin-bottom: var(--space-3);
  max-width: 640px;
}

/* Apps rail: narrower single-column cards */
.apps-rail { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.6rem; }
.rail-card {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: 0.7rem 0.8rem 0.7rem 1.9rem;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.rail-card .drag-grip { left: 0.4rem; opacity: 0; }
.rail-card:hover .drag-grip, .rail-card .drag-grip:focus-visible { opacity: 1; }
.rail-card__body { flex: 1; min-width: 0; }
main a.rail-card__name { display: block; color: var(--text); font-weight: 600; font-size: 0.88rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail-card__stats { margin: 0.1rem 0 0; font-size: 0.72rem; }
.rail-card__count {
  flex: none;
  font: 600 0.7rem var(--mono);
  color: var(--text-2);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.1rem 0.5rem;
}
.rail-card .kebab { position: static; }
.rail-card.dragging { opacity: 0.55; }

/* Assign-picker dialog (list of one-click targets) */
.assign-picker__list { list-style: none; margin: 0.75rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.4rem; }
.assign-picker__item { width: 100%; justify-content: flex-start; }
.assign-picker__empty { margin: 0.75rem 0 0; }

.rollup-glance--rail { display: block; font-size: 0.75rem; }

/* ---------- Utility ---------- */

.no-print { font-size: inherit; }
.muted { color: var(--text-3); }
.icon { vertical-align: -0.18em; }

@media print {
  .top-bar, .flash, .no-print, nav.pagy { display: none !important; }
  body { background: #fff; }
  table, .stat-tile, .detail-card, .form-card { box-shadow: none; }
  .matrix thead th { position: static; }
}

/* ---------- Cycle detail hub (cross-app cycle page) ---------- */

.hub { display: flex; flex-direction: column; gap: var(--space-5); max-width: 720px; }

.hub-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: 1.15rem 1.25rem 1rem;
}
.hub-card--center { text-align: center; }
.hub-overall__stat { margin: 0.6rem 0 0; font-size: 0.85rem; color: var(--text-2); }

.cycles-table__hub-link { margin-left: 0.4rem; color: var(--text-3); vertical-align: middle; }
.cycles-table__hub-link:hover { color: var(--text); }
