/* Projects & tasks (Kanban). Loaded automatically by app_stylesheets_tag. */
@layer components {
  :root {
    --kanban-column-width: 20rem;
    --kanban-card-gap: 0.55rem;
  }

  /* ---- Board ---- */

  .kanban-board {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    padding: 1rem 1.25rem 2rem;
    overflow-x: auto;
    /* Stretch to the bottom of the viewport so the horizontal scrollbar
       sits at the page's edge, not right under the columns. */
    min-height: calc(100dvh - 10.5rem);
  }

  /* The column wears its state color as a soft wash — color-mix keeps any
     user-picked color easy on the eye instead of painting it as a dot. */
  .kanban-column {
    flex: 0 0 var(--kanban-column-width);
    display: flex;
    flex-direction: column;
    gap: var(--kanban-card-gap);
    padding: 0.75rem;
    background: color-mix(in srgb, var(--state-accent, var(--muted-3)) var(--state-wash, 10%), var(--cream-soft));
    border: 1px solid color-mix(in srgb, var(--state-accent, var(--muted-3)) calc(var(--state-wash, 10%) + 12%), var(--surface-border));
    border-radius: var(--radius-card);
  }

  .kanban-column-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0 0.25rem 0.35rem;
    cursor: grab; /* columns are dragged by their header */
  }

  /* Drag & drop ghosts (SortableJS drops these classes on the placeholder). */
  .kanban-card-ghost,
  .kanban-column-ghost {
    opacity: 0.4;
  }

  /* Small print inside the move dialogs. */
  .dialog-hint {
    margin-top: 0.6rem;
    font-size: 0.8rem;
    color: var(--muted-2);
  }

  .kanban-column-dot {
    width: 0.55rem;
    height: 0.55rem;
    border-radius: 50%;
    background: var(--state-accent, var(--muted-3));
    flex-shrink: 0;
  }

  /* The name is a blitz inline_edit — the form takes the flexible middle,
     the editable span carries the old .kanban-column-name look. */
  .kanban-column-name-form {
    flex: 1;
    min-width: 0;
  }

  .kanban-column-name-form [data-editable-target="editable"] {
    display: inline-block;
    max-width: 100%;
    font-weight: 600;
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .kanban-column-menu {
    margin-left: auto;
  }

  /* Palette grid inside the column ⋮ menu — the only color source. */
  .column-color-grid {
    display: grid;
    grid-template-columns: repeat(5, 1.6rem);
    gap: 0.35rem;
    padding: 0.3rem 1rem 0.5rem;
  }

  .kanban-column-menu .column-color-grid a {
    display: inline-flex;
    padding: 0;
    background: none;
  }

  /* Swatches preview the RESULTING column wash, not the raw color —
     the same mix the column background gets (soft 10%, bold 26%). The
     border carries more of the color so the grid stays tellable. */
  .column-color-swatch span {
    width: 1.6rem;
    height: 1.6rem;
    border-radius: 50%;
    background: color-mix(in srgb, var(--swatch) var(--swatch-wash, 10%), var(--cream-soft));
    border: 1px solid color-mix(in srgb, var(--swatch) 55%, var(--surface-border));
  }

  .column-color-swatch:hover span {
    border-color: var(--wine);
  }

  /* Own selected look — blitz's a.selected paints a ✓ that has no room
     in a swatch circle. */
  .kanban-column-menu .column-color-grid a.selected {
    background: none;
  }

  .kanban-column-menu .column-color-grid a.selected::after {
    content: none;
  }

  .column-color-swatch.selected span {
    outline: 2px solid var(--wine);
    outline-offset: 2px;
  }

  /* Radio variant of the swatch (new-column dialog). */
  .column-color-swatch input[type="radio"] {
    position: absolute;
    opacity: 0;
    pointer-events: none;
  }

  .column-color-swatch:has(input:checked) span {
    outline: 2px solid var(--wine);
    outline-offset: 2px;
  }

  .column-color-swatch:has(input:focus-visible) span {
    outline: 2px solid var(--ink);
    outline-offset: 2px;
  }

  .column-color-grid--form {
    border: none;
    margin: 0.5rem 0 0;
    padding: 0;
  }

  .column-color-grid--form .column-color-swatch {
    position: relative;
    display: inline-flex;
    cursor: pointer;
  }

  .kanban-column-cards {
    display: flex;
    flex-direction: column;
    gap: var(--kanban-card-gap);
    min-height: 2.5rem; /* drop target stays reachable when empty */
  }

  /* ---- Project page background ---- */

  .kanban-surface {
    background-size: cover;
    background-position: center;
  }

  /* On a photo background the columns go translucent with a soft blur —
     the wash mixes into frosted white instead of cream, cards stay opaque. */
  .kanban-surface.has-background .kanban-column {
    background: color-mix(in srgb, var(--state-accent, var(--muted-3)) var(--state-wash, 10%), rgba(255, 255, 255, 0.65));
    backdrop-filter: blur(6px);
  }

  /* Muted greys drown over photos — column chrome switches to full ink. */
  .kanban-surface.has-background .kanban-column-name-form [data-editable-target="editable"],
  .kanban-surface.has-background .kanban-add-task,
  .kanban-surface.has-background .kanban-column-header .kanban-card-menu-btn {
    color: var(--ink);
  }

  .background-picker-button {
    position: fixed;
    right: 1.25rem;
    bottom: 1.25rem;
    z-index: 5;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.6rem;
    height: 2.6rem;
    border-radius: 50%;
    background: color-mix(in srgb, var(--surface) 85%, transparent);
    border: 1px solid var(--surface-border);
    color: var(--muted);
    box-shadow: 0 2px 8px rgba(60, 30, 40, 0.15);
  }

  .background-picker-button:hover {
    color: var(--wine);
    background: var(--surface);
  }

  .background-dialog {
    min-width: min(34rem, 86vw);
  }

  .background-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 0.6rem;
  }

  .background-option {
    display: block;
    aspect-ratio: 16 / 10;
    border-radius: var(--radius-control);
    overflow: hidden;
    border: 2px solid transparent;
  }

  .background-option img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }

  .background-option--none {
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--cream-soft);
    border-color: var(--surface-border);
    color: var(--muted);
    font-size: 0.82rem;
    text-decoration: none;
    text-align: center;
  }

  .background-option:hover {
    border-color: var(--wine);
  }

  .background-option.selected {
    border-color: var(--wine);
    outline: 2px solid var(--wine);
    outline-offset: 1px;
  }

  .background-hint {
    margin: 0.6rem 0 0;
    font-size: 0.78rem;
    color: var(--muted-2);
  }

  /* ---- Card ---- */

  .kanban-card {
    position: relative;
    background: var(--surface);
    border: 1px solid var(--surface-border);
    border-radius: var(--radius-control);
    padding: 0.6rem 0.75rem;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    box-shadow: 0 1px 3px rgba(60, 30, 40, 0.08);
    transition: transform 0.15s ease, box-shadow 0.15s ease;
  }

  /* The title link stretches over the whole card (notes-card pattern) —
     everywhere opens the dialog except star and ⋮, lifted above it. */
  .kanban-card-title-link::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
  }

  .kanban-card-favorite,
  .kanban-card-top .kanban-card-menu {
    position: relative;
    z-index: 2;
  }

  /* Hover lifts the card slightly, deepening the resting shadow. */
  .kanban-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 16px rgba(60, 30, 40, 0.16);
  }

  /* No lift while Sortable drags — the ghost should sit flat in place. */
  .kanban-card-ghost,
  .kanban-card-ghost:hover {
    transform: none;
    box-shadow: none;
  }

  /* Important tasks tint like favorite notes (item_list.css palette), and
     off the same attribute: favorite_controller flips it on click, so the
     star answers to the click alone — no round trip, no redraw. */
  .kanban-card[data-favorite-favorite="true"] {
    background: #faf0d4;
  }

  .kanban-card-top {
    display: flex;
    align-items: center;
    gap: 0.5rem;
  }

  .kanban-card-top .kanban-card-menu {
    margin-left: auto;
  }

  .kanban-card-favorite {
    display: inline-flex;
    border: none;
    background: none;
    padding: 0.1rem;
    cursor: pointer;
    color: #e5e7eb;
    transition: color 0.3s ease;
  }

  .kanban-card-favorite:hover {
    color: #fcd34d;
  }

  .kanban-card[data-favorite-favorite="true"] .kanban-card-favorite {
    color: #e0a800;
  }

  .kanban-card[data-favorite-favorite="true"] .kanban-card-favorite:hover {
    color: #f0c65a;
  }

  .kanban-card-id {
    font-size: 0.72rem;
    color: var(--muted-2);
    font-variant-numeric: tabular-nums;
  }

  .kanban-card-menu-btn {
    border: none;
    background: none;
    padding: 0.1rem;
    cursor: pointer;
    color: var(--muted-2);
    border-radius: 6px;
  }

  .kanban-card-menu-btn:hover {
    background: var(--rose-tint);
    color: var(--wine);
  }

  .kanban-card-title {
    color: var(--ink);
    line-height: 1.35;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
  }

  .kanban-card-footer {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 1.4rem;
  }

  .kanban-card-branch {
    font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
    font-size: 0.72rem;
    color: var(--gold-ink);
    background: var(--gold);
    border-radius: 5px;
    padding: 0.05rem 0.4rem;
    max-width: 9rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .kanban-card-comments,
  .kanban-card-images {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    font-size: 0.78rem;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
  }

  .kanban-card-assignee {
    margin-left: auto;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.68rem;
    font-weight: 600;
    color: var(--wine-deep);
    background: var(--rose);
    flex-shrink: 0;
  }

  /* "+ Aufgabe" ghost button in the first column (direct child — the card
     star forms are button_to too and must keep their natural size). */
  .kanban-column > form.button_to {
    display: flex;
  }

  /* "+" next to the last column, at title-bar height. */
  .kanban-add-column {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.2rem;
    height: 2.2rem;
    margin-top: 0.55rem; /* lines up with the column titles */
    border: 1px dashed var(--surface-border);
    border-radius: var(--radius-control);
    background: none;
    cursor: pointer;
    color: var(--muted);
  }

  .kanban-add-column:hover {
    background: var(--rose-tint);
    color: var(--wine);
    border-color: var(--muted-3);
  }

  .kanban-add-task {
    flex: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    border: none;
    background: none;
    cursor: pointer;
    padding: 0.45rem;
    border-radius: var(--radius-control);
    color: var(--muted);
    font-size: 0.88rem;
  }

  .kanban-add-task:hover {
    background: var(--rose-tint);
    color: var(--wine);
  }

  /* ---- List view ----
     The list renders as stacked cards on every screen size — the same
     recipe (radius, border, shadow, spacing) as the shared item lists.
     Sorting lives in the toolbar dropdown; there is no table header. */

  .kanban-list-wrap {
    padding: 0.75rem 0 2rem;
    /* Same stretch as the board: the cell dropdowns get room below
       instead of being clipped. */
    min-height: calc(100dvh - 10.5rem);
  }

  .kanban-list,
  .kanban-list tbody {
    display: block;
    width: 100%;
  }

  .kanban-list tbody {
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
  }

  /* Star and ✕ sit out of flow, vertically centered like the notes
     cards — the side paddings reserve their gutters. Both text lines
     start at the same x, so the state dot lines up with the id. The
     title's flex basis leaves no room next to it, forcing line two. */
  .kanban-list tbody tr {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 0.35rem;
    row-gap: 0.15rem;
    padding: 0.55rem 2.1rem 0.55rem 2.3rem;
    border-radius: 12px;
    border: 1px solid var(--surface-border);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.08);
    /* Plain surface like the other resources' list rows — the column
       color stays on the state dot, important rows go gold. */
    background: var(--surface);
  }

  /* Same gold as the notes/bookmarks rows (item_list.css). Restated here:
     favorite.css's bare attribute selector loses to the tr rule above. */
  .kanban-list tbody tr[data-favorite-favorite="true"] {
    background: #faf0d4;
  }

  .kanban-list tbody tr td {
    display: block;
    padding: 0;
    border: none;
  }

  .kanban-list-star {
    position: absolute;
    left: 0.45rem;
    top: 50%;
    transform: translateY(-50%);
  }

  .kanban-list-star .kanban-card-favorite {
    --icon-size: 1.15rem;
    padding: 0.1rem;
  }

  .kanban-list-actions {
    position: absolute;
    right: 0.35rem;
    top: 50%;
    transform: translateY(-50%);
  }

  .kanban-list-id {
    order: 1;
    color: var(--muted-2);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    font-size: 0.75rem;
  }

  .kanban-list-title {
    order: 2;
    flex: 1 1 70%;
    min-width: 0;
    font-size: 1.02rem;
    font-weight: 500;
  }

  /* The toggle's own left padding would nudge the dot off the id's x. */
  .kanban-list-state-cell {
    order: 3;
    margin-left: -0.2rem;
  }

  .kanban-list-assignee { order: 4; }

  .kanban-list-changed {
    order: 5;
    font-size: 0.75rem;
    color: var(--muted-2);
    white-space: nowrap;
  }

  /* Quiet in-cell toggles for status and assignee — the second line
     stays smaller and lighter than the title. */
  .kanban-list-field-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    border: none;
    background: none;
    padding: 0.1rem 0.2rem;
    border-radius: var(--radius-control);
    cursor: pointer;
    color: var(--muted);
    font-size: 0.78rem;
    white-space: nowrap;
  }

  .kanban-list-field-toggle:hover {
    background: var(--rose-tint);
  }

  .kanban-list-field-toggle .icon--caret-down-light {
    --icon-size: 0.9em;
    color: var(--muted);
  }

  /* Star state in the list rows — same attribute, same palette. */
  [data-favorite-favorite="true"] .kanban-card-favorite {
    color: #e0a800;
  }

  [data-favorite-favorite="true"] .kanban-card-favorite:hover {
    color: #f0c65a;
  }

  /* The glyph follows too: one icon in the markup, the filled star is a
     swapped variable — icons.css draws it from --svg. */
  [data-favorite-favorite="true"] .kanban-card-favorite .icon {
    --svg: url("/assets/star-fill-e6434a03.svg");
  }

  /* "+ Aufgabe" below the rows — the board button's look, stretched to
     the list's full width (button_to wraps it in a form). */
  .kanban-list-add {
    margin-top: 0.85rem;
  }

  .kanban-list-add .kanban-add-task {
    width: 100%;
  }

  .kanban-list-state {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    white-space: nowrap;
  }

  .kanban-list-assignee {
    color: var(--muted);
    white-space: nowrap;
  }

  /* Tasks toolbar on phones: it carries the sort AND two filter
     dropdowns — the generic .view-and-sort grid has one slot and stacks
     them into extra lines. One flex line fits them all here. */
  @media (max-width: 760px) {
    .task-filters .view-and-sort {
      display: flex;
      flex-wrap: wrap;
      column-gap: 0.15rem;
      row-gap: 0.3rem;
    }

    .task-filters .list-filter {
      flex: 1 1 100%;
    }

    .task-filters .favorites-switch {
      flex: 0 1 auto;
      min-width: 0;
    }

    /* A long state or member name must not push the row apart. */
    .task-filters .sort-toggle-label {
      max-width: 5.5rem;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    /* Three dropdowns share the line — the sort toggle keeps only its
       icon (the channel-dropdown pattern). */
    .task-filters .task-sort-dropdown .sort-toggle-label {
      position: absolute;
      inline-size: 1px;
      block-size: 1px;
      margin: -1px;
      padding: 0;
      overflow: hidden;
      clip: rect(0 0 0 0);
      white-space: nowrap;
      border: 0;
    }
  }

  /* Without the Popover API (older iOS) the open menu stays inside its
     card's stacking context — later sibling cards would paint over it.
     Lift whatever holds an open menu above its siblings. */
  .kanban-card:has(.dropdown-menu.show),
  .kanban-column:has(.dropdown-menu.show),
  .kanban-list tbody tr:has(.dropdown-menu.show) {
    z-index: 30;
  }

  /* ---- Empty state ---- */

  .kanban-empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.75rem;
    text-align: center;
    margin: 3rem auto;
    max-width: 34rem;
    padding: 2.5rem 2rem;
    background: var(--cream-soft);
    border: 1px dashed var(--surface-border);
    border-radius: var(--radius-card);
  }

  .kanban-empty-state h2 {
    margin: 0;
    color: var(--ink);
  }

  .kanban-empty-state p {
    margin: 0;
    color: var(--muted);
  }
}

/* ---- Task dialog ---- */
@layer components {
  .task-dialog {
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
    min-width: min(44rem, 86vw);
    max-width: 46rem;
  }

  /* Sticky like the footer: stays put while the dialog scrolls, bleeding
     into the dialog's 1rem padding. It carries its own ✕ — blitz's
     absolutely positioned close button would scroll away and is hidden
     for this dialog below. */
  .task-dialog-header {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    position: sticky;
    top: -1rem;
    z-index: 6;
    background: var(--surface, #fff);
    margin: -1rem -1rem 0;
    padding: 1rem 1rem 0.6rem;
    border-bottom: 1px solid var(--hairline);
  }

  dialog:has(.task-dialog) > .dialog-close-button {
    display: none;
  }

  .task-dialog-close {
    display: inline-flex;
    border: none;
    background: none;
    padding: 0.3rem;
    border-radius: var(--radius-control);
    cursor: pointer;
    color: var(--muted-2);
  }

  .task-dialog-close:hover {
    color: var(--wine);
    background: var(--rose-tint);
  }

  .task-title-form {
    flex: 1;
    min-width: 0;
  }

  .task-title-form [data-editable-target="editable"] {
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--ink);
  }

  .task-dialog-fields {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.6rem;
  }

  .task-field-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    border: 1px solid var(--surface-border);
    background: var(--surface);
    border-radius: var(--radius-control);
    padding: 0.3rem 0.7rem;
    cursor: pointer;
    color: var(--ink);
  }

  .task-field-toggle:hover {
    background: var(--rose-tint);
  }

  /* A link since the dialog row travels in broadcasts (forms would
     carry a dead CSRF token there). */
  .task-important-toggle {
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    border: 1px solid var(--surface-border);
    background: var(--surface);
    border-radius: var(--radius-control);
    padding: 0.3rem 0.7rem;
    cursor: pointer;
    color: var(--muted);
  }

  .task-important-toggle.is-important {
    color: var(--gold-ink);
    background: var(--gold);
    border-color: var(--gold);
  }

  .task-dialog-plainfields {
    display: flex;
    align-items: end;
    flex-wrap: wrap;
    gap: 0.75rem;
  }

  .task-plainfield {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    flex: 1;
    min-width: 12rem;
  }

  .task-plainfield span {
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted-2);
  }

  .task-test-url-link {
    padding-bottom: 0.4rem;
  }

  /* Dispatcher-managed fields: no input, just the value — the label span
     above stays in its uppercase style, the value must not inherit it. */
  .task-custom-field--readonly .task-custom-readonly-value {
    font-size: 0.9rem;
    font-weight: 400;
    text-transform: none;
    letter-spacing: normal;
    color: var(--ink);
    padding: 0.35rem 0;
    overflow-wrap: anywhere;
  }

  /* "Agents only" checkbox in the fields dialog. */
  .task-custom-readonly-toggle {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.8rem;
    color: var(--muted);
    white-space: nowrap;
    cursor: pointer;
  }

  .task-dialog-description h4,
  .task-dialog-stream h4 {
    margin: 0 0 0.4rem;
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted);
  }

  .task-description-toolbar {
    margin-bottom: 0.4rem;
    min-width: 0; /* let the responsive toolbar measure the dialog width */
  }

  /* Read-only view — same frame as the editor box, click to edit. */
  .task-description-view {
    min-height: 3.5rem;
    border: 1px solid var(--surface-border);
    border-radius: var(--radius-control);
    padding: 0.6rem 0.75rem;
    background: var(--surface);
    cursor: pointer;
  }

  .task-description-view:hover {
    border-color: var(--muted-3);
  }

  /* While this viewer edits, the read-only view steps aside. The state sits
     on the dialog, not on the view itself: the viewer's own autosave
     broadcast replaces that element, and a `hidden` set on it would be gone
     with the replacement — the text would stand twice, above the editor. */
  .task-dialog--editing-description .task-description-view {
    display: none;
  }

  .task-description-placeholder {
    color: var(--muted-2);
  }

  /* Notes pattern: the container is a flex column and the ProseMirror child
     fills it — a click anywhere inside the box focuses the editor. */
  .task-description-content {
    min-height: 7rem;
    border: 1px solid var(--surface-border);
    border-radius: var(--radius-control);
    padding: 0.6rem 0.75rem;
    background: var(--surface);
    display: flex;
    flex-direction: column;
    cursor: text;
  }

  .task-description-content > .tiptap,
  .task-description-content > .ProseMirror {
    flex: 1;
  }

  .task-description-content:focus,
  .task-description-content .ProseMirror:focus {
    outline: none;
  }

  .task-dialog-stream {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    border-top: 1px solid var(--hairline);
    padding-top: 0.75rem;
  }

  .task-stream {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    max-height: 40vh;
    overflow-y: auto;
  }

  .task-event {
    font-size: 0.82rem;
    color: var(--muted-2);
    padding-left: 0.5rem;
  }

  .task-stream-time {
    color: var(--muted-3);
    font-size: 0.78rem;
  }

  .task-comment {
    border: 1px solid var(--surface-border);
    border-radius: var(--radius-control);
    background: var(--cream-soft);
    padding: 0.5rem 0.75rem;
  }

  .task-comment-head {
    display: flex;
    justify-content: space-between;
    gap: 0.6rem;
    margin-bottom: 0.25rem;
  }

  .task-comment-content :first-child { margin-top: 0; }
  .task-comment-content :last-child { margin-bottom: 0; }

  .task-comment-composer {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
  }

  .task-add-comment {
    align-self: flex-start;
  }

  .task-composer-toolbar {
    min-width: 0;
  }

  .task-composer-row {
    display: flex;
    align-items: flex-end;
    gap: 0.5rem;
  }

  .task-composer-row .task-composer-form {
    flex: 1;
    min-width: 0;
  }

  .task-composer-form .tiptap-content {
    min-height: 4rem;
    border: 1px solid var(--surface-border);
    border-radius: var(--radius-control);
    padding: 0.5rem 0.75rem;
    background: var(--surface);
    display: flex;
    flex-direction: column;
    cursor: text;
  }

  .task-composer-form .tiptap-content > .tiptap,
  .task-composer-form .tiptap-content > .ProseMirror {
    flex: 1;
  }

  .task-composer-form .tiptap-content .ProseMirror:focus {
    outline: none;
  }

  /* Icon-only send button beside the comment box — neutral, like the
     field toggles above. */
  .task-composer-submit {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    border: 1px solid var(--surface-border);
    background: var(--surface);
    border-radius: var(--radius-control);
    cursor: pointer;
    color: var(--muted);
  }

  .task-composer-submit:hover {
    background: var(--rose-tint);
    color: var(--wine);
  }

  /* Layout and look come from the house .dialog-buttons; sticky keeps the
     bar visible while a long description scrolls. The -1rem offset matches
     .dialog-buttons' bleed into the dialog's 1rem padding. */
  .task-dialog-footer {
    position: sticky;
    bottom: -1rem;
    z-index: 5;
    border-top: 1px solid var(--hairline);
  }

  /* Mobile: the ✕ in the sticky header covers cancel; the remaining
     three buttons shrink to fit — and may wrap instead of clipping. */
  @media (max-width: 640px) {
    .task-cancel-button {
      display: none;
    }

    .task-dialog-footer {
      flex-wrap: wrap;
      gap: 0.4rem;
    }

    .task-dialog-footer .btn {
      padding: 0.45rem 0.7rem;
      font-size: 0.85rem;
    }
  }

  .task-stream-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
  }

  .task-events-toggle {
    border: none;
    background: none;
    cursor: pointer;
    font-size: 0.78rem;
    color: var(--muted-2);
    padding: 0.15rem 0.4rem;
    border-radius: var(--radius-control);
  }

  .task-events-toggle:hover {
    color: var(--wine);
    background: var(--rose-tint);
  }

  .task-dialog-stream:not(.task-stream--events-hidden) .when-events-hidden {
    display: none;
  }

  .task-dialog-stream.task-stream--events-hidden .when-events-shown {
    display: none;
  }

  .task-dialog-stream.task-stream--events-hidden .task-event {
    display: none;
  }

  /* ---- Members & agents dialog ---- */

  .members-dialog {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    min-width: min(32rem, 86vw);
  }

  .members-section h4 {
    margin: 0 0 0.5rem;
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted);
  }

  .members-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
  }

  .members-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
  }

  .members-row-actions {
    margin-left: auto;
    display: flex;
    gap: 0.4rem;
  }

  .members-row--available {
    color: var(--muted);
    margin-top: 0.4rem;
  }

  .members-row--available form {
    margin-left: auto;
  }

  .members-owner-badge {
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--gold-ink);
    background: var(--gold);
    border-radius: 5px;
    padding: 0.05rem 0.45rem;
  }

  .agent-new-form {
    display: flex;
    gap: 0.5rem;
    margin-top: 0.6rem;
  }

  .agent-new-form input[type="text"] {
    flex: 1;
    min-width: 10rem;
  }

  .members-invited-badge {
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--muted);
    background: var(--cream-soft);
    border: 1px solid var(--surface-border);
    border-radius: 5px;
    padding: 0.05rem 0.45rem;
  }

  .members-error {
    margin: 0 0 0.5rem;
    font-size: 0.85rem;
    color: #991b1b;
  }

  .agent-token-banner {
    background: #faf0d4;
    border: 1px solid var(--gold);
    border-radius: var(--radius-control);
    padding: 0.6rem 0.75rem;
  }

  .agent-token-hint {
    margin: 0 0 0.4rem;
    font-size: 0.85rem;
    color: var(--gold-ink);
  }

  .agent-token-row {
    display: flex;
    gap: 0.5rem;
  }

  .agent-token-row input {
    flex: 1;
    min-width: 0;
    font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
    font-size: 0.82rem;
  }
}
