/**
 * 업로드 공통 플러그인 스타일 — front 두 테마 + 관리자 겸용 한 벌
 *
 * 컴포넌트 로컬 토큰(--vue-up-*)만 소비하고, 로컬 토큰은 문맥 토큰으로 폴백
 * 사슬을 건다(--gb-* → --admin-* → 정적 hex 최후 방어). 다크 모드 블록은 만들지
 * 않는다 — 소비하는 문맥 토큰이 이미 3벌(라이트/수동다크/자동다크)로 뒤집힌다.
 * 셀렉터는 .vue-up 네임스페이스 밖으로 내보내지 않는다.
 *
 * P1: 진행 오버레이·오류 띠·끌어다 놓기 표시 (편집기 공용)
 * P2: 드롭존·항목 목록 (게시판 첨부)
 * P3: 단일 이미지 슬롯 (SEO 대표 이미지·콘텐츠 썸네일)
 *
 * @version 20260922-001
 */

.vue-up,
.vue-up-zone,
.vue-up-overlay,
.vue-up-error,
.vue-up-ddhint,
.vue-up-dragover {
    --vue-up-accent:      var(--gb-primary,        var(--admin-primary, #4F46E5));
    /* 브랜드색을 **밝은 바탕 위 글자**로 쓸 때만 쓰는 변형(§5.1 테마 토큰 · §99).
       테두리·배경 tint 는 위 원색 그대로다 — 여기를 바꾸면 강조가 흐려진다.
       플릭온 보라는 원색이 4.34:1(끌어오는 중 tint 위에서는 4.15:1)이라 글자로는
       모자란데, 그 테마에 이미 있는 -text 변형을 태우면 6.21:1(5.93:1)이 된다.
       -text 를 안 둔 테마(basic·루트 파랑 4.95:1)와 관리자(5.71:1)는 원색으로
       충분해서 폴백이 그대로 받는다. 아이콘은 글자가 아니라 이 토큰을 안 쓴다.
       수치는 전부 브라우저에서 실제 그려진 색을 읽어 잰 값이다 — color-mix 결과는
       계산으로 맞히기 어렵다(2026-09-14 Playwright 실측) */
    --vue-up-accent-text: var(--gb-primary-text,   var(--gb-primary, var(--admin-primary, #4F46E5)));
    --vue-up-accent-soft: var(--gb-primary-light,  var(--admin-info-soft, #EFF6FF));
    --vue-up-border:      var(--gb-border-default, var(--admin-border, #D1D5DB));
    --vue-up-bg:          var(--gb-surface-page,   var(--admin-bg, #F9FAFB));
    --vue-up-card:        var(--gb-surface-card,   #fff);
    --vue-up-text:        var(--gb-text-primary,   var(--admin-text, #111827));
    /* 잔글씨는 --vue-up-bg 위에 얹히는 자리가 넷이다(안내 줄·드롭존 아이콘·드롭존
       안내·썸네일 자리표시). 관리자 바탕은 프론트보다 한 단계 진한 Gray 100 이라
       Gray 500 을 쓰면 4.39:1 로 기준에 못 미쳤다(§99 · 2026-09-11 실측 → 6.87:1).
       --admin-secondary 는 원래 버튼 배경용 토큰이라 글자색 자리에서 떠나보낸다 */
    --vue-up-text-sub:    var(--gb-text-secondary, var(--admin-text-secondary, #4B5563));
    --vue-up-danger:      var(--gb-danger,         var(--admin-danger, #EF4444));
    /* accent 와 같은 짝(§99) — 원색은 아이콘·테두리, 이쪽은 **글자**.
       관리자에는 `-text` 변형이 없어 원색으로 떨어지고 있었다(오류 띠 3.44:1 ·
       실패 안내 3.76:1 · 슬롯 삭제 글자 3.76:1 — 2026-09-14 실측) */
    --vue-up-danger-text: var(--gb-danger-text,    var(--admin-danger-text, #842029));
    /* 관리자에는 --gb-radius 가 없어 0 — §7.3 각진 모서리 규칙이 폴백으로 지켜진다 */
    --vue-up-radius:      var(--gb-radius, 0);
}

/* ==========================================================================
   끌어다 놓는 자리 표시 — 코어 dragHint 가 대상 요소에 직접 붙인다
   ========================================================================== */
.vue-up-dragover {
    outline: 2px dashed var(--vue-up-accent);
    outline-offset: -4px;
    background-color: var(--vue-up-accent-soft);
}

/* ==========================================================================
   끌어다 놓기 상시 안내 줄 — 편집기 바닥 띠 (§90·§98)

   §98: 종전에는 편집기 **바깥**에 회색 글자 한 줄이라 안내로 안 읽혔다 — 바로 아래
   태그 안내와 글자 크기·색이 똑같아 어느 것이 편집기 소속인지 분간이 안 됐다.
   편집기에 이어 붙는 띠로 바꾸고 받는 형식·용량을 같은 줄 오른쪽에 둔다.
   ========================================================================== */
.vue-up-ddhint {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.25rem 0.5rem;
    /* 편집기 카드의 아래 테두리와 겹쳐 한 줄로 보이게 한다 */
    margin: -1px 0 0;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--vue-up-border);
    border-top: 0;
    background: var(--vue-up-bg);
    font-size: 0.8125rem;
    line-height: 1.5;
    color: var(--vue-up-text-sub);
}

/* 편집기 카드 **안**에 들어가는 갈래(summernote) — 좌우·아래 테두리와 모서리는
   카드가 이미 갖고 있어 위 구분선만 긋는다. 카드 안이라 코어 드롭존이 이 줄까지
   덮어서, 여기에 놓아도 실제로 받는다(§98 실측) */
.vue-up-ddhint-inside {
    margin: 0;
    border: 0;
    border-top: 1px solid var(--vue-up-border);
}

/* 글자와 같은 크기·같은 색 — 종전에는 둘 다 어긋나 장식처럼 보였다(§98) */
.vue-up-ddhint-ico {
    flex: 0 0 auto;
    font-size: 0.875rem;
    color: inherit;
}

/* 위계는 **크기로만** 준다 — 투명도를 얹으면 관리자 배경(--admin-bg)에서 명암비가
   3.6:1 까지 떨어진다(2026-09-11 실측). 형식·용량은 읽으라고 적는 글이다 */
.vue-up-ddhint-meta {
    margin-left: auto;
    font-size: 0.75rem;
}

/* 끌고 오는 동안 — 평소 문구·형식 안내를 감추고 「놓으면」으로 갈아 끼운다.
   summernote 는 코어 드롭존이 카드 전체를 덮어 이 전환이 안 보이고(그쪽 안내가
   대신 뜬다), toastui 는 덮개가 없어 이 전환이 유일한 신호다 — 2026-09-11 실측 */
.vue-up-ddhint-drop { display: none; }

.vue-up-ddhint.vue-up-dragover {
    /* 공용 표시(outline)를 여기서는 테두리·배경으로 받는다 — 드롭존과 같은 방식.
       **글자색은 건드리지 않는다**(드롭존도 `.vue-up-zone-text` 를 본문색으로 지킨다) —
       브랜드색을 밝은 tint 바탕 위 글자로 쓰면 명암비가 모자란다(§5.1 테마 토큰).
       플릭온 라이트에서 4.15:1 로 떨어지는데, 그 순간 화면에 남는 글자가 이것뿐이다. */
    outline: none;
    border-color: var(--vue-up-accent);
    background: var(--vue-up-accent-soft);
}

.vue-up-ddhint-inside.vue-up-dragover {
    border-top-color: var(--vue-up-accent);
}

.vue-up-ddhint.vue-up-dragover .vue-up-ddhint-rest,
.vue-up-ddhint.vue-up-dragover .vue-up-ddhint-meta { display: none; }

.vue-up-ddhint.vue-up-dragover .vue-up-ddhint-drop { display: inline; }

/* 터치 기기: 끌어다 놓기는 없어도 **붙여넣기는 된다** — 통째로 숨기던 종전 규칙은
   쓸 수 있는 방법을 아예 안 알려 주는 셈이었다(§98). 문구만 갈아 끼운다 */
.vue-up-ddhint-touch { display: none; }

@media (pointer: coarse) {
    .vue-up-ddhint-drag  { display: none; }
    .vue-up-ddhint-touch { display: inline; }
}

/* 좁은 폭: 형식 안내를 오른쪽 끝에 홀로 남기지 않고 아래 줄 왼쪽으로 내린다 */
@media (max-width: 576px) {
    .vue-up-ddhint-meta { margin-left: 0; flex-basis: 100%; }
}

/* ==========================================================================
   진행 오버레이 — 스크림 + 가운데 카드 (부착 요소는 position 문맥 필요)
   ========================================================================== */
.vue-up-overlay {
    position: absolute;
    inset: 0;
    display: none;
    align-items: center;
    justify-content: center;
    z-index: 20;
    background: rgba(0, 0, 0, 0.45);
    background: rgba(var(--gb-scrim-rgb, 0, 0, 0), 0.45);
}

.vue-up-overlay.active {
    display: flex;
}

.vue-up-overlay-box {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.625rem;
    padding: 1rem 1.5rem;
    max-width: calc(100% - 2rem);
    background: var(--vue-up-card);
    color: var(--vue-up-text);
    border-radius: var(--vue-up-radius);
    box-shadow: var(--gb-shadow-lg, 0 8px 24px rgba(0, 0, 0, 0.25));
    animation: vueUpFadeIn 0.18s ease;
}

.vue-up-overlay-text {
    font-size: 0.875rem;
    color: var(--vue-up-text);
}

.vue-up-progress {
    width: 180px;
    max-width: 100%;
    height: 4px;
    background: var(--vue-up-border);
    border-radius: 2px;
    overflow: hidden;
}

.vue-up-progress-bar {
    height: 100%;
    width: 0;
    background: var(--vue-up-accent);
    border-radius: 2px;
    /* width 값 갱신은 JS el.style.width — 수치 갱신이라 F2 의 setProperty 허용 범위 */
    transition: width 0.1s linear;
}

.vue-up-overlay-cancel {
    padding: 0.25rem 0.875rem;
    font-size: 0.8125rem;
    color: var(--vue-up-text-sub);
    background: transparent;
    border: 1px solid var(--vue-up-border);
    border-radius: var(--vue-up-radius);
    cursor: pointer;
}

.vue-up-overlay-cancel:hover {
    color: var(--vue-up-text);
    border-color: var(--vue-up-text-sub);
}

.vue-up-overlay-cancel:focus-visible {
    outline: none;
    /* --gb-focus-ring 은 그림자 전체값 — 색 토큰이 아니다 */
    box-shadow: var(--gb-focus-ring, 0 0 0 2px #fff, 0 0 0 4px #4F46E5);
}

/* ==========================================================================
   오류 띠 — 토스트가 없는 화면(프론트 편집기)용 인라인 안내
   ========================================================================== */
.vue-up-error {
    display: none;
    margin-top: 0.25rem;
    padding: 0.375rem 0.75rem;
    font-size: 0.8125rem;
    border-radius: var(--vue-up-radius);
    /* 정본 -text·-light 변형을 그대로 쓴다(파생 공식 복제 금지 — §5.1 테마 토큰).
       관리자는 --gb-* 가 없어 --admin-* 로, 그것도 없는 문맥만 정적 값이 받는다 */
    color: var(--vue-up-danger-text);
    background-color: var(--gb-danger-light, var(--admin-danger-soft, #f8d7da));
    /* 폴백이 이제 실제로 그려지므로 테마를 타는 값을 쓴다 — 고정 분홍(#f5c2c7)을 두면
       다크 카드 위에 밝은 선 하나가 뜬다(2026-09-22 리뷰 지적). */
    border: 1px solid var(--gb-border-default, #f5c2c7);
}

/* color-mix 는 @supports 로 가른다 (2026-09-22 · §115 실측).
   같은 블록에 「앞줄 폴백 · 뒷줄 color-mix」 두 줄로 쓰면 옛 브라우저도 뒷줄을 일단
   값으로 받아 앞줄을 덮고, 못 쓰는 값으로 판명될 때는 폴백이 이미 사라진 뒤다.
   var(--x, 폴백) 도 같은 까닭으로 안 걸린다. 블록 바로 뒤에 두어 종전 순서를 지킨다. */
@supports (color: color-mix(in srgb, red 50%, blue)) {
    .vue-up-error {
        border: 1px solid color-mix(in srgb, var(--vue-up-danger) 30%, var(--vue-up-card));
    }
}

.vue-up-error.active {
    display: block;
}

/* ==========================================================================
   드롭존 — 대시 보더 투톤. dragover 는 실선(「지금 놓으면 된다」 신호 계승)
   ========================================================================== */
.vue-up-zone {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 2.5rem 1rem;
    border: 2px dashed var(--vue-up-border);
    border-radius: var(--vue-up-radius);
    background: var(--vue-up-bg);
    cursor: pointer;
    text-align: center;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

.vue-up-zone:hover,
.vue-up-zone:focus-visible {
    border-color: var(--vue-up-accent);
    background: var(--vue-up-accent-soft);
}

.vue-up-zone:focus-visible {
    outline: none;
    box-shadow: var(--gb-focus-ring, 0 0 0 2px #fff, 0 0 0 4px #4F46E5);
}

/* 코어 dragHint 가 붙이는 공용 표시(outline)를 존에서는 보더 실선으로 바꿔 받는다 */
.vue-up-zone.vue-up-dragover {
    outline: none;
    border-style: solid;
    border-color: var(--vue-up-accent);
    background: var(--vue-up-accent-soft);
}

.vue-up-zone-icon {
    font-size: 2rem;
    color: var(--vue-up-text-sub);
    transition: color 0.15s ease;
}

.vue-up-zone:hover .vue-up-zone-icon,
.vue-up-zone.vue-up-dragover .vue-up-zone-icon {
    color: var(--vue-up-accent);
    animation: vueUpBounce 0.4s ease;
}

.vue-up-zone-text {
    margin: 0;
    font-size: 0.9375rem;
    color: var(--vue-up-text);
}

.vue-up-zone-browse {
    /* 밝은 바탕 위 글자라 원색이 아니라 -text 변형(§99) — 드롭존은 hover·끌어오는
       중에 바탕이 accent-soft 로 밝아져서 원색이면 4.15:1 까지 내려간다 */
    color: var(--vue-up-accent-text);
    font-weight: 600;
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.vue-up-zone-hint {
    margin: 0;
    font-size: 0.8125rem;
    color: var(--vue-up-text-sub);
}

.vue-up-zone-input {
    display: none;
}

/* 터치 기기: 끌어다 놓기 문구 대신 「눌러서 파일을 골라요」 */
.vue-up-zone-touch {
    display: none;
}

@media (pointer: coarse) {
    .vue-up-zone-drag { display: none; }
    .vue-up-zone-touch { display: inline; }
}

/* ==========================================================================
   그림 넣기 레이어 안 판 (§91) — 좁은 변형 + 두 편집기의 기본 파일 칸 감추기
   ========================================================================== */
/* 레이어 안은 세로 여유가 빠듯하다 — 편집기 안에 놓이는 창이라 편집기보다 커질 수
   없어서(실측: 편집기 366px 일 때 창 본문은 204px), 첨부 드롭존처럼 세로로 쌓으면
   글 넣는 칸이 화면 밖으로 밀린다. 그래서 아이콘 옆에 글줄을 세우는 가로 한 줄. */
.vue-up-zone-compact {
    flex-direction: row;
    justify-content: flex-start;
    gap: 0.625rem;
    padding: 0.625rem 0.875rem;
    text-align: left;
}

.vue-up-zone-lines {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    align-items: flex-start;
    min-width: 0;
}

.vue-up-zone-compact .vue-up-zone-icon {
    font-size: 1.25rem;
    flex: 0 0 auto;
}

.vue-up-zone-compact .vue-up-zone-text {
    font-size: 0.875rem;
}

.vue-up-zone-compact .vue-up-zone-hint {
    font-size: 0.75rem;
}

/* summernote 그림 레이어 — 기본 파일 칸과 그 짝 라벨은 판이 대신하므로 감춘다.
   라벨까지 감추는 이유는 두 가지 — `for` 로 이어져 있어 누르면 판을 거치지 않고
   파일 고르기가 열리는 숨은 길이 남고, 판이 이미 같은 말을 해서 읽어 주는 화면에
   같은 뜻이 두 번 들린다. `.note-modal .note-form-control { display: block }` 이
   클래스 하나짜리 규칙을 이기므로(실측) 여기도 같은 무게로 적는다. */
.note-modal .vue-up-replaced {
    display: none;
}

.note-group-select-from-files .vue-up-zone-compact {
    margin-top: 0.25rem;
}

/* toastui 그림 레이어 — 파일 줄의 기본 컨트롤(라벨·파일명·선택 버튼·파일 칸)은
   판이 대신하므로 감춘다(탭이 이미 「파일」임을 말해 준다). 구조로 지목하는 것은
   라이브러리가 줄을 다시 그려도 규칙이 살아남게 하기 위해서다. */
.vue-up-tui-filerow > label,
.vue-up-tui-filerow .toastui-editor-file-name,
.vue-up-tui-filerow .toastui-editor-file-select-button,
.vue-up-tui-filerow > input[type="file"] {
    display: none;
}

.vue-up-tui-filerow .vue-up-zone-compact {
    margin: 0.25rem 0 0.75rem;
}

/* ==========================================================================
   항목 목록 — 썸네일·이름·크기·항목별 진행 막대·실패 사유·삭제 토글
   ========================================================================== */
.vue-up-list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin-top: 1rem;
}

.vue-up-list:empty {
    display: none;
    margin: 0;
}

.vue-up-item {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 0.75rem;
    background: var(--vue-up-card);
    border: 1px solid var(--vue-up-border);
    border-radius: var(--vue-up-radius);
    transition: opacity 0.2s ease, border-color 0.2s ease;
}

.vue-up-item-thumb {
    flex: 0 0 auto;
    width: 48px;
    height: 48px;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border-radius: var(--vue-up-radius);
    background: var(--vue-up-bg);
    color: var(--vue-up-text-sub);
    font-size: 1.375rem;
}

.vue-up-item-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.vue-up-item-info {
    flex: 1 1 auto;
    min-width: 0;
}

.vue-up-item-name {
    font-size: 0.875rem;
    color: var(--vue-up-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.vue-up-item-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.125rem 0.5rem;
    font-size: 0.8125rem;
    color: var(--vue-up-text-sub);
}

.vue-up-item-failed {
    /* 폴백이 이제 실제로 그려지므로 테마를 타는 값을 쓴다 (2026-09-22 리뷰 지적) */
    border-color: var(--gb-border-default, #f5c2c7);
}

/* color-mix 는 @supports 로 가른다 (2026-09-22 · §115 실측).
   같은 블록에 「앞줄 폴백 · 뒷줄 color-mix」 두 줄로 쓰면 옛 브라우저도 뒷줄을 일단
   값으로 받아 앞줄을 덮고, 못 쓰는 값으로 판명될 때는 폴백이 이미 사라진 뒤다.
   var(--x, 폴백) 도 같은 까닭으로 안 걸린다. 블록 바로 뒤에 두어 종전 순서를 지킨다. */
@supports (color: color-mix(in srgb, red 50%, blue)) {
    .vue-up-item-failed {
        border-color: color-mix(in srgb, var(--vue-up-danger) 40%, var(--vue-up-card));
    }
}

/* 상태 문구는 밝은 바탕 위 글자 — 원색 danger(≈3.8:1)로는 모자라 -text 변형 사슬을 쓴다 */
.vue-up-item-failed .vue-up-item-status,
.vue-up-item-deleted .vue-up-item-status {
    color: var(--vue-up-danger-text);
}

/* 흐림은 썸네일에만 — 행 전체 opacity 는 문구 대비를 4.5:1 아래로 떨어뜨린다 */
.vue-up-item-deleted .vue-up-item-thumb {
    opacity: 0.55;
}

.vue-up-item-deleted .vue-up-item-name {
    text-decoration: line-through;
    color: var(--vue-up-text-sub);
}

.vue-up-item-desc {
    display: block;
    width: 100%;
    margin-top: 0.25rem;
    padding: 0.25rem 0.5rem;
    font-size: 0.8125rem;
    color: var(--vue-up-text);
    background: var(--vue-up-card);
    border: 1px solid var(--vue-up-border);
    border-radius: var(--vue-up-radius);
}

.vue-up-item-desc:focus-visible {
    outline: none;
    box-shadow: var(--gb-focus-ring, 0 0 0 2px #fff, 0 0 0 4px #4F46E5);
}

.vue-up-item-bar {
    height: 3px;
    margin-top: 0.375rem;
    background: var(--vue-up-border);
    border-radius: 2px;
    overflow: hidden;
}

.vue-up-item-bar-fill {
    height: 100%;
    width: 0;
    background: var(--vue-up-accent);
    border-radius: 2px;
    transition: width 0.1s linear;
}

.vue-up-item-actions {
    display: flex;
    flex: 0 0 auto;
    gap: 0.25rem;
}

.vue-up-item-btn {
    min-width: 32px;
    min-height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--vue-up-text-sub);
    border-radius: var(--vue-up-radius);
    cursor: pointer;
    transition: color 0.15s ease;
}

.vue-up-item-btn-remove:hover {
    color: var(--vue-up-danger);
}

.vue-up-item-btn-retry:hover {
    color: var(--vue-up-accent);
}

.vue-up-item-btn:focus-visible {
    outline: none;
    box-shadow: var(--gb-focus-ring, 0 0 0 2px #fff, 0 0 0 4px #4F46E5);
}

.vue-up-summary {
    margin: 0.5rem 0 0;
    font-size: 0.8125rem;
    color: var(--vue-up-text-sub);
}

.vue-up-summary:empty {
    display: none;
}

/* ==========================================================================
   단일 이미지 슬롯 — SEO 대표 이미지 · 콘텐츠 썸네일
   빈 상태는 .vue-up-zone 클래스를 함께 입어 존 스타일을 그대로 받는다
   ========================================================================== */
.vue-up-slot-stage {
    position: relative;
}

.vue-up-slot-stage.vue-up-slot-filled {
    display: inline-block;
    max-width: 100%;
    border: 1px solid var(--vue-up-border);
    border-radius: var(--vue-up-radius);
    background: var(--vue-up-bg);
    overflow: hidden;
    cursor: pointer;
}

.vue-up-slot-stage.vue-up-slot-filled:focus-visible {
    outline: none;
    box-shadow: var(--gb-focus-ring, 0 0 0 2px #fff, 0 0 0 4px #4F46E5);
}

/* 채워진 상태에 끌어다 놓기 = 교체 신호 */
.vue-up-slot-stage.vue-up-slot-filled.vue-up-dragover {
    outline: 2px solid var(--vue-up-accent);
    outline-offset: -2px;
}

.vue-up-slot-img {
    display: block;
    max-width: 100%;
    max-height: 220px;
    object-fit: contain;
}

/* 액션 — hover·포커스 때 하단 띠로 노출 (키보드 tab 으로도 닿는다)
   어둠은 scrim 토큰(항상 검정) — 정적 줄은 미지원 브라우저 폴백 */
.vue-up-slot-actions {
    position: absolute;
    inset: auto 0 0 0;
    display: flex;
    justify-content: flex-end;
    gap: 0.375rem;
    padding: 0.375rem;
    background: linear-gradient(transparent, rgba(0, 0, 0, 0.45));
    background: linear-gradient(transparent, rgba(var(--gb-scrim-rgb, 0, 0, 0), 0.45));
    opacity: 0;
    transition: opacity 0.15s ease;
}

.vue-up-slot-stage:hover .vue-up-slot-actions,
.vue-up-slot-stage:focus-within .vue-up-slot-actions {
    opacity: 1;
}

.vue-up-slot-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.3125rem;
    padding: 0.25rem 0.625rem;
    border: 0;
    border-radius: var(--vue-up-radius);
    background: var(--vue-up-card);
    color: var(--vue-up-text);
    font-size: 0.8125rem;
    cursor: pointer;
}

.vue-up-slot-btn-remove {
    color: var(--vue-up-danger);
}

/* 이 단추는 아이콘 **과 글자**를 함께 담는다(`makeAction()` 이 `<i>` 뒤에 `<span>` 을
   붙인다) — 아이콘은 그래픽이라 원색으로 3:1 을 넘지만 글자는 4.5:1 이 필요하다.
   그래서 글자만 -text 변형으로 덮는다(§99). 아이콘만 있는 단추와 섞지 말 것 */
.vue-up-slot-btn-remove span {
    color: var(--vue-up-danger-text);
}

.vue-up-slot-btn:focus-visible {
    outline: none;
    box-shadow: var(--gb-focus-ring, 0 0 0 2px #fff, 0 0 0 4px #4F46E5);
}

/* 올리는 중 — 스테이지 흐림 + 하단 진행 막대 */
.vue-up-slot-stage.vue-up-slot-busy {
    opacity: 0.7;
}

.vue-up-slot-bar {
    position: absolute;
    inset: auto 0 0 0;
    height: 4px;
    background: var(--vue-up-border);
    overflow: hidden;
}

.vue-up-slot-bar-fill {
    height: 100%;
    width: 0;
    background: var(--vue-up-accent);
    transition: width 0.2s ease;
}

/* 주소 직접 입력 줄 (SEO) */
.vue-up-slot-url {
    display: flex;
    gap: 0.375rem;
    margin-top: 0.5rem;
}

.vue-up-slot-url-input {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0.375rem 0.625rem;
    border: 1px solid var(--vue-up-border);
    border-radius: var(--vue-up-radius);
    background: var(--vue-up-card);
    color: var(--vue-up-text);
    font-size: 0.8125rem;
}

.vue-up-slot-url-input:focus-visible {
    outline: none;
    border-color: var(--vue-up-accent);
    box-shadow: var(--gb-focus-ring, 0 0 0 2px #fff, 0 0 0 4px #4F46E5);
}

.vue-up-slot-url-apply {
    flex: 0 0 auto;
    padding: 0.375rem 0.75rem;
    border: 1px solid var(--vue-up-border);
    border-radius: var(--vue-up-radius);
    background: var(--vue-up-card);
    color: var(--vue-up-text);
    font-size: 0.8125rem;
    cursor: pointer;
}

.vue-up-slot-url-apply:hover {
    /* 테두리는 원색, 글자는 -text 변형 — 같은 자리에서 둘의 역할이 다르다(§99) */
    border-color: var(--vue-up-accent);
    color: var(--vue-up-accent-text);
}

.vue-up-slot-url-apply:focus-visible {
    outline: none;
    box-shadow: var(--gb-focus-ring, 0 0 0 2px #fff, 0 0 0 4px #4F46E5);
}

/* 반응형 — 375px 대: 존 여백 축소, 액션 버튼 44px 터치 타깃 */
@media (max-width: 575.98px) {
    .vue-up-zone {
        padding: 1.5rem 0.75rem;
    }

    .vue-up-item-btn {
        min-width: 44px;
        min-height: 44px;
    }
}

/* 태블릿·터치 노트북(576px 이상 + coarse)도 같은 터치 타깃 */
@media (pointer: coarse) {
    .vue-up-item-btn {
        min-width: 44px;
        min-height: 44px;
    }

    /* 터치에는 hover 가 없다 — 슬롯 액션을 늘 보여준다 */
    .vue-up-slot-actions {
        opacity: 1;
    }

    .vue-up-slot-btn {
        min-height: 44px;
    }
}

/* ==========================================================================
   모션 — 줄여 달라는 환경에서는 전부 무효
   ========================================================================== */
@keyframes vueUpFadeIn {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: none; }
}

@keyframes vueUpBounce {
    0%, 100% { transform: translateY(0); }
    40% { transform: translateY(-6px); }
    70% { transform: translateY(-2px); }
}

@media (prefers-reduced-motion: reduce) {
    .vue-up-overlay-box { animation: none; }
    .vue-up-progress-bar { transition: none; }
    .vue-up-zone,
    .vue-up-zone-icon { transition: none; animation: none; }
    .vue-up-zone:hover .vue-up-zone-icon,
    .vue-up-zone.vue-up-dragover .vue-up-zone-icon { animation: none; }
    .vue-up-item { transition: none; }
    .vue-up-item-bar-fill { transition: none; }
    .vue-up-slot-actions { transition: none; }
    .vue-up-slot-bar-fill { transition: none; }
}
