/*
 * chats — bundled default styles.
 *
 * Self-contained on purpose: engine views can't lean on the host's CSS
 * framework (a Tailwind host never scans gem view files, so utility classes
 * used here would simply not exist in its build). Semantic chats-* classes +
 * plain modern CSS work in ANY host with zero build integration.
 *
 * Theme with CSS variables (override in any host stylesheet):
 *
 *   :root {
 *     --chats-accent: #facc15;          /\* brand color: own bubbles, send button *\/
 *     --chats-accent-contrast: #111827; /\* text on top of the accent *\/
 *     --chats-verified: #0284c7;        /\* the "official account" badge *\/
 *   }
 *
 * Want a completely different look? `rails g chats:views` ejects the
 * templates into your app — restyle them with your own framework and skip
 * including this file (it's linked by the views via the chats_styles helper).
 */

:root {
  --chats-accent: #111827;
  --chats-accent-contrast: #ffffff;
  --chats-bg: #ffffff;
  --chats-surface: #f3f4f6;
  --chats-border: rgba(0, 0, 0, 0.08);
  --chats-text: #111827;
  --chats-text-muted: #6b7280;
  --chats-verified: #0284c7;
  --chats-bubble-bg: #f3f4f6;
  --chats-bubble-text: #111827;
  --chats-radius: 1.1rem;
  --chats-max-width: 42rem;
  /* The conversation list beside an open conversation (two panes). */
  --chats-list-width: 320px;
  /* --chats-split-height, how tall the two-pane page is, is NOT declared
     here: the rules read it with the whole screen (100dvh) as the fallback,
     so a host's own declaration wins wherever it is, even on a :root rule
     loaded before this file (the bundled views link it in the body).
     Under a host header: calc(100dvh - <the header's height>). */

  /* Safe areas, re-pointable by a host with its own source of truth. */
  --chats-safe-top: env(safe-area-inset-top, 0px);
  --chats-safe-right: env(safe-area-inset-right, 0px);
  --chats-safe-bottom: env(safe-area-inset-bottom, 0px);
  --chats-safe-left: env(safe-area-inset-left, 0px);
}

/* --- Shell ----------------------------------------------------------------- */

.chats {
  margin: 0 auto;
  width: 100%;
  max-width: var(--chats-max-width);
  color: var(--chats-text);
  background: var(--chats-bg);
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
}

/* --- Inbox ------------------------------------------------------------------ */

.chats-inbox__header { padding: 1rem 1.25rem 0.25rem; }
.chats-inbox__title { font-size: 1.6rem; font-weight: 800; margin: 0; }

.chats-search { padding: 0.5rem 1.25rem; }
.chats-search__input {
  width: 100%;
  padding: 0.6rem 0.9rem;
  border-radius: 999px;
  border: 1px solid var(--chats-border);
  background: var(--chats-surface);
  font: inherit;
  outline-offset: 2px;
}

.chats-inbox__list { list-style: none; margin: 0; padding: 0.25rem 0 2rem; }

.chats-row__link {
  display: flex;
  gap: 0.85rem;
  align-items: center;
  padding: 0.8rem 1.25rem;
  text-decoration: none;
  color: inherit;
}
/* Hover only where there is one: a touch screen "hovers" what it tapped and
   keeps the tint after the finger is gone (the open row stayed grey). */
@media (hover: hover) {
  .chats-row__link:hover { background: var(--chats-surface); }
}
.chats-row__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.1rem; }
.chats-row__top, .chats-row__bottom {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
}
.chats-row__name {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  min-width: 0;
}
.chats-row__title {
  font-weight: 650;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chats-row__time { font-size: 0.75rem; color: var(--chats-text-muted); flex-shrink: 0; }
.chats-row__subject {
  font-size: 0.75rem;
  color: var(--chats-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chats-row__preview {
  font-size: 0.875rem;
  color: var(--chats-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chats-row--unread .chats-row__title,
.chats-row--unread .chats-row__preview { font-weight: 750; color: var(--chats-text); }

.chats-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.35rem;
  height: 1.35rem;
  padding: 0 0.35rem;
  border-radius: 999px;
  background: var(--chats-accent);
  color: var(--chats-accent-contrast);
  font-size: 0.72rem;
  font-weight: 800;
  line-height: 1;
  flex-shrink: 0;
}
.chats-badge--floating[hidden] { display: none; }
/* chats_unread_badge's wrapper (its catch-up controller): no box of its own,
   so the badge sits in the host's layout exactly as if it weren't there. */
.chats-unread-badge-live { display: contents; }

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

.chats-empty { text-align: center; padding: 4rem 1.5rem; }
.chats-empty__icon { font-size: 2.5rem; display: block; margin-bottom: 0.75rem; }
.chats-empty__title { font-weight: 750; margin: 0 0 0.35rem; }
.chats-empty__hint { color: var(--chats-text-muted); font-size: 0.9rem; margin: 0; }

/* --- Avatars ------------------------------------------------------------------ */

.chats-avatar {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 999px;
  object-fit: cover;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.chats-avatar--initials {
  background: var(--chats-surface);
  color: var(--chats-text-muted);
  font-weight: 750;
  font-size: 0.95rem;
  user-select: none;
}

/* --- Verified messagers ------------------------------------------------------
 *
 * The "official account" mark (`acts_as_messager verified: true`). It sizes
 * itself from the text it sits next to, so it reads right in an inbox row and
 * in the thread header without either one hard-coding a pixel value. Recolour
 * it with --chats-verified; replace the glyph with config.verified_badge.
 *
 * On #0284c7 rather than the more familiar #1d9bf0: the badge is a meaningful
 * graphic, so WCAG 1.4.11 asks for 3:1 against what it sits on, and #1d9bf0
 * is 3.00:1 on --chats-bg but only 2.73:1 on --chats-surface — which is the
 * row hover background, so the mark failed exactly while someone was pointing
 * at it. #0284c7 clears 3:1 on both (4.10 and 3.72) and on a dark ground too
 * (4.33 on #111827), so a host that inverts the palette inherits a badge that
 * still passes instead of one it has to remember to fix. Pinned by
 * test/verified_badge_contrast_test.rb.
 */
.chats-verified {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  color: var(--chats-verified);
  line-height: 0;
}
.chats-verified__glyph {
  width: 1em;
  height: 1em;
}

/* --- Thread -------------------------------------------------------------------- */

.chats-thread {
  height: 100dvh;
  overflow: hidden;
}

.chats-thread__header {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.65rem 1rem;
  border-bottom: 1px solid var(--chats-border);
  background: var(--chats-bg);
  position: sticky;
  top: 0;
  z-index: 10;
  flex: 0 0 auto;
}
.chats-thread__header .chats-avatar { width: 2.4rem; height: 2.4rem; }
.chats-thread__back {
  font-size: 1.8rem;
  line-height: 1;
  text-decoration: none;
  color: inherit;
  padding: 0 0.35rem;
}
.chats-thread__identity { flex: 1; min-width: 0; }
.chats-thread__title {
  font-size: 1rem;
  font-weight: 750;
  margin: 0;
  display: flex;
  align-items: center;
  gap: 0.25rem;
  min-width: 0;
}
.chats-thread__name {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chats-thread__subject {
  font-size: 0.75rem;
  color: var(--chats-text-muted);
  margin: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.chats-thread__scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  /* Keep the composer dock out of the scroll chain: the message pane is the
     only scroll container, and boundary drags should not bubble into browser /
     WebView pull-to-refresh. Sources:
     https://developer.mozilla.org/docs/Web/CSS/min-height#values
     https://developer.mozilla.org/docs/Web/CSS/overscroll-behavior */
  overscroll-behavior-y: contain;
  padding: 1rem 1rem 0.5rem;
  display: flex;
  flex-direction: column;
}
.chats-thread__messages { display: flex; flex-direction: column; gap: 0; }
.chats-day-separator {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  margin: 0.8rem 0;
  color: var(--chats-text-muted);
  font-size: 0.7rem;
  font-weight: 700;
  text-align: center;
}
.chats-day-separator::before,
.chats-day-separator::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--chats-border);
}

/* The «new messages» divider: same shape as the day separator but in the
   accent color — it marks where the unread catch-up starts on thread open
   (rendered server-side before the first unread bubble; see show.html.erb). */
.chats-thread__unread-line {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  margin: 0.8rem 0;
  color: var(--chats-text-muted);
  font-size: 0.7rem;
  font-weight: 700;
  text-align: center;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.chats-thread__unread-line::before,
.chats-thread__unread-line::after {
  content: "";
  flex: 1;
  height: 1px;
  background: currentColor;
  opacity: 0.4;
}
.chats-loader {
  text-align: center;
  color: var(--chats-text-muted);
  font-size: 0.8rem;
  padding: 0.5rem;
}

/* Paginated history loads inside (nested) turbo-frames, and a custom
   element's default display:block strands those bubbles in a block context —
   .chats-message--own aligns with align-self, which only works when the
   bubble is a direct flex item of a column. Make each page frame its own
   flex column mirroring the scroller. NOT display:contents: that removes
   the frame's box, and Turbo's lazy loading watches the frame with an
   IntersectionObserver — no box, no intersection, no fetch ("loading…"
   forever). https://developer.mozilla.org/en-US/docs/Web/CSS/display#contents */
turbo-frame[id^="chats_page_"] {
  display: flex;
  flex-direction: column;
  width: 100%;
}

/* --- Bubbles --------------------------------------------------------------------- */

.chats-message {
  display: flex;
  gap: 0.5rem;
  align-items: flex-end;
  max-width: 85%;
  margin: 0.2rem 0 0;
}
.chats-message--continuation { margin-top: 0.15rem; }
.chats-message__avatar { width: 1.9rem; height: 1.9rem; font-size: 0.7rem; }
.chats-message__content { display: flex; flex-direction: column; min-width: 0; }
/* relative: the anchor of the menu's "⋯" (see the popup section). */
.chats-message__bubble-row { position: relative; display: flex; align-items: flex-end; gap: 0.5rem; }
.chats-message__sender {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--chats-text-muted);
  margin: 0 0 0.1rem 2.4rem;
}
.chats-message--continuation .chats-message__sender { display: none; }
.chats-message--followed .chats-message__avatar { visibility: hidden; }

.chats-message__bubble {
  background: var(--chats-bubble-bg);
  color: var(--chats-bubble-text);
  border-radius: var(--chats-radius);
  border-bottom-left-radius: 0.3rem;
  padding: 0.5rem 0.8rem;
  overflow-wrap: anywhere;
}
/* pre-wrap: the body renders as plain escaped text, so every newline the
   sender typed shows literally (chat semantics — simple_format used to
   collapse consecutive blank lines into one paragraph break). */
.chats-message__text {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.chats-message__meta {
  display: flex;
  justify-content: flex-end;
  align-items: baseline;
  gap: 0.3rem;
  margin-top: 0.2rem;
  font-size: 0.62rem;
  color: var(--chats-text-muted);
  user-select: none;
}
.chats-message__receipt {
  display: inline-flex;
  align-items: baseline;
  white-space: nowrap;
}
.chats-message__receipt[data-state="seen"] {
  letter-spacing: -0.3em;
  padding-right: 0.3em;
}
.chats-message__receipt[hidden] { display: none; }
.chats-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Own messages: flipped to the right, accent-colored. The chats--thread
   controller adds .chats-message--own client-side (bubbles broadcast
   viewer-agnostic — see _message.html.erb). */
.chats-message--own { align-self: flex-end; flex-direction: row-reverse; }
.chats-message--own .chats-message__bubble-row { flex-direction: row-reverse; }
.chats-message--own .chats-message__avatar { display: none; }
.chats-message--own .chats-message__sender { display: none; }
.chats-message--own .chats-message__bubble {
  background: var(--chats-accent);
  color: var(--chats-accent-contrast);
  border-bottom-left-radius: var(--chats-radius);
  border-bottom-right-radius: 0.3rem;
}
.chats-message--own .chats-message__meta { color: var(--chats-accent-contrast); opacity: 0.75; }
.chats-message--continuation .chats-message__bubble { border-top-left-radius: 0.35rem; }
.chats-message--followed .chats-message__bubble { border-bottom-left-radius: var(--chats-radius); }
.chats-message--own.chats-message--continuation .chats-message__bubble { border-top-right-radius: 0.35rem; }
.chats-message--own.chats-message--followed .chats-message__bubble { border-bottom-right-radius: var(--chats-radius); }

/* System + deleted */
.chats-message--system { align-self: center; max-width: 100%; }
.chats-message__system {
  font-size: 0.78rem;
  color: var(--chats-text-muted);
  background: var(--chats-surface);
  border-radius: 999px;
  padding: 0.3rem 0.9rem;
  text-align: center;
  margin: 0.35rem auto;
}
.chats-message--deleted .chats-message__bubble { background: transparent; border: 1px dashed var(--chats-border); }
.chats-message__tombstone { color: var(--chats-text-muted); font-size: 0.85rem; }

/* --- Actions (Chats::Action) ------------------------------------------------------- */
/*
   A message's one featured button, under its words: full width, so it reads
   as something to tap rather than a link in the text. It is a sibling of
   .chats-message__text, never inside it, so the text's pre-wrap stays off it.

   Contrast comes from the pair a host already tunes together: the accent and
   the text that sits on it. A foreign bubble (and a system note) gets an
   accent button; an own bubble IS the accent, so its button swaps the pair.
   Same two colours either way, so whatever passes for the send button and
   own bubbles passes here — default theme or host theme alike.

   Scoped to the bubble with `>`: 0.1.0's inline actions row used this class
   name, and views ejected back then may still carry it on other elements.
   The long-press popup clones .chats-message__bubble, so this applies there
   too.
*/
.chats-message__bubble > .chats-message__action,
.chats-message__system > .chats-message__action {
  display: block;
  margin-top: 0.5rem;
  padding: 0.55rem 0.9rem;
  border-radius: calc(var(--chats-radius) * 0.7);
  background: var(--chats-accent);
  color: var(--chats-accent-contrast);
  font-size: 0.88rem;
  font-weight: 700;
  line-height: 1.25;
  text-align: center;
  text-decoration: none;
  overflow-wrap: anywhere;
  cursor: pointer;
}
.chats-message--own .chats-message__bubble > .chats-message__action {
  background: var(--chats-accent-contrast);
  color: var(--chats-accent);
}
.chats-message__bubble > .chats-message__action:hover,
.chats-message__system > .chats-message__action:hover { filter: brightness(0.92); }
/* The focus ring sits on the BUBBLE, just outside the button, so it takes
   the colour of the bubble's own text — which is what contrasts there. */
.chats-message__bubble > .chats-message__action:focus-visible,
.chats-message__system > .chats-message__action:focus-visible {
  outline: 2px solid var(--chats-bubble-text);
  outline-offset: 2px;
}
.chats-message--own .chats-message__bubble > .chats-message__action:focus-visible {
  outline-color: var(--chats-accent-contrast);
}
.chats-message__system > .chats-message__action:focus-visible { outline-color: var(--chats-text); }
/* A system note is a pill; one holding a button becomes a card, or the
   button's corners would poke through the pill's rounded ends. */
.chats-message__system--with-action {
  border-radius: var(--chats-radius);
  padding: 0.6rem 0.9rem;
}
.chats-message__system > .chats-message__action { margin-top: 0.45rem; }

/* --- Attachments -------------------------------------------------------------------- */

.chats-message__attachments {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-top: 0.35rem;
}
.chats-message__image {
  max-width: 14rem;
  max-height: 14rem;
  border-radius: 0.75rem;
  display: block;
}
.chats-message__image-link { cursor: zoom-in; }
.chats-message__file { font-size: 0.85rem; }

.chats-attachment-preview {
  position: fixed;
  inset: 0;
  z-index: 1000;
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: rgba(0, 0, 0, 0.82);
}
.chats-attachment-preview[hidden] { display: none; }
.chats-attachment-preview-open { overflow: hidden; }
.chats-attachment-preview__surface {
  position: relative;
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  padding: max(1rem, env(safe-area-inset-top)) 1rem max(1rem, env(safe-area-inset-bottom));
  background: rgba(0, 0, 0, 0.82);
}
.chats-attachment-preview__figure { margin: 0; max-width: 100%; max-height: 100%; }
.chats-attachment-preview__image {
  display: block;
  max-width: 100%;
  max-height: calc(100dvh - 5rem);
  object-fit: contain;
  border-radius: 0.5rem;
}
.chats-attachment-preview__close {
  position: absolute;
  top: max(0.75rem, env(safe-area-inset-top));
  right: 0.75rem;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.16);
  color: #ffffff;
  font: inherit;
  font-size: 1.75rem;
  line-height: 1;
  cursor: pointer;
}

/* --- Reactions ----------------------------------------------------------------------- */

.chats-message__reactions { display: flex; gap: 0.25rem; margin: 0.2rem 0 0 0.5rem; flex-wrap: wrap; }
.chats-message--own .chats-message__reactions { justify-content: flex-end; margin-right: 0.5rem; }
.chats-reaction {
  border: 1px solid var(--chats-border);
  background: var(--chats-bg);
  border-radius: 999px;
  font-size: 0.8rem;
  padding: 0.1rem 0.5rem;
  cursor: pointer;
  display: inline-flex;
  gap: 0.25rem;
  align-items: center;
}
.chats-reaction__count { font-size: 0.7rem; font-weight: 700; color: var(--chats-text-muted); }

/* --- Long-press popup (Telegram-style) ------------------------------------------------ */
/*
   Long-press / right-click a bubble: its clone morphs to the center over a
   blurred glass backdrop, the reactions pill above, the contextual menu
   below. Markup lives in conversations/show (the slots) + each bubble's
   <template data-chats-message-menu> (the content); the chats--thread
   controller does the FLIP transform dance.
*/

.chats-message--lifted { visibility: hidden; }

/* Long-press must read as a GESTURE on touch: without this, iOS Safari
   starts the selection loupe / callout mid-press and the popup never
   opens cleanly. Desktop keeps normal selection; on coarse pointers the
   popup menu’  Copy replaces it. (The native shells already suppress
   callouts globally; this covers the mobile WEB thread.) */
@media (pointer: coarse) {
  .chats-message {
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
  }
}

.chats-popup {
  position: fixed;
  inset: 0;
  z-index: 60;
}
.chats-popup[hidden] { display: none; }
.chats-popup__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(15, 23, 42, 0.3);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  opacity: 0;
  transition: opacity 180ms ease;
  will-change: opacity;
}
.chats-popup--open .chats-popup__backdrop { opacity: 1; }

.chats-popup__stack {
  /* Anchored to the pressed bubble's SIDE by the controller (inline
     left/right) — never dragged to the horizontal center. Vertically
     centered; the FLIP morph on the clone covers the y-travel. */
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.6rem;
  max-height: 92dvh;
}
.chats-popup__stack--own { align-items: flex-end; }
.chats-popup__slot--bubble { max-width: 100%; }
.chats-popup__bubble {
  /* Long messages scroll inside the lifted clone instead of pushing the
     pill/menu off-screen (Telegram does the same). */
  max-height: 45dvh;
  overflow-y: auto;
}
.chats-popup__bubble {
  /* No background/radius/overflow of its own: the clone IS the bubble, at
     its original width — wrapping it in a styled box reads as a different
     element. drop-shadow (not box-shadow) hugs the bubble's real shape. */
  transition: transform 260ms cubic-bezier(0.32, 0.72, 0.18, 1);
  will-change: transform;
  filter: drop-shadow(0 18px 40px rgba(0, 0, 0, 0.28));
}

.chats-popup__slot--reactions,
.chats-popup__slot--menu {
  opacity: 0;
  transform: scale(0.92);
  transition: opacity 180ms ease 60ms, transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1) 60ms;
}
.chats-popup--open .chats-popup__slot--reactions,
.chats-popup--open .chats-popup__slot--menu { opacity: 1; transform: scale(1); }

.chats-popup__reactions {
  display: flex;
  gap: 0.15rem;
  background: var(--chats-bg);
  border-radius: 999px;
  padding: 0.3rem 0.45rem;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.18);
}
.chats-popup__reactions form { display: inline; }
.chats-popup__reaction,
.chats-reaction {
  /* Emoji-FIRST family: iOS WKWebView per-glyph fallback through webfont
     stacks can dead-end at LastResort (the boxed "?") for VS16/ZWJ emoji;
     leading with the emoji fonts sidesteps it. Digits (reaction counts)
     fall through to sans-serif — Apple Color Emoji maps none. */
  font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Segoe UI Symbol", sans-serif;
}
.chats-popup__reaction {
  font-size: 1.35rem;
  line-height: 1;
  background: none;
  border: none;
  padding: 0.25rem;
  border-radius: 999px;
  cursor: pointer;
  transition: transform 120ms ease;
}
.chats-popup__reaction:hover { transform: scale(1.25); }

.chats-popup__menu {
  display: flex;
  flex-direction: column;
  min-width: 13rem;
  background: var(--chats-bg);
  border-radius: 1rem;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.18);
  overflow: hidden;
  padding: 0.25rem;
}
.chats-popup__menu form { display: contents; }
.chats-popup__item {
  display: block;
  width: 100%;
  text-align: left;
  font: inherit;
  font-size: 0.9rem;
  color: var(--chats-text);
  background: none;
  border: none;
  border-radius: 0.6rem;
  padding: 0.55rem 0.8rem;
  cursor: pointer;
  text-decoration: none;
}
.chats-popup__item:hover { background: var(--chats-surface); }
.chats-popup__item--danger { color: #dc2626; }

/* The menu's desktop door: "⋯" on the bubble's inner edge, shown while
   the pointer is on the message or the button has keyboard focus. A fine,
   hovering pointer only: a touch screen long-presses. Absolutely placed, so
   no bubble moves; the transparent border is hit area touching the bubble,
   so the pointer never crosses a gap that would un-hover it. */
.chats-message__menu-trigger { display: none; }
@media (hover: hover) and (pointer: fine) {
  .chats-message__menu-trigger {
    position: absolute;
    top: 50%;
    left: 100%;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.1rem;
    height: 2.1rem;
    padding: 0;
    border: 0.25rem solid transparent;
    border-radius: 999px;
    background: none;
    background-clip: padding-box;
    color: var(--chats-text-muted);
    font: inherit;
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
    opacity: 0;
    transition: opacity 120ms ease;
  }
  .chats-message--own .chats-message__menu-trigger { left: auto; right: 100%; }
  .chats-message:hover .chats-message__menu-trigger,
  .chats-message__menu-trigger:focus-visible { opacity: 1; }
  .chats-message__menu-trigger:hover { background-color: var(--chats-surface); color: var(--chats-text); }
  .chats-message__menu-trigger:focus-visible { outline: 2px solid var(--chats-accent); outline-offset: -0.25rem; }
}

/* --- Seen + typing --------------------------------------------------------------------- */

.chats-seen {
  align-self: flex-end;
  font-size: 0.68rem;
  color: var(--chats-text-muted);
  margin: 0.1rem 0.5rem 0.2rem;
}
.chats-typing {
  font-size: 0.8rem;
  color: var(--chats-text-muted);
  font-style: italic;
  padding: 0.4rem 0.5rem;
}

/* --- Composer ----------------------------------------------------------------------------- */

/* The dock (since 0.4.0 a div holding the host's composer_actions and the
   form; a composer ejected before that is the form itself, and this box
   styles it just the same). */
.chats-composer {
  flex: 0 0 auto;
  background: var(--chats-bg);
  border-top: 1px solid var(--chats-border);
  padding: 0.6rem 0.9rem calc(0.6rem + env(safe-area-inset-bottom, 0px));
}
.chats-composer__form { margin: 0; }

/* The composer_actions slot: a row of the host's own buttons above the
   input. `display: contents` lets button_to's wrapping <form> step aside so
   its button sits in the row like a link does. Every default here is
   wrapped in :where() — zero specificity — so ANY class the host puts on
   its buttons or forms wins, and an unstyled button still looks like it
   belongs. */
.chats-composer__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0 0 0.55rem;
}
:where(.chats-composer__actions) :where(form) { display: contents; }
:where(.chats-composer__actions) :where(a, button) {
  padding: 0.35rem 0.8rem;
  border: 1px solid var(--chats-border);
  border-radius: 999px;
  background: var(--chats-surface);
  color: var(--chats-text);
  font: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}
:where(.chats-composer__actions) :where(a, button):hover { filter: brightness(0.96); }
.chats-composer__bar { display: flex; align-items: flex-end; gap: 0.55rem; }
.chats-composer__input {
  flex: 1;
  resize: none;
  border: 1px solid var(--chats-border);
  background: var(--chats-surface);
  border-radius: 1.25rem;
  padding: 0.55rem 0.9rem;
  font: inherit;
  line-height: 1.35;
  max-height: 10rem;
  outline: none;
}
.chats-composer__input:focus { outline: none; }
.chats-composer__send {
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 999px;
  border: none;
  background: var(--chats-accent);
  color: var(--chats-accent-contrast);
  font-size: 1.05rem;
  cursor: pointer;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.chats-composer__send:hover { filter: brightness(0.92); }
.chats-composer__attach {
  cursor: pointer;
  font-size: 1.2rem;
  padding-bottom: 0.45rem;
  position: relative;
}
.chats-composer__file-count {
  position: absolute;
  top: -0.3rem;
  right: -0.5rem;
  background: var(--chats-accent);
  color: var(--chats-accent-contrast);
  border-radius: 999px;
  font-size: 0.62rem;
  font-weight: 800;
  min-width: 1rem;
  height: 1rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.chats-composer__error {
  color: #dc2626;
  font-size: 0.8rem;
  margin: 0 0 0.4rem;
}

/* Selected-attachment previews (rendered by the composer controller). */
.chats-composer__previews {
  display: flex;
  gap: 0.5rem;
  overflow-x: auto;
  padding: 0.25rem 0.25rem 0.6rem;
}

/* The edit cue (Telegram's flow): a quote-style bar over the input — left
   accent border, bold label, the original message trimmed to one line. */
.chats-composer__edit {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0 0.25rem 0.45rem;
  padding: 0.3rem 0.6rem;
  border-left: 3px solid var(--chats-accent);
  background: var(--chats-surface);
  border-radius: 0.45rem;
}
.chats-composer__edit-quote { min-width: 0; flex: 1; display: flex; flex-direction: column; }
.chats-composer__edit-label { font-size: 0.72rem; font-weight: 700; color: var(--chats-accent); }
.chats-composer__edit-preview {
  font-size: 0.8rem;
  color: var(--chats-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chats-composer__edit-cancel {
  border: none;
  background: none;
  font-size: 1.1rem;
  line-height: 1;
  color: var(--chats-text-muted);
  cursor: pointer;
  padding: 0.2rem 0.4rem;
}

/* `hidden` loses to any author `display`, so every element here that sets
   one and starts hidden says so explicitly — the edit cue and the file
   count used to show in hosts with no CSS reset of their own. */
.chats-composer__previews[hidden],
.chats-composer__edit[hidden],
.chats-composer__file-count[hidden] { display: none; }
.chats-composer__preview {
  position: relative;
  flex-shrink: 0;
}
.chats-composer__preview-image {
  height: 4.5rem;
  width: 4.5rem;
  border-radius: 0.75rem;
  object-fit: cover;
  border: 1px solid var(--chats-border);
}
.chats-composer__preview-name {
  display: inline-flex;
  align-items: center;
  height: 4.5rem;
  max-width: 10rem;
  padding: 0 0.75rem;
  border-radius: 0.75rem;
  border: 1px solid var(--chats-border);
  background: var(--chats-surface);
  font-size: 0.75rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chats-composer__preview-remove {
  position: absolute;
  top: -0.4rem;
  right: -0.4rem;
  width: 1.3rem;
  height: 1.3rem;
  border-radius: 999px;
  border: none;
  background: var(--chats-text);
  color: var(--chats-bg);
  font-size: 0.7rem;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
}

/* --- Menus (⋯ and emoji picker) ----------------------------------------------------------------- */

.chats-menu { position: relative; }
.chats-menu__trigger {
  cursor: pointer;
  list-style: none;
  padding: 0.2rem 0.5rem;
  border-radius: 0.5rem;
}
.chats-menu__trigger::-webkit-details-marker,
.chats-menu__trigger:hover { background: var(--chats-surface); }
.chats-menu__panel {
  position: absolute;
  right: 0;
  top: 100%;
  background: var(--chats-bg);
  border: 1px solid var(--chats-border);
  border-radius: 0.75rem;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  padding: 0.3rem;
  z-index: 20;
  min-width: 9rem;
}
.chats-menu__panel--emoji {
  display: flex;
  gap: 0.15rem;
  min-width: 0;
  left: 0;
  right: auto;
  bottom: 100%;
  top: auto;
}
.chats-message--own .chats-menu__panel--emoji { left: auto; right: 0; }
.chats-menu__item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  font: inherit;
  font-size: 0.85rem;
  padding: 0.45rem 0.7rem;
  border-radius: 0.5rem;
  cursor: pointer;
}
.chats-menu__item:hover { background: var(--chats-surface); }
.chats-menu__item--danger { color: #dc2626; }
.chats-emoji {
  background: none;
  border: none;
  font-size: 1.1rem;
  padding: 0.25rem;
  cursor: pointer;
  border-radius: 0.5rem;
}
.chats-emoji:hover { background: var(--chats-surface); }

/* ---------------------------------------------------------------------------
   0.2.0 — stacked inbox rows, locked composers, signed messages
   --------------------------------------------------------------------------- */

/* A stack (Chats::InboxGroup) reads as one row; the count line sits where a
   single conversation shows its subject. */
.chats-row--group .chats-row__subject { font-weight: 600; }

/* The inbox filtered to one counterpart (?with=). */
.chats-inbox__filter {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin: 0 0 0.5rem;
  font-size: 0.85rem;
  color: var(--chats-text-muted);
}
.chats-inbox__filter-clear { color: inherit; text-decoration: underline; }

/* "See all" back to the stack, under the thread title. */
.chats-thread__see-all { margin: 0; font-size: 0.72rem; }
.chats-thread__see-all-link { color: var(--chats-text-muted); text-decoration: underline; }

/* Who wrote it, when that isn't the seat it came from. */
.chats-message__signature {
  margin-top: 0.25rem;
  font-size: 0.7rem;
  font-style: italic;
  opacity: 0.75;
}

/* The composer, replaced by the reason the conversation is closed. Same box,
   so the thread doesn't jump when a subject locks under an open composer. */
.chats-composer--locked {
  display: block;
  padding: 0.85rem 1rem;
  text-align: center;
}
.chats-composer__locked-notice {
  margin: 0;
  font-size: 0.85rem;
  color: var(--chats-text-muted);
}


/* ---------------------------------------------------------------------------
   0.6.0 — two panes (config.inbox_layout = :split)
   ---------------------------------------------------------------------------

   The messaging page as LinkedIn has it: from 700px wide (the dock's phone
   rule, and chats--split's), the list on the left and the open conversation
   on the right, both as tall as the screen, each scrolling on its own. Below
   that, one column, exactly as the separate pages: /messages is the list,
   /messages/:id the thread. Every request renders both panes; this media
   query alone picks, so it holds before JavaScript runs and without it.

     .chats-split                 the page (.chats.chats-inbox too)
     .chats-split__list           the list's <nav>
     .chats-split__conversation   the `chats_conversation` frame
     .chats-split__pane           a conversation in it (section)
     .chats-split__thread         its permanent wrapper, around .chats-thread--pane
     .chats-split__empty          no conversation open (--inbox: no conversations at all)

   The page is as wide as the list plus a thread page (--chats-max-width),
   so the conversation reads at the width it always had, and as tall as
   --chats-split-height (the screen; a host with its own header above the
   page sets what is left: calc(100dvh - 4rem)). */
.chats-split {
  max-width: calc(var(--chats-list-width) + var(--chats-max-width));
  /* The list alone (a phone): at least what is left of the screen, never
     the shell's whole 100dvh, which would run past it under a host header. */
  min-height: var(--chats-split-height, 100dvh);
}
.chats-split__list { display: flex; flex-direction: column; min-width: 0; }
.chats-split__conversation {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}
.chats-split__pane,
.chats-split__thread {
  flex: 1;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
/* The thread page's body, inside the pane: no shell of its own (the page is
   the .chats shell), and the pane's height, not the screen's. */
.chats-thread--pane {
  flex: 1;
  min-height: 0;
  height: auto;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.chats-split__empty { margin: auto; }
.chats-row--current .chats-row__link {
  background: var(--chats-surface);
  box-shadow: inset 3px 0 0 var(--chats-accent);
}

@media (min-width: 700px) {
  .chats-split {
    box-sizing: border-box;
    flex-direction: row;
    height: var(--chats-split-height, 100dvh);
    min-height: 0;
    overflow: hidden;
    /* Its edges, where a page wider than it shows the host's background. */
    border-inline: 1px solid var(--chats-border);
  }
  .chats-split__list {
    box-sizing: border-box;
    flex: 0 0 var(--chats-list-width);
    width: var(--chats-list-width);
    border-right: 1px solid var(--chats-border);
  }
  /* The rows scroll; the title and the search stay. */
  .chats-split__list > turbo-frame {
    display: block;
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior-y: contain;
  }
  .chats-split__conversation { flex: 1 1 0; }
  /* The list is right there: no way back to it. */
  .chats-split .chats-thread__back { display: none; }
}

@media (max-width: 699.98px) {
  /* A conversation open: the page is the thread, as tall as the screen. */
  .chats-split:has(.chats-split__pane) {
    height: var(--chats-split-height, 100dvh);
    min-height: 0;
    overflow: hidden;
  }
  .chats-split:has(.chats-split__pane) > .chats-split__list { display: none; }
  .chats-split:has(.chats-split__pane) > .chats-split__conversation { flex: 1; }
  /* None: the page is the list. The empty pane shows under it only to say
     there are no conversations at all (the inbox's empty state lives there). */
  .chats-split:not(:has(.chats-split__pane, .chats-split__empty--inbox)) > .chats-split__conversation {
    display: none;
  }
}

/* ---------------------------------------------------------------------------
   0.5.0 — the messaging dock (chats_dock)
   ---------------------------------------------------------------------------

   Windows to the left, the recent-chats panel at the right edge, flush with
   the bottom of the viewport. One element (`.chats-dock`, permanent across
   Turbo visits) rendered by `chats_dock`; the chats--dock controller owns
   the state classes:

     .chats-dock[data-mode]        "dock" or "sheet", set once JS runs (the
                                   layout itself comes from the media query
                                   at the end, so it holds before and without)
     .chats-dock__panel[open]      the recent list shows (a <details>)
     .chats-window--minimized      folded to its header
     .chats-window--unread         something arrived the viewer hasn't seen
     .chats-window--front          the one window a phone shows
     .chats-window--entering       its first 200ms; --popped: opened by a message

   Derived variables live on :where(.chats-dock), NOT :root: a custom
   property that references another resolves where it is declared, so a host
   re-pointing --chats-bg on .chats-dock would otherwise leave --chats-dock-bg
   frozen at the :root value. :where() keeps them at zero specificity.

   Never a lasting transform, filter, backdrop-filter, perspective or contain
   on the dock or a window: each makes the window the containing block of the
   thread's position: fixed popup and photo preview, which would then open
   inside a 336px box. Entrance animations end at `transform: none`, and
   minimizing changes a height, not a transform. */

:where(.chats-dock) {
  --chats-dock-z: 40;                 /* above the page, under host modals */
  --chats-dock-offset: 0px;           /* bottom clearance (a cookie banner…) */
  --chats-dock-inset: 16px;           /* from the right edge */
  --chats-dock-gap: 8px;
  --chats-dock-bar-height: 48px;      /* the bar, every window header */
  --chats-dock-panel-width: 288px;
  --chats-dock-panel-height: 560px;
  --chats-dock-window-width: 336px;
  --chats-dock-window-height: 480px;
  --chats-dock-squeezed-width: 104px; /* a window folded to make room */
  --chats-dock-top-clearance: 24px;
  --chats-dock-radius: 12px;
  --chats-dock-bg: var(--chats-bg);
  --chats-dock-header-bg: var(--chats-bg);
  /* Not --chats-surface: muted text on it is 4.39:1; on this, 4.63:1. */
  --chats-dock-hover: #f9fafb;
  --chats-dock-edge: rgba(17, 24, 39, 0.12);
  --chats-dock-shadow: 0 12px 40px rgba(17, 24, 39, 0.16), 0 2px 6px rgba(17, 24, 39, 0.08);
  --chats-dock-unread-bg: var(--chats-accent);
  --chats-dock-unread-text: var(--chats-accent-contrast);
  --chats-dock-focus: var(--chats-accent);
  /* --chats-dock-title-font: unset on purpose; titles inherit the host font. */
  --chats-dock-launcher-size: 56px;
  /* 0.7.0, the tray: an open item is a window's width, the panel's height */
  --chats-tray-panel-width: var(--chats-dock-window-width);
  --chats-tray-panel-height: var(--chats-dock-panel-height);
  --chats-tray-launcher-size: 48px;   /* an item's round button on a phone */
  --chats-tray-min-width: 280px;      /* less room than this: an item opens as the sheet */
  --chats-dock-sheet-header-height: 56px;
  --chats-dock-duration: 200ms;
  --chats-dock-ease: cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* --- The dock ----------------------------------------------------------------- */

.chats-dock {
  position: fixed;
  z-index: var(--chats-dock-z);
  right: calc(var(--chats-dock-inset) + var(--chats-safe-right));
  bottom: var(--chats-dock-offset);
  display: flex;
  align-items: flex-end;
  gap: var(--chats-dock-gap);
  color: var(--chats-text);
  font-size: 0.875rem;
  line-height: 1.4;
  pointer-events: none; /* the gaps between surfaces never eat page clicks */
}
/* Fixed-size surfaces with padding: never trust the host to have a reset. */
.chats-dock,
.chats-dock *,
.chats-dock *::before,
.chats-dock *::after { box-sizing: border-box; }
.chats-dock[hidden] { display: none; }
.chats-dock__windows {
  display: flex;
  align-items: flex-end;
  gap: var(--chats-dock-gap);
}
.chats-window,
.chats-dock__panel,
.chats-tray__item { pointer-events: auto; }

/* Inset: every surface clips (overflow: hidden), so an outside ring is cut. */
.chats-dock :focus-visible {
  outline: 2px solid var(--chats-dock-focus);
  outline-offset: -2px;
}

/* --- Surfaces (panel and windows share one skin) -------------------------------- */

.chats-window,
.chats-dock__panel,
.chats-tray__item {
  background: var(--chats-dock-bg);
  border: 1px solid var(--chats-dock-edge);
  border-bottom: 0;
  border-radius: var(--chats-dock-radius) var(--chats-dock-radius) 0 0;
  box-shadow: var(--chats-dock-shadow);
  overflow: hidden;
}

.chats-dock__panel {
  position: relative;
  width: var(--chats-dock-panel-width);
  padding-bottom: var(--chats-safe-bottom);
}
.chats-dock__panel[open] {
  height: min(var(--chats-dock-panel-height),
              calc(100dvh - var(--chats-dock-offset) - var(--chats-dock-top-clearance)));
}

.chats-window {
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  width: var(--chats-dock-window-width);
  height: min(var(--chats-dock-window-height),
              calc(100dvh - var(--chats-dock-offset) - var(--chats-dock-top-clearance)));
}
/* Folded to its header (and the home-indicator strip). A height, not a
   transform: the thread keeps its box and its scroll position, and its
   zero-height scroller is how the thread controller knows not to read. */
.chats-window--minimized { height: calc(var(--chats-dock-bar-height) + var(--chats-safe-bottom)); }
/* Folded to make room (it was in use when another opened, and the row is
   full): the width the controller budgets for it, the avatar (which expands
   it) and ×. The name stays in the title's tooltip. */
.chats-window--squeezed { width: var(--chats-dock-squeezed-width); }
.chats-window--squeezed :is(.chats-window__identity, .chats-window__unread, .chats-window__menu,
                            .chats-window__expand, .chats-window__minimize) {
  display: none;
}
/* Nothing of the thread keeps a height while folded, padding included, so
   the geometry says what the state says: there's nothing to read. */
.chats-window--minimized .chats-thread--window .chats-thread__scroll { padding-top: 0; padding-bottom: 0; }
.chats-window__card {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

/* --- The bar (the panel's <summary>) and window headers -------------------------- */

.chats-dock__bar,
.chats-tray__launcher,
.chats-window__header {
  flex: 0 0 var(--chats-dock-bar-height);
  height: var(--chats-dock-bar-height);
  display: flex;
  align-items: center;
  background: var(--chats-dock-header-bg);
  border-bottom: 1px solid var(--chats-dock-edge);
}
.chats-dock__toggle,
.chats-tray__launcher {
  gap: 10px;
  color: inherit;
  padding: 0 8px 0 12px;
  list-style: none;
  cursor: pointer;
  user-select: none;
}
:is(.chats-dock__toggle, .chats-tray__launcher)::-webkit-details-marker { display: none; }
.chats-dock__panel:not([open]) .chats-dock__toggle,
.chats-tray__item:not([open]) .chats-tray__launcher { border-bottom-color: transparent; }
.chats-dock__toggle .chats-avatar { width: 32px; height: 32px; font-size: 0.8rem; }
.chats-dock__icon { display: none; width: 26px; height: 26px; flex-shrink: 0; }
.chats-dock__title,
.chats-tray__title {
  flex: 1;
  min-width: 0;
  font-family: var(--chats-dock-title-font);
  font-size: 0.9375rem;
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chats-dock__chevron,
.chats-dock__close-glyph,
.chats-tray__chevron,
.chats-tray__close-glyph {
  flex: 0 0 32px;
  width: 32px;
  height: 32px;
  display: inline-grid;
  place-items: center;
  border-radius: 8px;
  color: var(--chats-text-muted);
}
.chats-dock__chevron svg,
.chats-dock__close-glyph svg,
.chats-tray__chevron svg,
.chats-tray__close-glyph svg {
  width: 18px;
  height: 18px;
  transition: transform var(--chats-dock-duration) var(--chats-dock-ease);
}
@media (hover: hover) {
  .chats-dock__toggle:hover .chats-dock__chevron,
  .chats-tray__launcher:hover .chats-tray__chevron { background: var(--chats-dock-hover); color: var(--chats-text); }
}
.chats-dock__panel[open] .chats-dock__chevron svg,
.chats-tray__item[open] .chats-tray__chevron svg { transform: rotate(180deg); }
.chats-dock__close-glyph,
.chats-tray__close-glyph { display: none; }

/* Badges in the dock: one size, a hair smaller than the inbox's. */
.chats-dock .chats-badge {
  min-width: 1.25rem;
  height: 1.25rem;
  padding: 0 0.3rem;
  font-size: 0.6875rem;
  font-variant-numeric: tabular-nums;
}
.chats-dock .chats-badge[hidden] { display: none; }

/* --- The panel body -------------------------------------------------------------- */

.chats-dock__body {
  /* Positioned, not a flex item: a <details>' content sits in a UA slot box,
     so flex sizing from the panel never reaches it. */
  position: absolute;
  inset: var(--chats-dock-bar-height) 0 var(--chats-safe-bottom) 0;
  display: flex;
  flex-direction: column;
}
:is(.chats-dock__panel, .chats-tray__item)[open] :is(.chats-dock__body, .chats-tray__body) {
  animation: chats-dock-rise var(--chats-dock-duration) var(--chats-dock-ease);
}

.chats-dock__search { padding: 8px 12px 4px; }
.chats-dock__search .chats-search__input { padding: 0.45rem 0.8rem; font-size: 0.8125rem; }
.chats-dock__results {
  display: block;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.chats-dock__list { list-style: none; margin: 0; padding: 4px 0; }
.chats-dock__list .chats-row__link { padding: 9px 12px; gap: 10px; }
@media (hover: hover) {
  .chats-dock__list .chats-row__link:hover { background: var(--chats-dock-hover); }
}
.chats-dock__list .chats-avatar { width: 40px; height: 40px; font-size: 0.85rem; }
.chats-dock__list .chats-row__title { font-size: 0.875rem; }
.chats-dock__list .chats-row__subject { font-size: 0.6875rem; }
.chats-dock__list .chats-row__preview { font-size: 0.8125rem; }
.chats-dock__list .chats-row__time { font-size: 0.6875rem; }
/* A conversation that is open in a window right now. */
.chats-dock__list .chats-row__link[aria-current="true"] { box-shadow: inset 3px 0 0 var(--chats-accent); }
.chats-row__muted { flex-shrink: 0; width: 14px; height: 14px; color: var(--chats-text-muted); }

.chats-dock__see-all {
  flex: 0 0 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-top: 1px solid var(--chats-dock-edge);
  color: var(--chats-text);
  font-size: 0.8125rem;
  font-weight: 650;
  text-decoration: none;
}
@media (hover: hover) {
  .chats-dock__see-all:hover { background: var(--chats-dock-hover); text-decoration: underline; }
}

.chats-dock .chats-empty { padding: 40px 20px; }
.chats-dock .chats-empty__icon { font-size: 2rem; margin-bottom: 0.5rem; }
.chats-dock .chats-empty__title { font-size: 0.9375rem; }
.chats-dock .chats-empty__hint { font-size: 0.8125rem; }

/* The very first load: four still rows (no shimmer). */
.chats-dock__skeleton { display: flex; gap: 10px; align-items: center; padding: 9px 12px; }
.chats-dock__skeleton::before {
  content: "";
  width: 40px;
  height: 40px;
  border-radius: 999px;
  background: var(--chats-surface);
  flex-shrink: 0;
}
.chats-dock__skeleton span { flex: 1; display: flex; flex-direction: column; gap: 6px; }
.chats-dock__skeleton span::before,
.chats-dock__skeleton span::after { content: ""; height: 10px; border-radius: 4px; background: var(--chats-surface); }
.chats-dock__skeleton span::before { width: 45%; }
.chats-dock__skeleton span::after { width: 80%; }

/* --- The tray (0.7.0) --------------------------------------------------------------
   The items a host puts in the corner (chats_dock { … } or chats_tray { … }),
   between the windows and the panel. An item is a <details>: closed, a tab on
   the bottom edge as tall as the bar; open, a panel with the same skin and the
   bar on top. The windows' row gets what the tray leaves (dock_controller.js,
   room), so nothing ever overlaps. A corner holding only the tray
   (.chats-dock--tray-only) is the same element class: same variables, same
   place. */

.chats-tray {
  display: flex;
  align-items: flex-end;
  gap: var(--chats-dock-gap);
}
/* Emptied by a visit to a page that renders no items: no gap left behind. */
.chats-tray:not(:has(.chats-tray__item)) { display: none; }
.chats-tray__item {
  position: relative;
  flex-shrink: 0;
  padding-bottom: var(--chats-safe-bottom);
}
/* Never wider than what the corner leaves it (`--chats-tray-room`, measured by
   chats--tray as it opens: the screen less the insets, the dock's panel and
   every other item). Under --chats-tray-min-width it opens as the sheet. */
.chats-tray__item[open] {
  width: min(var(--chats-tray-panel-width), var(--chats-tray-room, var(--chats-tray-panel-width)));
  height: min(var(--chats-tray-panel-height),
              calc(100dvh - var(--chats-dock-offset) - var(--chats-dock-top-clearance)));
}
.chats-tray__item:not([open]) .chats-tray__launcher { padding: 0 14px 0 12px; gap: 8px; }
.chats-tray__icon {
  display: inline-grid;
  place-items: center;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}
.chats-tray__icon :is(svg, img) { width: 100%; height: 100%; }
.chats-tray__label {
  font-family: var(--chats-dock-title-font);
  font-size: 0.875rem;
  font-weight: 700;
  white-space: nowrap;
}
/* Closed, the tab's words; open, the panel's title and the way to close it. */
.chats-tray__item[open] .chats-tray__label,
.chats-tray__item:not([open]) :is(.chats-tray__title, .chats-tray__chevron) { display: none; }
.chats-tray__title { display: flex; align-items: center; gap: 4px; }
/* Positioned, as the panel's body is (a <details>' content sits in a UA slot
   box). The item's content fills it: a frame, a column. */
.chats-tray__body {
  position: absolute;
  inset: var(--chats-dock-bar-height) 0 var(--chats-safe-bottom) 0;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}
.chats-tray__body > turbo-frame {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

/* --- Windows --------------------------------------------------------------------- */

.chats-window__header { padding-right: 4px; position: relative; }
.chats-window__title {
  flex: 1;
  min-width: 0;
  height: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 4px 0 8px;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.chats-window__title .chats-avatar { width: 32px; height: 32px; font-size: 0.8rem; }
.chats-window__identity { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.chats-window__name-row { display: flex; align-items: center; gap: 4px; min-width: 0; }
.chats-window__name {
  font-family: var(--chats-dock-title-font);
  font-size: 0.875rem;
  font-weight: 700;
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chats-window__subject {
  font-size: 0.75rem;
  line-height: 1.25;
  color: var(--chats-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chats-window__title .chats-badge { margin-left: 2px; }
.chats-window__icon {
  flex: 0 0 32px;
  width: 32px;
  height: 32px;
  display: inline-grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--chats-text-muted);
  font: inherit;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  text-decoration: none;
  list-style: none;
}
.chats-window__icon::-webkit-details-marker { display: none; }
.chats-window__icon svg { width: 18px; height: 18px; }
@media (hover: hover) {
  .chats-window__icon:hover { background: var(--chats-dock-hover); color: var(--chats-text); }
}
.chats-window__back { display: none; }
/* The host's thread actions (conversation_header_actions). */
.chats-window__menu { flex: 0 0 auto; }
.chats-window__menu .chats-menu__panel { top: calc(100% - 4px); right: 4px; z-index: 30; }
/* Touch in dock mode (a tablet): the same 48px header, fatter targets. */
@media (pointer: coarse) {
  .chats-dock[data-mode="dock"] .chats-window__icon { flex-basis: 40px; width: 40px; height: 40px; }
}

/* Focused: a 2px accent line along the top of the header. */
.chats-window:focus-within .chats-window__header { box-shadow: inset 0 2px 0 var(--chats-dock-focus); }

/* Waiting to be seen (popped open, or minimized when something arrived): the
   header becomes the accent pair, the one every host already tunes. */
.chats-window--unread .chats-window__header {
  background: var(--chats-dock-unread-bg);
  color: var(--chats-dock-unread-text);
  border-bottom-color: transparent;
  box-shadow: none;
}
.chats-window--unread :is(.chats-window__subject, .chats-window__icon, .chats-verified) { color: inherit; }
@media (hover: hover) {
  .chats-window--unread .chats-window__icon:hover { background: color-mix(in srgb, currentColor 16%, transparent); color: inherit; }
}
.chats-window--unread .chats-window__header .chats-badge {
  background: var(--chats-dock-unread-text);
  color: var(--chats-dock-unread-bg);
}
.chats-window--unread .chats-window__header :focus-visible { outline-color: var(--chats-dock-unread-text); }

/* The thread, inside a window: the full page's body, compacted. Rooted at
   .chats-thread--window, never .chats (the full-page shell). */
.chats-thread--window {
  --chats-radius: 0.95rem;
  flex: 1;
  min-height: 0;
  height: auto;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--chats-dock-bg);
}
.chats-thread--window .chats-thread__scroll { padding: 10px 12px 6px; }
/* A short thread sits on the composer: the eye is at the bottom. */
.chats-thread--window .chats-thread__scroll > :first-child { margin-top: auto; }
.chats-thread--window .chats-day-separator,
.chats-thread--window .chats-thread__unread-line { margin: 0.5rem 0; }
.chats-thread--window .chats-message { max-width: 88%; }
.chats-thread--window .chats-message__avatar { width: 1.6rem; height: 1.6rem; font-size: 0.65rem; }
.chats-thread--window .chats-message__bubble { padding: 0.4rem 0.7rem; font-size: 0.875rem; line-height: 1.35; }
.chats-thread--window .chats-message__bubble > .chats-message__action { font-size: 0.8125rem; padding: 0.45rem 0.8rem; }
.chats-thread--window .chats-message__image { max-width: 11rem; max-height: 11rem; }
.chats-thread--window .chats-typing { font-size: 0.75rem; padding: 0.3rem 0.25rem; }
.chats-thread--window .chats-composer { padding: 8px 10px calc(8px + var(--chats-safe-bottom)); }
.chats-thread--window .chats-composer__actions { margin-bottom: 6px; }
.chats-thread--window :where(.chats-composer__actions) :where(a, button) { font-size: 0.75rem; padding: 0.25rem 0.7rem; }
.chats-thread--window .chats-composer__bar { gap: 8px; }
.chats-thread--window .chats-composer__input { font-size: 0.875rem; padding: 0.45rem 0.75rem; max-height: 7.5rem; }
.chats-thread--window .chats-composer__send { width: 2.25rem; height: 2.25rem; font-size: 0.95rem; }
.chats-thread--window .chats-composer--locked { padding: 12px 14px calc(12px + var(--chats-safe-bottom)); }
.chats-thread--window .chats-composer__locked-notice { font-size: 0.8125rem; }

/* Before anybody wrote (opened from a Message button). */
.chats-window__hello {
  margin: auto 0;
  padding: 24px 16px;
  text-align: center;
  color: var(--chats-text-muted);
  font-size: 0.8125rem;
}
.chats-window__hello .chats-avatar { display: flex; width: 56px; height: 56px; font-size: 1.2rem; margin: 0 auto 10px; }
.chats-window__hello strong { display: block; color: var(--chats-text); font-size: 0.9375rem; margin-bottom: 2px; }
.chats-thread__scroll:has(.chats-message) .chats-window__hello { display: none; }

/* Loading, and failure. */
.chats-window__status {
  margin: auto;
  padding: 24px 16px;
  text-align: center;
  color: var(--chats-text-muted);
  font-size: 0.8125rem;
}
.chats-window__status p { margin: 0; }
.chats-window__retry {
  margin-top: 10px;
  padding: 0.35rem 0.9rem;
  border: 1px solid var(--chats-border);
  border-radius: 999px;
  background: var(--chats-surface);
  color: var(--chats-text);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  cursor: pointer;
}

/* --- Motion ---------------------------------------------------------------------- */

@keyframes chats-dock-rise { from { transform: translateY(16px); opacity: 0; } }
@keyframes chats-dock-sheet-up { from { transform: translateY(100%); } }
@keyframes chats-dock-push { from { transform: translateX(100%); } }
@keyframes chats-dock-bump { 50% { transform: scale(1.2); } }
@keyframes chats-dock-flash { 50% { filter: brightness(1.18); } }

.chats-window--entering { animation: chats-dock-rise var(--chats-dock-duration) var(--chats-dock-ease); }
.chats-window--popped .chats-window__header { animation: chats-dock-flash 600ms ease-in-out 2; }
.chats-dock .chats-badge--bumped { animation: chats-dock-bump 160ms ease-out; }

/* --- Phones: the sheet ------------------------------------------------------------
   Under 700px wide or 500px tall (one window beside the panel needs 656px;
   a 480px window needs the height). A media query, not a class, so a phone
   gets this layout before the controller connects and without JavaScript —
   the panel is a <details>, so the closed bar is the launcher and opening it
   is the sheet. The controller reads the same query for its behaviour. */

@media (max-width: 699.98px), (max-height: 499.98px) {
  .chats-dock {
    inset: auto 0 0 auto;
    right: 0;
    display: block;
  }

  /* Closed: the round launcher, bottom-right above the home indicator. */
  .chats-dock__panel:not([open]) {
    position: fixed;
    right: calc(16px + var(--chats-safe-right));
    bottom: calc(16px + var(--chats-dock-offset) + var(--chats-safe-bottom));
    width: var(--chats-dock-launcher-size);
    height: var(--chats-dock-launcher-size);
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: var(--chats-accent);
    color: var(--chats-accent-contrast);
    box-shadow: 0 6px 20px rgba(17, 24, 39, 0.28);
    overflow: visible;
  }
  .chats-dock__panel:not([open]) .chats-dock__toggle {
    position: relative;
    justify-content: center;
    width: 100%;
    height: 100%;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: none;
  }
  .chats-dock__panel:not([open]) :is(.chats-avatar, .chats-dock__chevron) { display: none; }
  .chats-dock__panel:not([open]) .chats-dock__title {
    /* Still read out ("Messages, 3 unread"), just not shown. */
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
  }
  .chats-dock__panel:not([open]) .chats-dock__icon { display: block; }
  .chats-dock__panel:not([open]) .chats-badge {
    position: absolute;
    top: -2px;
    right: -2px;
    /* accent-on-accent would vanish: the launcher's badge is the inverse pair */
    background: var(--chats-accent-contrast);
    color: var(--chats-accent);
    box-shadow: 0 0 0 2px var(--chats-accent);
  }
  .chats-dock--no-launcher .chats-dock__panel:not([open]) { display: none; }
  /* Anything full-screen up: the launcher would only cover it. */
  .chats-dock:has(.chats-window--front, .chats-tray__item[open]) .chats-dock__panel:not([open]) { display: none; }

  /* The tray: its closed items are smaller round buttons stacked above the
     dock's (in its place when there is none), the same accent-free surface
     as a window. `launcher: false` takes them away with the dock's. */
  .chats-tray {
    position: fixed;
    z-index: 3;
    right: calc(16px + var(--chats-safe-right));
    bottom: calc(16px + var(--chats-dock-offset) + var(--chats-safe-bottom));
    flex-direction: column;
    align-items: flex-end;
    gap: 12px;
  }
  .chats-dock:not(.chats-dock--no-launcher, .chats-dock--tray-only) .chats-tray {
    bottom: calc(28px + var(--chats-dock-launcher-size) + var(--chats-dock-offset) + var(--chats-safe-bottom));
  }
  .chats-tray__item:not([open]) {
    width: var(--chats-tray-launcher-size);
    height: var(--chats-tray-launcher-size);
    padding: 0;
    border: 1px solid var(--chats-dock-edge);
    border-radius: 999px;
    box-shadow: 0 6px 20px rgba(17, 24, 39, 0.28);
    overflow: visible;
  }
  .chats-tray__item:not([open]) .chats-tray__launcher {
    position: relative;
    justify-content: center;
    width: 100%;
    height: 100%;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: none;
  }
  .chats-tray__item:not([open]) .chats-tray__icon { width: 22px; height: 22px; }
  .chats-tray__item:not([open]) .chats-tray__label {
    /* Still read out ("Need help?, 2 unread"), just not shown. */
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
  }
  .chats-tray__item:not([open]) .chats-badge { position: absolute; top: -4px; right: -4px; }
  .chats-dock--no-launcher .chats-tray__item:not([open]) { display: none; }
  .chats-dock:has(.chats-dock__panel[open], .chats-window--front, .chats-tray__item[open]) .chats-tray__item:not([open]) {
    display: none;
  }

  /* Open: the full-screen sheet; a window sits on top of the list. */
  .chats-dock__panel[open],
  .chats-window {
    position: fixed;
    left: 0;
    right: 0;
    top: var(--chats-vv-top, 0px);
    width: 100%;
    height: var(--chats-vvh, 100dvh);
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding: var(--chats-safe-top) var(--chats-safe-right) 0 var(--chats-safe-left);
  }
  .chats-dock__panel[open] {
    z-index: 1;
    padding-bottom: var(--chats-safe-bottom);
    animation: chats-dock-sheet-up 260ms cubic-bezier(0.32, 0.72, 0, 1);
  }
  .chats-dock__panel[open] .chats-dock__body { animation: none; }
  .chats-window { z-index: 2; }
  .chats-window:not(.chats-window--front) { display: none; }
  .chats-window--front.chats-window--entering { animation: chats-dock-push 260ms cubic-bezier(0.32, 0.72, 0, 1); }

  .chats-dock__body {
    inset: calc(var(--chats-safe-top) + var(--chats-dock-sheet-header-height)) var(--chats-safe-right)
           var(--chats-safe-bottom) var(--chats-safe-left);
  }
  .chats-dock__panel[open] .chats-dock__bar,
  .chats-window__header {
    flex-basis: var(--chats-dock-sheet-header-height);
    height: var(--chats-dock-sheet-header-height);
    box-shadow: none;
  }
  .chats-dock__panel[open] .chats-dock__toggle { padding: 0 6px 0 16px; }
  .chats-dock__panel[open] .chats-dock__title { font-size: 1.125rem; font-weight: 800; }
  .chats-dock__panel[open] :is(.chats-dock__toggle .chats-avatar, .chats-dock__chevron) { display: none; }
  .chats-dock__panel[open] .chats-dock__close-glyph {
    display: inline-grid;
    flex-basis: 44px;
    width: 44px;
    height: 44px;
  }
  .chats-dock__panel[open] .chats-dock__close-glyph svg { width: 22px; height: 22px; }

  .chats-dock__search { padding: 8px 16px; }
  /* 16px or iOS Safari zooms the page on focus. */
  .chats-dock__search .chats-search__input { font-size: 16px; padding: 0.55rem 0.9rem; }
  .chats-dock__list .chats-row__link { padding: 10px 16px; gap: 12px; }
  .chats-dock__list .chats-avatar { width: 48px; height: 48px; }
  .chats-dock__list .chats-row__title { font-size: 1rem; }
  .chats-dock__list .chats-row__preview { font-size: 0.875rem; }
  .chats-dock__list .chats-row__time { font-size: 0.75rem; }
  .chats-dock__see-all { flex-basis: 52px; font-size: 0.9375rem; }

  .chats-window__back { display: inline-grid; }
  .chats-window__header { padding: 0 4px; }
  .chats-window__header :is(.chats-window__minimize, .chats-window__close) { display: none; }
  .chats-window__icon { flex-basis: 44px; width: 44px; height: 44px; }
  .chats-window__icon svg { width: 22px; height: 22px; }
  .chats-window__title { cursor: default; padding-left: 2px; }
  .chats-window__title .chats-avatar { width: 36px; height: 36px; }
  .chats-window__name { font-size: 1rem; }
  /* Full screen: the conversation is in front of you, nothing to flag. */
  .chats-window--unread .chats-window__header {
    background: var(--chats-dock-header-bg);
    color: inherit;
    border-bottom-color: var(--chats-dock-edge);
  }
  .chats-thread--window .chats-thread__scroll { padding: 12px 14px 8px; }
  .chats-thread--window .chats-message__bubble { font-size: 1rem; padding: 0.5rem 0.8rem; }
  .chats-thread--window .chats-composer { padding: 8px 12px calc(8px + var(--chats-safe-bottom)); }
  .chats-thread--window .chats-composer__input { font-size: 16px; padding: 0.55rem 0.9rem; }
  .chats-thread--window .chats-composer__send { width: 2.75rem; height: 2.75rem; }
  /* The keyboard is up: the home-indicator strip is behind it. */
  .chats-dock--keyboard .chats-thread--window .chats-composer { padding-bottom: 8px; }
}

/* An item open as the sheet: on a phone, and on a desk whose corner can't fit
   it (chats--tray sets the class: .chats-tray__item--sheet). The dock's own
   sheet, for an item. */
.chats-tray__item--sheet[open] {
  position: fixed;
  z-index: 3;
  left: 0;
  right: 0;
  top: var(--chats-vv-top, 0px);
  width: 100%;
  height: var(--chats-vvh, 100dvh);
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: var(--chats-safe-top) var(--chats-safe-right) var(--chats-safe-bottom) var(--chats-safe-left);
  animation: chats-dock-sheet-up 260ms cubic-bezier(0.32, 0.72, 0, 1);
}
.chats-tray__item--sheet[open] .chats-tray__body {
  inset: calc(var(--chats-safe-top) + var(--chats-dock-sheet-header-height)) var(--chats-safe-right)
         var(--chats-safe-bottom) var(--chats-safe-left);
  animation: none;
}
.chats-tray__item--sheet[open] .chats-tray__launcher {
  flex-basis: var(--chats-dock-sheet-header-height);
  height: var(--chats-dock-sheet-header-height);
  padding: 0 6px 0 16px;
  box-shadow: none;
}
.chats-tray__item--sheet[open] .chats-tray__title { font-size: 1.125rem; font-weight: 800; }
.chats-tray__item--sheet[open] :is(.chats-tray__icon, .chats-tray__chevron) { display: none; }
.chats-tray__item--sheet[open] .chats-tray__close-glyph { display: inline-grid; flex-basis: 44px; width: 44px; height: 44px; }
.chats-tray__item--sheet[open] .chats-tray__close-glyph svg { width: 22px; height: 22px; }

html.chats-dock-sheet-open { overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
  .chats-dock,
  .chats-dock *,
  .chats-dock *::before,
  .chats-dock *::after {
    animation: none !important;
    transition-duration: 0s !important;
    transition-delay: 0s !important;
  }
}
