@layer components {
  /* No dividing lines. A list of short sentences separated by rules reads as a
     table of data; separated by space it reads as work. The rail of task
     references does the alignment a rule would otherwise be doing. */
  .task-list { list-style: none; margin: 0; padding: 0; }

  .task {
    position: relative;
    display: grid;
    /* The title takes the slack, and everything after it is a rail sized to
       what it holds — so the dates and the faces still line up down the page,
       and the space between a short title and its date is the only thing that
       varies. The trailing column used to be 1fr, which meant the row's spare
       width pooled to the right of everything and the title was capped at
       27rem while 120px sat empty past the faces. */
    grid-template-columns:
      var(--rail) var(--rail-status) minmax(0, 1fr)
      var(--rail-when) var(--rail-who);
    align-items: center;
    column-gap: var(--space-2);
    row-gap: var(--space);
    min-height: var(--row-h);
    padding: var(--space) var(--space-2);
    margin-inline: calc(var(--space-2) * -1);
    border-radius: var(--radius);
    transition: background var(--transition);
  }

  /* Inbox and Anytime show no date on any row, so they do not keep a
     rail for one — the title takes it. See TasksHelper#task_list_class.

     Bounded to the wide layout on purpose. A media query adds no specificity,
     so an unbounded `.task-list--dateless .task` outranks the phone rule below
     it and puts a five-rail desktop grid on a 390px screen — which is exactly
     what it did, with the pull landing on top of the reference. */
  @media (width > 48rem) {
    .task-list--dateless .task {
      grid-template-columns:
        var(--rail) var(--rail-status) minmax(0, 1fr) var(--rail-who);
    }
  }

  .task-list--dateless .task__when { display: none; }

  .task:hover { background: var(--surface-hover); }
  .task:has(.task__link:focus-visible) { background: var(--surface-hover); }

  /* One real link, stretched over the whole row, so the click target is the row
     and the tab stop is still a single sensible thing. */
  .task__link {
    display: block;
    min-width: 0;
    color: var(--ink);
    text-decoration: none;
    font-size: var(--text-md);
    letter-spacing: var(--tracking-tight);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .task__link::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }

  /* No underline on hover. The row already tints under the pointer, and a
     stretched link that underlines as well is the row answering twice — once
     as a row, once as a piece of text inside itself. */

  .task__ref {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    font-variant-numeric: tabular-nums;
    letter-spacing: var(--tracking-label);
    color: var(--ink-faint);
    white-space: nowrap;
  }

  /* The number on a row is also the way into the four things done to a task
     from a list, so there it is a button. Everywhere else in the product —
     search results, a notification, the settings page explaining what a prefix
     is — it is the same piece of type and nothing to press, which is why this
     is `button.task__ref` and not a second class nobody would remember to add.

     Above the row's stretched link, like everything else pressable here, and
     tall enough to be pressed: --control-h-sm is the 44px floor, and a strip of
     12px type is not a target. It fills the rail rather than the text, so the
     hit area is the column the eye is already reading down. */
  button.task__ref {
    display: flex;
    align-items: center;
    width: 100%;
    min-height: var(--control-h-sm);
    padding: 0;
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    text-align: left;
    cursor: pointer;
    position: relative;
    z-index: 1;
    transition: color var(--transition), var(--responds);
  }

  button.task__ref:hover { color: var(--ink); }

  /* The row a menu is acting on.

     The menus are in the top layer and land beside the row rather than on it,
     so a panel of four buttons floating over a list of a hundred says nothing
     about which of them it belongs to. Sunk rather than tinted like :hover,
     and it has to outrank :hover as well, because by the time the menu is open
     the pointer has usually moved off the row and onto the menu. */
  .task--acting,
  .task--acting:hover { background: var(--surface-sunk); }

  /* The trash's rows carry a Put back instead of a date and a face, so it
     takes both of those rails rather than sitting in the date's and
     overflowing it. Counted from the end so it does not have to know whether
     the list is showing a date column. */
  .task__meta {
    grid-column: -3 / -1;
    display: flex;
    justify-content: flex-end;
    position: relative;
    z-index: 1;
  }

  /* Status and date share a cell and are set flush right, so the dates form a
     column whatever is or is not next to them. */
  .task__when {
    display: flex;
    align-items: baseline;
    justify-content: flex-end;
    gap: var(--space);
    min-width: 0;
  }

  /* Who has it, and who is behind them. A button, because it is also how the
     work is handed on — the row's own link covers everything underneath, so
     anything pressable on the row has to sit above it.

     No overhang on the right. It used to reach 8px past its rail for free hit
     area, which was invisible while the pull sat after it; with the pull gone
     the faces end the row, and the same 8px ate half the row's own right
     padding — they finished 8px from the edge where everything on the left
     starts 16px in. */
  .task__who {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    min-width: 0;
    padding: var(--space) 0 var(--space) var(--space);
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--ink-muted);
    cursor: pointer;
    position: relative;
    z-index: 1;
  }

  .task__who:hover .faces__empty { color: var(--ink-muted); }

  /* Who else has this on their day, in a project and nowhere else.
     Small, and set before the date in the same rail rather than in a rail of
     its own: it is a warning about a collision, not a column of data, and a
     sixth rail on every row of every list to carry it on one would be the
     wrong trade. The rail is widened for it just on a project's list. */
  .task__playing { display: flex; align-items: center; }
  .task__playing .avatar { width: 1.25rem; height: 1.25rem; font-size: 0.625rem; }
  .task__playing .avatar + .avatar { margin-inline-start: -0.375rem; }

  @media (width > 48rem) {
    .task-list--project .task {
      grid-template-columns:
        var(--rail) var(--rail-status) minmax(0, 1fr)
        calc(var(--rail-when) + 2.5rem) var(--rail-who);
    }
  }

  .task__date {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    font-variant-numeric: tabular-nums;
    color: var(--ink-muted);
    white-space: nowrap;
  }

  /* Overdue is the only thing in a list allowed to shout, and it whispers. */
  .task__date--overdue { color: var(--warn); font-weight: var(--weight-medium); }

  .task--closed .task__link { color: var(--ink-muted); text-decoration: line-through; text-decoration-color: var(--line-strong); }

  /* The progress mark, between the number and the title. A circle that fills
     up — one drawing, see TasksHelper::MARK, with the parts it shows and how
     full its ring is decided below — and a button, because the state of a task
     is the thing most often changed about it and opening a page to change it
     is three clicks for one decision.

     It sits second rather than first because the number is what the eye lands
     on and what the list aligns against; the mark reads as a property of the
     task it is named after, in front of the name.

     Above the row's stretched link, like everything else pressable here. */
  .task__status {
    display: grid;
    place-items: center;
    width: var(--rail-status);
    height: var(--control-h-sm);
    padding: 0;
    border: 0;
    background: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    position: relative;
    z-index: 1;
    color: var(--ink-faint);
    transition: color var(--transition), var(--responds);
  }

  /* Which parts of the mark this status shows, and how full its ring is.
     TasksHelper::MARK draws all of them every time; these decide.

     20.42 is the fill circle's circumference and the only raw number in this
     file. It is geometry rather than design — it comes off the r=3.25 in the
     helper and changes only if that does — so it is not a token, and it is
     written out rather than hidden behind one that would suggest it could be
     tuned. Half of it is the halfway ring In progress has always drawn. */
  .mark__fill {
    stroke-dasharray: 20.42;
    stroke-dashoffset: 20.42;
    transition: stroke-dashoffset var(--transition-slow);
  }

  .mark__spokes,
  .mark__tick,
  .mark__cross {
    opacity: 0;
    transition: opacity var(--transition-enter);
  }

  .task__status--captured  .mark__spokes { opacity: 1; }

  /* New is the spokes and nothing else. Every other status is a ring that
     fills, empties or gets crossed out — a story about progress — and New is
     the one status with no progress to report yet, so it is not drawn as a
     less-full version of the others. The dot it used to be said the same thing
     more quietly; the ring that was around that dot was drawing the start of a
     story that has not begun. */
  .task__status--captured  .mark__track { opacity: 0; }
  .task__status--done      .mark__tick  { opacity: 1; }
  .task__status--cancelled .mark__cross { opacity: 1; }

  /* Nobody has taken this on, drawn the way an empty seat is (see
     .faces__empty): dashed means not yet. */
  .task__status--waiting_on .mark__track { stroke-dasharray: 2.7 2.7; }

  .task__status--in_progress .mark__fill { stroke-dashoffset: 10.21; }
  .task__status--done        .mark__fill { stroke-dashoffset: 0; }

  /* The tick is drawn in the page's own colour on top of the disc, so it does
     not need holding back until the ring has filled: until there is something
     under it, it is canvas on canvas. The fill arriving is what reveals it. */

  /* New now carries a colour of its own — see --new in _tokens.css, and the
     note there about it being a deliberate fourth in a palette that claims
     three. The mark takes it; the word "New" beside it in a picker stays
     muted, so the colour marks the row without the label shouting along with
     it. */
  .task__status--captured    { color: var(--new); }

  /* The empty seat is the size of the face it stands in for, and in a row that
     is now the masthead's 36px rather than the 28px the rest of the product
     uses. Scoped to the list: a seat in a menu still sits beside a small
     avatar and still matches it there. */
  .task .faces__empty { width: 2.25rem; height: 2.25rem; }
  .task .faces__empty svg { width: 1.5rem; height: 1.5rem; }
  .task__status--in_progress { color: var(--accent); }
  .task__status--waiting_on  { color: var(--warn); }
  .task__status--done        { color: var(--go); }
  .task__status--cancelled   { color: var(--ink-faint); }

  .task__status:hover { color: var(--ink); }
  .task__status--in_progress:hover { color: var(--accent); }
  .task__status--waiting_on:hover  { color: var(--warn); }
  .task__status--done:hover        { color: var(--go); }

  .task--dragging { opacity: 0.4; }

  .task--dragging { opacity: 0.4; }

  /* Rows do not animate in or out.

     They used to: a MutationObserver added `.arriving` to anything that turned
     up, and a hook on turbo:before-stream-render held a removal open while the
     row's height travelled to zero. Both are gone with arrivals_controller.
     A row that arrives is simply there and a row that is removed is simply
     gone, which is what a Turbo Stream does on its own — and the two
     mechanisms that made it otherwise were the most intricate code in the
     front end, in service of an effect nobody asked for. */

  /* The row on a phone. The desktop row is five fixed rails, which
     add up to more than a phone has: the title column is the one that gives, and
     at 390px it gives entirely — the most important thing on the row disappears
     while the assignee hangs off the right-hand edge.

     So the rails become three lines, in the order they are needed: what it is
     called, then who and when, with the reference on the line above. The
     title is allowed two lines here rather than one, because a
     45-character cap on a 390px screen is not a cap, it is a truncation of
     everything. */
  @media (max-width: 48rem) {
    .task {
      grid-template-columns: var(--rail-status) minmax(0, 1fr) auto;
      column-gap: var(--space);
      row-gap: var(--space);
      padding-block: var(--space-2);
    }

    .task__ref { grid-column: 2; grid-row: 1; }

    .task__link {
      grid-column: 2 / -1;
      grid-row: 2;
      display: -webkit-box;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: 2;
      line-clamp: 2;
      white-space: normal;
    }

    .task__when {
      grid-column: 2;
      grid-row: 3;
      justify-content: flex-start;
    }

    .waiting-note { grid-column: 2 / -1; grid-row: 4; }

    /* The status mark keeps its rail on a phone. Reordering does not: a drag
       here is the HTML5 kind, which a touch screen does not fire at all, and
       there is no longer a handle to hide — the whole row was the handle and
       on touch it simply does not answer. Known gap.

       It leads each row here rather than following the number as it does on a
       desk, because on three lines it is a rail beside all of them rather than
       one thing on a line, and a mark set against two lines of text belongs at
       the left where reading starts. */
    .task__status { grid-column: 1; grid-row: 1 / 3; align-self: start; }
    .task__who { grid-column: 3; grid-row: 3; }
  }
}
