@layer components {
  /* ——— Events ———
     A line of the trail. Lived in records.css until it had three readers — the
     record sheet, the project timeline and Me's activity — and a file whose
     header says "the CRM record sheet" is the wrong index entry for a component
     the whole app draws.

     The timeline mixes events and comments, so one hairline grammar runs across
     both: the trail reads as a single column of rows rather than two
     interleaved lists. (.comment + .comment carries its own, in comments.css.) */
  .event {
    padding-block: var(--block-space-half);
  }

  /* The clock, where a day's divider stands above the line and the date would
     only repeat it. On the feeds' rail it hangs in the clock column
     (activity.css, absolute, which this float gives way to); elsewhere — a
     record's timeline — it closes the line. */
  .event__time {
    float: inline-end;
    margin-inline-start: var(--inline-space);
    font-variant-numeric: tabular-nums;
    /* ink-subtle: faint is 4.1 : 1 on paper, below what small text needs
       (the Overview's redesign, 2026-10-20). */
    color: var(--color-ink-subtle);
  }

  /* A boost rides the line's end (BC), positioned so the sentence's own inline
     flow never learns about it. It lived in comments.css, scoped to the thread,
     and the two day-grouped feeds drew an orphaned + on a line of its own
     underneath instead (user, 2026-08-13) — so it belongs to .event, in the
     file that owns .event.

     :has(), so only a line that can be applauded pays the reserved end. The
     partial draws its + whether or not anyone has nodded yet, which is exactly
     the set of lines that need the room.

     Only the bare rocket, or the form it turns into, rides the end: once a
     pill exists the strip drops under the sentence, a chat line's rule
     (messages.css) — floated, a row of pills covered the words it applauded. */
  .event:has(.boosts) {
    position: relative;
    padding-inline-end: 3rem;

    .boosts:not(:has(.boosts__list .boost)) {
      position: absolute;
      inset-inline-end: 0;
      inset-block-start: 50%;
      translate: 0 -50%;
    }

    .boosts:has(.boosts__list .boost) {
      margin-block-start: var(--block-space-half);
    }
  }

  /* An edit said in the line (api-spec §16.4): the old value struck and faint,
     the new one in full ink. */
  .event del {
    color: var(--color-ink-faint);
  }

  .event ins {
    color: var(--color-ink);
    text-decoration: none;
  }

  /* The trail's own verbs — "put it back", "show the change" — are words of
     the sentence, underlined as a link is, never a box (with .unbuttoned). */
  .event__action {
    text-decoration: underline;
  }

  /* The old words and the new as one text, under the line (the words
     themselves are text-change.css). At the line's own size, as a part of
     the trail rather than a text of its own (user, 2026-10-08). */
  .event__diff {
    background: var(--color-surface);
    border-radius: var(--border-radius);
    color: var(--color-ink);
    margin-block-start: var(--block-space-half);
    padding: var(--block-space-half) var(--inline-space-double);
  }

  /* The frame the change arrives in. A block, so the lazy load has a box to
     see once the fold opens: an empty inline frame has none, and never loads
     (the card column's settings dialog learned it). */
  .event__change {
    display: flex;
    flex-direction: column;
    gap: var(--block-space-half);
  }

  .event__change-action {
    align-self: flex-end;
  }

  /* Read through a custom property so a surface can turn it off without
     restating it: inside a trail the rail separates the rows, and the two
     together read as a table (activity.css sets --event-rule: none). */
  .event + .event,
  .event + .comment,
  .comment + .event {
    border-block-start: var(--event-rule, var(--border));
  }
}
