/* Flowboard — table view */

/* Due-cell empty state */
.due-empty {
  display: inline-flex; align-items: center; gap: 4px;
  color: var(--ink-muted); font-size: 11.5px; font-weight: 500;
  opacity: 0.7;
}
.cell-date:hover .due-empty { opacity: 1; }

/* Mini calendar popover */
.mini-cal {
  width: 240px; padding: 10px;
  font-family: inherit; user-select: none;
}
.mini-cal-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 8px;
}
.mini-cal-title {
  font-weight: 600; font-size: 13px; color: var(--ink-strong);
}
.mini-cal-nav {
  width: 24px; height: 24px;
  border: none; background: transparent;
  font-size: 16px; line-height: 1; color: var(--ink-body);
  cursor: pointer; border-radius: 4px;
  display: inline-flex; align-items: center; justify-content: center;
}
.mini-cal-nav:hover { background: var(--bg-subtle); color: var(--ink-strong); }
.mini-cal-grid {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px;
}
.mini-cal-dow {
  text-align: center; font-size: 10px; font-weight: 700;
  color: var(--ink-faint); padding: 4px 0;
  text-transform: uppercase; letter-spacing: 0.04em;
}
.mini-cal-day {
  height: 28px;
  border: none; background: transparent;
  font: inherit; font-size: 12px; color: var(--ink-body);
  cursor: pointer; border-radius: 5px;
  transition: background .1s ease, color .1s ease;
}
.mini-cal-day:hover { background: var(--bg-subtle); }
.mini-cal-day.is-today {
  color: var(--brand); font-weight: 700;
  box-shadow: inset 0 0 0 1.5px var(--brand);
}
.mini-cal-day.is-selected {
  background: var(--brand); color: white; font-weight: 700;
  box-shadow: none;
}
.mini-cal-day.is-selected:hover { background: var(--brand); }
.mini-cal-foot {
  display: flex; gap: 6px; margin-top: 8px; padding-top: 8px;
  border-top: 1px solid var(--border-soft);
}
.mini-cal-foot-btn {
  flex: 1; padding: 5px 0;
  font: inherit; font-size: 11.5px; font-weight: 600;
  color: var(--ink-body); background: white;
  border: 1px solid var(--border); border-radius: 5px;
  cursor: pointer;
}
.mini-cal-foot-btn:hover { background: var(--bg-subtle); border-color: var(--border-strong); }
.mini-cal-foot-btn.is-clear { color: #e2445c; }
.mini-cal-foot-btn.is-clear:hover { background: #fff0f3; border-color: #f5c0cc; }

/* Optional time-of-day row inside the mini calendar */
.mini-cal-time {
  margin-top: 8px; padding-top: 8px;
  border-top: 1px solid var(--border-soft);
}
.mini-cal-time-label {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-muted);
  margin-bottom: 4px;
}
.mini-cal-time-row {
  display: flex; align-items: center; gap: 6px;
}
.mini-cal-time-input {
  flex: 1;
  font: inherit; font-size: 12.5px; color: var(--ink-strong);
  padding: 5px 8px;
  border: 1px solid var(--border-soft);
  border-radius: 6px;
  background: white;
  cursor: pointer;
  transition: border-color .12s ease, box-shadow .12s ease;
}
.mini-cal-time-input:hover:not(:disabled) { border-color: var(--border-strong); }
.mini-cal-time-input:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px rgba(0, 116, 217, .12); }
.mini-cal-time-input:disabled {
  background: var(--bg-subtle); color: var(--ink-faint);
  cursor: not-allowed;
}
.mini-cal-time-clear {
  width: 22px; height: 22px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border-soft); background: white;
  border-radius: 5px; color: var(--ink-faint);
  cursor: pointer; transition: all .1s ease;
}
.mini-cal-time-clear:hover { color: #e2445c; border-color: #f5c0cc; background: #fff0f3; }


.board-scroll {
  flex: 1; overflow: auto;
  background: var(--bg-app);
  padding: 14px 24px 40px;
}

.epic-group {
  margin-bottom: 18px;
  background: white;
  border-radius: var(--r-md);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.epic-group.is-no-epic {
  --epic-color: #a3a8b6;
  background: #fafbfc;
  opacity: 0.92;
}
.epic-group.is-no-epic .epic-header {
  background: #f5f6f8;
  border-left-style: dashed;
}
.epic-header {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px 10px 8px;
  border-left: 6px solid var(--epic-color, var(--brand));
  cursor: pointer; user-select: none;
  background: white;
}
.epic-chevron {
  width: 18px; height: 18px; color: var(--epic-color, var(--brand));
  transition: transform 0.15s;
  flex: none;
}
.epic-group.is-collapsed .epic-chevron { transform: rotate(-90deg); }
.epic-title {
  font-weight: 700; font-size: 15px;
  color: var(--epic-color, var(--brand));
  letter-spacing: -0.01em;
}
.epic-count {
  font-size: 12px; color: var(--ink-muted); font-weight: 500;
}
/* Tristate "select all in this epic" checkbox in the epic header.
   Looks like the row-level checkbox so the affordance reads
   immediately as "the same control, but for the whole group". */
.epic-select-wrap {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px; height: 18px;
  cursor: pointer;
  flex: none;
}
.epic-select {
  width: 14px; height: 14px;
  accent-color: var(--epic-color, var(--brand));
  cursor: pointer;
  margin: 0;
}
.epic-select:disabled { cursor: default; opacity: .4; }
.epic-selected-count {
  color: var(--epic-color, var(--brand));
  font-weight: 600;
}
.epic-progress {
  margin-left: auto; display: flex; align-items: center; gap: 10px;
  font-size: 12px; color: var(--ink-muted);
}
.epic-progress-bar {
  width: 110px; height: 6px; border-radius: 999px;
  background: var(--bg-subtle); overflow: hidden;
}
.epic-progress-fill {
  height: 100%; background: var(--epic-color, var(--brand)); border-radius: 999px;
  transition: width 0.3s;
}
.epic-status-summary {
  display: flex; gap: 2px; height: 8px; border-radius: 999px;
  overflow: hidden; width: 120px; background: var(--bg-subtle);
}
.epic-status-summary .seg { height: 100%; }

.table-wrap {
  overflow: hidden;
  transition: max-height 0.25s ease;
}
.epic-group.is-collapsed .table-wrap {
  max-height: 0;
}

/* Table */
.t {
  width: 100%; border-collapse: separate; border-spacing: 0;
  table-layout: fixed;
  font-size: 13px;
}
.t thead th {
  background: #fafbfc;
  color: var(--ink-muted);
  font-weight: 500;
  font-size: 12px;
  text-align: left;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  border-top: 1px solid var(--border);
  user-select: none;
}
.t thead th:first-child {
  border-left: 6px solid var(--epic-color, var(--brand));
  padding-left: 14px;
}
.t thead th.th-center { text-align: center; }
.t tbody td {
  border-bottom: 1px solid var(--border-row);
  padding: 0;
  height: var(--row-h);
  vertical-align: middle;
  background: white;
  transition: background 0.08s;
}
.t tbody td.cell-name {
  padding: 8px 10px 8px 14px;
  border-left: 6px solid var(--epic-color, var(--brand));
  color: var(--ink-strong);
  font-weight: 500;
}
.t tbody td.cell-text { padding: 8px 10px; }
.t tbody td.cell-center {
  text-align: center;
  padding: 0 4px;
}
.t tbody tr:hover td { background: #fbfcfe; }
.t tbody tr.is-selected td { background: #f0f6fe; }

.t .row-check {
  width: 34px; text-align: center;
}
.row-check input { accent-color: var(--brand); cursor: pointer; }
.cell-name .name-wrap {
  display: flex; align-items: center; gap: 8px;
  /* min-width:0 + nowrap on the text child lets the title ellipsise
     within the cell instead of wrapping to a second line. Without
     this, long task names in the My Work cross-project table
     pushed rows to ~2× height and broke vertical alignment with
     the rest of the columns. */
  min-width: 0;
  /* Hard cap horizontal overflow at the cell boundary. Without this
     a busy subtask row (project pill + parent chip + count + overdue
     badge + …) was spilling past the cell and overlapping the Owner
     column's avatar in the next cell. */
  overflow: hidden;
}
/* Default is "don't shrink" — that protects the small chips (project
   dot, owner avatars, due-date) from disappearing. Heavier elements
   that take real horizontal space override this individually below
   so they shrink with .name-text instead of pushing it off-screen. */
.cell-name .name-wrap > * { flex-shrink: 0; }
.cell-name .name-wrap > .row-project-pill,
.cell-name .name-wrap > .subtask-parent-chip,
.cell-name .name-wrap > .subtask-count,
.cell-name .name-wrap > .overdue-badge,
.cell-name .name-wrap > .late-badge {
  flex-shrink: 1;
  min-width: 0;
}
/* The title takes priority over neighbouring chips when space gets
   tight — without this it gets crowded out by the project pill +
   parent chip + count + badges and disappears entirely. */
.cell-name .name-wrap .name-text {
  flex: 1 1 0%;
  min-width: 80px;                 /* always reserve enough for ~10 chars */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  order: -1;                       /* render the title BEFORE the parent chip / count
                                      so the user sees it first when scanning */
}
/* Layout order on a row (left → right):
     -3  expand/collapse chevron OR subtask "↳" glyph
     -2  project pill
     -1  name-text
      0  parent chip, subtask count, badges, hover-only buttons
   Without `order: -3` on the toggle, the global default of 0 + our
   order:-1 on name-text would push the chevron past the title to the
   right edge of the cell — counter-intuitive, since the disclosure
   triangle on a tree-like outline always lives on the LEFT. */
.cell-name .name-wrap .subtask-toggle,
.cell-name .name-wrap .subtask-toggle-spacer,
.cell-name .name-wrap .subtask-glyph { order: -3; }
.cell-name .name-wrap .row-project-pill { order: -2; }
/* Empty-title rows show a muted "(Untitled)" so they're still
   visible / clickable instead of rendering as a blank cell. Opening
   the drawer lets the user fix the title. */
.cell-name .name-wrap .name-text.name-text-untitled {
  color: var(--ink-faint, #a3a8b6);
  font-style: italic;
  font-weight: 500;
}
/* The hover-only delete button hugs the right edge of the cell, so
   it should stay flush even when the title is short. */
.cell-name .name-wrap .row-delete-btn { margin-left: auto; }

/* Project pill — only rendered when TableView's showProjectPill prop is
   true (e.g. on the cross-project My Work table). Tinted by the project's
   own brand color so different projects scan at a glance. */
.row-project-pill {
  display: inline-flex; align-items: center; gap: 5px;
  flex-shrink: 1;                         /* allow the pill to ellipsize when the cell is tight */
  font-size: 10.5px; font-weight: 700; letter-spacing: .02em;
  padding: 2px 8px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--proj-color, #a3a8b6) 14%, white);
  color: color-mix(in srgb, var(--proj-color, #a3a8b6) 75%, var(--ink-strong));
  border: 1px solid color-mix(in srgb, var(--proj-color, #a3a8b6) 28%, transparent);
  max-width: 140px;
  min-width: 32px;                        /* reserve room for the dot + 2 chars */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* On a subtask row the parent chip already implies the project (the
   parent lives in the same project), so the project pill becomes
   double-information that eats the title slot. Collapse it to just
   the colored dot — keeps a visual hint of the project without
   stealing 140px of title width. */
tr.is-subtask-row .row-project-pill {
  max-width: 18px;
  padding: 2px 0;
  border: none;
  background: transparent;
  color: transparent;                     /* hide the text content */
}
tr.is-subtask-row .row-project-pill-dot {
  width: 8px; height: 8px;
  margin: 0 4px;
}
.row-project-pill-dot {
  width: 6px; height: 6px; border-radius: 999px;
  background: var(--proj-color, #a3a8b6);
  flex-shrink: 0;
}

/* Hover-only "move to bin" button on each row. Hidden at rest so the
   table reads cleanly; appears when the row is hovered. The 13×13 icon
   keeps the row height stable. */
.row-delete-btn {
  margin-left: auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: var(--ink-faint);
  cursor: pointer;
  opacity: 0;
  transition: opacity .12s ease, background .12s ease, color .12s ease, border-color .12s ease;
}
.t tbody tr:hover .row-delete-btn,
.row-delete-btn:focus-visible {
  opacity: 1;
}
.row-delete-btn:hover {
  color: #c0223a;
  background: #fff0f3;
  border-color: #f5c0cc;
}
.name-expand {
  width: 16px; height: 16px; color: var(--ink-faint); cursor: pointer; flex: none;
}
.name-expand:hover { color: var(--ink-strong); }
.subtask-count {
  font-size: 11px; color: var(--ink-muted); background: var(--bg-subtle);
  padding: 1px 6px; border-radius: 999px; margin-left: 6px;
}

/* ↻ glyph — recurring task marker. Shows next to the task name on
   any row whose recurrence_rule_id is set (template OR materialised
   instance). Brand-tinted so it's visible at a glance but doesn't
   compete with status / priority pills. */
.recur-glyph {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px;
  margin-left: 6px;
  font-size: 11px; font-weight: 700;
  color: var(--brand, #a25ddc);
  background: var(--brand-soft, rgba(162, 93, 220, .12));
  border-radius: 999px;
  flex-shrink: 0;
  line-height: 1;
}

/* Owner cell */
.cell-owners {
  display: flex; align-items: center; justify-content: center; gap: -4px;
  padding: 0 4px;
}
.cell-owners .avatar-stack { margin: 0 auto; }

/* Date cell */
.cell-date {
  text-align: center; color: var(--ink-body);
  font-size: 12.5px;
}
.cell-date.is-overdue { color: var(--status-blocked); font-weight: 600; }
.cell-date.is-done { color: var(--ink-faint); text-decoration: line-through; }

/* Points cell */
.points {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 26px; height: 22px; border-radius: var(--r-sm);
  background: var(--bg-subtle); font-size: 12px; font-weight: 600;
  color: var(--ink-body); padding: 0 6px;
}

/* Sprint tag — state-aware */
.sprint-tag {
  display: inline-flex; align-items: center; gap: 4px;
  background: #eef3ff; color: #0060b9;
  padding: 3px 9px; border-radius: 999px;
  font-size: 11.5px; font-weight: 600;
  white-space: nowrap;
  border: 1px solid transparent;
}
.sprint-tag.is-active {
  background: rgba(162, 93, 220, 0.12);
  color: #7e3fc4;
  border-color: rgba(162, 93, 220, 0.25);
}
.sprint-tag.is-upcoming {
  background: #eef3ff; color: #0060b9;
  border-color: rgba(0, 96, 185, 0.18);
}
.sprint-tag.is-past {
  background: var(--bg-subtle); color: #676879;
  border-color: var(--border-strong);
  opacity: 0.85;
}
.sprint-tag.no-sprint {
  background: transparent; color: var(--ink-muted);
  border-color: var(--border-soft);
  font-weight: 500;
}
.sprint-tag .dot { width: 5px; height: 5px; border-radius: 999px; background: currentColor; }

/* Updated cell */
.updated-cell {
  display: flex; align-items: center; gap: 6px; padding: 0 10px;
  color: var(--ink-muted); font-size: 12px;
}

/* Add row */
.add-row td {
  padding: 6px 14px !important;
  color: var(--ink-muted); font-size: 13px;
  cursor: pointer;
  border-left: 6px solid var(--epic-color, var(--brand));
  background: #fafbfc !important;
}
.add-row td:hover { color: var(--brand); }

/* ── Kanban ─────────────────────────────────────────────── */
.kanban-scroll {
  flex: 1;
  overflow-x: auto; overflow-y: hidden;
  padding: 16px 24px;
  background: var(--bg-app);
}
.kanban {
  display: flex; gap: 14px; height: 100%; align-items: flex-start;
}
.kanban-col {
  width: 280px; flex: none;
  background: white;
  border-radius: var(--r-md);
  border: 1px solid var(--border);
  display: flex; flex-direction: column;
  max-height: 100%;
  overflow: hidden;
}
.kanban-col-head {
  padding: 10px 12px; display: flex; align-items: center; gap: 8px;
  border-bottom: 1px solid var(--border);
  background: white;
}
.kanban-col-swatch { width: 10px; height: 10px; border-radius: 3px; }
.kanban-col-title { font-weight: 600; font-size: 13px; color: var(--ink-strong); }
.kanban-col-count {
  background: var(--bg-subtle); color: var(--ink-muted);
  padding: 1px 7px; border-radius: 999px; font-size: 11px; font-weight: 600;
}
.kanban-col-add {
  margin-left: auto; color: var(--ink-muted);
  background: transparent; border: none; cursor: pointer;
  width: 22px; height: 22px; border-radius: 4px;
  display: flex; align-items: center; justify-content: center;
}
.kanban-col-add:hover { background: var(--bg-subtle); color: var(--ink-strong); }
.kanban-col-body {
  padding: 8px; display: flex; flex-direction: column; gap: 8px;
  overflow-y: auto; flex: 1;
}
.k-card {
  background: white; border: 1px solid var(--border);
  border-left: 4px solid var(--epic-color, var(--brand));
  border-radius: var(--r-md); padding: 10px 12px;
  display: flex; flex-direction: column; gap: 8px;
  cursor: pointer; transition: box-shadow 0.1s, transform 0.1s;
  box-shadow: var(--shadow-sm);
}
.k-card:hover { box-shadow: var(--shadow-md); transform: translateY(-1px); }
.k-card-head { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.k-epic-tag {
  font-size: 10.5px; font-weight: 700; color: var(--epic-color);
  letter-spacing: 0.04em; text-transform: uppercase;
}

/* Type chip on Kanban card — surfaces task.type (Bug, Chore, Spike).
   Bug BG_02E81DCE4B. The .is-bug variant on the card itself flips
   the left-edge color to red so a Bug card stands out in a column
   even when the chip is briefly obscured by drag/hover state. */
.k-type-tag {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 10px; font-weight: 700;
  padding: 2px 7px; border-radius: 999px;
  letter-spacing: .04em; text-transform: uppercase;
  line-height: 1.2;
}
.k-type-tag-emoji { font-size: 11px; line-height: 1; }
.k-type-bug {
  color: #c0223a;
  background: rgba(226, 68, 92, .10);
  border: 1px solid rgba(226, 68, 92, .25);
}
.k-type-chore {
  color: var(--ink-muted, #676879);
  background: rgba(103, 104, 121, .10);
  border: 1px solid rgba(103, 104, 121, .25);
}
.k-type-spike {
  color: #b66f00;
  background: rgba(253, 171, 61, .14);
  border: 1px solid rgba(253, 171, 61, .30);
}
/* Bug card — keep the epic-color left border but tint it red on top
   so bugs are scannable even in a dense column. Uses a layered
   gradient so the existing --epic-color isn't lost. */
.k-card.is-bug {
  border-left-color: #e2445c;
  box-shadow: var(--shadow-sm), inset 4px 0 0 0 rgba(226, 68, 92, .04);
}

/* Type marker inline on table rows — small chip next to the task
   name, only rendered for non-default types. Compact (Kanban gets the
   bigger version) but still readable when scanning a 30-row table. */
.row-type-tag {
  display: inline-flex; align-items: center; gap: 3px;
  margin-left: 6px;
  font-size: 9.5px; font-weight: 700;
  padding: 1px 6px; border-radius: 999px;
  letter-spacing: .04em; text-transform: uppercase;
  line-height: 1.3; flex: none;
}
.row-type-tag-emoji { font-size: 10px; line-height: 1; }
.row-type-bug {
  color: #c0223a;
  background: rgba(226, 68, 92, .10);
  border: 1px solid rgba(226, 68, 92, .25);
}
.row-type-chore {
  color: var(--ink-muted, #676879);
  background: rgba(103, 104, 121, .10);
  border: 1px solid rgba(103, 104, 121, .25);
}
.row-type-spike {
  color: #b66f00;
  background: rgba(253, 171, 61, .14);
  border: 1px solid rgba(253, 171, 61, .30);
}

/* Bug-only quick-pill in the toolbar. Reuses `.btn.btn-mine.is-on`
   shape but uses bug-red instead of brand-purple so it visually
   advertises the filter is active. */
.btn-type-bug.is-on {
  background: #e2445c !important;
  border-color: #c0223a !important;
  color: white !important;
}
.btn-type-bug.is-on:hover {
  background: #c0223a !important;
}
.k-card-title {
  font-size: 13.5px; font-weight: 500; color: var(--ink-strong);
  line-height: 1.35;
}
.k-card-meta {
  display: flex; align-items: center; gap: 8px; margin-top: 2px;
  font-size: 11.5px; color: var(--ink-muted);
}
.k-card-meta .meta-group { display: flex; align-items: center; gap: 4px; }
.k-card-meta .meta-group.is-overdue { color: var(--status-blocked); font-weight: 700; }
.k-card-meta svg { width: 12px; height: 12px; }
.k-card-footer {
  display: flex; align-items: center; justify-content: space-between;
}

/* ── Drag-drop polish ─────────────────────────────────── */
.k-card.is-dragging {
  opacity: 0.45;
  transform: scale(0.97);
  box-shadow: 0 0 0 2px rgba(162, 93, 220, 0.45);
  cursor: grabbing;
}
.k-card.is-dragging:hover { transform: scale(0.97); }
.k-card.is-just-dropped { animation: fb-drop-flash 0.7s ease-out; }
@keyframes fb-drop-flash {
  0%   { box-shadow: 0 0 0 2px var(--brand), 0 8px 22px rgba(162,93,220,.32); transform: scale(1.02); }
  60%  { box-shadow: 0 0 0 1px var(--brand), 0 4px 14px rgba(162,93,220,.20); transform: scale(1); }
  100% { box-shadow: var(--shadow-sm); transform: scale(1); }
}
.kanban-col-body.is-receiving {
  background-image:
    linear-gradient(180deg, rgba(162,93,220,0.04), rgba(162,93,220,0.00));
  transition: background-image 0.2s ease-out;
}
.k-card[style*="--brand"] {
  /* drop indicator with subtle pulse */
  transition: box-shadow 0.12s ease-out;
}

/* Table row drag polish */
.t tr.is-dragging-row td { opacity: 0.45; transform: scale(0.995); }
.t tr.is-dragging-row { cursor: grabbing; }

/* Drop "settle" — row lifts down + a brand ribbon sweeps across name cell */
.t tr.is-just-dropped-row td {
  animation: fb-row-settle 0.55s cubic-bezier(.2,.85,.25,1.05);
  position: relative;
}
@keyframes fb-row-settle {
  0%   { transform: translateY(-4px); background-color: rgba(162,93,220,0.20); }
  55%  { transform: translateY(0);    background-color: rgba(162,93,220,0.10); }
  100% { transform: translateY(0);    background-color: transparent; }
}
.t tr.is-just-dropped-row td.cell-name { overflow: hidden; }
.t tr.is-just-dropped-row td.cell-name::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(110deg,
    rgba(162,93,220,0)   0%,
    rgba(162,93,220,0)  30%,
    rgba(162,93,220,0.30) 48%,
    rgba(255,255,255,0.50) 50%,
    rgba(162,93,220,0.30) 52%,
    rgba(162,93,220,0)  70%,
    rgba(162,93,220,0) 100%);
  background-size: 200% 100%;
  background-position: 100% 0;
  animation: fb-row-shine-purple 0.7s ease-out;
  pointer-events: none;
}
@keyframes fb-row-shine-purple {
  0%   { background-position: 120% 0; opacity: 0; }
  20%  { opacity: 1; }
  100% { background-position: -20% 0; opacity: 0; }
}
.row-grip { user-select: none; }
.row-grip:hover svg { color: var(--ink-muted); }
.t tr:hover .row-grip svg { color: var(--ink-muted); }

/* Row celebration when status → done: green sweep across the line */
.t tr.is-row-celebrate td {
  animation: fb-row-done-sweep 1.1s ease-out;
  position: relative;
}
@keyframes fb-row-done-sweep {
  0%   { background-color: transparent; }
  20%  { background-color: rgba(0,200,117,0.22); }
  60%  { background-color: rgba(0,200,117,0.10); }
  100% { background-color: transparent; }
}
/* Animated green ribbon that slides across the row's name cell */
.t tr.is-row-celebrate td.cell-name { overflow: hidden; }
.t tr.is-row-celebrate td.cell-name::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(110deg,
    rgba(0,200,117,0)    0%,
    rgba(0,200,117,0)   25%,
    rgba(0,200,117,0.28) 45%,
    rgba(255,255,255,0.55) 50%,
    rgba(0,200,117,0.28) 55%,
    rgba(0,200,117,0)   75%,
    rgba(0,200,117,0)  100%);
  background-size: 200% 100%;
  background-position: 100% 0;
  animation: fb-row-shine 1.0s ease-out;
  pointer-events: none;
}
@keyframes fb-row-shine {
  0%   { background-position: 120% 0; opacity: 0; }
  20%  { opacity: 1; }
  100% { background-position: -20% 0; opacity: 0; }
}

/* ── Subtask rows in the table ─────────────────────────────────────
   Visible when the BoardToolbar's "Show subtasks" toggle is on.
   The indent + faint left border + ↳ glyph + "in <parent>" chip
   together make the parent-child relationship clear even after
   sort/filter scatters the rows. */
tr.is-subtask-row .cell-name { padding-left: 28px; }
tr.is-subtask-row .name-wrap { gap: 8px; }
tr.is-subtask-row td:first-of-type {
  /* Faint left rule so the whole row reads as "indented under
     something else", borrowed from the kanban subtask styling. */
  position: relative;
}
tr.is-subtask-row td:first-of-type::before {
  content: "";
  position: absolute;
  left: 14px; top: 0; bottom: 0;
  width: 2px;
  background: var(--border-row, #eaecf1);
}
.subtask-glyph {
  display: inline-block;
  width: 16px;
  text-align: center;
  color: var(--brand, #a25ddc);
  font-weight: 700;
  font-size: 15px;
  flex-shrink: 0;
  opacity: .85;
}

/* ── Tree guides for nested subtasks ──────────────────────────
   For each subtask row we render:
     · Vertical lines (background-image) at every ancestor's
       text-start position. Pattern repeats every 22px starting at
       14px, clipped to depth*22px width.
     · An L-shape connector (`::before` pseudo) that joins the last
       ancestor's vertical line to this row's content. The L's
       vertical leg lives in the bg pattern; the ::before draws the
       horizontal leg + a short stub down to the row's centre.
     · A subtle per-depth row tint so nested rows visually cluster.

   --task-depth is set inline by TaskRow (0 for top-level, 1..4
   for subtasks). All values fall out from that one variable so we
   never re-render React layout — pure CSS. */
.t tbody td.cell-name {
  position: relative;             /* anchor for the L-connector */
  background-image:
    repeating-linear-gradient(
      to right,
      transparent 0px,
      transparent 13px,
      rgba(15, 23, 41, 0.16) 13px,
      rgba(15, 23, 41, 0.16) 14.5px,
      transparent 14.5px,
      transparent 22px
    );
  background-position: left center;
  background-size: calc(var(--task-depth, 0) * 22px) 100%;
  background-repeat: no-repeat;
}
/* L-shape: horizontal arm from the last ancestor's vertical
   line into this row's content. Positioned just before the
   text. */
.t tbody td.cell-name[style*="--task-depth"]:not([style*="--task-depth: 0"])::before {
  content: "";
  position: absolute;
  left: calc(((var(--task-depth, 0) - 1) * 22px) + 14px);
  top: 50%;
  width: calc(22px - 6px);          /* arm length: stops 6px short of content */
  height: 1.5px;
  background: rgba(15, 23, 41, 0.16);
  pointer-events: none;
}
/* Vertical stub from the arm UP to the row's top — completes the
   "L" shape so it visually anchors to the ancestor's vertical line
   passing through this row. */
.t tbody td.cell-name[style*="--task-depth"]:not([style*="--task-depth: 0"])::after {
  content: "";
  position: absolute;
  left: calc(((var(--task-depth, 0) - 1) * 22px) + 14px);
  top: 0;
  width: 1.5px;
  height: 50%;
  background: rgba(15, 23, 41, 0.16);
  pointer-events: none;
}
/* Per-depth row tint — subtle visual grouping. Hover overrides
   below stays strong. */
.t tbody tr:has(td.cell-name[style*="--task-depth: 1"]) td { background-color: rgba(15,23,41,0.015); }
.t tbody tr:has(td.cell-name[style*="--task-depth: 2"]) td { background-color: rgba(15,23,41,0.03); }
.t tbody tr:has(td.cell-name[style*="--task-depth: 3"]) td { background-color: rgba(15,23,41,0.045); }
.t tbody tr:has(td.cell-name[style*="--task-depth: 4"]) td { background-color: rgba(15,23,41,0.06); }
.t tbody tr:hover td { background-color: #fbfcfe !important; }
.subtask-parent-chip {
  appearance: none;
  border: 1px solid var(--border, #e6e9ef);
  background: var(--bg-subtle, #fafbfd);
  color: var(--ink-muted, #676879);
  font-size: 11px;
  font-weight: 500;
  padding: 2px 8px;
  border-radius: 999px;
  cursor: pointer;
  max-width: 180px;                /* was 240 — too greedy on the My Work table */
  min-width: 60px;                 /* but still readable when shrunk */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex-shrink: 1;                  /* yield room to the title when the cell is narrow */
  transition: background .12s ease, border-color .12s ease, color .12s ease;
}
.subtask-parent-chip:hover {
  background: rgba(0, 115, 234, .08);
  border-color: rgba(0, 115, 234, .25);
  color: var(--brand, #0073ea);
}

/* "+ Subtask" hover button on parent rows. Only visible when the
   row is hovered, so the table stays clean at rest. */
.row-subtask-btn {
  appearance: none;
  border: 1px solid var(--border, #e6e9ef);
  background: var(--bg-surface, #fff);
  color: var(--ink-muted, #676879);
  font-size: 11px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 999px;
  cursor: pointer;
  margin-left: 6px;
  display: none;
  align-items: center;
  gap: 4px;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.row-subtask-btn:hover {
  background: rgba(0, 115, 234, .08);
  border-color: rgba(0, 115, 234, .25);
  color: var(--brand, #0073ea);
}
/* Reveal on row hover. Mirrors the existing row-delete-btn pattern. */
tr:hover .row-subtask-btn { display: inline-flex; }

/* Inline subtask add row — same treatment as the regular AddTaskRow
   but with the subtask indent + ↳ glyph baked in. */
.is-subtask-add-row td {
  background: rgba(0, 115, 234, .03);
  border-left: 2px solid var(--border-row, #eaecf1);
}

/* Per-parent expand/collapse chevron. Replaces the static "name-expand"
   icon on parent rows that have at least one visible subtask. The
   chevron rotates to indicate state and exposes a useful click target. */
.subtask-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px; height: 18px;
  border: 0;
  background: transparent;
  color: var(--ink-muted, #676879);
  cursor: pointer;
  border-radius: 4px;
  flex: none;
  padding: 0;
}
.subtask-toggle:hover {
  background: rgba(0, 115, 234, .08);
  color: var(--brand, #0073ea);
}
.subtask-toggle-chev {
  transition: transform .12s ease;
}
.subtask-toggle-chev.is-open {
  transform: rotate(90deg);
}
.subtask-toggle-spacer {
  cursor: default;
  pointer-events: none;
}
.subtask-toggle-spacer:hover { background: transparent; }

/* ── StoryRow ──────────────────────────────────────────────────────
   User-story rendered as a row in the project task table. Sits at
   the position of its first child task and acts as a parent — the
   chevron toggles whether its children render below. Visual rhythm:
   subtle amber accent on the left so stories scan as "different from
   tasks" without being noisy. */
.t tr.story-row > td {
  background: linear-gradient(180deg,
    rgba(245, 158, 11, .04),
    rgba(245, 158, 11, .02));
  border-top: 1px solid rgba(245, 158, 11, .18);
  border-bottom: 1px solid rgba(245, 158, 11, .15);
}
.t tr.story-row > td.cell-name {
  border-left: 3px solid rgba(245, 158, 11, .55);
  padding-left: calc(14px - 3px);   /* compensate for the 3px border */
}
.t tr.story-row.needs-review > td.cell-name {
  border-left-color: #f59e0b;
  background: linear-gradient(180deg,
    rgba(245, 158, 11, .10),
    rgba(245, 158, 11, .04));
}
.t tr.story-row > td.cell-name { cursor: pointer; }
.t tr.story-row:hover > td.cell-name {
  background: linear-gradient(180deg,
    rgba(245, 158, 11, .10),
    rgba(245, 158, 11, .05));
}
.t tr.story-row > td.story-row-tail {
  /* The right-side filler cell that absorbs the unused columns. Now
     also hosts the Approve / Request changes action buttons when the
     current user is a reviewer (previous layout cramped them into the
     name cell and the title + buttons fought for the same pixels).
     padding-right keeps the buttons clear of the table's right edge. */
  color: transparent;
  padding-right: 14px;
  vertical-align: middle;
}

.story-row-emoji {
  font-size: 14px; line-height: 1;
  margin-left: 2px;
  font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
  flex: none;
}
.story-row-title {
  font-weight: 700 !important;
  color: var(--ink-strong) !important;
  letter-spacing: -.005em;
}
.story-row-status {
  margin-left: 6px;
  flex: none;
}
.story-row-progress {
  margin-left: auto;
  margin-right: 8px;
  font-size: 11px;
  font-weight: 700;
  color: var(--ink-muted);
  background: var(--bg-subtle);
  padding: 2px 8px;
  border-radius: 999px;
  font-variant-numeric: tabular-nums;
  flex: none;
}

/* Inline reviewer actions — pinned to the right edge of the story
   row's tail cell. justify-content: flex-end lets the buttons sit
   against the right padding regardless of how wide the tail cell
   ends up (varies with which data columns are visible / hidden). */
.story-row-actions {
  display: flex;
  gap: 6px;
  justify-content: flex-end;
  align-items: center;
  /* color: initial so the buttons aren't transparent like the tail */
  color: var(--ink-body);
}
.story-row-action-btn {
  display: inline-flex; align-items: center; gap: 4px;
  font: inherit; font-size: 11px; font-weight: 700;
  padding: 3px 9px; border-radius: 999px;
  cursor: pointer;
  border: 1px solid transparent;
  transition: filter .12s, transform .08s;
}
.story-row-action-btn:disabled { opacity: .45; cursor: not-allowed; }
.story-row-action-btn:not(:disabled):hover { filter: brightness(1.05); transform: translateY(-1px); }
.story-row-action-btn.is-approve {
  background: rgba(34, 197, 94, .12);
  color: #166534;
  border-color: rgba(34, 197, 94, .35);
}
.story-row-action-btn.is-reject {
  background: rgba(226, 68, 92, .10);
  color: #8a1024;
  border-color: rgba(226, 68, 92, .35);
}
