<script setup lang="ts">
// 프로젝트 멤버 — 멤버 목록 + 역할/제거 + 멤버 초대 모달 (백엔드 연동)
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import { useRoute, RouterLink } from 'vue-router'
import AppShell from '@/layouts/AppShell.vue'
import ProjectHeader from '@/components/ProjectHeader.vue'
import ProjectTabs from '@/components/ProjectTabs.vue'
import UserAvatar from '@/components/UserAvatar.vue'
import DropdownSelect from '@/components/DropdownSelect.vue'
import { useProjectStore } from '@/stores/project.store'
import { useMemberStore } from '@/stores/member.store'
import { useUser } from '@/composables/useUser'
import { useDialog } from '@/composables/useDialog'
import type { Project } from '@/mock/relay.mock'
import type { ApiMember } from '@/types/repo'
import type { MemberRoleType } from '@/types/member'

type RoleFilter = 'all' | 'admin' | 'member'

const route = useRoute()
const projectId = computed(() => String(route.params.projectId))
const projectStore = useProjectStore()
const memberStore = useMemberStore()
const dialog = useDialog()

// 프로젝트 헤더 — API 연동
const repo = ref<Project | null>(null)
async function loadProject() {
  try {
    repo.value = await projectStore.fetchOne(projectId.value)
  } catch {
    repo.value = null // 404/403 은 인터셉터가 알림 처리
  }
}

// 멤버 목록 — API 연동
const members = computed(() => memberStore.members)
const loading = computed(() => memberStore.loading)
async function loadMembers() {
  try {
    await memberStore.fetchList(projectId.value)
  } catch {
    // 인터셉터가 알림 처리
  }
}

watch(
  projectId,
  () => {
    loadProject()
    loadMembers()
  },
  { immediate: true },
)

// 현재 사용자가 이 프로젝트의 관리자인지(초대·역할변경·제거 권한)
const isAdmin = computed(() =>
  members.value.some((m) => m.isYou && m.roleType === 'admin'),
)

// 헤더 아바타용 멤버(전체 로스터)
const headerMembers = computed(() => members.value.map((m) => m.user))

const keyword = ref('')
const filter = ref<RoleFilter>('all')

const counts = computed(() => ({
  all: members.value.length,
  admin: members.value.filter((m) => m.roleType === 'admin').length,
  member: members.value.filter((m) => m.roleType === 'member').length,
}))

const filteredMembers = computed(() =>
  members.value.filter((m) => {
    const matchRole = filter.value === 'all' || m.roleType === filter.value
    const kw = keyword.value.trim()
    const matchKeyword = !kw || m.user.name.includes(kw) || m.email.includes(kw)
    return matchRole && matchKeyword
  }),
)

// --- 역할 변경 드롭다운 ---
const openRoleFor = ref<string | null>(null)
const roleBusy = ref(false)
function toggleRoleMenu(userId: string) {
  openRoleFor.value = openRoleFor.value === userId ? null : userId
}
async function changeRole(userId: string, roleType: MemberRoleType) {
  openRoleFor.value = null
  roleBusy.value = true
  try {
    await memberStore.update(projectId.value, userId, { roleType })
  } catch {
    // 인터셉터가 알림 처리
  } finally {
    roleBusy.value = false
  }
}

// --- 멤버 제거 ---
async function removeMember(userId: string, name: string) {
  const ok = await dialog.confirm(`${name} 님을 프로젝트에서 제거할까요?`, {
    variant: 'danger',
    confirmText: '제거',
  })
  if (!ok) return
  try {
    await memberStore.remove(projectId.value, userId)
  } catch {
    // 인터셉터가 알림 처리
  }
}

// --- 초대 모달 ---
const showInvite = ref(false)
const inviteEmail = ref('')
const inviteRole = ref<MemberRoleType>('member')
const ROLE_OPTIONS: { value: MemberRoleType; label: string }[] = [
  { value: 'member', label: '멤버' },
  { value: 'admin', label: '관리자' },
]
const inviteBusy = ref(false)

// --- 사용자 자동완성(타입어헤드) ---
const userApi = useUser()
const userResults = ref<ApiMember[]>([])
const usersLoading = ref(false)
const showUserDropdown = ref(false)
let userSearchTimer: ReturnType<typeof setTimeout> | null = null

// 현재 프로젝트에 이미 속한 사용자 id (자동완성에서 제외)
const memberIds = computed(() => new Set(members.value.map((m) => m.user.id)))
const userOptions = computed(() =>
  userResults.value.filter((u) => !memberIds.value.has(u.id)),
)

// 서버 검색 실행 — 입력값으로 이메일/이름 부분일치, 비어 있으면 전체
async function runUserSearch() {
  usersLoading.value = true
  try {
    userResults.value = await userApi.search(inviteEmail.value)
  } catch {
    userResults.value = [] // 인터셉터가 알림 처리
  } finally {
    usersLoading.value = false
  }
}
function scheduleUserSearch() {
  if (userSearchTimer) clearTimeout(userSearchTimer)
  userSearchTimer = setTimeout(runUserSearch, 200)
}
function onEmailFocus() {
  showUserDropdown.value = true
  void runUserSearch() // 포커스 시 전체(또는 현재 입력 기준) 목록 표시
}
function onEmailInput() {
  showUserDropdown.value = true
  scheduleUserSearch()
}
function onEmailBlur() {
  // 항목은 @mousedown.prevent 로 선택하지만, 안전하게 약간 지연 후 닫는다
  setTimeout(() => (showUserDropdown.value = false), 120)
}
function selectUser(u: ApiMember) {
  inviteEmail.value = u.email
  showUserDropdown.value = false
}

function openInvite() {
  inviteEmail.value = ''
  inviteRole.value = 'member'
  userResults.value = []
  showUserDropdown.value = false
  showInvite.value = true
}
function closeInvite() {
  showInvite.value = false
  showUserDropdown.value = false
}

const canSubmitInvite = computed(
  () => !inviteBusy.value && inviteEmail.value.trim().length > 0,
)

async function submitInvite() {
  if (!canSubmitInvite.value) return
  inviteBusy.value = true
  try {
    await memberStore.invite(projectId.value, {
      email: inviteEmail.value.trim(),
      roleType: inviteRole.value,
    })
    closeInvite()
  } catch {
    // 인터셉터가 알림 처리(미가입/중복 등 BIZ_001 서버 문구 노출)
  } finally {
    inviteBusy.value = false
  }
}

function onKeydown(e: KeyboardEvent) {
  if (e.key === 'Escape') {
    showInvite.value = false
    openRoleFor.value = null
  }
}
onMounted(() => window.addEventListener('keydown', onKeydown))
onUnmounted(() => window.removeEventListener('keydown', onKeydown))
</script>

<template>
  <AppShell>
    <div
      v-if="repo"
      class="page"
    >
      <div class="crumb">
        <RouterLink
          to="/projects"
          class="lnk"
        >
          프로젝트
        </RouterLink>
        <span class="sep">/</span>
        <RouterLink
          :to="`/projects/${repo.id}`"
          class="lnk"
        >
          {{ repo.name }}
        </RouterLink>
        <span class="sep">/</span>
        <b>멤버</b>
      </div>

      <ProjectHeader
        :repo="repo"
        :meta-text="`멤버 ${counts.all}명`"
        :members="headerMembers"
        :more-count="0"
      />
      <ProjectTabs
        :project-id="repo.id"
        active="members"
        :task-count="repo.totalCount"
        :member-count="counts.all"
        :can-manage="repo.canManage"
      />

      <div class="tab-note">
        <svg
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <circle
            cx="12"
            cy="12"
            r="10"
          /><path d="M12 16v-4M12 8h.01" />
        </svg>
        이 프로젝트의 멤버만 업무 담당자로 지정할 수 있습니다. <b>관리자</b>는 업무 지시·멤버 관리가 가능하고, <b>멤버</b>는 담당자로 배정되어 업무를 수행합니다.
      </div>

      <!-- 툴바 -->
      <div class="toolbar">
        <div class="search">
          <svg
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
          >
            <circle
              cx="11"
              cy="11"
              r="7"
            /><path d="m21 21-4-4" />
          </svg>
          <input
            v-model="keyword"
            type="text"
            placeholder="이름·이메일 검색…"
          >
        </div>
        <div class="segmented">
          <button
            :class="{ active: filter === 'all' }"
            @click="filter = 'all'"
          >
            전체 <span class="n">{{ counts.all }}</span>
          </button>
          <button
            :class="{ active: filter === 'admin' }"
            @click="filter = 'admin'"
          >
            관리자 <span class="n">{{ counts.admin }}</span>
          </button>
          <button
            :class="{ active: filter === 'member' }"
            @click="filter = 'member'"
          >
            멤버 <span class="n">{{ counts.member }}</span>
          </button>
        </div>
        <button
          v-if="isAdmin"
          class="btn primary invite"
          type="button"
          @click="openInvite"
        >
          <svg
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2.2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" /><circle
              cx="9"
              cy="7"
              r="4"
            /><path d="M19 8v6M22 11h-6" />
          </svg>
          멤버 초대
        </button>
      </div>

      <!-- 멤버 목록 -->
      <div
        v-if="loading"
        class="state-msg"
      >
        멤버를 불러오는 중…
      </div>
      <div
        v-else-if="filteredMembers.length === 0"
        class="state-msg"
      >
        표시할 멤버가 없습니다.
      </div>
      <div
        v-else
        class="list"
      >
        <div
          v-for="m in filteredMembers"
          :key="m.user.id"
          class="mem-row"
        >
          <span class="mem-av"><UserAvatar
            :url="m.user.avatarUrl"
            :name="m.user.name"
          /></span>
          <div class="mem-info">
            <div class="mem-name">
              {{ m.user.name }} <span
                v-if="m.isYou"
                class="you-pill"
              >나</span>
            </div>
            <div class="mem-sub">
              {{ m.email }}
            </div>
          </div>
          <div
            class="mem-tasks"
            :class="{ zero: m.taskCount === 0 }"
          >
            <b>{{ m.taskCount }}</b>건 담당
          </div>
          <!-- 소유자: 변경 불가 고정 표시 -->
          <div
            v-if="m.owner"
            class="role fixed admin"
          >
            <span class="role-dot" />관리자
            <span class="lock">
              <svg
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
              ><rect
                x="3"
                y="11"
                width="18"
                height="11"
                rx="2"
              /><path d="M7 11V7a5 5 0 0 1 10 0v4" /></svg>
            </span>
          </div>
          <!-- 관리자: 역할 변경 드롭다운 -->
          <div
            v-else-if="isAdmin"
            class="role-wrap"
          >
            <button
              class="role"
              :class="{ admin: m.roleType === 'admin' }"
              type="button"
              :disabled="roleBusy"
              @click.stop="toggleRoleMenu(m.user.id)"
            >
              <span class="role-dot" />{{ m.roleType === 'admin' ? '관리자' : '멤버' }}
              <span class="caret">
                <svg
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  stroke-width="2"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                ><path d="m6 9 6 6 6-6" /></svg>
              </span>
            </button>
            <div
              v-if="openRoleFor === m.user.id"
              class="role-menu"
            >
              <button
                type="button"
                :class="{ on: m.roleType === 'member' }"
                @click="changeRole(m.user.id, 'member')"
              >
                멤버
              </button>
              <button
                type="button"
                :class="{ on: m.roleType === 'admin' }"
                @click="changeRole(m.user.id, 'admin')"
              >
                관리자
              </button>
            </div>
          </div>
          <!-- 일반 멤버 시점: 변경 불가 표시 -->
          <div
            v-else
            class="role readonly"
            :class="{ admin: m.roleType === 'admin' }"
          >
            <span class="role-dot" />{{ m.roleType === 'admin' ? '관리자' : '멤버' }}
          </div>
          <button
            v-if="isAdmin && !m.owner"
            class="mem-remove"
            type="button"
            title="멤버 제거"
            @click="removeMember(m.user.id, m.user.name)"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            ><path d="M18 6 6 18M6 6l12 12" /></svg>
          </button>
        </div>
      </div>
    </div>

    <!-- 멤버 초대 모달 -->
    <Teleport to="body">
      <div
        class="modal-backdrop"
        :class="{ open: showInvite }"
        @click.self="showInvite = false"
      >
        <div
          class="invite-modal"
          role="dialog"
          aria-modal="true"
        >
          <div class="modal-head">
            <div>
              <h2>멤버 초대</h2>
              <div class="mh-sub">
                <b>{{ repo?.name }}</b> 프로젝트에 멤버를 추가합니다.
              </div>
            </div>
            <button
              class="modal-close"
              type="button"
              @click="showInvite = false"
            >
              <svg
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
              ><path d="M18 6 6 18M6 6l12 12" /></svg>
            </button>
          </div>
          <form
            class="modal-body"
            @submit.prevent="submitInvite"
          >
            <div class="m-field uc-field">
              <label class="m-label">초대할 사람 (이메일)</label>
              <input
                v-model="inviteEmail"
                class="m-input"
                type="email"
                placeholder="가입된 사용자의 이메일 입력…"
                autocomplete="off"
                @focus="onEmailFocus"
                @input="onEmailInput"
                @blur="onEmailBlur"
              >
              <!-- 사용자 자동완성 드롭다운 -->
              <div
                v-if="showUserDropdown"
                class="user-dd"
              >
                <div
                  v-if="usersLoading"
                  class="user-dd-msg"
                >
                  불러오는 중…
                </div>
                <template v-else>
                  <button
                    v-for="u in userOptions"
                    :key="u.id"
                    type="button"
                    class="user-dd-item"
                    @mousedown.prevent="selectUser(u)"
                  >
                    <span class="avatar user-av"><UserAvatar
                      :url="u.avatarUrl"
                      :name="u.name"
                    /></span>
                    <span class="user-nm">{{ u.name }}</span>
                    <span class="user-em">{{ u.email }}</span>
                  </button>
                  <div
                    v-if="userOptions.length === 0"
                    class="user-dd-msg"
                  >
                    {{ inviteEmail.trim() ? '일치하는 사용자가 없습니다.' : '초대할 수 있는 사용자가 없습니다.' }}
                  </div>
                </template>
              </div>
            </div>

            <div class="m-field">
              <label class="m-label">역할</label>
              <DropdownSelect
                v-model="inviteRole"
                :options="ROLE_OPTIONS"
                block
                class="role-select"
                aria-label="역할"
              />
            </div>
            <div class="role-hint">
              <b>멤버</b>는 담당자로 배정되어 업무를 수행하고, <b>관리자</b>는 업무 지시와 멤버 관리도 할 수 있습니다.
            </div>
          </form>
          <div class="modal-foot">
            <span class="fnote">이미 가입된 사용자만 초대할 수 있습니다.</span>
            <button
              class="mbtn"
              type="button"
              @click="closeInvite"
            >
              취소
            </button>
            <button
              class="mbtn primary"
              type="button"
              :disabled="!canSubmitInvite"
              @click="submitInvite"
            >
              <svg
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2.2"
                stroke-linecap="round"
                stroke-linejoin="round"
              ><path d="m22 2-7 20-4-9-9-4Z" /><path d="M22 2 11 13" /></svg>
              {{ inviteBusy ? '초대 중…' : '초대 보내기' }}
            </button>
          </div>
        </div>
      </div>
    </Teleport>
  </AppShell>
</template>

<style scoped>
.crumb {
  padding: 1.125rem 0 0.75rem;
}
.tab-note {
  font-size: 0.781rem;
  color: var(--text-3);
  margin: 0.75rem 0 1rem;
  display: flex;
  align-items: center;
  gap: 0.4375rem;
}
.tab-note svg {
  width: 0.875rem;
  height: 0.875rem;
  color: var(--text-3);
  flex-shrink: 0;
}
.tab-note b {
  color: var(--text-2);
  font-weight: 600;
  white-space: nowrap;
}

.toolbar .search {
  width: 15rem;
}
.invite {
  margin-left: auto;
  border: none;
}

/* 멤버 행 */
.mem-row {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  padding: 0.875rem 1.125rem;
  border-top: 1px solid var(--border);
}
.mem-row:first-child {
  border-top: none;
  border-radius: 0.625rem 0.625rem 0 0;
}
.mem-row:last-child {
  border-radius: 0 0 0.625rem 0.625rem;
}
.mem-row:hover {
  background: #fafbfc;
}
.mem-av {
  width: 2.375rem;
  height: 2.375rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 0.875rem;
  font-weight: 700;
  color: #fff;
  flex-shrink: 0;
}
.mem-info {
  flex: 1;
  min-width: 0;
}
.mem-name {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--text);
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.you-pill {
  font-size: 0.656rem;
  font-weight: 700;
  color: var(--accent);
  background: var(--accent-weak);
  border: 1px solid var(--accent-border);
  border-radius: 20px;
  padding: 0.0625rem 0.4375rem;
  line-height: 1.5;
}
.mem-sub {
  font-size: 0.781rem;
  color: var(--text-3);
  margin-top: 0.125rem;
}
.mem-sub .dot {
  margin: 0 0.375rem;
  opacity: 0.5;
}
.mem-tasks {
  font-size: 0.781rem;
  color: var(--text-2);
  width: 5.375rem;
  text-align: right;
  flex-shrink: 0;
  white-space: nowrap;
}
.mem-tasks b {
  color: var(--text);
}
.mem-tasks.zero {
  color: var(--text-3);
}

/* 역할 컨트롤 */
.role {
  display: inline-flex;
  align-items: center;
  gap: 0.4375rem;
  height: 2rem;
  padding: 0 0.6875rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text-2);
  background: #fff;
  cursor: pointer;
  white-space: nowrap;
  width: 6.5rem;
  font-family: inherit;
}
.role:hover {
  background: #f7f8fa;
}
.role .role-dot {
  width: 0.4375rem;
  height: 0.4375rem;
  border-radius: 50%;
  background: var(--text-3);
  flex-shrink: 0;
}
.role.admin {
  color: var(--accent);
  border-color: var(--accent-border);
  background: var(--accent-weak);
}
.role.admin .role-dot {
  background: var(--accent);
}
.role .caret {
  margin-left: auto;
  color: var(--text-3);
  display: grid;
  place-items: center;
}
.role .caret svg {
  width: 0.875rem;
  height: 0.875rem;
}
.role.fixed {
  background: #f1f2f4;
  cursor: not-allowed;
  color: var(--text-2);
  border-color: var(--border-strong);
}
.role.fixed .role-dot {
  background: var(--accent);
}
.role.fixed .lock {
  margin-left: auto;
  color: var(--text-3);
  display: grid;
  place-items: center;
}
.role.fixed .lock svg {
  width: 0.8125rem;
  height: 0.8125rem;
}

.mem-remove {
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-sm);
  border: none;
  background: transparent;
  color: var(--text-3);
  display: grid;
  place-items: center;
  cursor: pointer;
  flex-shrink: 0;
  opacity: 0;
}
.mem-row:hover .mem-remove {
  opacity: 1;
}
.mem-remove:hover {
  background: #f1f2f4;
  color: var(--red);
}
.mem-remove svg {
  width: 1rem;
  height: 1rem;
}
.mem-remove.disabled {
  opacity: 0 !important;
  pointer-events: none;
}

/* 로딩/빈 상태 메시지 */
.state-msg {
  padding: 2.5rem 1.125rem;
  text-align: center;
  font-size: 0.844rem;
  color: var(--text-3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: #fff;
}

/* 역할 변경 드롭다운 */
/* 전역 .list 의 overflow:hidden 이 드롭다운을 잘라내므로, 이 화면에서는 보이도록 해제.
   대신 컨테이너 라운드 코너 클리핑은 각 행의 첫/마지막 모서리에서 유지된다. */
.list {
  overflow: visible;
}
.role-wrap {
  position: relative;
}
.role:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}
.role.readonly {
  cursor: default;
  background: #f7f8fa;
}
.role-menu {
  position: absolute;
  top: calc(100% + 0.25rem);
  right: 0;
  z-index: 20;
  min-width: 6.5rem;
  background: #fff;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(20, 24, 33, 0.16);
  padding: 0.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
}
.role-menu button {
  height: 2rem;
  padding: 0 0.625rem;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  font-family: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text-2);
  text-align: left;
  cursor: pointer;
}
.role-menu button:hover {
  background: #f1f2f4;
  color: var(--text);
}
.role-menu button.on {
  color: var(--accent);
  background: var(--accent-weak);
}
</style>

<!-- 초대 모달은 Teleport 로 body 에 렌더되므로 비-scoped 전역 스타일 -->
<style>
.invite-modal {
  width: 100%;
  max-width: 32.75rem;
  background: #fff;
  border-radius: 0.875rem;
  box-shadow: 0 24px 64px rgba(20, 24, 33, 0.32);
  /* overflow:hidden 제거 — 자동완성 드롭다운이 모달 밖으로 넘쳐도 잘리지 않도록 */
}
.invite-modal .modal-head {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 1.375rem 1.5rem 1rem;
}
.invite-modal .modal-head h2 {
  font-size: 1.125rem;
  font-weight: 700;
  letter-spacing: -0.01875rem;
}
.invite-modal .mh-sub {
  font-size: 0.8125rem;
  color: var(--text-2);
  margin-top: 0.25rem;
}
.invite-modal .mh-sub b {
  color: var(--text-2);
  font-weight: 600;
}
.invite-modal .modal-close {
  margin-left: auto;
  width: 2rem;
  height: 2rem;
  border: none;
  background: transparent;
  color: var(--text-3);
  border-radius: var(--radius-sm);
  display: grid;
  place-items: center;
  cursor: pointer;
  flex-shrink: 0;
}
.invite-modal .modal-close:hover {
  background: #f1f2f4;
  color: var(--text);
}
.invite-modal .modal-close svg {
  width: 1.125rem;
  height: 1.125rem;
}
.invite-modal .modal-body {
  padding: 0.25rem 1.5rem 0.5rem;
  /* 전역 .modal-body(text-align:center) 상속 차단 — 폼은 좌측 정렬 */
  text-align: left;
}
/* 모달 입력 필드(이메일/역할) — 세로 스택, 전체 너비 */
.invite-modal .m-field {
  margin-top: 1.125rem;
}
.invite-modal .m-field:first-child {
  margin-top: 0;
}

/* 사용자 자동완성 드롭다운 */
.invite-modal .uc-field {
  position: relative;
}
.invite-modal .user-dd {
  position: absolute;
  top: calc(100% + 0.375rem);
  left: 0;
  right: 0;
  z-index: 10;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 0.5625rem;
  box-shadow: 0 12px 32px rgba(20, 24, 33, 0.16);
  padding: 0.375rem;
  max-height: 16rem;
  overflow-y: auto;
}
.invite-modal .user-dd-item {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  width: 100%;
  padding: 0.4375rem 0.5rem;
  border: none;
  background: transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-family: inherit;
  text-align: left;
}
.invite-modal .user-dd-item:hover {
  background: #f5f6f8;
}
.invite-modal .user-av {
  width: 1.625rem;
  height: 1.625rem;
  font-size: 0.6875rem;
  flex-shrink: 0;
}
.invite-modal .user-nm {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
}
.invite-modal .user-em {
  font-size: 0.75rem;
  color: var(--text-3);
  margin-left: auto;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 14rem;
  padding-left: 0.5rem;
}
.invite-modal .user-dd-msg {
  padding: 0.75rem 0.5rem;
  font-size: 0.781rem;
  color: var(--text-3);
  text-align: center;
}
.invite-modal .m-label {
  font-size: 0.781rem;
  font-weight: 600;
  color: var(--text-2);
  margin-bottom: 0.5rem;
  display: block;
}
.invite-modal .m-label .muted {
  color: var(--text-3);
  font-weight: 500;
}
/* 역할 선택 드롭다운(DropdownSelect) — 모달 폼 입력 높이/서체에 맞춤 */
.invite-modal .role-select :deep(.dd-trigger) {
  height: 2.5rem;
  font-size: 0.844rem;
  font-weight: 600;
  color: var(--text);
}
.invite-modal .m-input {
  width: 100%;
  height: 2.5rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  padding: 0 0.75rem;
  font-family: inherit;
  font-size: 0.844rem;
  color: var(--text);
  background: #fff;
}
.invite-modal .m-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-weak);
}
.invite-modal .m-input::placeholder {
  color: var(--text-3);
}
.invite-modal .role-hint {
  font-size: 0.75rem;
  color: var(--text-3);
  margin-top: 0.5rem;
  line-height: 1.5;
}
.invite-modal .role-hint b {
  color: var(--text-2);
  font-weight: 600;
}
.invite-modal .modal-foot {
  display: flex;
  align-items: center;
  gap: 0.5625rem;
  padding: 1rem 1.5rem;
  border-top: 1px solid var(--border);
  background: #fafbfc;
  margin-top: 1rem;
  /* overflow:hidden 제거에 따른 하단 라운드 보정 */
  border-radius: 0 0 0.875rem 0.875rem;
}
.invite-modal .modal-foot .fnote {
  font-size: 0.75rem;
  color: var(--text-3);
  margin-right: auto;
}
.invite-modal .mbtn {
  /* 전역 .mbtn(flex:1, 확인 다이얼로그용) 상속 차단 — 버튼은 내용 너비로 우측 정렬 */
  flex: 0 0 auto;
  height: 2.375rem;
  padding: 0 1rem;
  border-radius: var(--radius);
  font-size: 0.844rem;
  font-weight: 600;
  border: 1px solid var(--border-strong);
  background: #fff;
  color: var(--text-2);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  font-family: inherit;
}
.invite-modal .mbtn:hover {
  background: #f1f2f4;
  color: var(--text);
}
.invite-modal .mbtn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  box-shadow: 0 1px 2px rgba(79, 70, 229, 0.4);
}
.invite-modal .mbtn.primary:hover {
  background: var(--accent-hover);
}
.invite-modal .mbtn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}
.invite-modal .mbtn svg {
  width: 0.9375rem;
  height: 0.9375rem;
}
</style>
