/* qufox · mobile components
 * Requires tokens.css + components.css.
 * Touch targets: all interactive elements >= 44×44 (Apple HIG).
 * Safe areas: use env(safe-area-inset-*) inside .qf-m-safe-* wrappers.
 */

:root {
  --m-topbar-h:   52px;
  --m-tabbar-h:   56px;       /* excl. safe area */
  --m-tabbar-h-ios: 49px;     /* iOS bottom tab bar standard */
  --m-composer-h: 56px;
  --m-touch:      44px;       /* min hit target */
  --m-gutter:     16px;
  --m-sheet-r:    20px;       /* bottom-sheet top radius */
  --m-statusbar:  62px;       /* iOS status bar clearance (9:41 row) — device-frame mockups only */

  /* Drawer / swipe / keyboard / panel motion */
  --m-swipe-threshold: 60px;          /* drawer + reply swipe commit distance */
  --m-swipe-icon-size: 32px;
  --m-keyboard-pad:    env(keyboard-inset-height, 0px);
  --m-panel-ease: var(--ease-standard);
  --m-panel-dur:  var(--dur-slow);
  --m-sheet-ease: var(--ease-emphasized);
  --m-sheet-dur:  var(--dur-base);

  /* List row metrics */
  --m-tile-h:        64px;
  --m-dm-row-avatar: 40px;
  --m-dm-row-h:      64px;
}

/* ───── Safe-area helpers ───── */
.qf-m-safe-top    { padding-top: env(safe-area-inset-top); }
.qf-m-safe-bottom { padding-bottom: env(safe-area-inset-bottom); }

/* ───── Mobile shell: 3 screens = topbar + body + tabbar ───── */
.qf-m-screen {
  position: relative;
  display: flex; flex-direction: column;
  height: 100%; width: 100%;
  background: var(--bg-chat);
  overflow: hidden;
  /* reserve space for iOS/Android status bar when rendered inside a device frame */
  padding-top: var(--m-statusbar);
}
.qf-m-screen--bare { padding-top: 0; }

/* ───── Top bar ───── */
.qf-m-topbar {
  height: var(--m-topbar-h); flex-shrink: 0;
  padding: 0 var(--s-3);
  display: grid; grid-template-columns: auto 1fr auto;
  align-items: center; gap: var(--s-3);
  background: var(--bg-chat);
  border-bottom: 1px solid var(--divider);
  box-shadow: var(--elev-1);
  position: relative; z-index: 2;
}
.qf-m-topbar__back,
.qf-m-topbar__action {
  width: var(--m-touch); height: var(--m-touch);
  display: grid; place-items: center;
  border-radius: var(--r-md); background: transparent;
  border: none; color: var(--text); cursor: pointer;
  font-size: 20px;
}
.qf-m-topbar__back:active,
.qf-m-topbar__action:active { background: var(--bg-hover); }
.qf-m-topbar__title {
  font: 600 var(--fs-16) var(--font-sans); color: var(--text-strong);
  letter-spacing: var(--tracking-tight);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  display: flex; align-items: center; gap: var(--s-2);
}
.qf-m-topbar__subtitle {
  font-size: var(--fs-11); color: var(--text-muted);
  margin-top: 1px;
}
.qf-m-topbar__titleBlock { min-width: 0; }
.qf-m-topbar__actions { display: flex; gap: 2px; }

/* ───── Bottom tab bar (primary mobile nav) ───── */
/* Tab bar = app chrome. Render it at the SHELL level (outside swappable
   route content) so it stays globally pinned across channel/DM views.
   bg-app keeps it darker than the chat canvas (fixes layer inversion). */
.qf-m-tabbar {
  flex-shrink: 0;
  height: var(--m-tabbar-h);
  padding-bottom: env(safe-area-inset-bottom);
  background: var(--bg-app);
  border-top: 1px solid var(--divider);
  display: grid; grid-auto-flow: column;
  grid-auto-columns: 1fr;
  position: relative; z-index: var(--z-tabbar);
}
.qf-m-tab {
  position: relative;
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 2px;
  color: var(--text-muted); cursor: pointer;
  border: none; background: transparent;
  font: 500 var(--fs-11) var(--font-sans);
  padding-top: 6px;
}
.qf-m-tab__icon {
  width: 24px; height: 24px;
  display: grid; place-items: center;
  font-size: 22px; line-height: 1;
  transition: transform var(--dur-fast) var(--ease-spring);
}
.qf-m-tab__label { line-height: 1; }
.qf-m-tab[aria-selected="true"] { color: var(--accent); }
.qf-m-tab[aria-selected="true"] .qf-m-tab__label { color: var(--text-strong); }
.qf-m-tab[aria-selected="true"] .qf-m-tab__icon { transform: scale(1.1); }
/* Active-tab pill — color + shape dual-encode for low-vision parity (decision 050-3). */
.qf-m-tab__pill {
  position: absolute; bottom: 0; left: 50%; transform: translateX(-50%);
  width: var(--s-6); height: var(--s-1);
  border-radius: var(--r-pill) var(--r-pill) 0 0;
  background: var(--accent);
  opacity: 0; transition: opacity var(--dur-fast) var(--ease-standard);
}
.qf-m-tab[aria-selected="true"] .qf-m-tab__pill { opacity: 1; }
/* Generic unread = violet badge; @mention = danger badge (meaning split). */
.qf-m-tab__badge {
  position: absolute; top: 4px; left: 50%;
  margin-left: 6px;
  min-width: 16px; height: 16px; padding: 0 4px;
  background: var(--badge-unread-bg); color: var(--text-onAccent);
  border-radius: var(--r-pill);
  font: 600 10px var(--font-sans);
  display: grid; place-items: center;
  border: 2px solid var(--bg-app);
}
.qf-m-tab__badge--mention { background: var(--badge-mention-bg); }
.qf-m-tab__dot {
  position: absolute; top: 8px; left: 50%;
  margin-left: 6px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--text-strong);
  border: 2px solid var(--bg-app);
}

/* ───── Body (scrollable content between topbar & tabbar) ───── */
.qf-m-body {
  flex: 1; min-height: 0; overflow-y: auto;
  overscroll-behavior: contain;
}

/* ───── List row (DMs, server list, search results) ───── */
.qf-m-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 12px;
  align-items: center;
  padding: 10px var(--m-gutter);
  min-height: 64px;
  cursor: pointer;
  position: relative;
  /* DS bundle update 2026-04-27 [ds-ok]: when used as a `<button>` (DM
     list rows, friend rows) the browser default chrome leaks through.
     Reset background/border/color/font here so the row sits flush
     against the screen background regardless of the host element. */
  background: transparent;
  border: none;
  color: var(--text);
  font-family: inherit;
}
.qf-m-row:active { background: var(--bg-hover); }
.qf-m-row + .qf-m-row::before {
  content: ''; position: absolute; top: 0; left: 68px; right: 0;
  height: 1px; background: var(--divider);
}
.qf-m-row__primary { font: 600 var(--fs-15) var(--font-sans); color: var(--text-strong); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qf-m-row__secondary { font-size: var(--fs-13); color: var(--text-muted); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qf-m-row__aside { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.qf-m-row__time { font-size: var(--fs-11); color: var(--text-muted); }
.qf-m-row--unread .qf-m-row__primary { color: var(--text-strong); }
.qf-m-row--unread .qf-m-row__secondary { color: var(--text); font-weight: 500; }

/* ───── Section header in lists ───── */
.qf-m-section {
  padding: var(--s-5) var(--m-gutter) var(--s-2);
  font: 600 var(--fs-12) var(--font-sans);
  color: var(--text-muted);
  text-transform: uppercase; letter-spacing: var(--tracking-caps);
  display: flex; align-items: center; justify-content: space-between;
}
.qf-m-section__action { color: var(--accent); font-size: var(--fs-13); text-transform: none; letter-spacing: 0; }

/* ───── Search field inline in topbar ───── */
.qf-m-search {
  display: flex; align-items: center; gap: var(--s-2);
  height: 36px; padding: 0 var(--s-4);
  background: var(--bg-panel); border: 1px solid transparent;
  border-radius: var(--r-lg); margin: 0 var(--m-gutter);
  color: var(--text-muted);
}
.qf-m-search__input { flex: 1; background: transparent; border: none; outline: none; color: var(--text); font: 400 var(--fs-14) var(--font-sans); }
.qf-m-search__input::placeholder { color: var(--text-muted); }

/* ───── Channel row inside server drawer ───── */
.qf-m-channel {
  display: flex; align-items: center; gap: var(--s-3);
  padding: 10px var(--m-gutter);
  min-height: 44px;
  color: var(--text-muted);
  font: 500 var(--fs-15) var(--font-sans);
}
.qf-m-channel:active { background: var(--bg-hover); }
.qf-m-channel[aria-selected="true"] { background: var(--bg-selected); color: var(--text-strong); }
.qf-m-channel--unread { color: var(--text-strong); font-weight: 700; }
.qf-m-channel__suffix { margin-left: auto; }

/* ───── Composer (fixed above tabbar when in-channel) ───── */
.qf-m-composer {
  flex-shrink: 0;
  padding: var(--s-3);
  /* sticky above the keyboard: safe-area + visual-viewport keyboard inset */
  padding-bottom: calc(var(--s-3) + env(safe-area-inset-bottom) + var(--m-keyboard-pad));
  background: var(--bg-chat);
  border-top: 1px solid var(--divider);
  display: flex; align-items: flex-end; gap: var(--s-2);
}
.qf-m-composer__plus {
  width: var(--m-touch); height: var(--m-touch); flex-shrink: 0;
  border-radius: var(--r-pill);
  background: var(--bg-panel); color: var(--text);
  border: none; display: grid; place-items: center;
  font-size: 20px;
}
.qf-m-composer__input {
  flex: 1; min-height: var(--m-touch); max-height: 120px;
  padding: 8px 12px;
  background: var(--bg-input); color: var(--text);
  border: 1px solid var(--border); border-radius: 18px;
  font: 400 var(--fs-15)/1.4 var(--font-sans);
  outline: none; resize: none;
}
.qf-m-composer__input:focus { border-color: var(--accent); }
.qf-m-composer__send {
  width: var(--m-touch); height: var(--m-touch); flex-shrink: 0;
  border-radius: var(--r-pill);
  background: var(--accent); color: #fff;
  border: none; display: grid; place-items: center;
  font-size: 16px;
}
.qf-m-composer__send[disabled] { background: var(--n-5); color: var(--text-muted); }

/* ───── Mobile message (denser, no hover toolbar) ───── */
.qf-m-msg {
  padding: 4px var(--m-gutter);
  display: grid; grid-template-columns: 40px 1fr; gap: 0 12px;
}
.qf-m-msg--head { padding-top: var(--s-4); }
.qf-m-msg--cont .qf-m-msg__avatar { visibility: hidden; height: 0; }
.qf-m-msg--cont .qf-m-msg__meta { display: none; }
.qf-m-msg__avatar { grid-row: span 2; }
.qf-m-msg__meta { display: flex; align-items: baseline; gap: 6px; margin-bottom: 2px; }
.qf-m-msg__author { font: 600 var(--fs-15) var(--font-sans); color: var(--text-strong); }
.qf-m-msg__time { font-size: var(--fs-11); color: var(--text-muted); }
.qf-m-msg__body { font-size: var(--fs-15); line-height: var(--lh-snug); color: var(--text); word-wrap: break-word; }

/* Swipe-to-reply hint (revealed when row translated) */
.qf-m-swipe {
  position: absolute; left: var(--s-4); top: 50%; transform: translateY(-50%);
  width: var(--m-swipe-icon-size); height: var(--m-swipe-icon-size); border-radius: var(--r-pill);
  background: var(--accent); color: var(--text-onAccent);
  display: grid; place-items: center; font-size: var(--fs-14);
  opacity: 0; pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease-standard),
              transform var(--dur-fast) var(--ease-standard);
}

/* ───── Bottom sheet ───── */
.qf-m-sheet-backdrop {
  position: absolute; inset: 0; background: rgba(10,8,30,0.6);
  display: flex; flex-direction: column; justify-content: flex-end;
  z-index: var(--z-modal-bg);
}
.qf-m-sheet {
  background: var(--bg-elevated);
  border-radius: var(--m-sheet-r) var(--m-sheet-r) 0 0;
  padding: 8px 0;
  padding-bottom: calc(8px + env(safe-area-inset-bottom));
  box-shadow: var(--elev-4);
  max-height: 80%;
  display: flex; flex-direction: column;
}
.qf-m-sheet__grab {
  width: 36px; height: 4px; border-radius: 2px;
  background: var(--n-5);
  margin: 4px auto 12px;
}
.qf-m-sheet__title {
  padding: 0 var(--m-gutter) var(--s-3);
  font: 600 var(--fs-15) var(--font-sans); color: var(--text-strong);
}
.qf-m-sheet__item {
  display: flex; align-items: center; gap: var(--s-4);
  padding: 14px var(--m-gutter); min-height: var(--m-touch);
  color: var(--text); font: 500 var(--fs-15) var(--font-sans);
  cursor: pointer;
}
.qf-m-sheet__item:active { background: var(--bg-hover); }
.qf-m-sheet__item--danger { color: var(--danger-400); }
.qf-m-sheet__icon { width: 24px; display: grid; place-items: center; font-size: 18px; opacity: 0.9; }
.qf-m-sheet__divider { height: 1px; background: var(--divider); margin: 4px 0; }

/* ───── Segmented control (thread filters, DMs vs Requests, etc.) ───── */
.qf-m-segment {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  padding: 3px; margin: var(--s-3) var(--m-gutter);
  background: var(--bg-panel); border-radius: var(--r-lg);
}
.qf-m-segment__btn {
  padding: 6px 10px; border-radius: calc(var(--r-lg) - 3px);
  background: transparent; border: none;
  font: 500 var(--fs-13) var(--font-sans); color: var(--text-secondary);
  cursor: pointer;
}
.qf-m-segment__btn[aria-selected="true"] { background: var(--bg-elevated); color: var(--text-strong); box-shadow: var(--elev-1); }

/* ───── Reaction picker chip row ───── */
.qf-m-react-row {
  display: flex; gap: 8px; padding: var(--s-4) var(--m-gutter);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.qf-m-react-row::-webkit-scrollbar { display: none; }
.qf-m-react-chip {
  width: var(--m-touch); height: var(--m-touch); flex-shrink: 0;
  border-radius: var(--r-pill);
  background: var(--bg-panel); border: none;
  display: grid; place-items: center; font-size: 22px;
}
.qf-m-react-chip--more { background: var(--bg-hover); font-size: 18px; color: var(--text-muted); }

/* ───── Voice / call in-channel ───── */
.qf-m-voice {
  background: var(--bg-elevated);
  border-radius: var(--r-xl);
  margin: var(--s-4) var(--m-gutter);
  padding: var(--s-5);
  display: flex; flex-direction: column; gap: var(--s-4);
  border: 1px solid var(--border);
}
.qf-m-voice__head { display: flex; align-items: center; gap: var(--s-3); }
.qf-m-voice__live { display: flex; align-items: center; gap: 6px; padding: 2px 8px; background: rgba(74,222,128,0.15); color: var(--ok-400); border-radius: var(--r-xs); font: 600 var(--fs-11) var(--font-sans); }
.qf-m-voice__live::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.qf-m-voice__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-3); }
.qf-m-voice__tile { aspect-ratio: 1; border-radius: var(--r-md); background: var(--bg-panel); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--s-2); position: relative; overflow: hidden; }
.qf-m-voice__tile--speaking { box-shadow: 0 0 0 2px var(--ok-400); }
.qf-m-voice__tile-name { font: 500 var(--fs-12) var(--font-sans); color: var(--text); }
.qf-m-voice__tile-muted { position: absolute; top: 6px; right: 6px; width: 20px; height: 20px; border-radius: 50%; background: var(--danger-600); color: #fff; display: grid; place-items: center; font-size: 10px; }
.qf-m-voice__controls { display: flex; justify-content: center; gap: var(--s-3); padding-top: var(--s-2); }
.qf-m-voice__ctrl { width: 48px; height: 48px; border-radius: var(--r-pill); background: var(--bg-panel); color: var(--text); border: none; display: grid; place-items: center; font-size: 18px; }
.qf-m-voice__ctrl--leave { background: var(--danger-600); color: #fff; }

/* ───── Empty state (mobile, more compact) ───── */
.qf-m-empty {
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; text-align: center;
  padding: var(--s-11) var(--s-7); gap: var(--s-4);
  color: var(--text-muted);
}
.qf-m-empty__title { font: 600 var(--fs-16) var(--font-sans); color: var(--text-strong); }
.qf-m-empty__body { font-size: var(--fs-14); max-width: 280px; line-height: var(--lh-snug); }

/* ───── FAB (floating action button) ───── */
.qf-m-fab {
  position: absolute; right: var(--m-gutter);
  bottom: calc(var(--m-tabbar-h) + var(--m-gutter) + env(safe-area-inset-bottom));
  width: 56px; height: 56px; border-radius: var(--r-pill);
  background: var(--accent); color: #fff;
  border: none; box-shadow: var(--elev-3);
  display: grid; place-items: center; font-size: 24px;
  z-index: 3;
}

/* ───── Pull-to-refresh spinner area ───── */
.qf-m-ptr { display: flex; justify-content: center; padding: var(--s-3) 0; color: var(--text-muted); font-size: var(--fs-12); }
.qf-m-ptr__spin { width: 16px; height: 16px; border: 2px solid var(--divider); border-top-color: var(--accent); border-radius: 50%; animation: qfm-spin 0.8s linear infinite; }
@keyframes qfm-spin { to { transform: rotate(360deg); } }

/* ───── Notification item ───── */
.qf-m-notif {
  display: grid; grid-template-columns: auto 1fr;
  gap: 12px; padding: 12px var(--m-gutter);
  border-left: 3px solid transparent;
}
.qf-m-notif--unread { background: rgba(139,92,246,0.06); border-left-color: var(--accent); }
.qf-m-notif__avatar { grid-row: span 2; }
.qf-m-notif__head { display: flex; align-items: baseline; gap: var(--s-2); }
.qf-m-notif__actor { font: 600 var(--fs-14) var(--font-sans); color: var(--text-strong); }
.qf-m-notif__verb { font-size: var(--fs-14); color: var(--text-secondary); }
.qf-m-notif__time { margin-left: auto; font-size: var(--fs-11); color: var(--text-muted); }
.qf-m-notif__preview { font-size: var(--fs-13); color: var(--text-muted); margin-top: 2px; line-height: var(--lh-snug); }

/* ───── Responsive: hide desktop memberlist on narrow ───── */
@media (max-width: 768px) {
  .qf-memberlist { display: none; }
}


/* ═══════════════════════════════════════════════════════════════
   Task 050 — Slack/Discord-level upgrade: new component blocks.
   Authored against the 050 token vocabulary. qf-* desktop / qf-m-* mobile.
   ═══════════════════════════════════════════════════════════════ */


/* ───── OverlappingPanels: 3-panel mobile shell ─────
 * 모바일 핵심 동선 골격. left(서버레일+채널) / center(채팅) / right(멤버).
 * 기본은 center만 보이고, left/right 는 화면 밖으로 translate.
 * 상태 수식자(.qf-m-panels--show-*)가 각 패널의 transform 을 제어한다.
 * JS 가 pointer 이벤트로 --dragging/--snapping 토글:
 *   - 드래그 중 --dragging → transition:none, 손가락 위치를 transform 으로 직접 추종.
 *   - 손 뗄 때 --snapping → transition 복원 후 목표 상태(--show-*)로 스냅.
 *   - fling 판정: |vx| > 500px/s 이면 거리와 무관하게 진행 방향으로 스냅. */
.qf-m-panels {
  position: fixed; inset: 0;
  overflow: hidden;
  touch-action: pan-y;            /* 세로 스크롤은 패널 내부에 양보 */
  will-change: transform;
}

.qf-m-panel-left,
.qf-m-panel-center,
.qf-m-panel-right {
  transition: transform var(--m-panel-dur) var(--m-panel-ease);
  will-change: transform;
}

.qf-m-panel-left {
  position: absolute; top: 0; bottom: 0; left: 0;
  width: var(--w-drawer-left);
  transform: translateX(-100%);  /* 기본 숨김 */
  background: var(--bg-panel);
  z-index: var(--z-drawer);
}
.qf-m-panel-center {
  position: absolute; inset: 0;
  background: var(--bg-chat);
  z-index: 1;
}
.qf-m-panel-right {
  position: absolute; top: 0; bottom: 0; right: 0;
  width: var(--w-memberlist);
  transform: translateX(100%);   /* 기본 숨김 */
  background: var(--bg-panel);
  z-index: var(--z-drawer);
}

/* 좌 드로어 열림: left 가 제자리로, center 가 left 폭만큼 우측 이동 */
.qf-m-panels--show-left .qf-m-panel-left   { transform: translateX(0); }
.qf-m-panels--show-left .qf-m-panel-center { transform: translateX(var(--w-drawer-left)); }

/* 우 드로어 열림(대칭): right 가 제자리로, center 가 left 로 이동 */
.qf-m-panels--show-right .qf-m-panel-right  { transform: translateX(0); }
.qf-m-panels--show-right .qf-m-panel-center { transform: translateX(calc(-1 * var(--w-memberlist))); }

/* --show-center 는 기본 상태와 동일(명시용 수식자, 추가 규칙 불요) */

/* 드래그 중: JS 가 손가락을 추종하므로 transition 제거 */
.qf-m-panels--dragging .qf-m-panel-left,
.qf-m-panels--dragging .qf-m-panel-center,
.qf-m-panels--dragging .qf-m-panel-right { transition: none; }

/* 스냅 복원: 손 뗀 뒤 목표 상태로 애니메이션 */
.qf-m-panels--snapping .qf-m-panel-left,
.qf-m-panels--snapping .qf-m-panel-center,
.qf-m-panels--snapping .qf-m-panel-right {
  transition: transform var(--m-panel-dur) var(--m-panel-ease);
}

/* ───── Drawer scrim ─────
 * 드로어가 열렸을 때 center 위에 깔리는 딤. 탭하면 닫기.
 * z-index 는 center(1) 위, drawer(15) 아래. */
.qf-m-drawer-scrim {
  position: absolute; inset: 0;
  background: var(--scrim);   /* neutral night dim — theme-stable, decoupled from mention tint */
  opacity: 0;
  z-index: 2;
  pointer-events: none;
  transition: opacity var(--m-panel-dur) var(--m-panel-ease);
}
.qf-m-panels--show-left .qf-m-drawer-scrim,
.qf-m-panels--show-right .qf-m-drawer-scrim {
  opacity: 1;
  pointer-events: auto;
}

/* ───── Server header (left 패널 채널리스트 상단) ─────
 * 서버명 + 드롭다운 chevron + 우측 액션. 탭하면 서버 메뉴 시트. */
.qf-m-server-header {
  display: flex; align-items: center; gap: var(--s-2);
  height: var(--m-topbar-h); flex-shrink: 0;
  padding: 0 var(--m-gutter);
  background: var(--bg-panel);
  border-bottom: 1px solid var(--divider);
  cursor: pointer;
  border: none; width: 100%;
  color: var(--text);
  font-family: inherit;
}
.qf-m-server-header:active { background: var(--bg-hover); }
.qf-m-server-header__name {
  flex: 1; min-width: 0;
  font: 600 var(--fs-16) var(--font-sans);
  color: var(--text-strong);
  letter-spacing: var(--tracking-tight);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  text-align: left;
}
.qf-m-server-header__chevron {
  flex-shrink: 0;
  color: var(--text-muted);
  font-size: var(--fs-13);
  display: grid; place-items: center;
}
.qf-m-server-header__action {
  flex-shrink: 0;
  width: var(--m-touch); height: var(--m-touch);
  margin-right: calc(-1 * var(--s-3));   /* 히트박스는 44px 유지, 시각 정렬은 gutter 에 */
  display: grid; place-items: center;
  border: none; background: transparent;
  border-radius: var(--r-md);
  color: var(--text-muted);
  font-size: 18px; cursor: pointer;
}
.qf-m-server-header__action:active { background: var(--bg-hover); }


/* ───── Unread divider ("NEW MESSAGES") ───── */
/* 미읽 경계: 양옆 1px 라인 + 중앙 라벨 + 우측 작은 pill. accent=violet. */
.qf-m-unread-divider {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-2) var(--m-gutter);
  color: var(--unread-divider-accent);
}
.qf-m-unread-divider::before,
.qf-m-unread-divider::after {
  content: ''; flex: 1; height: 1px;
  background: var(--unread-divider-accent);
}
.qf-m-unread-divider__label {
  font: 600 var(--fs-11) var(--font-sans);
  text-transform: uppercase; letter-spacing: var(--tracking-caps);
  color: var(--unread-divider-accent);
}
.qf-m-unread-divider__pill {
  flex-shrink: 0;
  padding: 1px var(--s-3); border-radius: var(--r-pill);
  background: var(--badge-unread-bg); color: var(--text-onAccent);  /* a-600 so white text clears AA */
  font: 600 var(--fs-11) var(--font-sans); line-height: var(--lh-tight);
}

/* ───── Jump-to-bottom FAB ───── */
/* 컴포저+탭바 위에 떠 있는 알약형 버튼 + 미읽 배지. */
.qf-m-jump-btn {
  position: absolute; right: var(--m-gutter);
  bottom: calc(var(--m-composer-h) + var(--m-tabbar-h) + var(--s-3) + env(safe-area-inset-bottom));
  min-width: var(--m-touch); height: var(--m-touch);
  padding: 0 var(--s-3); border-radius: var(--r-pill);
  background: var(--bg-elevated); color: var(--text);
  border: 1px solid var(--border); box-shadow: var(--elev-3);
  display: grid; place-items: center;
  font-size: var(--fs-16); cursor: pointer;
  z-index: var(--z-sticky);
}
.qf-m-jump-btn:active { background: var(--bg-hover); }
.qf-m-jump-btn__badge {
  position: absolute; top: 0; right: 0;
  transform: translate(40%, -40%);
  min-width: var(--s-5); height: var(--s-5); padding: 0 var(--s-2);
  background: var(--badge-unread-bg); color: var(--text-onAccent);
  border: 1px solid var(--bg-elevated); border-radius: var(--r-pill);
  font: 600 var(--fs-11) var(--font-sans);
  display: grid; place-items: center; line-height: var(--lh-tight);
}

/* ───── Quick-react toast (double-tap) ───── */
/* 빠른 반응 시 하단 중앙에 스프링 등장하는 버블. */
.qf-m-react-toast {
  position: absolute; left: 50%;
  bottom: calc(var(--m-composer-h) + var(--m-tabbar-h) + var(--s-5) + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-4); border-radius: var(--r-pill);
  background: var(--bg-elevated); color: var(--text-strong);
  border: 1px solid var(--border); box-shadow: var(--elev-3);
  font: 500 var(--fs-13) var(--font-sans); white-space: nowrap;
  z-index: var(--z-toast);
  will-change: transform, opacity;
  animation: qfm-react-toast-in var(--dur-base) var(--ease-spring);
}
.qf-m-react-toast__emoji { font-size: var(--fs-20); line-height: var(--lh-tight); }
@keyframes qfm-react-toast-in {
  0%   { opacity: 0; transform: translateX(-50%) translateY(var(--s-3)) scale(0.9); }
  100% { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
}

/* ───── Image attachment grid (1~4+) ───── */
/* 메시지 내 다중 이미지: aspect 타일 + 4+ 시 마지막 타일 "+N" 오버레이. */
.qf-m-img-grid {
  display: grid; gap: var(--s-1);
  grid-template-columns: 1fr 1fr;
  border-radius: var(--r-md); overflow: hidden;
  max-width: 280px;
}
.qf-m-img-grid--1 { grid-template-columns: 1fr; }
.qf-m-img-grid--4 { grid-template-columns: 1fr 1fr; }   /* 2×2 — explicit so the 4-up case is documented */
.qf-m-img-grid--3 .qf-m-img-grid__tile:first-child { grid-row: span 2; }
.qf-m-img-grid__tile {
  position: relative; aspect-ratio: 1;
  background: var(--bg-panel); overflow: hidden;
}
.qf-m-img-grid--1 .qf-m-img-grid__tile { aspect-ratio: 4 / 3; }
.qf-m-img-grid__tile img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.qf-m-img-grid__more {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  background: var(--bg-floating);
  color: var(--text-strong);
  font: 600 var(--fs-20) var(--font-sans);
}

/* ───── Composer keyboard accessory bar ───── */
/* 컴포저 위 1탭 행: 이모지/포맷/멘션/@/카메라. 가로 스크롤, 44 히트. */
.qf-m-composer__accessory {
  display: flex; align-items: center; gap: var(--s-1);
  height: var(--m-touch); padding: 0 var(--s-2);
  background: var(--bg-chat);
  border-top: 1px solid var(--divider);
  overflow-x: auto; -webkit-overflow-scrolling: touch;
}
.qf-m-composer__accessory::-webkit-scrollbar { display: none; }
.qf-m-composer__accessory-btn {
  width: var(--m-touch); height: var(--m-touch); flex-shrink: 0;
  display: grid; place-items: center;
  background: transparent; border: none; border-radius: var(--r-md);
  color: var(--text-secondary); cursor: pointer;
  font-size: var(--fs-20);
}
.qf-m-composer__accessory-btn:active { background: var(--bg-hover); color: var(--text); }


/* ───── Mobile IA: Home quick tiles ───── */
/* Home 상단 퀵타일 가로 행 (Catch Up / Threads / Mentions / Saved).
   가로 스크롤 기본, 좁은 폭에서 grid로 떨어지지 않도록 flex + 고정 폭 카드. */
.qf-m-tile-row {
  display: flex; gap: var(--s-3);
  padding: var(--s-4) var(--m-gutter);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: x proximity;
}
.qf-m-tile-row::-webkit-scrollbar { display: none; }

.qf-m-tile {
  flex: 0 0 auto;
  width: 116px; min-height: var(--m-tile-h);
  scroll-snap-align: start;
  display: flex; flex-direction: column; justify-content: space-between;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  background: var(--bg-panel);
  border: 1px solid transparent;
  border-radius: var(--r-lg);
  color: var(--text-secondary);
  text-align: left; cursor: pointer;
  font-family: inherit;
}
.qf-m-tile:active { background: var(--bg-hover); }
.qf-m-tile__icon {
  width: var(--s-7); height: var(--s-7);
  display: grid; place-items: center;
  color: var(--text-muted);
  font-size: var(--fs-20); line-height: 1;
}
.qf-m-tile__label {
  font: 600 var(--fs-13) var(--font-sans);
  color: var(--text-strong);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.qf-m-tile__count {
  font: 500 var(--fs-11) var(--font-sans);
  color: var(--text-muted);
}
/* 미읽 강조: accent 보더 + accent 아이콘 + accent 카운트 */
.qf-m-tile--unread {
  border-color: var(--accent-subtle);
}
.qf-m-tile--unread .qf-m-tile__icon { color: var(--accent); }
.qf-m-tile--unread .qf-m-tile__count {
  color: var(--text-onAccent);
  background: var(--badge-mention-bg);
  align-self: flex-start;
  min-width: var(--s-5); padding: 0 var(--s-2);
  border-radius: var(--r-pill);
  text-align: center; line-height: var(--s-5);
}

/* ───── Mobile IA: Filter chip bar ───── */
/* Activity/검색 상단 필터 칩 행. 가로 스크롤, scrollbar 숨김. */
.qf-m-filter-bar {
  display: flex; gap: var(--s-2);
  padding: var(--s-3) var(--m-gutter);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.qf-m-filter-bar::-webkit-scrollbar { display: none; }

.qf-m-filter-chip {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: var(--s-2);
  min-height: var(--m-touch);
  padding: var(--s-2) var(--s-4);
  background: var(--bg-panel);
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  color: var(--text-secondary);
  font: 500 var(--fs-13) var(--font-sans);
  cursor: pointer; white-space: nowrap;
}
.qf-m-filter-chip:active { background: var(--bg-hover); }
.qf-m-filter-chip__count { color: var(--text-muted); }
/* 선택 상태: accent-subtle 배경 + accent 텍스트 */
.qf-m-filter-chip[aria-selected="true"] {
  background: var(--accent-subtle);
  border-color: var(--accent);
  color: var(--accent);
}
.qf-m-filter-chip[aria-selected="true"] .qf-m-filter-chip__count { color: var(--accent); }
/* 솔리드 변형: accent 배경 + onAccent 텍스트 (강조 칩) */
.qf-m-filter-chip--solid[aria-selected="true"] {
  background: var(--a-600);          /* darker violet so white label clears AA (5.52:1) */
  border-color: var(--a-600);
  color: var(--text-onAccent);
}
.qf-m-filter-chip--solid[aria-selected="true"] .qf-m-filter-chip__count { color: var(--text-onAccent); }

/* ───── Mobile IA: You (profile) tab ───── */
/* 프로필 탭 헤더: 아바타 xl + 이름 + 상태 + 핸들 */
.qf-m-you-header {
  display: flex; align-items: center; gap: var(--s-4);
  padding: var(--s-6) var(--m-gutter);
}
.qf-m-you-header__meta { min-width: 0; }
.qf-m-you-header__name {
  font: 600 var(--fs-20) var(--font-sans);
  color: var(--text-strong);
  letter-spacing: var(--tracking-tight);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.qf-m-you-header__handle {
  font: 400 var(--fs-13) var(--font-sans);
  color: var(--text-muted);
  margin-top: var(--s-1);
}
.qf-m-you-header__state {
  display: inline-flex; align-items: center; gap: var(--s-2);
  margin-top: var(--s-2);
  font: 500 var(--fs-13) var(--font-sans);
  color: var(--text-secondary);
}
.qf-m-you-header__state::before {
  content: ''; flex: 0 0 auto;
  width: var(--sz-status-dot); height: var(--sz-status-dot);
  border-radius: var(--r-pill);
  background: var(--status-online);
}
.qf-m-you-header--idle .qf-m-you-header__state::before    { background: var(--status-idle); }
.qf-m-you-header--dnd .qf-m-you-header__state::before     { background: var(--status-dnd); }
.qf-m-you-header--offline .qf-m-you-header__state::before { background: var(--status-offline); }

/* 상태 설정 행 (탭하면 상태 시트 오픈) */
.qf-m-you-status {
  display: flex; align-items: center; gap: var(--s-3);
  min-height: var(--m-touch);
  margin: 0 var(--m-gutter) var(--s-4);
  padding: var(--s-3) var(--s-4);
  background: var(--bg-panel);
  border-radius: var(--r-lg);
  color: var(--text); cursor: pointer;
  font: 500 var(--fs-14) var(--font-sans);
}
.qf-m-you-status:active { background: var(--bg-hover); }
.qf-m-you-status__icon {
  width: var(--s-6); display: grid; place-items: center;
  color: var(--text-muted); font-size: var(--fs-18);
}
.qf-m-you-status__suffix { margin-left: auto; color: var(--text-muted); }

/* 설정 그룹 컨테이너 카드 — 내부 qf-m-row 재사용, 행 사이 divider */
.qf-m-you-section { margin: 0 var(--m-gutter) var(--s-5); }
.qf-m-you-card {
  background: var(--bg-panel);
  border-radius: var(--r-lg);
  overflow: hidden;
}
/* 카드 내부 행 구분선 (좌측 gutter 들여쓰기) */
.qf-m-you-card .qf-m-row + .qf-m-row::before { left: var(--m-gutter); }
.qf-m-you-card .qf-m-row:active { background: var(--bg-hover); }

/* ───── Mobile IA: Thread inbox ───── */
/* 스레드 인박스 리스트 아이템: 채널 컨텍스트 + 마지막 답글 미리보기
   + 미읽 카운트 + 참여 아바타 스택. 64px row. */
.qf-m-thread-inbox__item {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: var(--s-3);
  align-items: center;
  min-height: var(--m-dm-row-h);
  padding: var(--s-3) var(--m-gutter);
  background: transparent; border: none;
  color: var(--text); font-family: inherit;
  cursor: pointer; position: relative;
}
.qf-m-thread-inbox__item:active { background: var(--bg-hover); }
.qf-m-thread-inbox__item + .qf-m-thread-inbox__item::before {
  content: ''; position: absolute; top: 0; left: var(--m-gutter); right: 0;
  height: 1px; background: var(--divider);
}
/* 참여 아바타 스택 */
.qf-m-thread-inbox__avatars { display: inline-flex; flex-direction: row-reverse; }
.qf-m-thread-inbox__avatars > * { margin-left: -8px; box-shadow: 0 0 0 2px var(--bg-chat); border-radius: var(--r-pill); }
.qf-m-thread-inbox__avatars > *:last-child { margin-left: 0; }
.qf-m-thread-inbox__body { min-width: 0; }
.qf-m-thread-inbox__context {
  font: 600 var(--fs-13) var(--font-sans);
  color: var(--text-secondary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.qf-m-thread-inbox__preview {
  font: 400 var(--fs-13) var(--font-sans);
  color: var(--text-muted);
  margin-top: var(--s-1);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.qf-m-thread-inbox__count {
  flex: 0 0 auto;
  min-width: var(--s-5); height: var(--s-5);
  padding: 0 var(--s-2);
  display: grid; place-items: center;
  background: var(--badge-unread-bg);
  color: var(--text-onAccent);
  border-radius: var(--r-pill);
  font: 600 var(--fs-11) var(--font-sans);
}
.qf-m-thread-inbox__item--unread .qf-m-thread-inbox__context { color: var(--text-strong); }
.qf-m-thread-inbox__item--unread .qf-m-thread-inbox__preview { color: var(--text); }

/* ───── Mobile IA: Channel browser ───── */
/* 서버 채널 디스커버/브라우즈 리스트 아이템: 채널명 + 설명 + 멤버수 + join 버튼 */
.qf-m-channel-browser__item {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--s-1) var(--s-3);
  align-items: center;
  min-height: var(--m-touch);
  padding: var(--s-3) var(--m-gutter);
  background: transparent; position: relative;
}
.qf-m-channel-browser__item + .qf-m-channel-browser__item::before {
  content: ''; position: absolute; top: 0; left: var(--m-gutter); right: 0;
  height: 1px; background: var(--divider);
}
.qf-m-channel-browser__name {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font: 600 var(--fs-15) var(--font-sans);
  color: var(--text-strong);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.qf-m-channel-browser__name::before {
  content: '#'; color: var(--text-muted); font-weight: 500;
}
.qf-m-channel-browser__desc {
  grid-column: 1;
  font: 400 var(--fs-13) var(--font-sans);
  color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.qf-m-channel-browser__members {
  grid-column: 1;
  font: 500 var(--fs-11) var(--font-sans);
  color: var(--text-muted);
}
/* join 버튼: 우측 2개 행 span, 최소 44px 히트박스 */
.qf-m-channel-browser__join {
  grid-column: 2; grid-row: 1 / span 3;
  align-self: center;
  min-height: var(--m-touch); padding: 0 var(--s-4);
  display: grid; place-items: center;
  background: var(--accent); color: var(--text-onAccent);
  border: none; border-radius: var(--r-md);
  font: 600 var(--fs-13) var(--font-sans);
  cursor: pointer;
}
.qf-m-channel-browser__join:active { background: var(--accent-press); }
.qf-m-channel-browser__join[aria-pressed="true"] {
  background: var(--bg-panel); color: var(--text-secondary);
}

/* ───── Mobile IA: Search FAB ───── */
/* 검색 FAB 변형 — 기존 .qf-m-fab 톤 상속, 아이콘만 검색 (마커 클래스) */
.qf-m-fab--search { /* 톤 동일, 의미 구분용 */ }

/* ───── Mobile IA: density (compact) ───── */
/* 밀도 2단계. compact 오버라이드만 작성 (cozy=기본). */
[data-density="compact"] .qf-m-tile { min-height: var(--s-9); padding: var(--s-2) var(--s-3); }
[data-density="compact"] .qf-m-thread-inbox__item { min-height: var(--s-9); }
[data-density="compact"] .qf-m-channel-browser__item { padding: var(--s-2) var(--m-gutter); }
[data-density="compact"] .qf-m-you-header { padding: var(--s-4) var(--m-gutter); }


/* ───── Emoji drawer (mobile) ───── */
/* ───── Emoji drawer (mobile) — qf-m-sheet 톤 확장, 60vh bottom drawer ───── */
.qf-m-emoji-drawer {
  background: var(--bg-elevated);
  border-radius: var(--m-sheet-r) var(--m-sheet-r) 0 0;
  box-shadow: var(--elev-4);
  height: 60vh; max-height: 60vh;
  display: flex; flex-direction: column;
  overflow: hidden;
  padding-bottom: env(safe-area-inset-bottom);
}
.qf-m-emoji-drawer__grab {
  width: 36px; height: 4px; border-radius: 2px;
  background: var(--n-5);
  margin: var(--s-2) auto var(--s-3);
  flex-shrink: 0;
}
.qf-m-emoji-drawer__search {
  flex-shrink: 0;
  padding: 0 var(--m-gutter) var(--s-3);
}
.qf-m-emoji-drawer__search-field {
  display: flex; align-items: center; gap: var(--s-3);
  height: var(--m-touch); padding: 0 var(--s-4);
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}
.qf-m-emoji-drawer__search-icon {
  width: 18px; height: 18px; flex-shrink: 0;
  display: grid; place-items: center;
  color: var(--text-muted);
}
.qf-m-emoji-drawer__search-icon svg { width: 18px; height: 18px; }
.qf-m-emoji-drawer__search-input {
  flex: 1; min-width: 0;
  background: transparent; border: none;
  color: var(--text);
  font: 400 var(--fs-15) var(--font-sans);
}
.qf-m-emoji-drawer__search-input::placeholder { color: var(--text-muted); }
.qf-m-emoji-drawer__search-input:focus { outline: none; }

/* 카테고리 탭 — 히트박스 >= --m-touch, 내부 아이콘만 작게 */
.qf-m-emoji-drawer__tabs {
  flex-shrink: 0;
  display: flex; align-items: stretch;
  padding: 0 var(--s-3);
  border-bottom: 1px solid var(--divider);
}
.qf-m-emoji-drawer__tab {
  position: relative;
  flex: 1; height: var(--m-touch);
  display: grid; place-items: center;
  background: transparent; border: none;
  color: var(--text-muted); cursor: pointer;
  font-size: 22px; line-height: 1;
}
.qf-m-emoji-drawer__tab[aria-selected="true"] { color: var(--text-strong); }
.qf-m-emoji-drawer__tab[aria-selected="true"]::after {
  content: ''; position: absolute;
  left: var(--s-5); right: var(--s-5); bottom: 0;
  height: 2px; background: var(--accent);
  border-radius: 2px 2px 0 0;
}

/* 8열 grid, __cell 44px 히트 */
.qf-m-emoji-drawer__grid {
  flex: 1; min-height: 0;
  overflow-y: auto;
  padding: var(--s-2) var(--m-gutter) var(--s-4);
}
.qf-m-emoji-drawer__category-label {
  position: sticky; top: 0; z-index: var(--z-sticky);
  padding: var(--s-3) var(--s-1) var(--s-2);
  background: var(--bg-elevated);
  font: 600 var(--fs-11)/var(--lh-tight) var(--font-sans);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--text-muted);
}
.qf-m-emoji-drawer__row {
  display: grid; grid-template-columns: repeat(8, 1fr); gap: var(--s-1);
}
.qf-m-emoji-drawer__cell {
  width: 100%; min-height: var(--m-touch); aspect-ratio: 1;
  display: grid; place-items: center;
  background: transparent; border: none;
  border-radius: var(--r-sm);
  font-size: 26px; line-height: 1;
  cursor: pointer; user-select: none;
}
.qf-m-emoji-drawer__cell:active { background: var(--bg-hover); }


/* ───── Real-app screen variant ─────
   Device-frame mockups keep the fixed --m-statusbar clearance; the real PWA
   uses true safe areas + dynamic viewport height so the shell shrinks when
   the keyboard opens. Apply .qf-m-screen--app in production. */
.qf-m-screen--app {
  padding-top: env(safe-area-inset-top);
}
@supports (height: 100dvh) {
  .qf-m-screen--app { height: 100dvh; }
}
