<script setup lang="ts">
/**
 * 消息中心：C 端产品向收件箱
 * 按日分组时间线 + 氛围头图 + 未读浅底，避免后台表格/芯片堆砌感
 */
import { computed, onMounted, ref, watch } from 'vue';

import { Page, VbenIcon } from '@vben/common-ui';

import { notification } from 'ant-design-vue';
import {
  ArrowRight,
  Bell,
  CheckCheck,
  Inbox,
  Search,
  Sparkles,
  X,
} from 'lucide-vue-next';

import { openNoticeAction } from '#/views/notice/action/bus';
import {
  resolveNoticeColor,
  resolveNoticeGlow,
} from '#/views/notice/action/types';
import {
  getNoticeListApi,
  readAllApi,
  readApi,
} from '#/views/notice/api';
import { getAdminNoticeTypeOption } from '#/views/system/admin-notice-type/api';

defineOptions({ name: 'NoticeList' });

const messages = ref<any[]>([]);
const loading = ref(false);
const currentPage = ref(1);
const pageSize = ref(20);
const total = ref(0);
const totalPages = ref(1);
const unreadTotal = ref(0);
const filterTypeCode = ref('');
const filterUnreadOnly = ref(false);
const searchQuery = ref('');
const searchOpen = ref(false);
const typeOptions = ref<
  { label: string; value: string; icon?: string; color?: string }[]
>([]);

const segments = computed(() => {
  const list = [
    { key: 'all', label: '全部' },
    { key: 'unread', label: '未读' },
  ];
  // 只露出高频类型，避免一排后台筛选芯片
  const prefer = [
    'withdraw_audit',
    'warehouse_product_audit',
    'period_report',
    'vip',
    'system',
    'broadcast',
  ];
  const ranked = [...typeOptions.value].sort((a, b) => {
    const ai = prefer.indexOf(a.value);
    const bi = prefer.indexOf(b.value);
    return (ai === -1 ? 99 : ai) - (bi === -1 ? 99 : bi);
  });
  for (const t of ranked.slice(0, 5)) {
    list.push({ key: t.value, label: t.label });
  }
  return list;
});

const activeSegment = computed(() => {
  if (filterUnreadOnly.value) return 'unread';
  return filterTypeCode.value || 'all';
});

function selectSegment(key: string) {
  if (key === 'unread') {
    filterUnreadOnly.value = true;
    filterTypeCode.value = '';
    return;
  }
  filterUnreadOnly.value = false;
  filterTypeCode.value = key === 'all' ? '' : key;
}

/** 解析时间戳或已格式化字符串 → Date */
function toDate(raw: string | number): Date | null {
  if (raw == null || raw === '') return null;
  if (typeof raw === 'number') {
    const ms = raw < 1e12 ? raw * 1000 : raw;
    const d = new Date(ms);
    return Number.isNaN(d.getTime()) ? null : d;
  }
  const s = String(raw).trim();
  if (/^\d+$/.test(s)) {
    const n = Number(s);
    const ms = n < 1e12 ? n * 1000 : n;
    const d = new Date(ms);
    return Number.isNaN(d.getTime()) ? null : d;
  }
  const d = new Date(s.replace(/-/g, '/'));
  return Number.isNaN(d.getTime()) ? null : d;
}

function startOfDay(d: Date) {
  return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
}

function groupLabel(d: Date): string {
  const today = startOfDay(new Date());
  const day = startOfDay(d);
  const diff = Math.round((today - day) / 86400000);
  if (diff === 0) return '今天';
  if (diff === 1) return '昨天';
  if (diff > 1 && diff < 7) return '近 7 天';
  return '更早';
}

function formatClock(d: Date) {
  const hh = String(d.getHours()).padStart(2, '0');
  const mm = String(d.getMinutes()).padStart(2, '0');
  return `${hh}:${mm}`;
}

function formatDay(d: Date) {
  return `${d.getMonth() + 1}月${d.getDate()}日`;
}

/** 按日分组：今天 / 昨天 / 近7天 / 更早（保持 messages 引用，便于就地改已读） */
const grouped = computed(() => {
  const order = ['今天', '昨天', '近 7 天', '更早'];
  const map = new Map<string, any[]>();
  for (const item of messages.value) {
    const d = toDate(item.date || item.created_at);
    const label = d ? groupLabel(d) : '更早';
    if (!map.has(label)) map.set(label, []);
    // 就地挂展示字段，不 spread 成新对象，否则点开改 status 不会回写列表
    item._date = d;
    item._clock = d ? formatClock(d) : '';
    item._day = d ? formatDay(d) : '';
    map.get(label)!.push(item);
  }
  return order
    .filter((k) => map.has(k))
    .map((k) => ({ label: k, items: map.get(k)! }));
});

const heroSubtitle = computed(() => {
  if (unreadTotal.value > 0) {
    return `有 ${unreadTotal.value} 条事需要你留意`;
  }
  return '所有消息都已读完';
});

async function loadTypes() {
  try {
    typeOptions.value = (await getAdminNoticeTypeOption()) || [];
  } catch {
    typeOptions.value = [];
  }
}

async function getNoticeList(append = false) {
  loading.value = true;
  try {
    const res = await getNoticeListApi({
      page: currentPage.value,
      pageSize: pageSize.value,
      type_code: filterTypeCode.value || undefined,
      status: filterUnreadOnly.value ? 0 : undefined,
      title: searchQuery.value || undefined,
    });
    const items = (res.items || []).map((item: any) => ({
      ...item,
      date: item.created_at,
    }));
    messages.value = append ? [...messages.value, ...items] : items;
    total.value = res.total || 0;
    totalPages.value =
      res.page_count || Math.ceil(total.value / pageSize.value) || 1;
    unreadTotal.value = Number(res.unread_total ?? unreadTotal.value);
  } catch (error) {
    console.error('获取消息列表失败:', error);
  } finally {
    loading.value = false;
  }
}

watch([filterTypeCode, filterUnreadOnly, searchQuery], () => {
  currentPage.value = 1;
  getNoticeList(false);
});

onMounted(async () => {
  await loadTypes();
  getNoticeList(false);
});

function colorOf(item: any) {
  return resolveNoticeColor(item.type_color);
}

/** 重要未读微光：跟消息类型字典 color 走 */
function glowOf(item: any) {
  return resolveNoticeGlow(item.type_color);
}

/**
 * 点击消息：立刻取消未读态，再打开详情/动作（已读接口不挡 UI）
 */
async function handleItemClick(item: any) {
  if (Number(item.status) === 0) {
    item.status = 1;
    unreadTotal.value = Math.max(0, unreadTotal.value - 1);
  }
  await openNoticeAction(item);
}

async function markAsRead(event: Event, item: any) {
  event.stopPropagation();
  try {
    await readApi(item.id);
    item.status = 1;
    unreadTotal.value = Math.max(0, unreadTotal.value - 1);
  } catch (error) {
    console.error('标记已读失败:', error);
  }
}

async function markAllAsRead() {
  try {
    await readAllApi();
    messages.value.forEach((msg) => {
      msg.status = 1;
    });
    unreadTotal.value = 0;
    notification.success({ message: '已全部读完', duration: 1.5 });
  } catch (error) {
    console.error('标记全部已读失败:', error);
  }
}

async function loadMore() {
  if (loading.value || currentPage.value >= totalPages.value) return;
  currentPage.value += 1;
  await getNoticeList(true);
}
</script>

<template>
  <Page auto-content-height content-class="!p-0">
    <div class="inbox">
      <div class="inbox-atmosphere" aria-hidden="true"></div>

      <header class="inbox-hero">
        <div class="hero-copy">
          <p class="hero-kicker">
            <Sparkles class="size-3.5" />
            Inbox
          </p>
          <h1 class="hero-title">消息</h1>
          <p class="hero-sub">{{ heroSubtitle }}</p>
        </div>
        <div class="hero-actions">
          <button
            type="button"
            class="ghost-btn"
            :class="{ on: searchOpen }"
            aria-label="搜索"
            @click="searchOpen = !searchOpen"
          >
            <Search class="size-4" />
          </button>
          <button
            type="button"
            class="ghost-btn"
            :disabled="unreadTotal <= 0"
            @click="markAllAsRead"
          >
            <CheckCheck class="size-4" />
            <span>全部已读</span>
          </button>
        </div>
      </header>

      <div v-if="searchOpen" class="search-bar">
        <Search class="search-ico" />
        <input
          v-model="searchQuery"
          autofocus
          type="search"
          placeholder="搜标题、摘要…"
        />
        <button
          v-if="searchQuery"
          type="button"
          class="clear-btn"
          @click="searchQuery = ''"
        >
          <X class="size-3.5" />
        </button>
      </div>

      <nav class="seg" aria-label="消息筛选">
        <button
          v-for="s in segments"
          :key="s.key"
          type="button"
          class="seg-item"
          :class="{ active: activeSegment === s.key }"
          @click="selectSegment(s.key)"
        >
          {{ s.label }}
          <span
            v-if="s.key === 'unread' && unreadTotal > 0"
            class="seg-badge"
          >{{ unreadTotal > 99 ? '99+' : unreadTotal }}</span>
        </button>
      </nav>

      <div class="timeline">
        <!-- 首次加载骨架屏：占位形状与真实消息行一致，避免布局跳动 -->
        <div v-if="loading && messages.length === 0" class="skeleton-list" aria-label="加载中">
          <div v-for="n in 5" :key="n" class="skeleton-row" :style="{ '--i': n - 1 }">
            <div class="sk sk-avatar"></div>
            <div class="sk-lines">
              <div class="sk sk-title"></div>
              <div class="sk sk-text"></div>
            </div>
          </div>
        </div>

        <div v-else-if="messages.length === 0" class="empty-state">
          <div class="empty-ico">
            <Inbox class="size-7" />
          </div>
          <p class="empty-title">这里很安静</p>
          <p class="empty-desc">新的审核、报表和系统提醒会出现在这里</p>
        </div>

        <section
          v-for="group in grouped"
          :key="group.label"
          class="day-group"
        >
          <h3 class="day-label">{{ group.label }}</h3>
          <ul class="day-list">
            <li
              v-for="(item, idx) in group.items"
              :key="item.id"
              class="msg"
              :class="{
                unread: Number(item.status) === 0,
                actionable: !!item.action_type,
                priority: Number(item.priority) === 1,
              }"
              :style="{
                '--i': idx,
                '--notice-glow': glowOf(item),
              }"
              role="button"
              tabindex="0"
              @click="handleItemClick(item)"
              @keydown.enter="handleItemClick(item)"
            >
              <div
                class="avatar"
                :class="[colorOf(item).bg, colorOf(item).text]"
              >
                <img
                  v-if="item.cover_url"
                  :src="item.cover_url"
                  alt=""
                  class="avatar-img"
                />
                <template v-else>
                  <VbenIcon
                    v-if="item.type_icon"
                    :icon="item.type_icon"
                    class="size-5"
                  />
                  <Bell v-else class="size-5" />
                </template>
              </div>

              <div class="content">
                <div class="row-top">
                  <h4 class="title">{{ item.title }}</h4>
                  <time class="time">{{
                    group.label === '今天' || group.label === '昨天'
                      ? item._clock
                      : item._day
                  }}</time>
                </div>
                <p class="preview">{{ item.detail || item.type_name || '查看详情' }}</p>
                <div class="meta">
                  <span
                    v-if="item.type_name"
                    class="type-tag"
                    :class="colorOf(item).text"
                  >{{ item.type_name }}</span>
                  <span v-if="Number(item.priority) === 1" class="prio-tag"
                    >重要</span
                  >
                </div>
              </div>

              <div class="aside">
                <span
                  v-if="Number(item.status) === 0"
                  class="dot"
                  aria-label="未读"
                ></span>
                <button
                  v-if="Number(item.status) === 0"
                  type="button"
                  class="mark"
                  @click="markAsRead($event, item)"
                >
                  标已读
                </button>
                <span v-if="item.action_type" class="go">
                  去处理
                  <ArrowRight class="size-3.5" />
                </span>
              </div>
            </li>
          </ul>
        </section>

        <div v-if="currentPage < totalPages" class="more-wrap">
          <button type="button" class="more-btn" @click="loadMore">
            {{ loading ? '加载中…' : '加载更早的消息' }}
          </button>
        </div>
      </div>
    </div>
  </Page>
</template>

<style scoped>
.inbox {
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  position: relative;
  min-height: 100%;
  padding: 28px 20px 48px;
  overflow: hidden;
}

.inbox-atmosphere {
  pointer-events: none;
  position: absolute;
  inset: 0 0 auto 0;
  height: 280px;
  background:
    radial-gradient(
      60% 80% at 12% 0%,
      hsl(var(--primary) / 0.16),
      transparent 70%
    ),
    radial-gradient(
      45% 60% at 88% 10%,
      hsl(var(--primary) / 0.08),
      transparent 65%
    );
  mask-image: linear-gradient(to bottom, #000 40%, transparent);
}

.inbox-hero,
.search-bar,
.seg,
.timeline {
  position: relative;
  z-index: 1;
  max-width: 760px;
  margin-left: auto;
  margin-right: auto;
}

.inbox-hero {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 20px;
}

.hero-kicker {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 8px;
  color: hsl(var(--primary));
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.hero-title {
  margin: 0;
  font-size: clamp(34px, 5vw, 44px);
  font-weight: 700;
  letter-spacing: -0.04em;
  line-height: 1.05;
  color: hsl(var(--foreground));
}

.hero-sub {
  margin: 10px 0 0;
  font-size: 15px;
  color: hsl(var(--muted-foreground));
  letter-spacing: -0.01em;
}

.hero-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-bottom: 4px;
}

.ghost-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 12px;
  border: 1px solid hsl(var(--border) / 0.8);
  border-radius: 999px;
  background: hsl(var(--card, var(--background)) / 0.7);
  backdrop-filter: blur(8px);
  color: hsl(var(--foreground));
  font-size: 13px;
  cursor: pointer;
  transition:
    background 0.2s var(--ease),
    border-color 0.2s var(--ease),
    transform 0.2s var(--ease);
}

.ghost-btn:hover:not(:disabled) {
  background: hsl(var(--muted) / 0.45);
  border-color: hsl(var(--foreground) / 0.18);
}

.ghost-btn.on {
  border-color: hsl(var(--primary) / 0.4);
  background: hsl(var(--primary) / 0.1);
  color: hsl(var(--primary));
}

.ghost-btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.search-bar {
  position: relative;
  margin-bottom: 14px;
  animation: rise 0.28s var(--ease);
}

.search-bar input {
  width: 100%;
  height: 44px;
  padding: 0 40px 0 42px;
  border: 1px solid hsl(var(--border) / 0.7);
  border-radius: 14px;
  background: hsl(var(--card, var(--background)) / 0.85);
  color: hsl(var(--foreground));
  font-size: 14px;
  outline: none;
  box-shadow: 0 10px 30px hsl(var(--foreground) / 0.03);
}

.search-bar input:focus {
  border-color: hsl(var(--primary) / 0.45);
  box-shadow: 0 0 0 3px hsl(var(--primary) / 0.12);
}

.search-ico {
  position: absolute;
  left: 14px;
  top: 50%;
  width: 16px;
  height: 16px;
  transform: translateY(-50%);
  color: hsl(var(--muted-foreground));
}

.clear-btn {
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border: none;
  border-radius: 50%;
  background: hsl(var(--muted) / 0.7);
  color: hsl(var(--muted-foreground));
  cursor: pointer;
}

.seg {
  display: flex;
  gap: 4px;
  overflow-x: auto;
  margin-bottom: 22px;
  padding: 4px;
  border-radius: 14px;
  background: hsl(var(--muted) / 0.35);
  scrollbar-width: none;
}

.seg::-webkit-scrollbar {
  display: none;
}

.seg-item {
  position: relative;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 34px;
  padding: 0 14px;
  border: none;
  border-radius: 10px;
  background: transparent;
  color: hsl(var(--muted-foreground));
  font-size: 13px;
  cursor: pointer;
  transition:
    background 0.2s var(--ease),
    color 0.2s var(--ease),
    box-shadow 0.2s var(--ease);
}

.seg-item.active {
  background: hsl(var(--card, var(--background)));
  color: hsl(var(--foreground));
  font-weight: 600;
  box-shadow: 0 1px 2px hsl(var(--foreground) / 0.06);
}

.seg-badge {
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 9px;
  background: hsl(var(--primary));
  color: hsl(var(--primary-foreground));
  font-size: 11px;
  font-weight: 650;
  line-height: 18px;
  text-align: center;
}

.day-group + .day-group {
  margin-top: 28px;
}

/* 日期分组标题：滚动时粘性吸顶，毛玻璃底保证盖在消息行上仍可读 */
.day-label {
  position: sticky;
  top: 0;
  z-index: 2;
  margin: 0 0 10px;
  padding: 6px 4px;
  font-size: 12px;
  font-weight: 650;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: hsl(var(--muted-foreground));
  background: hsl(var(--background) / 0.82);
  backdrop-filter: blur(8px);
}

.day-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.msg {
  --i: 0;
  display: grid;
  grid-template-columns: 48px minmax(0, 1fr) auto;
  gap: 14px;
  align-items: center;
  padding: 14px 14px 14px 12px;
  border: 1px solid transparent;
  border-radius: 18px;
  background: hsl(var(--card, var(--background)) / 0.55);
  cursor: pointer;
  outline: none;
  animation: rise 0.45s var(--ease) both;
  animation-delay: calc(var(--i) * 35ms);
  transition:
    background 0.2s var(--ease),
    border-color 0.2s var(--ease),
    transform 0.2s var(--ease);
}

.msg:hover,
.msg:focus-visible {
  background: hsl(var(--card, var(--background)));
  border-color: hsl(var(--border) / 0.9);
  transform: translateY(-1px);
}

.msg:active {
  transform: translateY(0);
}

.msg.unread {
  background: hsl(var(--primary) / 0.06);
  border-color: hsl(var(--primary) / 0.12);
}

.msg.unread:hover,
.msg.unread:focus-visible {
  background: hsl(var(--primary) / 0.09);
  border-color: hsl(var(--primary) / 0.22);
}

/* 重要且未读：按消息类型色发光；已读后取消 */
.msg.priority.unread {
  border-color: hsl(var(--notice-glow, 38 92% 50%) / 0.45);
  box-shadow:
    0 0 0 1px hsl(var(--notice-glow, 38 92% 50%) / 0.18),
    0 0 18px hsl(var(--notice-glow, 38 92% 50%) / 0.22);
}

.msg.priority.unread:hover,
.msg.priority.unread:focus-visible {
  border-color: hsl(var(--notice-glow, 38 92% 50%) / 0.6);
  box-shadow:
    0 0 0 1px hsl(var(--notice-glow, 38 92% 50%) / 0.28),
    0 0 22px hsl(var(--notice-glow, 38 92% 50%) / 0.3);
}

.avatar {
  width: 48px;
  height: 48px;
  border-radius: 16px;
  display: grid;
  place-items: center;
  overflow: hidden;
  box-shadow: inset 0 0 0 1px hsl(var(--foreground) / 0.04);
}

.avatar-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.content {
  min-width: 0;
}

.row-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.title {
  margin: 0;
  min-width: 0;
  flex: 1;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.015em;
  line-height: 1.35;
  color: hsl(var(--foreground));
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.msg.unread .title {
  font-weight: 680;
}

.time {
  flex-shrink: 0;
  font-size: 12px;
  color: hsl(var(--muted-foreground));
  font-variant-numeric: tabular-nums;
}

.preview {
  margin: 5px 0 0;
  font-size: 13px;
  line-height: 1.45;
  color: hsl(var(--muted-foreground));
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}

.type-tag,
.prio-tag {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 2px 8px;
  border-radius: 999px;
  background: hsl(var(--muted) / 0.55);
}

.prio-tag {
  color: hsl(var(--warning, 38 92% 40%));
  background: hsl(var(--warning, 38 92% 50%) / 0.12);
}

.aside {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  min-width: 72px;
}

.dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: hsl(var(--primary));
  box-shadow: 0 0 0 4px hsl(var(--primary) / 0.12);
}

.mark {
  border: none;
  background: transparent;
  color: hsl(var(--muted-foreground));
  font-size: 12px;
  padding: 0;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s ease, color 0.15s ease;
}

.msg:hover .mark,
.msg:focus-within .mark {
  opacity: 1;
}

.mark:hover {
  color: hsl(var(--primary));
}

.go {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  height: 28px;
  padding: 0 10px;
  border-radius: 999px;
  background: hsl(var(--primary));
  color: hsl(var(--primary-foreground));
  font-size: 12px;
  font-weight: 600;
  letter-spacing: -0.01em;
  transition: transform 0.2s var(--ease), opacity 0.2s var(--ease);
}

.msg:hover .go {
  transform: translateX(2px);
}

.empty-state {
  display: grid;
  place-items: center;
  gap: 8px;
  padding: 72px 20px;
  text-align: center;
  color: hsl(var(--muted-foreground));
}

.empty-ico {
  width: 64px;
  height: 64px;
  margin-bottom: 8px;
  border-radius: 22px;
  display: grid;
  place-items: center;
  background: hsl(var(--muted) / 0.45);
  color: hsl(var(--foreground) / 0.55);
}

.empty-title {
  margin: 0;
  font-size: 16px;
  font-weight: 650;
  color: hsl(var(--foreground));
}

.empty-desc {
  margin: 0;
  font-size: 13px;
  max-width: 260px;
}

/* ===== 骨架屏：与 .msg 同布局同圆角，闪烁动画走主题 muted 色 ===== */
.skeleton-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.skeleton-row {
  --i: 0;
  display: grid;
  grid-template-columns: 48px minmax(0, 1fr);
  gap: 14px;
  align-items: center;
  padding: 14px 14px 14px 12px;
  border-radius: 18px;
  background: hsl(var(--card, var(--background)) / 0.55);
  animation: rise 0.45s var(--ease) both;
  animation-delay: calc(var(--i) * 50ms);
}

.sk {
  border-radius: 10px;
  background: linear-gradient(
    90deg,
    hsl(var(--muted) / 0.5) 25%,
    hsl(var(--muted) / 0.3) 50%,
    hsl(var(--muted) / 0.5) 75%
  );
  background-size: 200% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
}

.sk-avatar {
  width: 48px;
  height: 48px;
  border-radius: 16px;
}

.sk-lines {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.sk-title {
  width: 45%;
  height: 14px;
}

.sk-text {
  width: 80%;
  height: 12px;
}

@keyframes shimmer {
  0% {
    background-position: 200% 0;
  }
  100% {
    background-position: -200% 0;
  }
}

.more-wrap {
  display: flex;
  justify-content: center;
  margin-top: 24px;
}

.more-btn {
  border: none;
  background: transparent;
  color: hsl(var(--muted-foreground));
  font-size: 13px;
  padding: 10px 16px;
  border-radius: 999px;
  cursor: pointer;
}

.more-btn:hover {
  color: hsl(var(--primary));
  background: hsl(var(--primary) / 0.08);
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .msg,
  .search-bar,
  .skeleton-row,
  .sk {
    animation: none;
  }
  .msg:hover,
  .msg:hover .go {
    transform: none;
  }
}

@media (max-width: 720px) {
  .inbox {
    padding: 18px 12px 36px;
  }

  .inbox-hero {
    flex-direction: column;
    align-items: flex-start;
  }

  .msg {
    grid-template-columns: 44px minmax(0, 1fr);
    grid-template-areas:
      'avatar content'
      'avatar aside';
  }

  .avatar {
    width: 44px;
    height: 44px;
    border-radius: 14px;
    grid-area: avatar;
  }

  .content {
    grid-area: content;
  }

  .aside {
    grid-area: aside;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    min-width: 0;
    margin-top: 4px;
  }

  .mark {
    opacity: 1;
  }

  .ghost-btn span {
    display: none;
  }
}
</style>
