/* ============================================================
   HEART AI BytePlus Studio — design system
   A deliberately "designed" editorial look: warm off-black/off-white
   tones (never pure #000/#FFF), a serif display face paired with a
   clean sans for UI, hairline rules instead of glow/gradient chrome.
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap');

/* ============================================================
   Z-INDEX 레이어 테이블 (2026-07-30 전면 정리)
   ------------------------------------------------------------
   이전에는 층위 기준이 없어서 값이 서로 충돌했다. 대표적으로
   .sim3d-tut-modal-back / .comfy-modal-overlay / .comfy-jobs-drawer 가 모두
   1200 으로 동일했고, 결과 상세 라이트박스(1000)가 작업 드로어(1200)와 생성
   오버레이(1100) 뒤로 숨는 버그가 있었다. 아래 밴드로 재배치했다.
   같은 밴드 안에서는 10 단위로 띄워 동일값(타이)을 만들지 않는다.

     밴드            범위        용도
     ─────────────────────────────────────────────────────────
     콘텐츠          0 ~ 99      문서 흐름 안의 겹침(배지, ::before, 스티키 헤더 등)
     드로어          1000~1099   측면 도킹 패널과 그 백드롭/핸들
     플로팅          1100~1199   셸 내비 + 떠 있는 액션/배너
                                 (드로어보다 위 — 드로어를 열어둔 채로도 이동·조작 가능해야 함)
     모달            2000~2999   전면 오버레이(폼·편집기·게이트)
     상세 라이트박스 3000~3199   이미지/결과 상세 및 확대 뷰어 (모달보다 위)
     다이얼로그      4000~4099   확인/입력 대체 창 (모달·라이트박스 어디서 띄워도 위)
     팝오버·드래그   5000~5399   앵커드 메뉴/팝오버, 드래그 고스트·마퀴
     토스트·전역배너 6000~6999   공지 배너와 토스트 (항상 최상단)

   현재 배정값
     1000 .comfy-jobs-drawer-backdrop   1010 .comfy-jobs-drawer
     1020 .comfy-jobs-handle
     1100 .mobile-tabbar                1110 .comfy-gen-min-dock
     1120 .assistant-fab                1130 .assistant-panel
     1140 .music-join-banner
     2000 .comfy-gen-overlay            2100 .gal-modal-overlay
     2150 .feedpage-detail(모바일 상세)  2200 .mask-editor-overlay
     2300 .comfy-maske-overlay          2400 .sim3d-tut-modal-back
     2500 .cmdk-overlay                 2600 .music-url-pop-overlay
     2900 .auth-overlay(로그인 게이트 — 모달 밴드 최상단)
     3000 .lightbox-overlay             3020 .comfy-lightbox-overlay
     3030 .comfy-ov-jobnav              3100 .comfy-inputzoom-overlay
     4000 .comfy-modal-overlay
     5000 .gal-marquee                  5010 .gal-drag-ghost
     5100 .mmp-queue-pop                5200 .gt-jobmenu
     5210 .notif-panel                  5300 .comfy-img-ctxmenu
     6000 .announce-banner              6500 .copy-toast
     6600 .toast-stack
   서브앱(public/apps 하위) CSS는 별도 문서/스택 컨텍스트라 이 표의 대상이 아니다.
   ============================================================ */

:root{
  --radius: 14px;
  --radius-sm: 9px;
  /* Display face — swapped from a warm serif (Fraunces) to a tight, bold
     geometric grotesk after checking Higgsfield's real, live site (checked
     2026-07-02): their headings/titles use Space Grotesk, font-weight 700,
     uppercase, letter-spacing around -4%, on a near-black/near-white base —
     not a serif-editorial look at all. Falls back to the UI sans for any
     Korean text (Space Grotesk has no Hangul glyphs), which is standard
     multi-script behavior. */
  --display: 'Space Grotesk', 'Inter', 'Pretendard', sans-serif;
  --serif: var(--display); /* legacy alias, existing rules still reference --serif */
  --sans: 'Inter', 'Pretendard', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --mono: 'JetBrains Mono', 'SF Mono', Consolas, monospace;
  font-size:18px;
}

/* ---------------- Night (default) ----------------
   Matched directly against the Higgsfield reference screenshots the user
   supplied (checked 2026-07-02): near-black stage, dark elevated cards,
   a single bright chartreuse/neon-lime accent used for the primary CTA,
   selected chips, and active nav — everything else stays neutral gray so
   the lime reads as a deliberate accent, not decoration everywhere. */
html[data-theme="night"]{
  --bg:#131215;
  --bg-elev:#19181c;
  --rail:#0b0a0d;
  --sidebar:#0e0d10;
  --surface:#1c1b1f;
  --surface-2:#232227;
  --border:#333238;
  --border-soft:#28272c;
  --text:#f4f3ef;
  --text-dim:#a9a8ad;
  --text-faint:#717077;
  --accent:#d7fb3d;
  --accent-soft:rgba(215,251,61,.14);
  --ink:#7dc8fb;
  --ink-soft:rgba(125,200,251,.14);
  --gold:#dcae63;
  --success:#4ade8a;
  --warn:#dcae63;
  --danger:#ff6259;
  --on-accent:#111008;
  --shadow: 0 18px 44px -18px rgba(0,0,0,.75);
}

/* ---------------- Day ----------------
   Same accent family (kept on-brand), deepened so it stays legible as
   text/icon color against a light surface — the bright lime is reserved
   for filled buttons/chips where the dark on-accent text does the work. */
html[data-theme="day"]{
  --bg:#f5f5f1;
  --bg-elev:#ececE6;
  --rail:#e9e9e3;
  --sidebar:#eeeeea;
  --surface:#ffffff;
  --surface-2:#f2f2ed;
  --border:#dcdcd5;
  --border-soft:#e7e7e1;
  --text:#19191c;
  --text-dim:#57565d;
  --text-faint:#88878e;
  --accent:#7a9a00;
  --accent-soft:rgba(122,154,0,.10);
  --ink:#2566a8;
  --ink-soft:rgba(37,102,168,.09);
  --gold:#a5762c;
  --success:#2f9e5c;
  --warn:#a5762c;
  --danger:#c23a3a;
  --on-accent:#fafaf6;
  --shadow: 0 18px 44px -22px rgba(20,20,15,.22);
}

*{box-sizing:border-box;}
html,body{height:100%;}

/* ---------------- Scrollbars ----------------
   Slim, theme-aware scrollbar applied app-wide, replacing the stock OS
   scrollbar (thick gray thumb + arrow buttons on a white track) that clashed
   with the dark stage. Firefox uses scrollbar-width/scrollbar-color; WebKit
   (Chrome/Edge/Safari) uses the ::-webkit-scrollbar pseudo-elements. Every
   color is a design token, so the day/night toggle recolors the scrollbar
   automatically — --border/--text-faint already carry their light values in
   the html[data-theme="day"] block above, which IS the day override. The
   rounded thumb (border-radius:999px) matches the app's rounded-corner look;
   the 2px transparent border + padding-box clip keeps the thumb visually slim
   inside a 10px gutter. More specific rules (e.g. .icon-rail width:0) still
   win because they come later in the cascade.
   Thumb is a lime(--accent)-tinted color at rest, going full lime on
   hover/drag — a plain var(--border) thumb (previous version) read as
   generic gray, not the brand lime the rest of the shell uses. */
*{scrollbar-width:thin;scrollbar-color:color-mix(in srgb, var(--accent) 45%, var(--border)) transparent;}
::-webkit-scrollbar{width:10px;height:10px;}
::-webkit-scrollbar-track{background:transparent;}
::-webkit-scrollbar-thumb{
  background:color-mix(in srgb, var(--accent) 45%, var(--border));
  border-radius:999px;
  border:2px solid transparent;
  background-clip:padding-box;
}
::-webkit-scrollbar-thumb:hover{background:var(--accent);}
::-webkit-scrollbar-corner{background:transparent;}
body{
  margin:0;
  background:var(--bg);
  color:var(--text);
  font-family:var(--sans);
  -webkit-font-smoothing:antialiased;
  overflow:hidden;
  transition:background .2s ease, color .2s ease;
}
a{color:inherit;}
::selection{background:var(--accent-soft);}

.eyebrow{
  font-size:11.5px; font-weight:700; letter-spacing:.12em; text-transform:uppercase;
  color:var(--text-faint);
}
.serif{font-family:var(--serif);}

/* ---------- SHELL GRID (icon rail + context panel + main) ----------
   Redesign: the top bar spans the full width; beneath it three columns —
   a compact vertical ICON RAIL holding the top-level module buttons
   (re-homed from the old top-bar .gt-nav), the SIDEBAR now acting as a
   per-module CONTEXT PANEL, and the MAIN work area. All nav JS hooks
   (.module-item[data-module], #modnav-<id>, #sidebarctx-<view>, the
   #topbar* title elements, #view-<name>) are preserved verbatim — only
   their position/skin changed. */
/* 상단바 높이 — .app-shell의 grid-template-rows와 값이 항상 같아야 하며,
   ComfyUI 우측 도킹 패널(.comfy-jobs-drawer, .app-shell 바깥 body 직계
   자식이라 .app-shell에 변수를 둬도 상속되지 않는다)도 이 값을 그대로 참조해
   상단바를 가리지 않고 그 아래부터 시작한다. :root에 둬서 반응형 분기점마다
   함께 갱신되면 두 곳 모두 자동으로 맞는다. */
:root{ --topbar-h:60px; }
.app-shell{
  display:grid;
  grid-template-columns:88px 236px 1fr;
  grid-template-rows:var(--topbar-h) 1fr;
  grid-template-areas:
    "topbar topbar topbar"
    "rail   sidebar main";
  height:100vh;
}
/* NAI / 3D 시뮬레이터처럼 컨텍스트 패널이 필요 없는 임베드 모듈에서는
   가운데 sidebar(컨텍스트 패널) 열을 폭 0으로 접어, 그 폭을 main(=iframe)이
   가져가게 한다. app.js의 updateTopbarTitleVisibility()가 .app-shell에
   'ctx-collapsed'를 얹고/뗀다 (기존 content-embed 토글과 같은 지점). */
.app-shell.ctx-collapsed{
  grid-template-columns:88px 0 1fr;
}
.app-shell.ctx-collapsed .sidebar{
  padding:0;
  border-left:none;
  border-right:none;
  overflow:hidden;
}
/* Hidden until the auth check in app.js confirms a logged-in, approved
   session — prevents a flash of the full app UI before that check resolves. */
body:not(.authed) .app-shell{display:none;}

/* ---------- GLOBAL TOP BAR ---------- */
.global-topbar{
  grid-area:topbar;
  background:var(--sidebar);
  border-bottom:1px solid var(--border-soft);
  display:flex; align-items:center; gap:16px;
  padding:0 22px 0 20px;
  z-index:20; position:relative;
}
/* Root cause of the persistent "top-left logo looks off-center" report:
   .logo (defined further down, for the sidebar/auth-screen decorative logo)
   sets padding:2px 4px 24px. Since ".logo" and ".gt-logo" are both single-
   class selectors of equal specificity, whichever is declared LATER in this
   file wins ties — and .logo comes after .gt-logo in source order, so its
   asymmetric 2px-top/24px-bottom padding was silently overriding this
   block's intended padding:0, pushing the topbar logo's visual center ~11px
   above the nav items next to it (looked "top-aligned" instead of centered).
   Targeting both classes together (.logo.gt-logo) raises specificity so this
   rule always wins regardless of source order. */
.logo.gt-logo{padding:0; gap:11px; flex-shrink:0;}
.gt-logo .logo-mark{width:36px;height:36px;border-radius:9px;}
.gt-logo .logo-mark svg{width:18px;height:18px;}
.gt-logo .logo-text{font-size:17px;}
.gt-logo .logo-sub{font-size:10px;margin-top:4px;letter-spacing:.14em;}
.gt-logo .logo-mark{background:var(--accent); color:var(--on-accent);}

/* Vertical hairline between brand and breadcrumb (mockup). */
.gt-brand-div{width:1px; height:26px; background:var(--border-soft); margin:0 4px; flex-shrink:0;}

/* ---------- BREADCRUMB (module → view path in the top bar) ----------
   Presents the current location as a path. #gtCrumbModule / #gtCrumbView are
   updated alongside the preserved #topbarTitle/#topbarSub (which still live
   in the main area's #topbarRow and are still written by switchView). */
.gt-breadcrumb{display:flex; align-items:center; gap:8px; min-width:0; flex-shrink:1;}
.gt-crumb-mod{font-size:13px; font-weight:600; color:var(--text-faint); white-space:nowrap;}
.gt-crumb-sep{width:15px; height:15px; color:var(--text-faint); opacity:.6; flex-shrink:0;}
.gt-crumb-view{
  font-family:var(--display); font-size:16px; font-weight:700; letter-spacing:-.2px;
  color:var(--text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}

/* 콘텐츠 제작(content) 모듈 전용 상단바 서브탭 — 챗봇 메이커 / 스토리보드
   메이커. 역할이 서로 다른 두 서브앱을 사이드바 목록이 아니라 상단바에서
   바로 구분할 수 있도록 breadcrumb 옆에 pill 탭 두 개로 배치한다. 표시 여부는
   app.js의 updateTopbarTitleVisibility()가 module==='content'일 때만 켠다. */
.gt-subtabs{display:flex; align-items:center; gap:4px; margin-left:4px; flex-shrink:0;}
.gt-subtab{
  display:flex; align-items:center; gap:6px;
  height:32px; padding:0 12px; border-radius:8px;
  font-size:13px; font-weight:600; color:var(--text-faint);
  background:transparent; border:1px solid transparent;
  cursor:pointer; white-space:nowrap; transition:background .12s, color .12s;
}
.gt-subtab svg{width:15px; height:15px; flex-shrink:0;}
.gt-subtab:hover{color:var(--text-dim); background:rgba(255,255,255,.04);}
.gt-subtab.active{color:var(--accent); background:var(--accent-soft); border-color:transparent;}

/* ---------- GLOBAL SEARCH FIELD (Ctrl/⌘+K command palette) ----------
   Styled as the mockup's 300px search field: icon + placeholder text + ⌘K
   chip on a --bg-elev field. It is still a button that opens the command
   palette (the label acts as the placeholder). */
.gt-search-trigger{
  display:flex; align-items:center; gap:9px; flex-shrink:0;
  width:300px; height:38px;
  background:var(--bg-elev); border:1px solid var(--border-soft); color:var(--text-faint);
  border-radius:10px; padding:0 13px; cursor:pointer; font-size:13.5px; text-align:left;
  transition:border-color .12s, color .12s;
}
.gt-search-trigger:hover{border-color:var(--border); color:var(--text-dim);}
.gt-search-trigger svg{width:15px; height:15px; flex-shrink:0;}
.gt-search-label{flex:1; font-weight:400; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.gt-search-kbd{
  font-family:var(--mono); font-size:10.5px; color:var(--text-faint);
  border:1px solid var(--border-soft);
  border-radius:5px; padding:2px 5px; line-height:1; flex-shrink:0;
}

/* ---------- TOP-BAR ICON BUTTONS (theme toggle, notification bell) ---------- */
.tbtn{
  width:38px; height:38px; border-radius:10px;
  background:var(--bg-elev); border:1px solid var(--border-soft);
  display:flex; align-items:center; justify-content:center;
  color:var(--text-dim); cursor:pointer; flex-shrink:0; transition:.12s;
}
.tbtn:hover{color:var(--text); border-color:var(--border);}
.tbtn svg{width:16px; height:16px;}

/* ---------- USER CHIP (avatar + two-line name/role, mockup pill) ---------- */
.gt-user .uchip{
  display:flex; align-items:center; gap:9px;
  padding:5px 12px 5px 5px;
  background:var(--bg-elev); border:1px solid var(--border-soft);
  border-radius:999px; cursor:pointer; transition:border-color .12s;
}
.gt-user .uchip:hover{border-color:var(--border); background:var(--bg-elev);}
.gt-user .uav{
  width:28px; height:28px; border-radius:50%;
  background:var(--accent); color:var(--on-accent);
  display:flex; align-items:center; justify-content:center;
  font-size:12px; font-weight:700; flex-shrink:0;
  overflow:hidden;
}
.gt-user .uchip-tx{display:flex; flex-direction:column; line-height:1;}
.gt-user .uchip .user-name{font-size:13px; font-weight:700; color:var(--text); line-height:1;}
.gt-user .uchip .user-plan{font-size:11px; color:var(--text-faint); line-height:1; margin-top:3px;}

/* ---------- 레벨/포인트 배지(게이미피케이션) — 상단 사용자 칩 옆 ---------- */
.uchip-level{
  display:flex; align-items:center; gap:7px;
  padding:5px 9px; border-radius:999px;
  background:var(--accent-soft); border:1px solid var(--border-soft);
  cursor:pointer; flex-shrink:0; user-select:none;
  transition:border-color .15s, background .15s;
}
.uchip-level:hover{ border-color:var(--accent); }
.uchip-level .lvl-badge{
  font-family:var(--display); font-weight:700; font-size:12px; letter-spacing:-.02em;
  color:var(--accent); white-space:nowrap;
}
.uchip-level .lvl-bar{
  width:52px; height:6px; border-radius:999px; overflow:hidden;
  background:var(--surface-2);
}
.uchip-level .lvl-bar-fill{
  height:100%; border-radius:999px; background:var(--accent);
  transition:width .4s ease;
}
/* 좁은 화면에선 배지만 남기고 미니 바는 숨겨 공간 절약(사용자 칩 축소와 동일 정책) */
@media (max-width: 820px){ .uchip-level .lvl-bar{ display:none; } }

/* ---------- 레벨 상세 모달 내부 ---------- */
.lvl-modal-top{ display:flex; align-items:center; gap:14px; margin-bottom:12px; }
.lvl-modal-badge{
  font-family:var(--display); font-weight:700; font-size:22px; letter-spacing:-.02em;
  color:var(--on-accent); background:var(--accent);
  padding:8px 14px; border-radius:12px; white-space:nowrap;
}
.lvl-modal-meta{ display:flex; flex-direction:column; gap:3px; }
.lvl-modal-points{ font-size:16px; font-weight:700; color:var(--text); }
.lvl-modal-next{ font-size:12.5px; color:var(--text-dim); }
.lvl-modal-bar{
  width:100%; height:10px; border-radius:999px; overflow:hidden;
  background:var(--surface-2); margin-bottom:20px;
}
.lvl-modal-bar-fill{ height:100%; border-radius:999px; background:var(--accent); transition:width .4s ease; }
.lvl-modal-section-label{
  font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.04em;
  color:var(--text-faint); margin:14px 0 8px;
}
.lvl-modal-table{ display:flex; flex-direction:column; gap:1px; border-radius:9px; overflow:hidden; }
.lvl-modal-table .lvl-row{
  display:flex; align-items:center; justify-content:space-between;
  padding:8px 11px; background:var(--bg-elev); font-size:13px;
}
.lvl-modal-table .lvl-row .lvl-row-label{ color:var(--text-dim); }
.lvl-modal-table .lvl-row .lvl-row-pts{ color:var(--accent); font-weight:700; font-family:var(--display); }
.lvl-modal-history{ display:flex; flex-direction:column; gap:1px; border-radius:9px; overflow:hidden; max-height:220px; overflow-y:auto; }
.lvl-modal-history .lvl-hrow{
  display:flex; align-items:center; justify-content:space-between;
  padding:7px 11px; background:var(--bg-elev); font-size:12.5px;
}
.lvl-modal-history .lvl-hrow .lvl-h-label{ color:var(--text-dim); }
.lvl-modal-history .lvl-hrow .lvl-h-time{ color:var(--text-faint); font-size:11px; margin-left:auto; margin-right:10px; }
.lvl-modal-history .lvl-hrow .lvl-h-pts{ color:var(--success); font-weight:700; }
.lvl-modal-history .lvl-h-empty{ padding:14px 11px; text-align:center; color:var(--text-faint); font-size:12.5px; background:var(--bg-elev); border-radius:9px; }

/* ---------- COMMAND PALETTE OVERLAY (Ctrl/⌘+K) ---------- */
.cmdk-overlay{
  display:none; position:fixed; inset:0; z-index:2500;   /* 모달 밴드 */
  background:color-mix(in srgb, var(--bg) 30%, black 55%);
  align-items:flex-start; justify-content:center; padding-top:14vh;
}
.cmdk-overlay.open{display:flex;}
.cmdk-panel{
  width:min(560px, 92vw); background:var(--bg-elev);
  border:1px solid var(--border); border-radius:14px; box-shadow:var(--shadow);
  overflow:hidden; display:flex; flex-direction:column; max-height:64vh;
}
.cmdk-input-row{display:flex; align-items:center; gap:10px; padding:14px 16px; border-bottom:1px solid var(--border-soft);}
.cmdk-input-row svg{width:18px; height:18px; color:var(--text-faint); flex-shrink:0;}
.cmdk-input{
  flex:1; background:none; border:none; outline:none; color:var(--text);
  font-family:var(--sans); font-size:16px;
}
.cmdk-input::placeholder{color:var(--text-faint);}
.cmdk-list{overflow-y:auto; padding:6px;}
.cmdk-item{
  display:flex; align-items:center; gap:11px; padding:10px 12px; border-radius:9px;
  cursor:pointer; color:var(--text-dim); font-size:14px;
}
.cmdk-item:hover, .cmdk-item.active{background:var(--surface); color:var(--text);}
.cmdk-item svg{width:16px; height:16px; flex-shrink:0; opacity:.85;}
.cmdk-item-sub{margin-left:auto; font-size:11.5px; color:var(--text-faint);}
.cmdk-empty{padding:22px 16px; text-align:center; color:var(--text-faint); font-size:13.5px;}

/* ---------- 🎵 easter-egg: 미니 음악 플레이어 (검색창 왼쪽) ---------- */
/* 큐에 곡이 있을 때만 노출. 기존 상단바 토큰(--bg-elev/--border-soft/--accent)을
   그대로 재사용해 검색 트리거·테마 버튼과 톤을 맞춘다. */
.mini-music-player{ display:none; }
.mini-music-player:not([hidden]){
  display:flex; align-items:center; gap:3px; flex-shrink:0;
  height:34px; padding:0 5px; border-radius:999px;
  background:var(--bg-elev); border:1px solid var(--border-soft);
}
.mmp-btn{
  display:inline-flex; align-items:center; justify-content:center;
  width:26px; height:26px; padding:0; border:none; border-radius:50%;
  background:none; color:var(--text-dim); cursor:pointer; transition:.12s; flex-shrink:0;
}
.mmp-btn:hover{ color:var(--text); background:rgba(255,255,255,.06); }
.mmp-btn svg{ width:15px; height:15px; }
.mmp-play{ color:var(--accent); }
/* 진행자가 아닐 때 재생 제어 버튼: 흐리게 + 잠금 커서(눌러도 안내 토스트만). */
.mmp-btn.mmp-locked{ opacity:.4; cursor:not-allowed; }
.mmp-btn.mmp-locked:hover{ background:none; color:var(--text-dim); }
.mmp-play.mmp-locked{ color:color-mix(in srgb, var(--accent) 45%, transparent); }
.mmp-range{
  width:64px; height:3px; accent-color:var(--accent);
  background:transparent; cursor:pointer; flex-shrink:0;
}
/* ---------- 🎵 음악 같이 듣기: "참여하기" 배너(우하단, 커스텀) ---------- */
.music-join-banner{
  position:fixed; right:18px; bottom:18px; z-index:1140;  /* 플로팅 밴드 */
  display:flex; align-items:center; gap:10px;
  max-width:min(360px, 92vw); padding:11px 12px 11px 14px;
  background:var(--bg-elev); border:1px solid var(--border); border-radius:var(--radius);
  box-shadow:var(--shadow); animation:mjbIn .18s ease;
}
@keyframes mjbIn{ from{ opacity:0; transform:translateY(8px); } to{ opacity:1; transform:none; } }
.mjb-ico{ font-size:16px; flex-shrink:0; }
.mjb-tx{ flex:1; min-width:0; font-size:12.5px; color:var(--text-dim); line-height:1.4; }
.mjb-tx b{ color:var(--text); }
.mjb-sub{ color:var(--text-faint); }
.mjb-join{
  flex-shrink:0; padding:7px 13px; border-radius:999px; border:1px solid var(--accent);
  background:var(--accent); color:var(--on-accent); font-size:12px; font-weight:700; cursor:pointer;
}
.mjb-join:hover{ filter:brightness(1.05); }
.mjb-x{
  flex-shrink:0; width:24px; height:24px; padding:0; border:none; border-radius:50%;
  background:none; color:var(--text-faint); font-size:13px; cursor:pointer;
}
.mjb-x:hover{ color:var(--text); background:rgba(255,255,255,.06); }

/* ---------- 📢 공지 브로드캐스트 배너(상단 중앙 고정) ---------- */
.announce-banner{
  position:fixed; top:16px; left:50%; transform:translateX(-50%); z-index:6000;  /* 전역 배너 밴드 */
  display:flex; align-items:flex-start; gap:11px;
  max-width:min(560px, 94vw); padding:13px 14px 13px 16px;
  background:var(--bg-elev); border:1px solid var(--accent);
  border-radius:var(--radius); box-shadow:var(--shadow); animation:abIn .2s ease;
}
@keyframes abIn{ from{ opacity:0; transform:translate(-50%,-10px); } to{ opacity:1; transform:translate(-50%,0); } }
.ab-ico{ font-size:17px; flex-shrink:0; line-height:1.4; }
.ab-body{ flex:1; min-width:0; }
.ab-msg{ font-size:13.5px; color:var(--text); line-height:1.5; white-space:pre-wrap; word-break:break-word; }
.ab-from{ margin-top:3px; font-size:11.5px; color:var(--text-faint); }
.ab-x{
  flex-shrink:0; width:24px; height:24px; padding:0; border:none; border-radius:50%;
  background:none; color:var(--text-faint); font-size:13px; cursor:pointer;
}
.ab-x:hover{ color:var(--text); background:rgba(255,255,255,.06); }

/* 공지 작성 모달 보조 요소(comfy-modal-* 스타일 재사용, 추가분만 정의) */
.ann-textarea{
  width:100%; box-sizing:border-box; margin-top:10px; padding:10px 12px;
  background:var(--surface-2); border:1px solid var(--border); border-radius:var(--radius-sm);
  color:var(--text); font-size:13.5px; line-height:1.5; resize:vertical; font-family:inherit;
}
.ann-textarea:focus{ outline:none; border-color:var(--accent); }
/* 공지 이력(작성 모달 하단) — 최근 공지 목록 + 개별 삭제 */
.ann-hist{ margin-top:14px; }
.ann-hist-tag{ font-size:11px; font-weight:700; color:var(--text-faint); margin-bottom:6px; }
.ann-hist-list{ max-height:220px; overflow-y:auto; display:flex; flex-direction:column; gap:6px; }
.ann-hist-empty{ font-size:12.5px; color:var(--text-faint); padding:8px 2px; }
.ann-hist-item{
  display:flex; align-items:flex-start; gap:9px;
  padding:9px 10px; background:var(--surface-2);
  border:1px solid var(--border-soft); border-radius:var(--radius-sm);
}
.ann-hist-item.is-system{ opacity:.72; }
.ann-hist-main{ flex:1; min-width:0; }
.ann-hist-msg{ font-size:13px; color:var(--text); line-height:1.5; white-space:pre-wrap; word-break:break-word; }
.ann-hist-meta{ margin-top:4px; font-size:11px; color:var(--text-faint); }
.ann-hist-del{
  flex-shrink:0; width:22px; height:22px; padding:0; border:none; border-radius:50%;
  background:none; color:var(--text-faint); font-size:12px; cursor:pointer;
}
.ann-hist-del:hover{ color:var(--text); background:rgba(255,255,255,.06); }
.ann-hist-del:disabled{ opacity:.5; cursor:default; }

/* 숨김 유지용 YouTube IFrame 호스트 — display:none은 일부 브라우저에서 재생을
   막으므로 1px 투명 처리(오디오만 사용). */
#musicYtHost{ position:fixed; left:0; bottom:0; width:1px; height:1px; opacity:0; pointer-events:none; overflow:hidden; z-index:-1; }

/* ---------- 🎵 easter-egg: URL 붙여넣기 팝오버 ---------- */
.music-url-pop-overlay{
  position:fixed; inset:0; z-index:2600; display:flex;   /* 모달 밴드 */
  align-items:flex-start; justify-content:center; padding-top:16vh;
  background:color-mix(in srgb, var(--bg) 30%, black 55%);
}
.music-url-pop{
  width:min(420px, 92vw); background:var(--bg-elev);
  border:1px solid var(--border); border-radius:var(--radius); box-shadow:var(--shadow);
  padding:20px; display:flex; flex-direction:column; gap:12px;
}
.mup-head{ font-size:16px; font-weight:700; color:var(--text); }
.mup-desc{ font-size:13px; color:var(--text-dim); line-height:1.5; }
.mup-input{
  width:100%; box-sizing:border-box; padding:11px 13px;
  background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-sm);
  color:var(--text); font-family:var(--sans); font-size:14px; outline:none;
}
.mup-input:focus{ border-color:var(--accent); }
.mup-input::placeholder{ color:var(--text-faint); }
.mup-actions{ display:flex; justify-content:flex-end; gap:8px; }
.mup-btn{
  padding:9px 16px; border-radius:var(--radius-sm); font-size:13px; font-weight:700; cursor:pointer;
  border:1px solid var(--border); transition:.12s;
}
.mup-btn.ghost{ background:none; color:var(--text-dim); }
.mup-btn.ghost:hover{ color:var(--text); border-color:var(--border); background:rgba(255,255,255,.04); }
.mup-btn.primary{ background:var(--accent); color:var(--on-accent); border-color:var(--accent); }
.mup-btn.primary:hover{ filter:brightness(1.05); }
/* URL 팝오버 안 채널 선택(1~99) 행 + 활성 채널 힌트 칩 */
.mup-ch-row{ display:flex; align-items:center; gap:10px; }
.mup-ch-label{ font-size:12.5px; font-weight:700; color:var(--text-dim); flex-shrink:0; }
.mup-ch-input{ width:90px; }
.mup-ch-input::-webkit-inner-spin-button{ opacity:1; }
.mup-ch-hints[hidden]{ display:none; }
.mup-ch-hints{ display:flex; flex-wrap:wrap; align-items:center; gap:6px; }
.mup-ch-hints-tag{ width:100%; font-size:11px; font-weight:700; color:var(--text-faint); }
.mup-ch-chip{
  max-width:100%; padding:5px 10px; border-radius:999px; cursor:pointer;
  background:var(--surface); border:1px solid var(--border-soft); color:var(--text-dim);
  font-size:11.5px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.mup-ch-chip:hover{ border-color:var(--accent); color:var(--text); }
.mup-ch-chip b{ color:var(--accent); }

/* ---------- 🎵 미니 플레이어 v3: 채널 뱃지·마퀴 제목·대기열 hover·참여 유도 바 ---------- */
/* 현재 채널 뱃지 */
/* 현재 채널 뱃지 = 채널 전환 버튼(클릭 시 채널 입력 팝오버). */
.mmp-badge{
  flex-shrink:0; padding:2px 7px; border-radius:999px; margin-right:1px;
  background:color-mix(in srgb, var(--accent) 18%, transparent);
  border:1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  color:var(--accent); font-size:10.5px; font-weight:800; letter-spacing:.02em; white-space:nowrap;
  cursor:pointer; transition:.12s;
}
.mmp-badge:hover{ background:color-mix(in srgb, var(--accent) 30%, transparent); border-color:var(--accent); }
/* 진행자(호스트) 뱃지 — 내가 진행자면 강조, 남이 진행자면 이름과 함께 조용히 표시. */
.mmp-hostbadge{
  flex-shrink:0; max-width:120px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  padding:2px 7px; border-radius:999px; margin-right:1px;
  font-size:10px; font-weight:700; letter-spacing:.01em;
  color:var(--text-faint); border:1px solid var(--border);
  background:rgba(255,255,255,.04);
}
.mmp-hostbadge.is-host{
  color:var(--accent); border-color:color-mix(in srgb, var(--accent) 40%, transparent);
  background:color-mix(in srgb, var(--accent) 14%, transparent);
}

/* 마퀴(흐르는) 제목 — 고정폭 컨테이너(overflow:hidden) 안에서 짧으면 정지,
   길면 좌로 무한 스크롤(요청 #1). 표준 단일 텍스트 패턴으로 폭이 UI를 넓히지 않는다. */
.mmp-title-wrap{ position:relative; overflow:hidden; max-width:120px; flex:0 1 auto; min-width:0; }
.mmp-title-text{ display:inline-block; white-space:nowrap; font-size:11.5px; font-weight:600; color:var(--text-dim); will-change:transform; }
/* 넘칠 때만 흐른다. 짧으면 애니메이션 없이 좌측 정렬 정적 표시.
   핑퐁 방식: 왼쪽으로 흘러 제목 끝까지 보여준 뒤(양끝 잠깐 정지) 다시 돌아온다.
   translateX 이동량은 "넘친 만큼"(--mmp-shift, 음수 px)이라 텍스트가 절대 화면 밖으로
   통째로 사라지지 않는다(이전 100%↔-100% 방식이 제목을 비게 만들던 근본 원인 수정). */
.mmp-title-wrap.is-marquee .mmp-title-text{ animation:mmpMarquee var(--mmp-marquee-dur,10s) ease-in-out infinite alternate; }
@keyframes mmpMarquee{
  0%, 16%   { transform:translateX(0); }
  84%, 100% { transform:translateX(var(--mmp-shift, 0)); }
}

/* 대기열 hover 확장(요청 #2) + 맨 앞 +URL(요청 #5) */
.mmp-queue{ position:relative; }
.mmp-queue-pop{
  display:none; position:absolute; top:calc(100% + 8px); right:0; z-index:5100;  /* 앵커드 팝오버 밴드 */
  width:min(260px, 78vw); padding:8px; flex-direction:column; gap:6px;
  background:var(--bg-elev); border:1px solid var(--border); border-radius:var(--radius-sm);
  box-shadow:var(--shadow);
}
.mmp-queue:hover .mmp-queue-pop, .mmp-queue:focus-within .mmp-queue-pop{ display:flex; }
.mmp-queue-head{ font-size:11px; font-weight:700; color:var(--text-faint); padding:2px 4px; }
.mmp-queue-add{
  display:inline-flex; align-items:center; gap:6px; justify-content:center;
  padding:8px 10px; border-radius:7px; cursor:pointer;
  background:color-mix(in srgb, var(--accent) 14%, transparent);
  border:1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  color:var(--accent); font-size:12.5px; font-weight:700;
}
.mmp-queue-add:hover{ filter:brightness(1.08); }
.mmp-queue-add svg{ width:14px; height:14px; }
.mmp-queue-list{ display:flex; flex-direction:column; gap:1px; max-height:230px; overflow-y:auto; }
.mmp-queue-item{
  padding:7px 8px; border-radius:6px; font-size:12px; color:var(--text-dim);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.mmp-queue-item.current{ background:var(--surface); color:var(--text); font-weight:700; }
.mmp-queue-empty{ padding:8px; font-size:11.5px; line-height:1.45; color:var(--text-faint); }

/* 미구독자용 참여 유도 바(요청 #4): 활성 채널이 있으면 미니 플레이어가 이 형태로 등장 */
.mmp-joinbar{ gap:8px; padding:0 8px; }
.mmp-live{ display:inline-flex; align-items:center; gap:6px; font-size:11.5px; font-weight:700; color:var(--text-dim); white-space:nowrap; }
.mmp-live-dot{ width:7px; height:7px; border-radius:50%; background:var(--accent); box-shadow:0 0 0 0 color-mix(in srgb, var(--accent) 60%, transparent); animation:mmpPulse 1.6s ease-out infinite; }
@keyframes mmpPulse{ 0%{ box-shadow:0 0 0 0 color-mix(in srgb, var(--accent) 55%, transparent); } 70%{ box-shadow:0 0 0 7px transparent; } 100%{ box-shadow:0 0 0 0 transparent; } }
.mmp-live-hint{ max-width:140px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:11px; color:var(--text-faint); }
.mmp-joinpill{
  display:inline-flex; align-items:center; gap:5px; flex-shrink:0;
  padding:5px 11px 5px 8px; border-radius:999px; cursor:pointer;
  background:var(--accent); border:1px solid var(--accent); color:var(--on-accent);
  font-size:11.5px; font-weight:700;
}
.mmp-joinpill:hover{ filter:brightness(1.05); }
.mmp-joinpill svg{ width:14px; height:14px; }

/* 좁은 화면에서 상단바가 눌릴 때 미니 플레이어가 레이아웃을 깨지 않도록 축소 */
@media (max-width:720px){
  .mmp-title-wrap{ max-width:96px; }
  .mmp-range{ width:44px; }
  .mmp-live-hint{ display:none; }
}
/* ---------- ICON RAIL (top-level modules, re-homed from the top bar) ----------
   The module buttons (.module-item[data-module]) were moved out of the top
   bar's .gt-nav into this vertical rail. .gt-nav is now a column of compact
   icon+micro-label tiles. Every hook attribute/id on the tiles is unchanged;
   only the layout/skin differs, so switchModule()/switchView()/applyModule-
   MenuVisibility() keep working untouched. */
.icon-rail{
  grid-area:rail;
  background:var(--rail, var(--sidebar));
  border-right:1px solid var(--border-soft);
  display:flex; flex-direction:column; align-items:center;
  padding:12px 0 14px; gap:3px;
  overflow-y:auto; overflow-x:hidden;
}
.icon-rail::-webkit-scrollbar{width:0;}
.gt-nav{display:flex; flex-direction:column; align-items:center; gap:3px;}
/* Rail tiles per the mockup: 64px wide, icon housed in a 44×38 rounded box,
   a 10px micro-label below. The soft-accent chip sits behind the ICON box on
   the active tile (not the whole tile), plus a 3px left accent bar + accent
   text — matching the redesign spec exactly. */
.gt-nav .nav-item{
  width:64px; flex-direction:column; align-items:center; justify-content:center; gap:6px;
  padding:10px 0 8px; font-size:10px; font-weight:600; line-height:1.15; letter-spacing:-.1px;
  border-left:none; border-radius:12px; text-align:center; white-space:normal;
  color:var(--text-faint);
}
.gt-nav .nav-item svg{
  width:20px; height:20px; opacity:1;
  display:flex; align-items:center; justify-content:center;
}
/* Icon box: a 44×38 rounded slot that fills with soft-accent when active. */
.gt-nav .nav-item{position:relative;}
.gt-nav .nav-item::before{
  content:""; position:absolute; top:8px; left:50%; transform:translateX(-50%);
  width:44px; height:38px; border-radius:11px; background:transparent;
  transition:background .12s; z-index:0;
}
.gt-nav .nav-item svg{z-index:1;}
.gt-nav .nav-item span, .gt-nav .nav-item{position:relative;}
.gt-nav .nav-item .nav-idx{display:none;}
.gt-nav .nav-item:hover{color:var(--text-dim); background:rgba(255,255,255,.04);}
/* Day theme now uses a coherent LIGHT rail (--rail:#e9e9e3), matching the rest
   of the light theme so day mode reads as one consistent surface (the earlier
   near-black rail against otherwise-light chrome looked half-broken). The idle
   icon color falls through to var(--text-faint); only the hover wash is
   overridden here because the base rule's white-tinted hover is invisible on a
   light strip — a dark-tinted wash reads correctly instead. */
html[data-theme="day"] .gt-nav .nav-item:hover{color:var(--text); background:rgba(20,20,15,.055);}
.gt-nav .nav-item.active{border-left:none; background:none; color:var(--accent);}
.gt-nav .nav-item.active::before{background:var(--accent-soft);}
.gt-nav .nav-item.active svg{opacity:1; color:var(--accent);}
/* Accent bar on the left edge marks the active module (replaces the old
   underline that suited a horizontal bar). */
.gt-nav .nav-item.active::after{
  content:""; position:absolute; left:-1px; top:14px; bottom:14px; width:3px;
  background:var(--accent); border-radius:0 3px 3px 0; z-index:2;
}
/* The pending-signup / missing-key dots that live inside 회원 관리·설정 tiles
   sit at the top-right corner of the compact tile instead of inline. */
.gt-nav .nav-item{position:relative;}
.gt-nav .nav-item > span[id="navPendingWarn"],
.gt-nav .nav-item > span[id="navKeyWarn"]{
  position:absolute; top:7px; right:12px; margin-left:0 !important;
}
/* Apps dropdown */
.gt-apps{position:relative;}
.gt-apps-trigger{padding:7px 10px; font-size:13px; border-radius:8px;}
.gt-apps-trigger .chev{width:13px;height:13px;opacity:.6;transition:transform .15s;}
.gt-apps.open .gt-apps-trigger{background:var(--surface); color:var(--text);}
.gt-apps.open .gt-apps-trigger .chev{transform:rotate(180deg);}
.gt-apps-menu{
  display:none; position:absolute; top:calc(100% + 6px); left:0; z-index:40;
  min-width:210px; background:var(--bg-elev);
  border:1px solid var(--border-soft); border-radius:11px;
  box-shadow:0 12px 30px rgba(0,0,0,.22); padding:6px; gap:1px; flex-direction:column;
}
.gt-apps.open .gt-apps-menu{display:flex;}
.gt-apps-menu .nav-item{font-size:13px; border-left:none;}
.gt-spacer{flex:1;}
.gt-user{display:flex; align-items:center; gap:9px; flex-shrink:0;}

/* ---------- SIDEBAR (context panel) ---------- */
/* The SIDEBAR now acts as the 2nd-tier CONTEXT PANEL, sitting to the right of
   the icon rail. A slightly lighter surface + a hairline against the rail
   visually separates the two tiers (rail = which module, panel = within it). */
.sidebar{
  grid-area:sidebar;
  background:color-mix(in srgb, var(--sidebar) 70%, var(--surface));
  border-right:1px solid var(--border-soft);
  border-left:1px solid var(--border-soft);
  display:flex; flex-direction:column;
  padding:18px 12px;
  overflow-y:auto;
}
/* Active module's own internal nav (re-homed from the old global top bar).
   Shown only while that module is active. */
.sidebar-modnav{display:none; flex-direction:column; gap:2px; margin-bottom:14px;}
.sidebar-modnav.active{display:flex;}
.sidebar-modnav .modview-item{width:100%; font-size:14px;}
.sidebar-modnav .modview-item.active{background:var(--surface); color:var(--text); border-left-color:var(--accent);}
.sidebar-modnav .modview-item.active svg{opacity:1;color:var(--accent);}

/* ── 사이드 메뉴 디자인 통일 (스토리 메이커 신규 네비 기준) ─────────────────
   기존 셸 사이드바는 왼쪽 세로바(border-left) + 사각 하이라이트라 앱 내부에서
   새로 만든 스토리 메이커 네비(라운드 박스 + 라임 아이콘)와 인상이 달랐다.
   사용자 지시로 전 모듈 사이드 메뉴를 그 디자인으로 맞춘다.
   · 항목: 배경 투명 + 라운드 9px + 1px 투명 테두리
   · hover: 살짝 밝은 면
   · active: 한 단계 더 밝은 면 + 테두리 + 라임 아이콘
   왼쪽 세로 액센트바는 제거해 라운드 박스만 남긴다. */
.sidebar-modnav .nav-item,
.sidebar-ctx .nav-item{
  border-left:none;
  border:1px solid transparent;
  border-radius:9px;
  padding:8px 10px;
  gap:9px;
  font-size:13px;
  font-weight:700;
  color:var(--text-dim);
  background:transparent;
}
.sidebar-modnav .nav-item svg,
.sidebar-ctx .nav-item svg{width:15px;height:15px;opacity:.9;color:var(--accent);}
.sidebar-modnav .nav-item:hover,
.sidebar-ctx .nav-item:hover{
  background:var(--surface); color:var(--text); border-color:transparent;
}
.sidebar-modnav .nav-item.active,
.sidebar-modnav .modview-item.active,
.sidebar-ctx .nav-item.active{
  background:var(--bg-elev);
  border-color:var(--border-soft);
  border-left-color:var(--border-soft);
  color:var(--text);
}
.sidebar-modnav .nav-item.active svg,
.sidebar-ctx .nav-item.active svg{opacity:1;color:var(--accent);}
/* 섹션 라벨도 같은 톤(작고 넓은 자간의 대문자)으로 */
.sidebar-modnav .sidebar-ctx-label,
.sidebar .sidebar-ctx-label{
  font-size:9px; letter-spacing:.12em; font-weight:800;
  color:var(--text-faint); padding:2px 6px 7px;
}

/* 챗봇 메이커·스토리 메이커의 셸 사이드바 페이지 목록은 앱 내부 워크플로 네비와
   중복되어 제거했다(#modnav-content는 빈 컨테이너로만 남음). 관련 규칙도 함께 정리.
   app.js가 아직 showing-prompt/showing-mov 클래스를 토글하지만 대상이 없어 무해하다. */
.content-subnav-actions{display:none; flex-direction:column; gap:2px;}

.sidebar-ctx{display:none; flex-direction:column; gap:5px; margin-bottom:14px;}
.sidebar-ctx.active{display:flex;}
.sidebar-ctx-label{
  font-size:11px; color:var(--text-faint); font-weight:700;
  text-transform:uppercase; letter-spacing:.1em;
  padding:2px 6px 8px;
}
/* create-tab restyled as a vertical sidebar list */
.sidebar-ctx .create-tab{
  padding:9px 12px; font-size:13px; border-radius:9px; width:100%;
  color:var(--text-dim); background:var(--surface); border:1px solid var(--border-soft);
  justify-content:flex-start;
}
.sidebar-ctx .create-tab svg{width:15px;height:15px;}
.sidebar-ctx .create-tab.active{background:var(--accent); color:var(--on-accent); border-color:transparent;}
/* settings-tab restyled as a vertical sidebar list */
.sidebar-ctx .settings-tab{
  padding:9px 12px; font-size:13px; border-radius:9px; width:100%;
  background:var(--surface); border:1px solid var(--border-soft); color:var(--text-dim);
}
.sidebar-ctx .settings-tab.active{background:var(--accent); color:var(--on-accent); border-color:transparent;}
/* gallery filter chips as a vertical sidebar list */
#galleryFilterChips{display:flex; flex-direction:column; gap:5px;}
#galleryFilterChips .chip{width:100%; border-radius:9px; text-align:center; padding:9px 12px; font-size:13px;}

.logo{display:flex; align-items:center; gap:11px; padding:2px 4px 24px;}
.logo-mark{
  width:34px; height:34px; border-radius:9px;
  background:var(--ink); color:var(--on-accent);
  display:flex; align-items:center; justify-content:center;
  flex-shrink:0; position:relative;
}
html[data-theme="day"] .logo-mark{ background:var(--ink); color:#fafaf6; }
.logo-mark svg{width:17px;height:17px;}
/* The two-line text stack was visually sitting higher than the icon square
   next to it even though align-items:center was centering their bounding
   boxes correctly — tight line-height on all-caps, no-descender text still
   leaves ascender padding above the glyphs that the icon square doesn't
   have, so the *visible* text always read a few px above true center.
   line-height:1 removes that slack so the boxes' centers line up with what
   you actually see, not just their CSS box heights. */
.logo-textwrap{display:flex; flex-direction:column; justify-content:center;}
.logo-text{font-family:var(--display); font-weight:700; font-size:18px; letter-spacing:-.4px; line-height:1; text-transform:uppercase;}
.logo-sub{font-size:11px; line-height:1; color:var(--text-faint); margin-top:3px; letter-spacing:.06em; text-transform:uppercase;}
/* Topbar logo doubles as a "홈으로" shortcut back to 살펴보기 — cursor +
   subtle hover feedback signal that, unlike the auth-screen logo (decorative
   only, no onclick), this one is clickable. */
.gt-logo{cursor:pointer;}
.gt-logo:hover .logo-mark{opacity:.85;}

.nav-group-label{
  font-size:11px; color:var(--text-faint); font-weight:700;
  text-transform:uppercase; letter-spacing:.1em;
  padding:16px 10px 7px; display:flex; align-items:center; gap:8px;
}
.nav-group-label::after{content:""; flex:1; height:1px; background:var(--border-soft);}
.nav{display:flex; flex-direction:column; gap:1px;}
.nav-item{
  display:flex; align-items:center; gap:11px;
  padding:9px 10px; border-radius:8px;
  color:var(--text-dim); text-decoration:none;
  font-size:14.5px; font-weight:600;
  cursor:pointer; border-left:2px solid transparent;
  transition:background .12s, color .12s, border-color .12s;
  position:relative;
}
.nav-item .nav-idx{
  font-family:var(--mono); font-size:11px; color:var(--text-faint);
  width:14px; flex-shrink:0;
}
.nav-item svg{width:16px;height:16px;flex-shrink:0;opacity:.8;}
.nav-item:hover{background:var(--surface); color:var(--text);}
.nav-item.active{background:var(--surface); color:var(--text); border-left-color:var(--accent);}
.nav-item.active svg{opacity:1;color:var(--accent);}
.nav-item.active .nav-idx{color:var(--accent);}
/* "Apps" group items — external sub-apps opened in a new tab (↗ affordance). */
.nav-item.nav-app{color:var(--text-dim);}
.nav-item.nav-app .nav-ext{
  margin-left:auto; font-size:11px; color:var(--text-faint); opacity:.7; flex-shrink:0;
}
.nav-item.nav-app:hover .nav-ext{opacity:1; color:var(--accent);}

.sidebar-spacer{flex:1;}

.local-card{
  background:var(--surface); border:1px solid var(--border-soft); border-radius:var(--radius);
  padding:13px 14px; margin-bottom:10px;
}
.local-card-top{display:flex; justify-content:space-between; align-items:baseline; margin-bottom:5px;}
.local-label{font-size:11.5px; color:var(--text-faint); letter-spacing:.04em;}
.local-val{font-family:var(--display); font-size:21px; font-weight:700; letter-spacing:-.5px;}
/* Thin usage progress bar (mockup .usage-bar). */
.usage-bar{height:5px; border-radius:3px; background:var(--bg-elev); overflow:hidden; margin:9px 0 7px;}
.usage-bar > i{display:block; height:100%; width:0%; border-radius:3px; background:var(--accent); transition:width .3s ease;}
.local-sub{font-size:11.5px; color:var(--text-faint); margin-top:2px;}
.status-line{display:flex; align-items:center; gap:6px; margin-top:9px; font-size:12px; color:var(--text-faint);}
.status-dot{width:6px;height:6px;border-radius:50%;background:var(--text-faint);flex-shrink:0;}
.status-dot.ok{background:var(--success);}
.status-dot.warn{background:var(--warn);}

/* 버전 정보 타일 — 아이콘 레일(.icon-rail) 최하단에 margin-top:auto로 고정되는
   컴팩트 타일. 폭 좁은 컨텍스트 열에서 텍스트가 잘리던 문제를 피해, 항상 같은
   폭으로 보이는 좌측 레일에 아이콘+버전번호만 압축 노출한다. 클릭 시 업데이트
   내역 모달. 레일 타일과 동일한 토큰(--text-faint/--accent)만 사용해 셸 디자인과
   일관성 유지. */
.rail-version{
  margin-top:auto; width:64px;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:5px;
  padding:9px 2px 6px; border-radius:12px;
  font-size:9.5px; font-weight:600; line-height:1.15; letter-spacing:-.2px;
  color:var(--text-faint); text-align:center; white-space:normal; word-break:break-all;
  cursor:pointer; user-select:none; transition:color .15s ease, background .15s ease;
}
.rail-version svg{ width:17px; height:17px; flex-shrink:0; }
.rail-version:hover{ color:var(--accent); background:rgba(255,255,255,.04); }
html[data-theme="day"] .rail-version:hover{ color:var(--accent); background:rgba(20,20,15,.055); }
.rail-version:focus-visible{ outline:1px solid var(--accent); outline-offset:1px; }

/* 업데이트 내역 모달 본문 — comfy-modal-box 안에서 스크롤되는 버전 목록. */
.changelog-list{ max-height:56vh; overflow-y:auto; margin:4px 0 2px; }
.changelog-entry{ padding:11px 0; border-top:1px solid var(--border-soft); }
.changelog-entry:first-child{ border-top:none; }
.changelog-entry-head{ display:flex; align-items:baseline; gap:8px; margin-bottom:6px; }
.changelog-ver{ font-family:var(--display); font-size:13px; font-weight:700; color:var(--accent); letter-spacing:-.3px; }
.changelog-title{ font-size:13px; font-weight:600; color:var(--text); }
.changelog-date{ margin-left:auto; font-size:11px; color:var(--text-faint); white-space:nowrap; }
.changelog-items{ margin:0; padding-left:16px; }
.changelog-items li{ font-size:12.5px; line-height:1.6; color:var(--text-faint); }

.user-row{display:flex; align-items:center; gap:9px; padding:8px; border-radius:8px; cursor:pointer;}
.user-row:hover{background:var(--surface);}
.avatar{
  width:28px;height:28px;border-radius:50%;
  background:var(--accent); color:var(--on-accent);
  display:flex;align-items:center;justify-content:center;
  font-size:12px;font-weight:700;flex-shrink:0;
}
.avatar svg{width:15px;height:15px;}
.user-name{font-size:14px;font-weight:700;}
.user-plan{font-size:11.5px;color:var(--text-faint);}
.theme-toggle{
  margin-left:auto; width:30px;height:30px;border-radius:8px;
  background:var(--bg-elev); border:1px solid var(--border-soft);
  display:flex;align-items:center;justify-content:center;cursor:pointer;color:var(--text-dim);
}
.theme-toggle svg{width:14px;height:14px;}

/* ---------- MAIN ---------- */
.main{grid-area:main; display:flex; flex-direction:column; min-height:0; height:100%; overflow:hidden; position:relative;}
/* 임베드 모듈(LoRA/챗봇/무비/NAI/3D)은 상단 모듈 네비 탭 자체에 이미 이름이
   보이고, 그 아래 sidebar-ctx-label에도 같은 이름이 또 나온다 — 이 줄까지
   같은 이름을 세 번째로 보여주는 건 중복이라 접는다(높이째로 0). generate는
   뷰마다 제목/부제가 실제로 바뀌므로(살펴보기/만들기/...) 그대로 둔다. */
.topbar.topbar-collapsed{height:0; border-bottom:none; padding:0; overflow:hidden;}
.topbar{
  height:58px; flex-shrink:0;
  border-bottom:1px solid var(--border-soft);
  display:flex; align-items:center;
  padding:0 26px; gap:14px;
  background:color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter:blur(6px);
  position:relative; z-index:5;
}
.topbar-title{font-family:var(--serif); font-size:18px; font-weight:700; letter-spacing:-.2px;}
/* The big view title now lives in the top-bar breadcrumb (#gtCrumbView), so
   hide the duplicate here on wide screens — the descriptive #topbarSub line
   and the "새로 생성" button remain. #topbarTitle is NOT removed (nav JS still
   writes its .textContent, and updateTopbarTitleVisibility toggles the wrap);
   it's only visually hidden. On mobile (breadcrumb label collapses) it comes
   back so the row still names the current view. */
.topbar-title{display:none;}
.topbar-sub{font-size:12px; color:var(--text-faint); margin-top:1px;}
.topbar-spacer{flex:1;}
.icon-btn{
  width:34px;height:34px;border-radius:8px;background:var(--surface);
  border:1px solid var(--border-soft);display:flex;align-items:center;justify-content:center;
  color:var(--text-dim);cursor:pointer;flex-shrink:0;
}
.icon-btn:hover{color:var(--text);border-color:var(--border);}
.icon-btn svg{width:16px;height:16px;}
.btn-primary{
  display:flex;align-items:center;gap:7px;
  background:var(--accent); border:1px solid var(--accent); color:var(--on-accent);
  padding:8.5px 16px;border-radius:8px;font-weight:700;font-size:14px;
  cursor:pointer;white-space:nowrap;flex-shrink:0;
}
.btn-primary:hover{filter:brightness(1.06);}
.btn-primary svg{width:15px;height:15px;}
.btn-ghost{
  display:flex;align-items:center;gap:7px;
  background:var(--surface); border:1px solid var(--border-soft); color:var(--text);
  padding:8.5px 16px;border-radius:8px;font-weight:700;font-size:14px;cursor:pointer;
  white-space:nowrap;flex-shrink:0;
}
.btn-ghost:hover{border-color:var(--border);}
.btn-ghost.danger{color:var(--danger); border-color:color-mix(in srgb, var(--danger) 35%, var(--border-soft));}
/* [버튼 세로 줄바꿈 수정] 이 두 클래스는 앱 전체 액션 버튼의 기반 클래스인데
   white-space:nowrap이 없어, 좁은 flex 툴바(예: .comfy-group-actions의 flex:1 1 auto,
   .feedback-toolbar-actions의 flex:1)에서 flex-shrink로 짓눌리면 한글 라벨이
   "저장"→"저/장"처럼 세로로 쪼개질 수 있었다. 라벨은 모두 한 줄짜리 짧은 동작명이라
   nowrap을 적용해도 기존에 의도한 줄바꿈은 없다(회귀 없음). */

.content{
  flex:1;overflow-y:auto;padding:24px 30px 60px;position:relative;
  background-image:radial-gradient(color-mix(in srgb, var(--text) 7%, transparent) 1px, transparent 1px);
  background-size:20px 20px;
  background-position:-9px -9px;
}
/* 임베드 모듈(LoRA/챗봇 메이커/무비 메이커/NAI/3D 시뮬레이터)이 활성화되면 이
   .content의 패딩+점무늬 배경이 iframe 가장자리 밖으로 얇게 드러나 "흰색/밝은
   테두리 선"처럼 보이는 문제가 있었다 — 각 모듈은 자기 자신의 배경을 이미 꽉
   채워 그리므로, 임베드 모드에서는 패딩과 점무늬를 완전히 제거해 이음매 없이
   맞닿게 한다. (public/app.js의 updateTopbarTitleVisibility()가 토글) */
.content.content-embed{padding:0;background-image:none;}
.view{display:none;animation:fadeIn .2s ease;height:100%;}
.view.active{display:block;}
/* Embedded iframe module views (LoRA/prompt/mov/nai/sim3d) need a flex column
   layout so their iframe/status-strip stack and stretch to fill height. This
   used to be an inline style="display:flex..." on each view div, but inline
   styles always beat stylesheet rules — including .view{display:none} above
   — so every one of these views stayed visibly on screen regardless of the
   'active' class, stacking underneath whichever module was opened next.
   Scoping "flex" to .view-embed.active (3 classes) keeps it hidden by
   default and only switches to flex once active, same as every other view. */
.view.view-embed.active{display:flex;flex-direction:column;position:relative;}
/* ComfyUI 실 웹 UI iframe은 ComfyUI 자체의 캔버스/단축키가 클릭·키보드 이벤트를
   모두 가로채서, 우리 셸의 아이콘 레일을 다시 누를 방법이 없으면 사용자가 그
   화면에 "갇히는" 문제가 있었다("들어갔는데 나갈 수가 없다"). iframe 위에 항상
   떠 있는 고정 위치 탈출 버튼을 두어, 셸 내비게이션과 무관하게 항상 확실한
   탈출 경로를 보장한다. */
.comfy-webui-exit{
  position:absolute; top:14px; right:14px; z-index:40;
  display:flex; align-items:center; gap:6px;
  padding:9px 14px; border-radius:10px; border:1px solid var(--accent);
  background:color-mix(in srgb, var(--bg) 88%, transparent);
  color:var(--accent); font-size:12.5px; font-weight:700; cursor:pointer;
  box-shadow:0 10px 26px -14px rgba(0,0,0,.5);
  backdrop-filter:blur(6px);
}
.comfy-webui-exit svg{width:15px;height:15px;flex-shrink:0;}
.comfy-webui-exit:hover{background:var(--accent); color:var(--on-accent);}
/* 3D 시뮬레이터 튜토리얼 진입 버튼 (작업 #33) — 좌측 셸 사이드 탭을 없애고 나서
   #view-sim3d(.view-embed) 우상단에 떠 있는 작은 오버레이 버튼으로, 기존 iframe
   (public/apps/3d/*, 무접촉) 위에 얹혀 sim3d-tutorial 뷰로 라우팅한다. 위
   .comfy-webui-exit와 같은 "iframe 위에 떠 있는 고정 오버레이" 패턴을 재사용. */
.sim3d-tut-btn{
  position:absolute; top:14px; right:14px; z-index:6;
  padding:7px 13px; font-size:12.5px; font-weight:700;
  box-shadow:0 10px 26px -14px rgba(0,0,0,.5);
}
.sim3d-tut-btn svg{width:15px;height:15px;}
/* AI 채팅 view도 같은 이유로 flex column이 필요하다 — #chatPanel(.chat-panel)이
   flex:1;min-height:0으로 부모의 남은 세로 공간을 꽉 채우도록 설계되어 있는데
   (예전엔 .create-view{display:flex;flex-direction:column}의 자식이었다), 승격
   이후 이 view가 곧바로 부모가 되었으므로 여기서도 flex 컨텍스트를 만들어줘야
   채팅 스레드가 옛날처럼 전체 높이를 채운다. */
.view.view-chat-flex.active{display:flex;flex-direction:column;}
@keyframes fadeIn{from{opacity:0;transform:translateY(4px);}to{opacity:1;transform:none;}}

h1.page-h{font-family:var(--serif); font-size:24px;margin:0 0 3px;font-weight:700;letter-spacing:-.3px;}
p.page-sub{color:var(--text-faint);font-size:14px;margin:0 0 22px;}

.card{background:var(--surface);border:1px solid var(--border-soft);border-radius:var(--radius);padding:17px;}

/* LoRA 메이커 — slim status strip above the embedded tool iframe (the tool
   auto-starts with the studio server now, so this is no longer a full-page
   status/launch screen — just a thin header over the live embedded app). */
.lora-status-strip{flex-shrink:0;padding:10px 20px 8px;}
/* 좁아져도 줄바꿈·세로 접힘 없이 한 줄을 유지하고, 넘치면 가로 스크롤로 처리한다
   (스튜디오 레이아웃 철칙: 재배치 금지). */
.lora-status{display:flex;align-items:center;gap:14px;flex-wrap:nowrap;overflow-x:auto;}
.lora-status>*{flex-shrink:0;}
.lora-status-note{margin:0;font-size:12px;white-space:nowrap;word-break:keep-all;overflow:hidden;text-overflow:ellipsis;}
/* Mirrored LoRA Maker project list, rendered natively in #modnav-lora from
   postMessage broadcasts — reuses .nav-item's look so it reads as the same
   sidebar, not a bolted-on extra widget. */
.lora-project-item{font-size:13px;font-weight:500;}
.lora-project-item.active{background:var(--surface); color:var(--text); border-left-color:var(--accent);}
.lora-proj-emoji{font-size:14px;flex-shrink:0;}
.lora-proj-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.lora-proj-dot{width:6px;height:6px;border-radius:50%;background:var(--text-faint);flex-shrink:0;}
.lora-proj-dot.status-training,.lora-proj-dot.status-running{background:var(--ink);}
.lora-proj-dot.status-completed,.lora-proj-dot.status-done{background:var(--success);}
.lora-proj-dot.status-error,.lora-proj-dot.status-failed{background:var(--danger);}
.lora-badge{padding:5px 12px;border-radius:999px;font-size:12.5px;font-weight:700;border:1px solid var(--border-soft);}
.lora-badge.running{background:color-mix(in srgb, var(--accent) 16%, transparent);color:var(--accent);border-color:color-mix(in srgb, var(--accent) 40%, var(--border-soft));}
.lora-badge.stopped{background:var(--bg-elev);color:var(--text-dim);}
.lora-badge.checking{background:var(--bg-elev);color:var(--text-faint);}
.lora-status code{font-size:12.5px;background:var(--bg-elev);padding:1px 5px;border-radius:5px;}

.banner{
  display:flex; gap:11px; border-radius:11px; padding:13px 15px; font-size:13.5px; line-height:1.6;
  border:1px solid var(--border-soft); background:var(--surface); color:var(--text-dim); margin-bottom:16px;
}
.banner svg{width:15px;height:15px;flex-shrink:0;margin-top:1px;}
.banner.info{border-color:color-mix(in srgb, var(--ink) 30%, var(--border-soft)); background:var(--ink-soft);}
.banner.info svg{color:var(--ink);}
.banner.warn{border-color:color-mix(in srgb, var(--warn) 40%, var(--border-soft)); background:color-mix(in srgb, var(--warn) 12%, transparent);}
.banner.warn svg{color:var(--warn);}
.banner.danger{border-color:color-mix(in srgb, var(--danger) 40%, var(--border-soft)); background:color-mix(in srgb, var(--danger) 10%, transparent); color:var(--text);}
.banner.danger svg{color:var(--danger);}
.banner a{color:var(--accent); text-decoration:none; font-weight:700; margin-left:4px;}

/* ---- dashboard ---- */
.stat-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:14px;margin-bottom:22px;}
.stat-card{background:var(--surface);border:1px solid var(--border-soft);border-radius:var(--radius);padding:17px 18px;}
.stat-top{display:flex;justify-content:space-between;align-items:flex-start;}
.stat-icon{width:28px;height:28px;border-radius:7px;background:var(--accent-soft);display:flex;align-items:center;justify-content:center;color:var(--accent);}
.stat-icon svg{width:14px;height:14px;}
.stat-val{font-family:var(--display); font-size:29px;font-weight:700;margin:13px 0 2px;letter-spacing:-1px;}
.stat-label{font-size:12.5px;color:var(--text-faint);}

.quick-row{display:grid;grid-template-columns:1.4fr 1.4fr 1fr;gap:14px;margin-bottom:28px;}
.quick-card{
  border-radius:var(--radius);padding:20px;cursor:pointer;
  border:1px solid var(--border-soft); background:var(--surface);
  transition:transform .12s, border-color .12s;
}
.quick-card:hover{transform:translateY(-2px);border-color:var(--accent);}
/* Feature card (first quick-action) gets the mockup's subtle ink→surface wash. */
.quick-card.feat{background:linear-gradient(150deg, color-mix(in srgb, var(--ink) 26%, var(--surface)), var(--surface) 62%);}
/* Compact "setup" card (3rd) — muted icon, laid out as a top/bottom split. */
.quick-card.mini{display:flex;flex-direction:column;justify-content:space-between;}
.quick-card.mini .quick-icon{background:var(--surface-2);}
.quick-card.mini .quick-icon svg{color:var(--text-dim);}
.quick-tag{font-size:11px;font-weight:700;color:var(--accent);text-transform:uppercase;letter-spacing:.08em;margin-bottom:11px;}
.quick-icon{width:40px;height:40px;border-radius:11px;background:var(--ink);display:flex;align-items:center;justify-content:center;margin-bottom:15px;}
.quick-icon svg{width:20px;height:20px;color:#fff;}
html[data-theme="day"] .quick-icon svg{color:#fafaf6;}
.quick-title{font-family:var(--serif); font-size:17px;font-weight:700;margin-bottom:5px;}
.quick-desc{font-size:12.5px;color:var(--text-faint);line-height:1.55;}

.section-head{display:flex;align-items:baseline;margin-bottom:13px;gap:10px;}
.section-title{font-family:var(--serif); font-size:17px;font-weight:700;}
.section-head::after{content:"";flex:1;height:1px;background:var(--border-soft);}
.section-link{font-size:12.5px;color:var(--text-faint);cursor:pointer;flex-shrink:0;}
.section-link:hover{color:var(--accent);}

/* ===== 신고(마스터 전용) ===== */
.feedback-toolbar{display:flex; align-items:center; justify-content:space-between; gap:12px; margin:6px 0 14px;}
.feedback-toolbar-actions{display:flex; gap:8px;}
.feedback-count{font-family:var(--sans); font-size:12.5px; font-weight:600; color:var(--text-dim); margin-left:4px;}
.feedback-list{display:flex; flex-direction:column; gap:10px;}
.feedback-card{
  background:var(--surface); border:1px solid var(--border-soft); border-radius:var(--radius-sm);
  padding:12px 14px;
}
.feedback-card-head{display:flex; align-items:center; justify-content:space-between; gap:10px; margin-bottom:7px;}
.feedback-card-who{font-size:13px; font-weight:700; color:var(--text);}
.feedback-card-meta{display:flex; align-items:center; gap:10px;}
.feedback-card-time{font-size:11.5px; color:var(--text-faint);}
.feedback-card-del{
  background:transparent; border:none; cursor:pointer; color:var(--text-faint);
  width:24px; height:24px; display:flex; align-items:center; justify-content:center; border-radius:6px;
}
.feedback-card-del:hover{background:var(--surface-2); color:var(--danger);}
.feedback-card-del svg{width:14px; height:14px;}
.feedback-card-text{font-size:13.5px; line-height:1.6; color:var(--text); white-space:pre-wrap; word-break:break-word;}
.feedback-empty{padding:28px 10px; text-align:center; color:var(--text-faint); font-size:13px;}

/* 신고 처리 상태 뱃지 + 관리자 회신 UI + 회원 "내 신고" — 셸 디자인 토큰만 사용 */
.fb-badge{display:inline-flex; align-items:center; font-family:var(--sans); font-size:11px; font-weight:700; line-height:1; padding:4px 8px; border-radius:999px; border:1px solid transparent; white-space:nowrap;}
.fb-badge-received{color:var(--text-dim); background:var(--surface-2); border-color:var(--border-soft);}
.fb-badge-in_progress{color:var(--accent); background:var(--accent-soft); border-color:color-mix(in srgb, var(--accent) 40%, transparent);}
.fb-badge-resolved{color:var(--success); background:color-mix(in srgb, var(--success) 14%, transparent); border-color:color-mix(in srgb, var(--success) 40%, transparent);}
.fb-badge-rejected{color:var(--danger); background:color-mix(in srgb, var(--danger) 12%, transparent); border-color:color-mix(in srgb, var(--danger) 38%, transparent);}

.feedback-comments{margin-top:10px; padding-top:10px; border-top:1px dashed var(--border-soft); display:flex; flex-direction:column; gap:8px;}
.feedback-comments-title{font-size:11px; font-weight:700; color:var(--text-faint); letter-spacing:.02em;}
.fb-comment{background:var(--surface-2); border:1px solid var(--border-soft); border-radius:var(--radius-sm); padding:8px 10px;}
.fb-comment-head{display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-bottom:3px;}
.fb-comment-who{font-size:12px; font-weight:700; color:var(--text);}
.fb-comment-status{font-size:10.5px; font-weight:700; color:var(--accent);}
.fb-comment-time{font-size:11px; color:var(--text-faint); margin-left:auto;}
.fb-comment-text{font-size:12.5px; line-height:1.55; color:var(--text); white-space:pre-wrap; word-break:break-word;}

.feedback-reply{margin-top:10px; padding-top:10px; border-top:1px solid var(--border-soft); display:flex; flex-direction:column; gap:8px;}
.feedback-reply-row{display:flex; align-items:center; gap:8px;}
.feedback-reply-lbl{font-size:12px; font-weight:600; color:var(--text-dim);}
.feedback-status-select{font-family:var(--sans); font-size:12.5px; color:var(--text); background:var(--surface-2); border:1px solid var(--border-soft); border-radius:8px; padding:6px 8px; cursor:pointer;}
.feedback-reply-input{width:100%; box-sizing:border-box; resize:vertical; min-height:44px; font-family:var(--sans); font-size:12.5px; color:var(--text); background:var(--surface-2); border:1px solid var(--border-soft); border-radius:8px; padding:8px 10px; line-height:1.5;}
.feedback-reply-actions{display:flex; justify-content:flex-end;}
.btn.btn-sm{padding:6px 12px; font-size:12.5px;}

/* 신고 팝오버 탭 + 회원 내 신고 목록 */
.feedback-pop-tabs{display:flex; gap:4px; margin-bottom:8px;}
.feedback-pop-tab{flex:1; display:inline-flex; align-items:center; justify-content:center; gap:5px; font-family:var(--sans); font-size:12.5px; font-weight:600; color:var(--text-dim); background:transparent; border:1px solid var(--border-soft); border-radius:8px; padding:6px 8px; cursor:pointer; transition:.12s;}
.feedback-pop-tab:hover{color:var(--text);}
.feedback-pop-tab.active{color:var(--accent); background:var(--accent-soft); border-color:color-mix(in srgb, var(--accent) 40%, transparent);}
.fb-mine-badge{display:inline-flex; align-items:center; justify-content:center; min-width:16px; height:16px; padding:0 4px; font-size:10px; font-weight:800; line-height:1; color:var(--on-accent); background:var(--danger); border-radius:999px;}
.feedback-mine-list{display:flex; flex-direction:column; gap:8px; max-height:320px; overflow-y:auto;}
.feedback-mine-card{background:var(--surface-2); border:1px solid var(--border-soft); border-radius:var(--radius-sm); padding:10px 12px;}
.feedback-mine-card.fb-mine-new{border-color:color-mix(in srgb, var(--accent) 55%, transparent);}
.feedback-mine-head{display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-bottom:6px;}
.feedback-mine-head .feedback-card-time{margin-left:auto;}
.fb-new-tag{font-size:10px; font-weight:800; color:var(--on-accent); background:var(--accent); padding:2px 6px; border-radius:999px;}

/* 신고 버튼(assistantFeedbackBtn) 새 답변 알림 점 */
.assistant-gear{position:relative;}
.assistant-gear-dot{position:absolute; top:2px; right:2px; width:8px; height:8px; border-radius:50%; background:var(--danger); box-shadow:0 0 0 2px var(--bg-elev);}

.thumb-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:12px;margin-bottom:30px;}
.thumb{
  aspect-ratio:1/1;border-radius:9px;position:relative;overflow:hidden;
  border:1px solid var(--border-soft);cursor:pointer;background:var(--surface-2);
}
.thumb img,.thumb video{width:100%;height:100%;object-fit:cover;display:block;}
.thumb-ph{width:100%;height:100%;display:flex;align-items:center;justify-content:center;color:var(--text-faint);}
.thumb-ph svg{width:20px;height:20px;opacity:.5;}

/* Real first-frame preview for video results: a muted <video preload=
   metadata> sits on top (object-fit:cover), seeked to ~0.08s on load so the
   browser paints an actual frame instead of staying blank. The gradient +
   icon underneath is the fallback shown only while metadata is loading or
   if it fails (onerror hides the <video>). */
.result-tile{
  width:100%;height:100%;display:flex;align-items:center;justify-content:center;position:relative;
  background:linear-gradient(155deg, var(--ink) 0%, color-mix(in srgb, var(--ink) 78%, var(--bg) 22%) 100%);
}
.result-tile-video{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:1;
}
.result-tile-icon{width:34px;height:34px;color:var(--on-accent);opacity:.55;position:relative;z-index:0;}
html[data-theme="day"] .result-tile-icon{color:#fafaf6;}
.result-tile-icon svg{width:100%;height:100%;}
.thumb-badge{
  position:absolute;top:6px;left:6px;font-size:12px;font-weight:700;letter-spacing:.03em;
  background:color-mix(in srgb, var(--bg) 65%, transparent);backdrop-filter:blur(3px);color:var(--text);
  padding:2px 6px;border-radius:5px;border:1px solid var(--border-soft);
}
.thumb-status{
  position:absolute;top:6px;right:6px;font-size:12px;font-weight:700;letter-spacing:.02em;
  backdrop-filter:blur(3px);padding:2px 6px;border-radius:5px;border:1px solid var(--border-soft);
}
.thumb-status.queued,.thumb-status.running{background:color-mix(in srgb, var(--warn) 22%, var(--bg));color:var(--warn);}
.thumb-status.succeeded{background:color-mix(in srgb, var(--success) 22%, var(--bg));color:var(--success);}
.thumb-status.failed,.thumb-status.expired,.thumb-status.cancelled{background:color-mix(in srgb, var(--danger) 22%, var(--bg));color:var(--danger);}
.thumb-play{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  opacity:0;transition:opacity .15s;background:color-mix(in srgb, var(--bg) 25%, transparent);
}
.thumb:hover .thumb-play{opacity:1;}
.thumb-play svg{width:24px;height:24px;color:#fff;filter:drop-shadow(0 2px 6px rgba(0,0,0,.5));}
/* 마우스 오버 시 노출되는 다운로드 버튼(우하단). 완료 썸네일에만 렌더된다. */
.thumb-dl{
  position:absolute;right:6px;bottom:6px;z-index:4;width:30px;height:30px;padding:0;
  display:flex;align-items:center;justify-content:center;cursor:pointer;
  border:1px solid var(--border-soft);border-radius:7px;
  background:color-mix(in srgb, var(--bg) 62%, transparent);backdrop-filter:blur(3px);
  color:#fff;opacity:0;transform:translateY(3px);transition:opacity .15s, transform .15s, background .15s;
}
.thumb:hover .thumb-dl,.thumb-dl:focus-visible{opacity:1;transform:translateY(0);}
.thumb-dl:hover{background:var(--accent);color:var(--on-accent, #fff);}
.thumb-dl svg{width:16px;height:16px;display:block;}
@media (hover:none){ .thumb-dl{opacity:1;transform:none;} }
.empty-hint{grid-column:1/-1; text-align:center; padding:30px 10px; color:var(--text-faint); font-size:13.5px; border:1px dashed var(--border); border-radius:var(--radius);}

/* 3D 시뮬레이터 튜토리얼 모달(#42) — view-sim3d 위 오버레이. 기본 숨김, .open일 때 표시. */
.sim3d-tut-modal-back{
  display:none; position:fixed; inset:0; z-index:2400;   /* 모달 밴드 */
  background:rgba(8,8,12,.62); -webkit-backdrop-filter:blur(3px); backdrop-filter:blur(3px);
  /* 세로는 vh — 고정 32px 이면 창(max-height:90vh)과 합이 100vh 를 넘어 잘린다. */
  align-items:center; justify-content:center; padding:3vh 32px;
}
.sim3d-tut-modal-back.open{display:flex;}
.sim3d-tut-modal{
  width:min(1100px,96vw); max-height:90vh; display:flex; flex-direction:column;
  background:var(--surface,#16161c); border:1px solid var(--border-soft,#2a2a34); border-radius:14px;
  box-shadow:0 24px 70px rgba(0,0,0,.55); overflow:hidden;
}
.sim3d-tut-modal-hd{
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  padding:14px 18px; border-bottom:1px solid var(--border-soft,#2a2a34); flex-shrink:0;
}
.sim3d-tut-modal-title{font-size:15px; font-weight:700; color:var(--text,#f4f3ef);}
.sim3d-tut-x{
  width:32px; height:32px; border-radius:8px; cursor:pointer; flex-shrink:0;
  border:1px solid var(--border-soft,#2a2a34); background:transparent; color:var(--text-faint,#9a9aa4);
  font-size:15px; line-height:1; transition:background .15s,color .15s;
}
.sim3d-tut-x:hover{background:var(--surface-2,#20202b); color:var(--text,#f4f3ef);}
.sim3d-tut-modal-body{padding:18px 20px 24px; overflow-y:auto;}
/* 공용 튜토리얼 모달: 이주해 온 튜토리얼 뷰를 모달 안에서 항상 표시 */
.sim3d-tut-modal-body > .view{display:block;height:auto;animation:none}

.recipe-row{display:flex;gap:14px;overflow-x:auto;padding-bottom:6px;}
.recipe-card{
  min-width:250px;max-width:250px;background:var(--surface);border:1px solid var(--border-soft);
  border-radius:var(--radius);overflow:hidden;flex-shrink:0;cursor:pointer;
}
.recipe-card:hover{border-color:var(--accent);}
.recipe-thumb{height:64px;background:var(--surface-2);display:flex;align-items:center;justify-content:center;color:var(--text-faint);}
.recipe-thumb svg{width:20px;height:20px;opacity:.5;}
.recipe-body{padding:12px 13px;}
.recipe-title{font-size:13.5px;font-weight:700;color:var(--text);font-family:var(--display);letter-spacing:-.2px;margin-bottom:5px;
  display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;overflow:hidden;}
.recipe-prompt{font-size:12.5px;color:var(--text-dim);line-height:1.45;margin-bottom:9px;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}
.recipe-foot{display:flex;align-items:center;justify-content:space-between;gap:8px;}
.recipe-model{font-size:11px;color:var(--text-faint);font-weight:600;}

/* ---- prompt tutorial (board view) ---- */
.tut-tips{display:flex;flex-direction:column;gap:9px;margin-bottom:28px;padding:16px 18px;background:var(--surface);border:1px solid var(--border-soft);border-radius:var(--radius);}
.tut-tip{display:flex;align-items:flex-start;gap:9px;font-size:13px;color:var(--text-dim);line-height:1.5;}
.tut-tip svg{width:15px;height:15px;flex-shrink:0;margin-top:3px;color:var(--accent);}
.tut-section{margin-bottom:26px;}
.tut-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;}
.tut-card{
  background:var(--surface);border:1px solid var(--border-soft);border-radius:var(--radius);
  padding:15px 16px;cursor:pointer;display:flex;flex-direction:column;gap:9px;
}
.tut-card:hover{border-color:var(--accent);}
.tut-card-head{display:flex;align-items:center;gap:8px;}
.tut-card-icon{width:26px;height:26px;flex-shrink:0;border-radius:7px;background:var(--surface-2);display:flex;align-items:center;justify-content:center;color:var(--text-faint);}
.tut-card-icon svg{width:14px;height:14px;}
.tut-card-title{font-size:14px;font-weight:700;font-family:var(--display);letter-spacing:-.2px;color:var(--text);}
.tut-card-desc{font-size:12.5px;color:var(--text-dim);line-height:1.5;}
.tut-card-example{
  font-family:var(--mono);font-size:11px;color:var(--text-faint);line-height:1.55;
  background:var(--bg-elev);border:1px solid var(--border-soft);border-radius:8px;padding:9px 10px;
  white-space:pre-wrap;
  display:-webkit-box;-webkit-line-clamp:4;-webkit-box-orient:vertical;overflow:hidden;
}
.tut-card-foot{display:flex;align-items:center;justify-content:space-between;font-size:11.5px;font-weight:700;color:var(--accent);margin-top:auto;}
.tut-card-foot svg{width:13px;height:13px;}
.tut-source-note{font-size:11.5px;color:var(--text-faint);text-align:center;margin-top:6px;}
.recipe-use{font-size:11.5px;font-weight:700;color:var(--accent);}

/* ================= CREATE ================= */
.create-view{display:flex;flex-direction:column;height:100%;}
.create-toolbar{display:flex;align-items:center;gap:10px;margin-bottom:17px;flex-shrink:0;}
.create-tabs{display:flex;background:var(--surface);border:1px solid var(--border-soft);border-radius:9px;padding:3px;}
.create-tab{padding:7px 16px;font-size:14px;font-weight:700;color:var(--text-faint);border-radius:7px;cursor:pointer;display:flex;align-items:center;gap:6px;}
.create-tab svg{width:14px;height:14px;}
.create-tab.active{background:var(--accent);color:var(--on-accent);}
.feed-filters{display:flex;gap:8px;margin-left:auto;}

.feed-scroll{flex:1;overflow-y:auto;padding-bottom:200px;}
.feed-day{font-size:12.5px;font-weight:700;color:var(--text-faint);margin:4px 0 12px;letter-spacing:.04em;text-transform:uppercase;}
.feed-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;}
.feed-card{background:var(--surface);border:1px solid var(--border-soft);border-radius:12px;overflow:hidden;}
.feed-card.is-pending .feed-meta-row,.feed-card.is-pending .feed-actions{opacity:.6;}
.feed-thumb{aspect-ratio:1/1;position:relative;cursor:pointer;background:var(--surface-2);display:flex;align-items:center;justify-content:center;}
.feed-thumb img,.feed-thumb video{width:100%;height:100%;object-fit:cover;display:block;}
.feed-thumb .thumb-play{opacity:1;background:transparent;}
.feed-spinner{width:20px;height:20px;border-radius:50%;border:2px solid var(--border);border-top-color:var(--accent);animation:spin .8s linear infinite;}
/* 생성 중 시각적 진행 효과 — a plain static spinner gave no sense that
   anything was actively happening over a long video generation. A shimmer
   sweep behind the spinner, an indeterminate sliding progress bar, and a
   live elapsed-time readout together read as "actively working", without
   pretending to know a real completion percentage we don't have. */
.feed-card.is-pending .feed-thumb::before,.thumb-ph.is-pending::before{
  content:'';position:absolute;inset:0;
  background:linear-gradient(100deg, transparent 30%, color-mix(in srgb, var(--accent) 16%, transparent) 50%, transparent 70%);
  background-size:200% 100%;animation:pendingShimmer 1.7s ease-in-out infinite;
}
@keyframes pendingShimmer{0%{background-position:160% 0;}100%{background-position:-60% 0;}}
.pending-stack{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;gap:8px;}
.pending-bar{width:64px;height:3px;border-radius:2px;background:var(--border-soft);overflow:hidden;position:relative;}
.pending-bar::after{
  content:'';position:absolute;top:0;bottom:0;width:40%;border-radius:2px;
  background:var(--accent);animation:pendingBarSweep 1.3s ease-in-out infinite;
}
@keyframes pendingBarSweep{0%{left:-45%;}100%{left:105%;}}
.pending-elapsed{font-size:11px;font-family:var(--mono);color:var(--text-faint);letter-spacing:.02em;}
@keyframes spin{to{transform:rotate(360deg);}}
.feed-meta-row{display:flex;align-items:center;gap:6px;padding:10px 11px 6px;font-size:12px;color:var(--text-dim);flex-wrap:wrap;}
.feed-meta-row b{color:var(--text);font-weight:700;font-family:var(--display);letter-spacing:-.2px;}
.feed-tag{background:var(--bg-elev);border:1px solid var(--border-soft);padding:2px 7px;border-radius:5px;font-size:11px;color:var(--text-faint);font-family:var(--mono);letter-spacing:.01em;}
.feed-status{padding:1.5px 6px;border-radius:5px;font-size:11px;font-weight:700;display:inline-flex;align-items:center;gap:3px;}
.feed-status svg{width:11px;height:11px;}
.feed-status.queued,.feed-status.running{background:color-mix(in srgb, var(--warn) 16%, transparent);color:var(--warn);}
.feed-status.succeeded{background:color-mix(in srgb, var(--success) 16%, transparent);color:var(--success);}
.feed-status.failed,.feed-status.expired,.feed-status.cancelled{background:color-mix(in srgb, var(--danger) 14%, transparent);color:var(--danger);}
.feed-warn{display:flex;align-items:flex-start;gap:6px;padding:0 11px 9px;font-size:12px;color:var(--text-dim);line-height:1.5;}
.feed-warn svg{width:12px;height:12px;flex-shrink:0;margin-top:1.5px;color:var(--danger);}
.feed-error{padding:0 11px 9px;font-size:12px;color:var(--danger);line-height:1.5;}
.feed-actions{display:flex;gap:6px;padding:0 11px 11px;}
.feed-btn{flex:1;font-size:12px;font-weight:600;color:var(--text-dim);background:var(--bg-elev);border:1px solid var(--border-soft);border-radius:7px;padding:6px 4px;display:flex;align-items:center;justify-content:center;gap:4px;cursor:pointer;}
.feed-btn svg{width:11px;height:11px;}
.feed-btn.more{flex:0 0 30px;}

.empty-feed{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:80px 0;color:var(--text-faint);text-align:center;gap:11px;}
.empty-feed svg{width:40px;height:40px;opacity:.35;}
.empty-feed b{color:var(--text-dim);font-size:15px;font-family:var(--serif);}
.empty-feed span{font-size:13.5px;max-width:300px;}

/* ---- floating command dock ---- */
.dock-wrap{position:absolute;left:0;right:0;bottom:0;display:flex;justify-content:center;padding:0 30px 22px;pointer-events:none;z-index:20;}
.dock{
  pointer-events:auto;
  width:100%;max-width:920px;
  background:var(--surface);border:1px solid var(--border);border-radius:16px;
  box-shadow:var(--shadow);
  padding:14px 16px 12px;
  position:relative;
}
.dock-input-row{display:flex;gap:10px;padding:2px 2px 10px;}
.dock-text{flex:1;min-height:44px;display:flex;align-items:center;}
.dock-text textarea{
  width:100%;background:none;border:none;outline:none;resize:none;
  color:var(--text);font-size:14.5px;font-family:var(--sans);line-height:1.4;
  max-height:90px;padding:6px 2px;
}
.dock-text textarea::placeholder{color:var(--text-faint);}
.dock-controls{display:flex;align-items:center;gap:7px;flex-wrap:wrap;}
.dock-pill{
  display:flex;align-items:center;gap:6px;padding:7px 10px;border-radius:8px;
  background:var(--bg-elev);border:1px solid var(--border-soft);
  font-size:12.5px;font-weight:600;color:var(--text-dim);cursor:pointer;white-space:nowrap;
}
.dock-pill:hover{border-color:var(--border);color:var(--text);}
.dock-pill svg{width:14px;height:14px;flex-shrink:0;}
.dock-pill .chev{width:11px;height:11px;opacity:.7;}
.dock-spacer{flex:1;}
.dock-cost{font-size:12px;color:var(--text-faint);font-weight:600;display:flex;align-items:center;gap:5px;}
.dock-cost svg{width:12px;height:12px;color:var(--accent);}
.dock-kbd-hint{font-size:10.5px;color:var(--text-faint);font-family:ui-monospace,Consolas,monospace;white-space:nowrap;border:1px solid var(--border-soft);border-radius:5px;padding:2px 6px;cursor:default;}
@media (max-width:760px){.dock-kbd-hint{display:none;}}
.dock-submit{
  width:34px;height:34px;border-radius:50%;background:var(--accent);border:none;color:var(--on-accent);
  display:flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0;
}
.dock-submit:disabled{opacity:.4;cursor:not-allowed;}
.dock-submit svg{width:16px;height:16px;}
.dock-error{font-size:12px;color:var(--danger);padding:0 4px 6px;}

/* ---- popovers ---- */
.popover{
  position:absolute;bottom:calc(100% + 10px);left:0;
  width:300px;background:var(--surface);border:1px solid var(--border);border-radius:13px;
  box-shadow:var(--shadow);padding:14px;display:none;max-height:400px;overflow-y:auto;
}
.popover.show{display:block;}
.popover-title{font-size:12px;color:var(--text-faint);margin-bottom:10px;line-height:1.5;}
.popover-title b{color:var(--text-dim);}
.model-row{display:flex;gap:10px;padding:9px 8px;border-radius:8px;cursor:pointer;align-items:flex-start;}
.model-row:hover{background:var(--bg-elev);}
.model-row.selected{background:var(--accent-soft);}
.model-icon{width:25px;height:25px;border-radius:6px;background:var(--ink);flex-shrink:0;display:flex;align-items:center;justify-content:center;margin-top:1px;}
.model-icon svg{width:12px;height:12px;color:var(--on-accent);}
html[data-theme="day"] .model-icon svg{color:#fafaf6;}
.model-name-row{display:flex;align-items:center;gap:6px;font-size:14px;font-weight:700;font-family:var(--display);letter-spacing:-.2px;}
.model-desc{font-size:11.5px;color:var(--text-faint);margin-top:2px;line-height:1.4;}
.model-check{margin-left:auto;width:15px;height:15px;color:var(--accent);flex-shrink:0;}

.pop-section-label{font-size:11.5px;font-weight:700;color:var(--text-faint);text-transform:uppercase;letter-spacing:.05em;margin:12px 0 8px;}
.pop-section-label:first-child{margin-top:0;}
.pop-chip-grid{display:flex;flex-wrap:wrap;gap:6px;}
.pop-chip{padding:6px 10px;border-radius:7px;background:var(--bg-elev);border:1px solid var(--border-soft);font-size:12px;font-weight:600;color:var(--text-dim);cursor:pointer;}
.pop-chip.active{background:var(--accent);color:var(--on-accent);border-color:transparent;}
.pop-size-row{display:flex;align-items:center;gap:8px;margin-top:8px;}
.pop-size-row input{width:0;flex:1;background:var(--bg-elev);border:1px solid var(--border-soft);color:var(--text);border-radius:7px;padding:6px 8px;font-size:12.5px;}
.pop-size-row span{font-size:12px;color:var(--text-faint);}

.refmode-row{display:flex;align-items:center;gap:10px;padding:9px 8px;border-radius:8px;cursor:pointer;}
.refmode-row:hover{background:var(--bg-elev);}
.refmode-row svg{width:16px;height:16px;color:var(--text-dim);flex-shrink:0;}
.refmode-label{font-size:14px;font-weight:600;flex:1;}
.refmode-check{width:15px;height:15px;color:var(--accent);opacity:0;}
.refmode-row.selected .refmode-check{opacity:1;}

.pop-slider-row{margin-bottom:14px;}
.pop-slider-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;}
.pop-slider-head span:first-child{font-size:12.5px;font-weight:700;color:var(--text-dim);}
.pop-slider-input{width:56px;background:var(--bg-elev);border:1px solid var(--border-soft);color:var(--text);border-radius:7px;padding:4px 6px;font-size:12px;text-align:center;}
.pop-slider-row input[type=range]{width:100%;accent-color:var(--accent);}
.pop-toggle-row{display:flex;align-items:center;justify-content:space-between;padding:9px 0;border-top:1px solid var(--border-soft);}
.pop-toggle-row:first-of-type{border-top:none;}
.pop-toggle-label{font-size:13.5px;font-weight:600;}
.switch{width:34px;height:19px;border-radius:20px;background:var(--border);position:relative;cursor:pointer;flex-shrink:0;}
.switch.on{background:var(--accent);}
.switch-knob{position:absolute;top:2px;left:2px;width:15px;height:15px;border-radius:50%;background:#fff;transition:left .15s;}
.switch.on .switch-knob{left:17px;}
.pop-input-row{display:flex;gap:8px;align-items:center;margin-top:8px;}
.pop-input-row input{flex:1;background:var(--bg-elev);border:1px solid var(--border-soft);color:var(--text);border-radius:7px;padding:7px 9px;font-size:12.5px;}
.pop-shuffle{width:30px;height:30px;flex-shrink:0;background:var(--bg-elev);border:1px solid var(--border-soft);border-radius:7px;color:var(--text-dim);display:flex;align-items:center;justify-content:center;cursor:pointer;}
.pop-shuffle svg{width:13px;height:13px;}

.element-empty{text-align:center;padding:16px 6px;}
.element-empty svg{width:32px;height:32px;color:var(--text-faint);margin-bottom:10px;}
.element-empty p{font-size:12.5px;color:var(--text-dim);line-height:1.6;margin:0 0 12px;}

/* ---- board (prompt tutorial) ---- */
.canvas-hero{text-align:center;padding:40px 0 26px;}
.canvas-hero h1{font-family:var(--serif); font-size:26px;font-weight:700;margin:0;}
.canvas-hero p{color:var(--text-faint);font-size:14px;margin:8px 0 0;max-width:640px;margin-left:auto;margin-right:auto;}
.tut-container{max-width:960px;margin:0 auto;}
.tmpl-label{font-size:13.5px;font-weight:700;margin:0 0 12px;color:var(--text-dim);text-transform:uppercase;letter-spacing:.05em;}

/* ---- gallery ---- */
.asset-tabs{display:flex;gap:22px;border-bottom:1px solid var(--border-soft);margin-bottom:16px;}
.asset-tab{padding:0 0 12px;font-size:14.5px;font-weight:700;color:var(--text-faint);cursor:pointer;border-bottom:2px solid transparent;}
.asset-tab.active{color:var(--text);border-color:var(--accent);}
.filter-bar{display:flex;align-items:center;gap:10px;margin-bottom:18px;flex-wrap:wrap;}
.search-box{
  flex:1;max-width:320px;display:flex;align-items:center;gap:8px;background:var(--surface);
  border:1px solid var(--border-soft);border-radius:9px;padding:8px 12px;color:var(--text-faint);
}
.search-box svg{width:14px;height:14px;flex-shrink:0;}
.search-box input{background:none;border:none;outline:none;color:var(--text);font-size:14px;width:100%;}
.chip{
  padding:6px 12px;border-radius:20px;background:var(--bg-elev);border:1px solid var(--border-soft);
  font-size:12.5px;font-weight:600;color:var(--text-dim);cursor:pointer;
}
.chip.active{background:var(--accent);color:var(--on-accent);border-color:transparent;}
.gallery-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:13px;}

/* ---- history table ---- */
.hist-row{display:grid;grid-template-columns:90px 1fr 150px 130px 110px 100px 170px;padding:13px 16px;font-size:14px;align-items:center;border-bottom:1px solid var(--border-soft);}
.hist-row:last-child{border-bottom:none;}
.hist-head{font-size:11.5px;color:var(--text-faint);font-weight:700;border-bottom:1px solid var(--border-soft);text-transform:uppercase;letter-spacing:.04em;}
.hist-prompt{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-dim);}
.status-pill{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:700;padding:4px 9px;border-radius:20px;}
.status-pill.ok{background:color-mix(in srgb, var(--success) 15%, transparent);color:var(--success);}
.status-pill.pending{background:color-mix(in srgb, var(--warn) 15%, transparent);color:var(--warn);}
.status-pill.bad{background:color-mix(in srgb, var(--danger) 15%, transparent);color:var(--danger);}
.status-pill .dot{width:6px;height:6px;border-radius:50%;background:currentColor;}
.hist-del{color:var(--text-faint);cursor:pointer;font-size:14px;}
.hist-del:hover{color:var(--danger);}

/* ---- settings ---- */
.settings-tabs{display:flex;gap:4px;background:var(--surface);border:1px solid var(--border-soft);border-radius:10px;padding:3px;width:fit-content;margin-bottom:20px;}
.settings-tab{padding:8px 16px;border-radius:7px;font-size:14px;font-weight:700;color:var(--text-faint);cursor:pointer;}
.settings-tab.active{background:var(--accent);color:var(--on-accent);}
.settings-pane{display:none;}
.settings-pane.active{display:block;}

table.keys-table{width:100%;border-collapse:collapse;background:var(--surface);border:1px solid var(--border-soft);border-radius:var(--radius);overflow:hidden;}
.keys-table th{text-align:left;font-size:11.5px;color:var(--text-faint);font-weight:700;text-transform:uppercase;letter-spacing:.04em;padding:11px 14px;border-bottom:1px solid var(--border-soft);}
.keys-table td{padding:13px 14px;font-size:14px;border-bottom:1px solid var(--border-soft);color:var(--text-dim);vertical-align:middle;}
.keys-table tr:last-child td{border-bottom:none;}
.key-mono{font-family:var(--mono);letter-spacing:.3px;color:var(--text);}
.key-eye{color:var(--text-faint);cursor:pointer;margin-left:6px;}
.keys-action{color:var(--accent);font-size:12.5px;font-weight:700;cursor:pointer;margin-right:12px;background:none;border:none;padding:0;font-family:inherit;}
.keys-action.danger{color:var(--danger);}

.field-row{display:flex; flex-direction:column; gap:6px; margin-bottom:14px;}
.field-row label{font-size:12.5px; font-weight:700; color:var(--text-dim);}
.field-row .hint{font-size:11.5px; color:var(--text-faint); line-height:1.5;}
.field-row input[type=text], .field-row input[type=password], .field-row select{
  background:var(--bg-elev); border:1px solid var(--border-soft); color:var(--text);
  border-radius:8px; padding:9px 11px; font-size:14px; font-family:var(--mono); width:100%;
}
.field-row select{font-family:var(--sans);}
.key-input-row{display:flex; gap:8px;}
.key-input-row input{flex:1;}

/* 계정 모달 — 대표 이미지(아바타) 피커 */
.acct-avatar-row{flex-direction:row; align-items:center; gap:14px;}
.acct-avatar-picker{display:flex; align-items:center; gap:14px;}
.acct-avatar-preview{
  width:64px; height:64px; border-radius:50%; overflow:hidden; flex-shrink:0;
  background:var(--accent); color:var(--on-accent);
  display:flex; align-items:center; justify-content:center;
  font-size:20px; font-weight:700; border:1px solid var(--border-soft);
}
.acct-avatar-preview img{width:100%; height:100%; object-fit:cover;}
.acct-avatar-btn{font-size:12.5px; cursor:pointer; white-space:nowrap;}

/* ---- 제공자 API 키 페이지: 핵심/제공자/커스텀 섹션 구분 ---- */
.provider-section-label{
  font-size:11.5px; font-weight:700; color:var(--text-faint); text-transform:uppercase;
  letter-spacing:.05em; margin:18px 0 8px; padding-top:14px; border-top:1px solid var(--border-soft);
}
.provider-section-label:first-child{margin-top:0; padding-top:0; border-top:none;}
.provider-readonly-row input[readonly]{color:var(--text-faint); font-family:var(--mono);}
.custom-provider-add-row{margin-top:4px;}
.custom-provider-add-row label{color:var(--accent);}

.model-id-table{width:100%; border-collapse:collapse; background:var(--surface); border:1px solid var(--border-soft); border-radius:var(--radius); overflow:hidden;}
.model-id-table th{text-align:left;font-size:11.5px;color:var(--text-faint);font-weight:700;text-transform:uppercase;letter-spacing:.04em;padding:10px 14px;border-bottom:1px solid var(--border-soft);}
.model-id-table td{padding:9px 14px;border-bottom:1px solid var(--border-soft);}
.model-id-table tr:last-child td{border-bottom:none;}
.model-id-table td:first-child{font-size:14px;font-weight:700;white-space:nowrap;}
.model-id-table input{width:100%; background:var(--bg-elev); border:1px solid var(--border-soft); color:var(--text); border-radius:7px; padding:6px 9px; font-size:12.5px; font-family:var(--mono);}

.settings-grid{display:grid;grid-template-columns:1fr 320px;gap:20px;}
.settings-stack{display:flex;flex-direction:column;gap:16px;}
.setting-title{font-family:var(--serif); font-size:15.5px;font-weight:700;margin-bottom:6px;}
.setting-desc{font-size:12.5px;color:var(--text-faint);margin-bottom:12px;line-height:1.55;}
.kv-row{display:flex;justify-content:space-between;padding:9px 0;border-top:1px solid var(--border-soft);font-size:13.5px;}
.kv-row:first-of-type{border-top:none;}

.theme-picker{display:flex;gap:10px;}
.theme-opt{padding:10px 16px;border-radius:9px;border:1px solid var(--border-soft);background:var(--bg-elev);color:var(--text-dim);font-size:13.5px;font-weight:600;cursor:pointer;}
.theme-opt.active{background:var(--accent);color:var(--on-accent);border-color:transparent;}

/* ---- lightbox (view a generated result) ---- */
.lightbox-overlay{
  position:fixed; inset:0; z-index:3000; display:none;   /* 상세 라이트박스 밴드 */
  align-items:center; justify-content:center; gap:22px;
  background:color-mix(in srgb, var(--bg) 55%, black 45%);
  backdrop-filter:blur(4px);
  padding:40px;
}
.lightbox-overlay.show{display:flex;}
.lightbox-content{
  display:flex; align-items:center; gap:20px; max-width:min(1100px, 92vw); max-height:88vh;
}
.lightbox-body{
  flex:1; min-width:0; max-height:88vh; display:flex; align-items:center; justify-content:center;
  background:var(--bg-elev); border:1px solid var(--border); border-radius:var(--radius); overflow:hidden;
}
.lightbox-body img, .lightbox-body video{max-width:100%; max-height:88vh; display:block; object-fit:contain;}
.lightbox-meta{
  width:260px; flex-shrink:0; background:var(--surface); border:1px solid var(--border-soft);
  border-radius:var(--radius); padding:16px;
}
.lightbox-close{
  position:absolute; top:22px; right:26px; width:38px; height:38px; border-radius:50%;
  background:var(--surface); border:1px solid var(--border-soft); color:var(--text);
  display:flex; align-items:center; justify-content:center; cursor:pointer;
}
.lightbox-close svg{width:16px;height:16px;}
/* Prev/next arrows — floating circles at the vertical center of each side,
   same visual language as .lightbox-close. Hidden (not just disabled) via
   JS when the current result has no surrounding list to browse. */
.lightbox-nav{
  position:absolute; top:50%; transform:translateY(-50%); width:44px; height:44px; border-radius:50%;
  background:var(--surface); border:1px solid var(--border-soft); color:var(--text);
  display:flex; align-items:center; justify-content:center; cursor:pointer; opacity:.85; transition:opacity .12s ease;
}
.lightbox-nav:hover{opacity:1; border-color:var(--accent); color:var(--accent);}
.lightbox-nav svg{width:20px;height:20px;}
.lightbox-prev{left:26px;}
.lightbox-next{right:26px;}
.lightbox-nav.hidden{display:none;}

/* ---- multi-reference thumbnails (Create dock) ----
   Inline strip inside the dock itself (Higgsfield-style: small avatar
   thumbnails + a "+" tile sit directly above the prompt box), rather than
   hidden behind a popover you have to open to see what's attached. */
.dock-ref-strip{display:flex; flex-wrap:wrap; gap:11px; padding:4px 2px 4px; border-radius:var(--radius-sm); transition:background-color .12s ease, box-shadow .12s ease;}
.dock-ref-strip:empty{display:none;}
.dock-ref-strip.drop-active{background:color-mix(in srgb, var(--accent) 12%, transparent); box-shadow:0 0 0 2px var(--accent) inset;}
.ref-thumb-grid{display:flex; flex-wrap:wrap; gap:8px;}
.ref-thumb-tile{
  position:relative; width:64px; height:64px; border-radius:9px; overflow:hidden;
  border:1px solid var(--border-soft); background:var(--bg-elev); cursor:pointer;
}
/* Bigger, easier-to-see/click reference tiles inside the Create dock — the
   old 44px size made the thumbnail, remove button, and role/tag overlays all
   cramped on top of each other. Sized close to the dock's own width increase
   so the ref row reads as a first-class part of the composer, not an
   afterthought squeezed above the prompt box. */
.dock-ref-strip .ref-thumb-tile,
.dock-ref-strip .ref-thumb-add{width:80px; height:80px; border-radius:12px;}
.ref-thumb-tile img,
.ref-thumb-tile video{width:100%; height:100%; object-fit:cover; display:block;}
.ref-kind-audio{width:100%; height:100%; display:flex; align-items:center; justify-content:center; background:var(--bg-elev); color:var(--text-faint); font-size:26px;}
.ref-thumb-tile .remove-ref{
  position:absolute; top:4px; right:4px; width:20px; height:20px; background:var(--danger); color:#fff;
  border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:13.5px; line-height:1;
}
.ref-thumb-tile .ref-edit-hint{
  position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  background:rgba(0,0,0,.42); color:#fff; font-size:12.5px; font-weight:700; opacity:0; transition:opacity .12s;
  text-align:center; padding:4px;
}
.ref-thumb-tile:hover .ref-edit-hint{opacity:1;}
.ref-thumb-tile.has-role{border-color:var(--accent);}
.ref-thumb-tile.pending-mask-tile{border-color:var(--accent); border-width:2px;}
.ref-role-row{position:absolute; left:0; right:0; bottom:0; display:flex; gap:2px;}
.ref-role-btn{
  flex:1; text-align:center; font-size:11.5px; font-weight:700; padding:4px 0; cursor:pointer;
  background:rgba(0,0,0,.55); color:#fff; opacity:.75;
}
.ref-role-btn.active{background:var(--accent); opacity:1;}
.ref-omni-badge{
  position:absolute; top:4px; left:4px; width:20px; height:20px; border-radius:50%;
  background:var(--accent); color:var(--on-accent); font-size:12.5px; font-weight:700;
  display:flex; align-items:center; justify-content:center;
}
.ref-tag-btn{
  position:absolute; bottom:0; left:0; right:0; text-align:center; font-size:11px; font-weight:700;
  padding:4px 0; background:rgba(0,0,0,.55); color:#fff; opacity:0; transition:opacity .12s;
}
.ref-thumb-tile:hover .ref-tag-btn{opacity:1;}
.ref-thumb-add{
  width:64px; height:64px; border-radius:9px; border:1px dashed var(--border); background:var(--bg-elev);
  display:flex; align-items:center; justify-content:center; color:var(--text-faint); cursor:pointer;
}
.ref-thumb-add svg{width:22px;height:22px;}
.ref-thumb-add:hover{border-color:var(--accent); color:var(--accent);}

/* ---- mask editor (real client-mask + server-composite editing) ---- */
.mask-editor-overlay{
  position:fixed; inset:0; z-index:2200; display:none;   /* 모달 밴드 */
  align-items:center; justify-content:center;
  background:color-mix(in srgb, var(--bg) 55%, black 45%);
  backdrop-filter:blur(4px);
  /* 세로 여백은 vh 로 — 고정 30px 이면 창(max-height:90vh)과 합이 100vh 를 넘어
     화면이 낮을 때(가로 모드 등) 패널 위아래가 잘려 나간다. */
  padding:3vh 30px;
}
.mask-editor-overlay.show{display:flex;}
.mask-editor-panel{
  width:min(1000px, 94vw); max-height:90vh; background:var(--surface); border:1px solid var(--border);
  border-radius:var(--radius); overflow:hidden; display:flex; flex-direction:column;
}
.outpaint-pad-grid{display:grid; grid-template-columns:1fr 1fr; gap:8px; margin-bottom:12px;}
.outpaint-pad-field{display:flex; flex-direction:column; gap:4px;}
.outpaint-pad-field label{font-size:13px; color:var(--text-faint); font-weight:600;}
.outpaint-pad-field input{
  background:var(--bg-elev); border:1px solid var(--border-soft); color:var(--text);
  border-radius:7px; padding:7px 9px; font-size:14.5px; width:100%;
}
.mask-editor-head{display:flex; align-items:center; gap:12px; padding:14px 18px; border-bottom:1px solid var(--border-soft);}
.mask-editor-head b{font-family:var(--display); font-size:14.5px; letter-spacing:-.2px;}
.mask-editor-hint{flex:1; font-size:11.5px; color:var(--text-faint); line-height:1.5;}
.mask-editor-close{
  width:30px; height:30px; border-radius:50%; background:var(--bg-elev); border:1px solid var(--border-soft);
  color:var(--text); display:flex; align-items:center; justify-content:center; cursor:pointer; flex-shrink:0;
}
.mask-editor-close svg{width:14px;height:14px;}
.mask-editor-body{display:flex; gap:16px; padding:16px; overflow:auto;}
/* The stage is a single expandable canvas: #maskStageCanvas paints a
   checkerboard (alpha) background plus the source photo at its offset,
   #maskCanvas is an absolutely-positioned twin overlay (same pixel size,
   exact top:0/left:0 match since .mask-stage sizes itself off the first
   block-level canvas) that the brush paints onto — anywhere, including over
   the checkerboard padding. No more separate inpaint/outpaint screens. */
.mask-canvas-wrap{
  position:relative; flex:1; min-width:0; display:flex; align-items:center; justify-content:center;
  background:var(--bg-elev); border:1px solid var(--border-soft); border-radius:var(--radius-sm); overflow:auto;
}
.mask-stage{position:relative; line-height:0; flex-shrink:0; margin:auto;}
.mask-stage canvas{display:block;}
#maskStageCanvas{user-select:none;}
#maskCanvas{position:absolute; top:0; left:0; opacity:.55; cursor:crosshair; touch-action:none;}
.mask-brush-cursor{
  position:absolute; border-radius:50%; pointer-events:none; z-index:5; display:none;
  border:1.5px solid var(--accent); background:color-mix(in srgb, var(--accent) 30%, transparent);
  transform:translate(-50%,-50%);
}
.mask-editor-side{width:260px; flex-shrink:0; display:flex; flex-direction:column; overflow-y:auto;}
.mask-tool-row{display:flex; gap:6px; margin-bottom:12px;}
.mask-tool-btn{
  flex:1; text-align:center; padding:7px 6px; border-radius:7px; background:var(--bg-elev);
  border:1px solid var(--border-soft); font-size:14px; font-weight:600; color:var(--text-dim); cursor:pointer;
}
.mask-tool-btn.active{background:var(--accent); color:var(--on-accent); border-color:transparent;}
.mask-icon-toolbar{display:flex; gap:6px; margin-bottom:14px;}
.mask-icon-btn{
  width:34px; height:34px; border-radius:8px; background:var(--bg-elev); border:1px solid var(--border-soft);
  color:var(--text-dim); display:flex; align-items:center; justify-content:center; cursor:pointer;
}
.mask-icon-btn svg{width:16px;height:16px;}
.mask-icon-btn.active{background:var(--accent); color:var(--on-accent); border-color:transparent;}
.mask-icon-btn:hover{border-color:var(--accent);}
.mask-guide-row{display:flex; gap:8px; flex-wrap:wrap; margin-bottom:6px; border-radius:9px; transition:background-color .12s ease, box-shadow .12s ease;}
.mask-guide-row.drop-active{background:color-mix(in srgb, var(--accent) 12%, transparent); box-shadow:0 0 0 2px var(--accent) inset;}
.mask-guide-add{
  width:52px; height:52px; border-radius:9px; border:1px dashed var(--border); background:var(--bg-elev);
  display:flex; align-items:center; justify-content:center; color:var(--text-faint); cursor:pointer;
}
.mask-guide-add svg{width:16px;height:16px;}
.mask-guide-add:hover{border-color:var(--accent); color:var(--accent);}
.mask-guide-thumb{position:relative; width:52px; height:52px; border-radius:9px; overflow:hidden; border:1px solid var(--border-soft);}
.mask-guide-thumb img{width:100%; height:100%; object-fit:cover;}
.mask-guide-thumb .remove-ref{
  position:absolute; top:2px; right:2px; width:16px; height:16px; background:var(--danger); color:#fff;
  border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:10.5px; line-height:1; cursor:pointer;
}
.mask-editor-hint-small{font-size:13px; color:var(--text-faint); line-height:1.5; margin-bottom:14px;}
.mask-model-select{
  width:100%; background:var(--bg-elev); border:1px solid var(--border-soft); color:var(--text);
  border-radius:7px; padding:7px 9px; font-size:14.5px; margin-bottom:4px;
}
.mask-prompt-textarea{
  width:100%; background:var(--bg-elev); border:1px solid var(--border-soft); color:var(--text);
  border-radius:7px; padding:9px 10px; font-size:14.5px; line-height:1.5; resize:none; font-family:inherit;
}
.mask-editor-error{font-size:14px; color:var(--danger); margin-top:8px;}
.mask-submit-btn{
  margin-top:auto; padding-top:12px;
  background:var(--accent); color:var(--on-accent); border:none; border-radius:9px;
  padding:11px; font-size:15.5px; font-weight:700; cursor:pointer; margin-top:14px;
}
.mask-submit-btn:disabled{opacity:.5; cursor:not-allowed;}
@media (max-width: 720px){
  .mask-editor-body{flex-direction:column;}
  .mask-editor-side{width:auto;}
}
@media (max-width: 780px){
  .lightbox-content{flex-direction:column;}
  .lightbox-meta{width:100%;}
}

/* ---- sidebar user-row extra actions (logout + theme toggle) ---- */
.user-row-actions{display:flex; gap:6px; margin-left:auto; flex-shrink:0;}

/* ---- auth overlay (master setup / login / signup) ---- */
.auth-overlay{
  position:fixed; inset:0; z-index:2900; display:none;   /* 모달 밴드 최상단(로그인 게이트) */
  align-items:center; justify-content:center;
  background:var(--bg);
  overflow:auto; padding:24px;
}
/* 포털 랜딩(#153/#182): 로그인 폼 왼쪽에 브랜드/모듈 소개 컬럼을 붙인 2단
   레이아웃. 넓은 화면에서만 2컬럼이고, 좁은 화면은 폼 하나만 보이던 예전
   모습 그대로 유지(아래 미디어쿼리). */
.auth-portal{
  width:min(980px, 96vw); display:flex; align-items:center; justify-content:center;
  gap:56px; margin:auto;
}
.auth-portal-hero{flex:1 1 440px; max-width:480px; display:none;}
.auth-portal-tagline{font-family:var(--serif); font-size:19px; font-weight:600; line-height:1.5; color:var(--text); margin-bottom:26px;}
.auth-portal-features{display:grid; grid-template-columns:1fr 1fr; gap:12px;}
.auth-feature-card{display:flex; align-items:flex-start; gap:10px; padding:14px; background:var(--surface); border:1px solid var(--border-soft); border-radius:var(--radius-sm);}
.auth-feature-icn{flex:0 0 auto; width:30px; height:30px; border-radius:8px; background:var(--bg-elev); color:var(--accent); display:flex; align-items:center; justify-content:center;}
.auth-feature-icn svg{width:17px; height:17px;}
.auth-feature-txt{display:flex; flex-direction:column; gap:3px; min-width:0;}
.auth-feature-txt b{font-size:12.5px; font-weight:700; color:var(--text);}
.auth-feature-txt span{font-size:11.5px; color:var(--text-faint); line-height:1.45;}
.auth-panel-logo{justify-content:center; margin-bottom:22px;}
@media (min-width:861px){ .auth-portal-hero{display:block;} }
.auth-panel{width:min(400px, 92vw); flex:0 0 auto; background:var(--surface); border:1px solid var(--border-soft); border-radius:var(--radius); padding:28px 26px;}
.auth-title{font-family:var(--serif); font-size:18px; font-weight:700; margin-bottom:6px;}
.auth-desc{font-size:12.5px; color:var(--text-faint); line-height:1.6; margin-bottom:18px;}
.auth-field{display:flex; flex-direction:column; gap:6px; margin-bottom:13px;}
.auth-field label{font-size:12px; font-weight:700; color:var(--text-dim);}
.auth-field input{
  background:var(--bg-elev); border:1px solid var(--border-soft); color:var(--text);
  border-radius:8px; padding:10px 12px; font-size:14px; width:100%;
}
.auth-error{font-size:12px; color:var(--danger); margin-bottom:12px; line-height:1.5;}
.auth-switch{text-align:center; font-size:12.5px; color:var(--text-faint); margin-top:16px;}
.auth-switch span{color:var(--accent); font-weight:700; cursor:pointer;}

/* ---- member management (master-only) ---- */
.member-list{display:flex; flex-direction:column; gap:10px;}
.member-row{
  display:flex; align-items:center; gap:12px; background:var(--surface);
  border:1px solid var(--border-soft); border-radius:var(--radius); padding:13px 16px;
}
.member-avatar{
  width:34px; height:34px; border-radius:50%; background:var(--bg-elev); color:var(--text-dim);
  display:flex; align-items:center; justify-content:center; font-weight:700; font-size:13px; flex-shrink:0;
}
.member-name{font-weight:700; font-size:13.5px;}
.member-sub{font-size:11.5px; color:var(--text-faint);}
.member-role-badge{font-size:10.5px; font-weight:700; padding:2px 8px; border-radius:20px; text-transform:uppercase; letter-spacing:.03em;}
.member-role-badge.master{background:color-mix(in srgb, var(--accent) 20%, transparent); color:var(--accent);}
.member-role-badge.admin{background:color-mix(in srgb, var(--accent) 10%, transparent); color:var(--accent); border:1px solid color-mix(in srgb, var(--accent) 35%, transparent);}
.member-role-badge.engineer{background:color-mix(in srgb, var(--success) 12%, transparent); color:var(--success); border:1px solid color-mix(in srgb, var(--success) 35%, transparent);}
.member-role-badge.pd{background:color-mix(in srgb, var(--gold) 12%, transparent); color:var(--gold); border:1px solid color-mix(in srgb, var(--gold) 35%, transparent);}
.member-role-badge.member{background:var(--bg-elev); color:var(--text-faint); border:1px solid var(--border-soft);}
/* API 키 실사용 검증 상태 뱃지 — member-role-badge / comfy 상태 점과 동일 톤 */
.provider-test-badge{display:inline-block; white-space:nowrap; flex-shrink:0; font-size:10.5px; font-weight:700; padding:2px 9px; border-radius:20px; margin-right:8px; letter-spacing:.02em; vertical-align:middle; cursor:default;}
.provider-test-badge.ptest-idle{background:var(--bg-elev); color:var(--text-faint); border:1px solid var(--border-soft);}
.provider-test-badge.ptest-checking{background:color-mix(in srgb, var(--gold) 12%, transparent); color:var(--gold); border:1px solid color-mix(in srgb, var(--gold) 30%, transparent);}
.provider-test-badge.ptest-ok{background:color-mix(in srgb, var(--success) 14%, transparent); color:var(--success); border:1px solid color-mix(in srgb, var(--success) 35%, transparent);}
.provider-test-badge.ptest-fail{background:color-mix(in srgb, var(--danger) 12%, transparent); color:var(--danger); border:1px solid color-mix(in srgb, var(--danger) 35%, transparent);}
.member-role-select{
  font-size:11.5px; font-weight:600; color:var(--text-dim); background:var(--bg-elev);
  border:1px solid var(--border-soft); border-radius:7px; padding:4px 8px; cursor:pointer; flex-shrink:0;
}
.member-status-badge{font-size:11px; font-weight:700; padding:2px 8px; border-radius:20px;}
.member-status-badge.pending{background:color-mix(in srgb, var(--warn) 16%, transparent); color:var(--warn);}
.member-status-badge.approved{background:color-mix(in srgb, var(--success) 16%, transparent); color:var(--success);}
.member-status-badge.rejected{background:color-mix(in srgb, var(--danger) 14%, transparent); color:var(--danger);}
.member-actions{display:flex; gap:8px; margin-left:auto; flex-shrink:0;}
.member-btn{padding:6px 13px; border-radius:7px; font-size:12px; font-weight:700; cursor:pointer; border:1px solid var(--border-soft); background:var(--bg-elev); color:var(--text-dim); white-space:nowrap;}
.member-btn.approve{background:var(--accent); color:var(--on-accent); border-color:var(--accent);}
.member-btn.danger{color:var(--danger); border-color:color-mix(in srgb, var(--danger) 40%, var(--border-soft));}

/* ---- per-member app permission toggles ---- */
/* 모듈 권한 체크박스 묶음.
   ⚠ flex-wrap 금지 — 레이아웃 철칙(재배치 금지). 예전에는 wrap 이라 창을 조금만
   좁혀도 체크박스가 2~3줄로 접히면서 회원 한 명의 행이 세 배로 커지고, 어느 체크박스가
   누구 것인지 눈으로 따라가기 어려웠다(2026-08-04 지적). 좁아지면 이 칸 안에서
   가로 스크롤로 처리한다. 스크롤은 행이 아니라 이 칸 레벨에서만 생긴다. */
.member-apps{
  display:flex; align-items:center; gap:10px; flex-wrap:nowrap;
  margin-left:auto; padding-left:6px;
  min-width:0; overflow-x:auto; overflow-y:hidden;
  scrollbar-width:thin; padding-bottom:2px;
}
.member-app-toggle, .member-subperm-toggle{flex:0 0 auto; white-space:nowrap;}
/* Apps cell takes the auto-margin; actions then sit right after it. */
.member-apps + .member-actions{margin-left:14px;}
.member-app-toggle{
  display:flex; align-items:center; gap:4px; font-size:11px; font-weight:600;
  color:var(--text-dim); cursor:pointer; user-select:none;
}
.member-app-toggle input{
  width:14px; height:14px; margin:0; cursor:pointer; accent-color:var(--accent); flex-shrink:0;
}
.member-apps-all{
  font-size:11px; font-weight:700; color:var(--text-faint);
  padding:2px 10px; border-radius:20px; background:var(--bg-elev); border:1px solid var(--border-soft);
}

/* ---- ComfyUI 세부 권한(2단계 권한의 세부 레이어) 토글 ---- */
/* 상위 모듈 체크박스(ComfyUI 접근) 바로 뒤에 붙어, 시각적으로 "딸린" 세부 권한임을
   나타낸다(왼쪽 구분선 + 약간 들여쓰기). 역할로 이미 부여된 엔지니어 행에서는
   .locked로 흐리게 + 커서 기본값 처리. */
.member-subperm-toggle{
  display:flex; align-items:center; gap:4px; font-size:11px; font-weight:600;
  color:var(--text-dim); cursor:pointer; user-select:none;
  padding-left:8px; margin-left:2px; border-left:1px solid var(--border-soft);
}
.member-subperm-toggle input{width:14px; height:14px; margin:0; cursor:pointer; accent-color:var(--accent); flex-shrink:0;}
.member-subperm-toggle.locked{opacity:.7; cursor:default;}
.member-subperm-toggle.locked input{cursor:not-allowed;}
.member-subperm-tag{
  margin-left:5px; font-size:9px; font-weight:800; letter-spacing:.03em; text-transform:uppercase;
  color:var(--success); background:color-mix(in srgb, var(--success) 12%, transparent);
  border:1px solid color-mix(in srgb, var(--success) 35%, transparent);
  padding:1px 5px; border-radius:10px; vertical-align:middle;
}

/* ---- 회원 관리 2단 레이아웃 (본문 + 우측 권한 구조/요약 사이드) ---- */
/* align-items:stretch(기존 flex-start) — 회원 목록(왼쪽)이 사이드 카드(오른쪽)보다
   훨씬 길어 두 칼럼의 아래쪽 끝이 서로 안 맞던 것을 정렬("정렬 높이 맞춰줘" 요청).
   두 칼럼 박스 높이를 서로 맞추고, sticky는 그대로 유지해 짧은 쪽은 스크롤해도
   위쪽에 붙어있는 동작을 보존한다. */
.members-layout{display:flex; gap:20px; align-items:stretch;}
.members-main{flex:1; min-width:0;}
/* 2026-07-09: "현황요약/권한구조를 왼쪽 전체회원과 높이 정렬" — 예전엔 align-self:
   flex-start라 사이드가 자기 콘텐츠 높이만큼만 차지해 왼쪽 회원 목록보다 짧게
   끝나 보였다. 부모(.members-layout)의 align-items:stretch가 실제로 적용되게
   flex-start를 빼고, 사이드 안의 마지막 카드(권한 구조)가 남는 세로 공간을
   흡수하도록 flex:1을 준다 — 그래서 사이드 전체 바닥이 왼쪽 목록 바닥과 맞는다. */
.members-side{width:300px; flex-shrink:0; display:flex; flex-direction:column; gap:14px; position:sticky; top:12px;}
.members-side-card{
  background:var(--surface); border:1px solid var(--border-soft); border-radius:var(--radius); padding:14px 15px;
}
.members-side-card:last-child{flex:1; display:flex; flex-direction:column; min-height:0;}
.members-side-card:last-child .tier-legend{flex:1; min-height:0; overflow-y:auto;}
.members-side-title{font-size:12px; font-weight:800; color:var(--text-dim); letter-spacing:.02em; margin-bottom:11px; text-transform:uppercase;}

/* 요약 */
.members-summary{display:flex; flex-direction:column; gap:8px;}
.members-stat{display:flex; align-items:baseline; justify-content:space-between; gap:10px; font-size:12px;}
.members-stat-label{color:var(--text-faint); font-weight:600;}
.members-stat-val{color:var(--text); font-weight:800; text-align:right;}
.members-stat-sub{color:var(--text-faint); font-weight:600; font-size:10.5px;}
.members-stat-divider{height:1px; background:var(--border-soft); margin:3px 0;}
.members-tier-counts{display:flex; flex-wrap:wrap; gap:5px; margin-top:2px;}
.members-tier-counts .member-role-badge{font-size:9.5px; padding:2px 7px; text-transform:none;}

/* 권한 구조 레전드 */
.tier-legend{list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:11px;}
.tier-legend-item{display:flex; flex-direction:column; gap:4px;}
.tier-legend-item .member-role-badge{align-self:flex-start;}
.tier-legend-desc{font-size:11.5px; line-height:1.5; color:var(--text-dim);}
.tier-legend-desc b{color:var(--text); font-weight:700;}
.tier-legend-note{
  margin-top:12px; padding-top:11px; border-top:1px solid var(--border-soft);
  font-size:11px; line-height:1.6; color:var(--text-faint);
}
.tier-legend-note b{color:var(--text-dim); font-weight:700;}

@media (max-width:900px){
  .members-layout{flex-direction:column;}
  .members-side{width:100%; position:static; flex-direction:column;}
}

/* ---- creator badge on shared feed/gallery cards ---- */
.creator-badge{font-size:11px; color:var(--text-faint); font-weight:600;}

/* ---- 준비 중 (coming-soon) module placeholder ---- */
.placeholder-wrap{
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  text-align:center; min-height:52vh; gap:14px; padding:40px 20px;
}
.placeholder-icon{
  width:64px; height:64px; border-radius:18px;
  display:flex; align-items:center; justify-content:center;
  background:var(--surface); border:1px solid var(--border-soft); color:var(--accent);
}
.placeholder-icon svg{width:30px;height:30px;}
/* Mirrors the canonical .page-h / .page-sub type scale so 준비 중 reads like
   every other module's page heading rather than a one-off size. */
.placeholder-title{font-family:var(--serif); font-size:24px; font-weight:700; letter-spacing:-.3px;}
.placeholder-desc{font-size:14px; color:var(--text-dim); max-width:440px; line-height:1.6;}

/* ============================================================
   RESPONSIVE — phone/tablet support
   Desktop stays exactly as-is (the fixed topbar + sidebar grid).
   On narrow viewports the sidebar becomes a slide-out drawer toggled
   by a hamburger, the top-bar module nav scrolls horizontally, and
   content grids reflow toward fewer columns. Touch targets stay ~40px+.
   ============================================================ */

/* Hamburger — hidden on desktop, shown on tablet/phone. */
.mobile-nav-toggle{
  display:none; width:40px; height:40px; flex-shrink:0; margin-right:2px;
  border-radius:9px; background:var(--surface); border:1px solid var(--border-soft);
  color:var(--text); align-items:center; justify-content:center; cursor:pointer;
}
.mobile-nav-toggle svg{width:20px;height:20px;}
/* 모바일 헤더 뒤로가기 — 데스크톱에서는 항상 숨김. 모바일(≤768px)에서 내비게이션
   스택이 2단계 이상일 때(body.mnav-canback)만 노출된다(아래 @media 참조). 셸의
   기존 테마 토큰만 사용해 햄버거와 동일한 룩앤필을 유지한다. */
.mobile-back{
  display:none; width:40px; height:40px; flex-shrink:0; margin-right:2px;
  border-radius:9px; background:var(--surface); border:1px solid var(--border-soft);
  color:var(--text); align-items:center; justify-content:center; cursor:pointer;
}
.mobile-back svg{width:20px;height:20px;}
/* Backdrop behind the open drawer — hidden until .drawer-open is set on <body>. */
.mobile-nav-backdrop{
  display:none; position:fixed; inset:0; z-index:29;
  background:color-mix(in srgb, var(--bg) 40%, black 55%);
}

/* -------- Mobile bottom tab bar + 생성 FAB (phone only) --------
   Hidden on desktop; shown at ≤640px once authed. Each tab calls the existing
   switchModule/switchView — no reimplemented nav. The center tab is raised as a
   FAB for the primary "생성" action. */
.mobile-tabbar{display:none;}
body.authed .mobile-tabbar{}
.mtab{
  flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:3px; background:none; border:none; cursor:pointer;
  color:var(--text-faint); font-size:10.5px; font-weight:600; font-family:var(--sans);
  padding:8px 2px;
}
.mtab svg{width:22px; height:22px;}
.mtab.active{color:var(--accent);}
.mtab-fab{
  position:relative; color:var(--on-accent);
}
/* Raised, floating 생성 FAB — larger accent circle that sits above the bar with
   an accent glow, matching the redesign mockup's center action. */
.mtab-fab svg{
  width:26px; height:26px;
  background:var(--accent); color:var(--on-accent);
  border-radius:50%; padding:13px; box-sizing:content-box;
  margin-top:-26px;
  box-shadow:0 8px 24px -6px color-mix(in srgb, var(--accent) 60%, transparent);
}
.mtab-fab span{color:var(--accent); margin-top:6px;}

/* -------- Tablet (≤ 900px): rail + sidebar collapse into one slide-out drawer -------- */
@media (max-width: 900px){
  .mobile-nav-toggle{display:flex;}

  /* Single-column shell: topbar spans full width, main fills the rest.
     The icon rail AND the context sidebar are both lifted out of the grid
     flow and positioned together as one off-canvas drawer (rail on the left
     edge of the drawer, context panel beside it). */
  :root{ --topbar-h:64px; }
  /* 모바일에서는 어떤 .app-shell 변형이든 예외 없이 단일 컬럼 셸로 접는다.
     [근본 원인] 이전에는 모바일 접힘 규칙을 변형별 선택자(.app-shell,
     .app-shell.ctx-collapsed …)로 하나씩 나열해 커버하려 했다. 그런데
     CTX_COLLAPSED_MODULES = {members, portal} 뿐이라 ctx-collapsed는
     포탈/회원관리에만 붙고, 나머지 전 모듈(생성·ComfyUI·갤러리·설정·NAI·3D·
     챗봇·스토리보드·팀피드 등)은 plain .app-shell 을 쓴다. 변형을 하나라도
     빠뜨리면 그 화면만 데스크톱 3열(88px 236px 1fr)이 남아 main이 좁은 트랙에
     갇혀 붕괴됐고, 실제로 "포탈만 고쳐지고 나머지는 그대로"가 반복됐다.
     [해결] 변형 나열을 끊고 plain .app-shell 하나에 !important 를 걸어
     특이도 경쟁 자체를 제거한다. .app-shell 을 가진 요소는 어떤 클래스 조합
     (ctx-collapsed / comfy-jobs-docked / 향후 추가될 조합 무엇이든)이든
     모바일에서 항상 1열이 된다. grid-template-columns/areas 에 !important 를
     쓰는 다른 .app-shell 규칙은 코드베이스에 존재하지 않으므로 이 규칙이
     무조건 승리한다(데스크톱 규칙은 이 미디어쿼리 밖이라 영향 없음). */
  .app-shell{
    grid-template-columns:1fr !important;
    grid-template-rows:var(--topbar-h) 1fr;
    grid-template-areas:"topbar" "main" !important;
  }

  .global-topbar{padding:0 14px; gap:10px;}
  .gt-user .uchip-tx{display:none;}   /* keep just the avatar to save room */
  .gt-user .uchip{padding:5px;}
  .tbtn#notifBtn{display:none;}   /* bell hidden on small screens to save room */
  .gt-search-label, .gt-search-kbd{display:none;}   /* search collapses to an icon */
  .gt-search-trigger{padding:8px;}
  /* Breadcrumb tightens to just the current view name on small screens (the
     module is already implied by the bottom tab bar / drawer). */
  .gt-crumb-mod, .gt-crumb-sep{display:none;}
  /* 콘텐츠 제작 상단 서브탭도 좁은 화면에서는 라벨을 접어 아이콘만 남긴다
     (모바일에서 한 줄에 다 안 들어와 화면이 잘려 보인다는 피드백 반영). */
  .gt-subtab{padding:0 9px; gap:0; font-size:0;}
  .gt-subtab svg{width:17px; height:17px;}

  /* Icon rail → left column of the drawer.
     [견고화 — 닫힘 상태 봉인] 이전에 두 차례(fix #2/#3) "닫았는데 화면에 조각이
     남거나 콘텐츠를 덮는다"가 반복된 근본 원인은, 닫힘을 오직 transform 오프셋
     '계산'에만 의존했기 때문이다(계산이 조금이라도 어긋나면 fixed 요소가 콘텐츠
     위에 그대로 남는다). 그래서 슬라이드 애니메이션(transform)은 그대로 두되,
     닫힘 상태에서는 visibility:hidden + pointer-events:none 로 '그리지도, 덮지도,
     받지도' 않게 이중으로 봉인한다 — 앞으로 어떤 오프셋 오류가 생겨도 닫힌 드로어가
     콘텐츠를 가리는 일은 구조적으로 불가능해진다. visibility 전환은 슬라이드가 끝난
     뒤(.22s 지연) 일어나게 해 닫는 애니메이션은 그대로 보인다. */
  .icon-rail{
    position:fixed; top:0; left:0; z-index:31;
    width:72px; height:100vh;
    transform:translateX(-100%);
    visibility:hidden; pointer-events:none;
    transition:transform .22s ease;
    box-shadow:var(--shadow); padding-top:14px;
  }
  /* Context panel → sits immediately to the right of the rail within the drawer. */
  .sidebar{
    position:fixed; top:0; left:72px; z-index:30;
    width:240px; max-width:calc(82vw - 72px); height:100vh;
    /* [근본 원인 — 모바일 "좌측에 빈 사이드 조각이 남는다" 버그]
       이 컨텍스트 패널은 드로어 안에서 아이콘 레일(72px) '오른쪽'에 붙도록
       left:72px 로 밀어 배치한다. 닫을 때 transform:translateX(-100%) 만 걸면
       -100% = 자기 폭(240px)만큼만 왼쪽으로 이동하므로, left:72px 오프셋이
       상쇄되지 않아 우측 끝이 정확히 x=72px 에 걸린다 → 화면 좌측에 폭 72px
       짜리 사이드바 조각(z-index:30 이라 main 위에 뜸)이 그대로 남는다.
       members/portal 은 .app-shell.ctx-collapsed 로 width:0 이 되어 조각이
       없었지만(그래서 "포탈만 고쳐지고 나머지 모듈은 그대로"였다), 그 외
       모든 모듈(생성·ComfyUI·설정·신고·챗봇·스토리보드·NAI·3D·LoRA)은 이
       72px 조각이 노출됐다. 이전 두 차례 수정은 grid-template-columns/areas 만
       손봐 셸을 1열로 만들었을 뿐, 이 조각은 grid 가 아니라 position:fixed +
       transform 문제라 절대 사라지지 않았다.
       [해결] 자기 폭(-100%)에 더해 left 오프셋(72px)까지 함께 밀어내
       우측 끝을 정확히 x=0(뷰포트 밖)에 두면 어떤 모듈에서도 완전히 사라진다.
       드로어를 열면 아래 body.drawer-open .sidebar{transform:translateX(0)}
       이 다시 제자리(left:72)로 슬라이드 인시킨다. */
    transform:translateX(calc(-100% - 72px));
    /* 위 .icon-rail 과 동일한 닫힘 봉인: 계산에 의존하지 않고 닫히면 아예 안 보이고
       입력도 안 받는다(콘텐츠를 가리는 조각이 원천적으로 불가능). visibility 는
       전환 없이 즉시 적용돼, 닫는 순간 곧바로 숨는다(조각이 잠깐이라도 보일 창이 0). */
    visibility:hidden; pointer-events:none;
    transition:transform .22s ease;
    box-shadow:var(--shadow);
    padding-top:16px;
  }
  /* 열림 상태: 제자리로 슬라이드 인 + 즉시 보이고 입력을 받는다(지연 없이). */
  body.drawer-open .icon-rail{transform:translateX(0); visibility:visible; pointer-events:auto; transition:transform .22s ease;}
  body.drawer-open .sidebar{transform:translateX(0); visibility:visible; pointer-events:auto; transition:transform .22s ease;}
  body.drawer-open .mobile-nav-backdrop{display:block;}

  /* BUG FIX (모바일 드로어에 빈 다크 사각형이 뜨는 원인): NAI/3D 시뮬레이터처럼
     컨텍스트 패널이 필요 없는 모듈은 데스크톱에서 .app-shell.ctx-collapsed가
     grid-template-columns를 88px 0 1fr로 접어 sidebar 폭을 0으로 만들지만,
     이 미디어쿼리의 .sidebar{position:fixed;width:240px;left:72px}는 grid를
     타지 않는 규칙이라 ctx-collapsed와 무관하게 항상 240px짜리 빈 패널을 그려
     드로어를 열면 아이콘 레일 옆에 텅 빈 어두운 사각형이 보이는 버그가 있었다.
     여기서 그 경우만 폭 0으로 강제 축소해 드로어가 아이콘 레일만으로 구성되게
     맞춘다(데스크톱 동작과 동일하게). */
  .app-shell.ctx-collapsed .sidebar{
    width:0; max-width:0; padding:0; border:none; overflow:hidden;
    box-shadow:none;
    /* 위 .sidebar 와 동일한 오프셋 보정: width:0 이라 조각이 안 남긴 하지만,
       규칙을 통일해 어떤 경우에도 left:72px 만큼 함께 밀려나 완전히 사라지게
       한다(향후 이 열에 최소 폭이 생겨도 안전). */
    transform:translateX(calc(-100% - 72px)) !important;
  }

  /* Comfortable tap targets inside the drawer. */
  .sidebar-modnav .modview-item,
  .sidebar-ctx .create-tab,
  .sidebar-ctx .settings-tab,
  #galleryFilterChips .chip{min-height:42px;}

  /* Content grids reflow to fewer columns. */
  .stat-row{grid-template-columns:repeat(2,1fr);}
  .quick-row{grid-template-columns:repeat(2,1fr);}
  .thumb-grid{grid-template-columns:repeat(4,1fr);}
  .feed-grid{grid-template-columns:repeat(3,1fr);}
  .gallery-grid{grid-template-columns:repeat(3,1fr);}
  .tut-grid{grid-template-columns:repeat(2,1fr);}
  .settings-grid{grid-template-columns:1fr;}

  .content{padding:18px 18px 60px;}
  .topbar{padding:0 18px;}
  .dock-wrap{padding:0 16px 16px;}

  /* Wide history table would overflow — let it scroll horizontally. */
  .card > .hist-row{min-width:640px;}
  #historyList{min-width:640px;}
  .view#view-history .card{overflow-x:auto;}

  /* [전역 가로 오버플로 안전망]
     .content 는 세로 스크롤 컨테이너(overflow-y:auto)이지만 overflow-x 는
     visible 이라, 내부 어떤 요소가 뷰포트보다 넓으면 CSS 규칙상 가로축도
     auto 로 승격돼 콘텐츠 영역 전체가 가로로 스크롤된다("모바일에서 전체를
     한 번에 볼 수 없다"의 실제 원인 — body 는 overflow:hidden 이라 페이지가
     아니라 이 .content 가 가로로 밀린다). 여기서 가로만 잘라내 최상위 콘텐츠가
     통째로 옆으로 밀리는 일을 원천 차단한다. 내부에서 '의도적으로' 가로
     스크롤되는 컴포넌트(히스토리 표 .card{overflow-x:auto}, .recipe-row,
     .comfy-ov-thumbstrip 등)는 각자 자신의 스크롤 컨테이너를 가지므로 이
     규칙의 영향을 받지 않고 정상 동작한다. */
  .content{overflow-x:hidden;}

  /* ComfyUI 서버 카드 그리드는 최소열폭 320px 고정이라 좁은 폰에서 뷰포트를
     넘겼다(위 안전망이 켜지면 넘친 부분이 잘려 카드 오른쪽이 보이지 않는다).
     좁은 화면에서는 1열로 접어 카드가 항상 폭 안에 들어오게 한다. */
  .comfy-server-grid{grid-template-columns:1fr;}

  /* 결과 상세(개요) 정보 열의 min-width:270px 가 아주 좁은 폭에서 모달을
     넘길 수 있어, 모바일에서는 최소폭 제약을 풀어 폭에 맞춰 줄어들게 한다. */
  .comfy-ov-info-col{min-width:0;}
  .comfy-ov-media-col{min-width:0; max-width:100%;}
}

/* -------- Phone (≤ 560px): tighten to single/double column -------- */
@media (max-width: 560px){
  :root{ --topbar-h:56px; }
  .app-shell{grid-template-rows:var(--topbar-h) 1fr;}
  .global-topbar{padding:0 10px; gap:8px;}
  .gt-logo .logo-sub{display:none;}

  .stat-row{grid-template-columns:1fr 1fr; gap:10px;}
  .quick-row{grid-template-columns:1fr;}
  .thumb-grid{grid-template-columns:repeat(3,1fr); gap:9px;}
  .feed-grid{grid-template-columns:repeat(2,1fr); gap:10px;}
  .gallery-grid{grid-template-columns:repeat(2,1fr); gap:10px;}
  .tut-grid{grid-template-columns:1fr;}

  .content{padding:14px 12px 60px;}
  .topbar{padding:0 12px; gap:10px;}
  /* Long secondary label under the module title crowds the small screen. */
  .topbar-sub{display:none;}

  /* Dock controls wrap freely; the composer spans the full width. */
  .dock-wrap{padding:0 10px 12px;}
  .dock{padding:12px 12px 10px;}
  .dock-controls{gap:6px;}

  /* Filter bars / toolbars wrap instead of overflowing. */
  .filter-bar .search-box{max-width:none; flex:1 1 100%;}

  /* 좁은 화면(≤900px)에서는 회원 한 명이 한 줄에 다 들어가지 않는다. 여기서는
     "행 안에서 요소 순서를 흩뜨리는" 재배치가 아니라, 신원 / 권한 / 액션이라는
     의미 단위로만 줄을 나눈다. 각 묶음 내부는 여전히 nowrap + 가로 스크롤이라
     체크박스가 낱개로 접히지는 않는다. */
  .member-row{flex-wrap:wrap;}
  .member-apps{margin-left:0; padding-left:0; width:100%; flex-wrap:nowrap; overflow-x:auto;}
  .member-apps + .member-actions{margin-left:0;}
  .member-actions{width:100%; overflow-x:auto; flex-wrap:nowrap;}

  /* Lightbox / mask editor use the full small screen. */
  .lightbox-overlay{padding:16px;}
  .mask-editor-overlay{padding:12px;}
}

/* -------- Mobile shell (≤ 768px): 하단 탭 바가 1차 내비게이션 --------
   모바일 개편 #282 Phase 1. 이 브레이크포인트가 셸의 "모바일" 기준이다.
   데스크톱 아이콘 레일·컨텍스트 패널은 상위 @media(≤900px)에서 이미 off-canvas
   드로어로 접혀 있고(햄버거/뒤로가기로 접근), 여기서는 상단 헤더(뒤로가기·타이틀·
   테마) + 하단 5구좌 탭 바(홈/피드/＋생성/채팅/설정)를 셸 크롬으로 얹는다.
   기존 데이/나이트 테마 토큰(--sidebar/--accent/--surface 등)만 재사용한다. */
@media (max-width: 768px){
  /* 5구좌 하단 탭 바 — 고정, 하단 세이프에어리어 반영, 각 탭 터치영역 ≥44px. */
  body.authed .mobile-tabbar{
    /* 플로팅 밴드 — 드로어(1010)보다 위: 드로어를 열어둔 채로도 탭 이동이 되어야 한다. */
    display:flex; position:fixed; left:0; right:0; bottom:0; z-index:1100;
    background:var(--sidebar); border-top:1px solid var(--border-soft);
    padding-bottom:env(safe-area-inset-bottom, 0);
  }
  .mtab{min-height:44px;}
  /* Keep scrollable content and the floating dock clear of the tab bar. */
  .content{padding-bottom:84px;}
  .dock-wrap{padding-bottom:calc(16px + 64px);}
  /* 채팅이 이제 하단 탭이므로 떠 있는 도우미 FAB는 중복이라 숨겨 화면을
     정리한다(진입은 하단 "채팅" 탭 하나로 통일). */
  body.authed .assistant-fab{display:none;}

  /* 헤더 뒤로가기 — 내비 스택이 2단계 이상일 때만(body.mnav-canback) 노출. */
  body.mnav-canback .mobile-back{display:flex;}

  /* 채팅(도우미/메신저) 패널은 모바일에서 전체화면 오버레이로 확장한다.
     iOS Safari 100vh 이슈를 피하려 dvh를 쓴다(미지원 브라우저는 vh로 폴백). */
  #assistantPanel.assistant-panel{
    left:0; right:0; top:0; bottom:0;
    width:100%; max-width:none;
    height:100vh; height:100dvh; max-height:none;
    border:none; border-radius:0; z-index:1130;   /* 데스크톱과 같은 플로팅 밴드 값 */
  }
  #assistantPanel .assistant-resize-handle{display:none;}
}

/* -------- 모바일 개편 #282 Phase 2 — 만들기 dock / 팀 피드 상세 / 내 작업물 --------
   데스크톱 로직·DOM·/api/generate/* 는 일절 건드리지 않고, 좁은 폭(≤768px)에서의
   레이아웃 재배치와 터치 타깃 확대만 얹는다. Phase 1의 셸 크롬(하단 탭 바·헤더
   뒤로가기) 위에서 각 화면이 손가락만으로 완결되도록 하는 것이 목표다. */
@media (max-width: 768px){
  /* [1] 생성 dock — 팝오버(모델/비율/파라미터 목록)가 화면 밖으로 넘치지 않게
     폭을 뷰포트에 맞추고, 핵심 조작(모델·비율 pill, 생성 버튼) 터치 타깃을 키운다.
     dock 자체의 입력/컨트롤 로직·마크업은 그대로 두고 CSS 폭·크기만 조정. */
  .dock .popover{ width:min(320px, calc(100vw - 36px)); max-width:calc(100vw - 36px); }
  .dock-pill{ min-height:38px; }
  .dock-submit{ width:40px; height:40px; }
  .dock-submit svg{ width:18px; height:18px; }

  /* [1] 결과 라이트박스 — 좁은 화면에서 여백을 줄여 거의 전체화면으로 띄우고,
     닫기·이전/다음 버튼을 터치하기 쉬운 크기·위치로. 미디어 높이는 60vh로 제한해
     그 아래 메타(프롬프트/파라미터) 패널까지 스크롤로 닿을 수 있게 한다. */
  .lightbox-overlay{ padding:12px; }
  .lightbox-content{ width:100%; max-width:100%; max-height:none; overflow-y:auto; }
  .lightbox-body img, .lightbox-body video{ max-height:60vh; }
  .lightbox-close{ top:12px; right:12px; width:44px; height:44px; }
  .lightbox-nav{ width:40px; height:40px; }
  .lightbox-prev{ left:10px; }
  .lightbox-next{ right:10px; }

  /* [2] 팀 피드 상세 — 데스크톱에서는 그리드 우측 sticky 패널이지만, 모바일에서는
     타일을 누르면(상세에 .fd-media가 채워지면) 전체화면 오버레이로 확장한다.
     비어 있을 때(안내 문구)는 세로 공간을 차지하지 않도록 감춘다. 닫기 버튼은
     모바일에서만 노출(renderFeedDetail이 항상 렌더, 데스크톱은 display:none). */
  .feedpage-detail > .feed-detail-empty{ display:none; }
  .feedpage-detail:has(.fd-media){
    position:fixed; inset:0; z-index:2150;   /* 모달 밴드(모바일 전면 상세) */
    height:100dvh; max-height:none; border:none; border-radius:0;
    padding:52px 16px calc(20px + env(safe-area-inset-bottom, 0)); overflow:auto;
  }
  .feedpage-detail:has(.fd-media) .fd-mobile-close{ display:inline-flex; }
  .fd-actbtn{ min-height:40px; }

  /* [3] 내 작업물 — 타일의 공개/비공개 토글을 터치 타깃(≥44px)에 맞춰 키운다.
     코너 뱃지 룩은 유지하되 padding/min-height로 탭 영역만 확대. */
  .portal-works .pwork-vis{ top:8px; right:8px; padding:10px 13px; min-height:44px; font-size:12px; }
  .portal-works .pwork-vis svg{ width:13px; height:13px; }
}

/* -------- 모바일 개편 #282 Phase 3 — 회원 관리 / 설정 / 서버 관리 / 팀 채팅 / 신고 --------
   Phase 1(셸 크롬)·Phase 2(생성·피드·작업물)와 동일한 원칙: 데스크톱 로직·DOM·
   /api/generate/* 은 일절 건드리지 않고, 좁은 폭(≤768px)에서의 레이아웃 재배치와
   터치 타깃(≥44px) 확대만 얹는다. 회원 행은 기존 flex-wrap 행(≤900px에서 이미 줄바꿈)
   위에 정렬 규칙만 얹어 카드형으로 만들고(마크업/렌더 함수 그대로), 설정·서버 관리
   폼은 세로로 쌓고 입력/버튼을 손가락에 맞춘다. 팀 채팅·신고 팝오버는 Phase 1이
   전체화면으로 확장해 둔 도우미 패널 안에서 편히 쓰이도록 입력 폰트(iOS 포커스 확대
   방지 16px)와 버튼을 키운다. 모든 규칙을 이 미디어쿼리 안에 한정해 데스크톱 회귀가
   없게 한다. */
@media (max-width: 768px){
  /* [1] 회원 관리 — 가로로 늘어서던 회원 행을 카드처럼 재배치한다. .member-row는
     상위 ≤900px 규칙으로 이미 flex-wrap(+ .member-apps 전체폭)이므로, 마크업 변경
     없이 조작 요소만 각 줄 전체 폭 + 큰 터치 타깃으로 얹는다: 신원(아바타+이름)과
     역할·상태 뱃지는 위쪽에 자연스레 흐르고, 역할 변경 드롭다운·권한 체크박스 묶음·
     액션 버튼은 손가락으로 완전히 조작 가능하도록 커진다. */
  .member-row{ padding:14px; gap:10px; }
  /* 역할 변경 드롭다운 — 자기 줄 전체 폭 + ≥44px. */
  .member-role-select{ flex:1 1 100%; min-height:44px; font-size:14px; }
  /* 모듈 권한 체크박스(권한 게이팅 UI) — 각 라벨을 넉넉한 탭 영역으로, 체크박스도 키움.
     터치에서도 줄바꿈은 하지 않는다(가로로 밀어 본다) — 낱개로 접히면 어느 체크박스가
     누구 것인지 따라가기 어렵다. */
  .member-apps{ gap:8px 14px; }
  .member-app-toggle, .member-subperm-toggle{ min-height:40px; font-size:13px; }
  .member-app-toggle input, .member-subperm-toggle input{ width:18px; height:18px; }
  /* 승인/거부/삭제 — 한 줄 가득 채우고 각 버튼 ≥44px. */
  .member-actions{ width:100%; }
  .member-actions .member-btn{
    flex:1; min-height:44px; display:flex; align-items:center; justify-content:center; font-size:13px;
  }

  /* [2] 설정 — 폼 카드가 뷰포트를 넘지 않게(inline max-width 무력화) 하고, 입력·
     셀렉트·버튼을 큰 터치 타깃으로. iOS는 폰트 16px 미만 입력에 포커스하면 화면을
     확대하므로 텍스트 입력류를 16px로 올려 확대를 막는다. */
  #view-settings .card{ max-width:none; }
  .field-row input[type=text], .field-row input[type=password], .field-row select{ min-height:44px; font-size:16px; }
  .key-input-row{ flex-wrap:wrap; }
  .key-input-row input{ flex:1 1 100%; min-height:44px; font-size:16px; }
  .key-input-row .btn-ghost{ min-height:44px; }
  #view-settings .btn-primary, #view-settings .btn-ghost{ min-height:44px; }
  /* 모델 ID 매핑 표 — 좁은 폭에서 첫 칸(모델명)의 nowrap을 풀고 여백을 줄여 두 칸이
     가로 넘침 없이 들어가게 한다. 입력칸은 16px로 확대 방지 + 탭하기 편한 높이. */
  .model-id-table td:first-child{ white-space:normal; font-size:13px; }
  .model-id-table th, .model-id-table td{ padding:8px 10px; }
  .model-id-table input{ font-size:16px; min-height:40px; }

  /* [3] 서버 관리(마스터 전용) — 카드가 한 칸으로 쌓이는 건 상위 ≤900px에서 이미
     처리됨. 여기서는 업데이트 확인/적용+재시작/지금 재시작·ngrok 저장 버튼을 전체
     폭 + ≥44px로 키우고(긴 한글 라벨이 좁은 폭에서 세로로 쪼개지지 않게), git 상태
     값(커밋 해시·원격 URL)이 좁은 폭에서 줄바꿈되게 한다. */
  #btnCheckUpdate, #btnApplyUpdate, #btnRestart{ width:100%; }
  #pane-server .kv-row{ flex-wrap:wrap; gap:2px 10px; }
  #gitCommit, #gitRemote, #gitBranch, #gitCommitMsg, #tunnelPublicUrl{ word-break:break-all; }

  /* [4] 팀 채팅 & AI 도우미 입력 — 전체화면 도우미 패널(Phase 1) 안에서 입력창 폰트를
     16px로 올려 iOS 포커스 확대를 막고, 전송/첨부 버튼과 대화 전환 드롭다운을 손가락
     타깃(≥44px)으로 키운다. 메시지 버블은 좁은 폭에 맞춰 살짝 넓히고 글자를 키운다. */
  #assistantPanel .assistant-input{ font-size:16px; }
  #assistantPanel .assistant-send, #assistantPanel .assistant-attach-btn{ width:44px; height:44px; }
  #assistantPanel .assistant-send svg, #assistantPanel .assistant-attach-btn svg{ width:19px; height:19px; }
  #assistantPanel .team-chat-recipient-sel{ min-height:44px; font-size:15px; }
  #assistantPanel .team-chat-leave-btn{ width:42px; height:42px; }
  #assistantPanel .assistant-tab{ min-height:44px; font-size:14px; }
  #assistantPanel .assistant-msg{ max-width:88%; font-size:14px; }

  /* [5] 신고 팝오버 — 전체화면 패널 안 우측 상단에 뜨는 팝오버가 화면 밖으로 밀리지
     않게 폭을 뷰포트에 맞춘다(설정·투명도 팝오버도 동일). 입력 폰트 16px·전송/첨부
     버튼 44px는 위 [4] 규칙이 함께 적용된다. 마스터 전용 신고 열람 뷰(#view-feedback)의
     툴바는 세로로 쌓고 액션 버튼을 큰 타깃으로. */
  #assistantPanel .assistant-settings{ right:12px; width:min(320px, calc(100vw - 24px)); }
  #assistantPanel .assistant-feedback-pop{ width:min(360px, calc(100vw - 24px)); }
  #assistantFeedback .assistant-input{ min-height:88px; }
  .feedback-toolbar{ flex-direction:column; align-items:stretch; }
  .feedback-toolbar-actions .btn{ flex:1; min-height:44px; }
}

/* ============================================================
   Studio assistant — Gemini-backed floating chat widget.
   Fixed to the bottom-right, on the app's dark theme tokens.
   Hidden until the body gains .authed (set in enterApp), so it
   never shows on the login/setup screens.
   ============================================================ */
.assistant-fab{
  display:none;
  position:fixed; right:22px; bottom:22px; z-index:1120;  /* 플로팅 밴드 */
  width:54px; height:54px; border-radius:50%;
  align-items:center; justify-content:center;
  background:var(--accent); color:var(--on-accent);
  border:none; cursor:pointer;
  box-shadow:var(--shadow);
  transition:transform .12s ease, filter .12s ease;
}
body.authed .assistant-fab{display:flex;}
.assistant-fab:hover{transform:translateY(-1px); filter:brightness(1.05);}
.assistant-fab svg{width:24px; height:24px;}
/* 드래그로 위치 이동 가능(initAssistantFabDrag, app.js). 드래그 중에는 커서를
   grabbing으로 바꾸고 hover의 translateY를 꺼서 손가락 아래에 붙어 움직이게. */
.assistant-fab{touch-action:none; cursor:grab;}
.assistant-fab.dragging{cursor:grabbing; transform:none; transition:none;}
.assistant-fab.dragging:hover{transform:none;}
/* 팀 채팅 안 읽음 배지(요청 #2) — FAB 우상단에 붙는 빨간 카운터. */
.assistant-fab-badge{
  position:absolute; top:-2px; right:-2px; min-width:18px; height:18px;
  padding:0 5px; box-sizing:border-box;
  display:flex; align-items:center; justify-content:center;
  font-size:11px; font-weight:800; line-height:1;
  color:#fff; background:var(--danger);
  border:2px solid var(--surface); border-radius:999px;
  pointer-events:none;
}
.assistant-fab-badge[hidden]{display:none;}

.assistant-panel{
  display:none;
  position:fixed; right:22px; bottom:88px; z-index:1130;  /* 플로팅 밴드 */
  width:380px; max-width:calc(100vw - 44px);
  height:520px; max-height:calc(100vh - 130px);
  flex-direction:column;
  color:var(--text);
  /* 배경에만 알파를 적용(--assistant-panel-bg-alpha, 기본 1=불투명). 텍스트·아이콘은
     각자 별도 요소라 이 값의 영향을 받지 않아 항상 100% 선명하다(요청). color-mix
     미지원 브라우저는 위의 불투명 var(--surface)로 폴백. */
  background:var(--surface);
  background:color-mix(in srgb, var(--surface) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
  /* 테두리는 .assistant-panel-inner의 opacity 대상 밖(리사이즈 그립과 형제)이라
     그동안 계속 100% 선명하게 남아 배경만 흐려져도 사각 아웃라인이 도드라져
     보이는 문제가 있었다("투명도 줄이면 아웃라인도 사라져야함") — border-color에도
     동일한 알파를 적용해 배경과 함께 옅어지도록 수정. */
  border:1px solid var(--border);
  border-color:color-mix(in srgb, var(--border) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
  border-radius:var(--radius);
  box-shadow:var(--shadow); overflow:hidden;
}
.assistant-panel.open{display:flex;}
/* 투명도 wrapper — 리사이즈 그립을 뺀 패널 내용 전체(텍스트·아이콘 포함)를
   opacity로 함께 바랜다. "글자는 투명해지지 않는 문제" 대응. */
.assistant-panel-inner{
  display:flex; flex-direction:column; flex:1; min-height:0;
  opacity:var(--assistant-panel-bg-alpha, 1);
}

/* 패널 맨 위쪽 가장자리의 가로 리사이즈 그립 — 위로 끌면 높이만 늘어난다
   (app.js startAssistantResize, 2026-07-09 재작업: 폭 조절 없음). 얇은 띠라
   헤더 타이틀·아이콘과 살짝 겹치지만 클릭 판정 영역만 위 8px로 한정된다. */
/* 보이는 표시 없이 판정 영역만(2026-08-05 지시) — 바·화살표 아이콘 제거. */
.assistant-resize-handle{
  position:absolute; top:0; left:0; right:0; height:8px; z-index:6;
  cursor:ns-resize;
}
/* 호버 강조 없음(2026-08-05 지시) — 손잡이 위에 오면 **커서 모양만** 바뀐다.
   라임 배경이 켜지면 헤더 위를 지나갈 때마다 띠가 번쩍여 거슬린다. */
@media (max-width: 560px){ .assistant-resize-handle{display:none;} }

/* 가로(왼쪽 가장자리) · 대각선(좌상단 모서리) 리사이즈 — 2026-08-05.
   패널은 오른쪽·아래가 FAB 기준으로 정렬되므로 왼쪽/위로만 자란다.
   손잡이는 기존 위쪽 그립과 같은 톤(평소 --text-faint, 호버 시 --accent 10% 배경)
   으로 맞춰 새 색을 만들지 않는다. touch-action:none 은 **손잡이에만** 걸어
   본문 스크롤을 막지 않는다. */
.assistant-resize-handle,
.assistant-resize-edge-l,
.assistant-resize-corner-tl{touch-action:none;}
.assistant-resize-edge-l{
  position:absolute; left:0; top:0; bottom:0; width:8px; z-index:6;
  cursor:ew-resize;
  border-top-left-radius:var(--radius); border-bottom-left-radius:var(--radius);
}
.assistant-resize-corner-tl{
  position:absolute; left:0; top:0; width:18px; height:18px; z-index:7;
  cursor:nwse-resize;
}
/* 세 손잡이 모두 호버 강조를 두지 않는다 — 커서 모양이 이미 무엇을 할 수 있는지
   알려 준다(가로 ↔ · 세로 ↕ · 대각선 ⤡). 색이 켜지면 오히려 산만하다. */
/* 모바일(≤768px)에서는 패널이 전체화면 오버레이라 리사이즈·이동 손잡이를 감춘다
   (기존 위쪽 그립도 같은 폭에서 숨겨진다 — 2268행대 미디어쿼리). */
@media (max-width: 768px){ .assistant-resize-edge-l, .assistant-resize-corner-tl{display:none;} }
/* 리사이즈·이동 중에는 내용의 텍스트 선택/전환 애니메이션이 끼어들지 않게. */
.assistant-panel.resizing, .assistant-panel.dragging{user-select:none;}
.assistant-panel.dragging{box-shadow:0 18px 46px rgba(0,0,0,.42);}

/* 설정 팝오버 안의 패널 환경설정(투명도 슬라이더·크기 초기화·알림 토글) */
.assistant-pref-sec{border-top:1px solid var(--border-soft); margin-top:8px; padding-top:8px;}
.assistant-pref-row{display:flex; align-items:center; justify-content:space-between; gap:8px; padding:5px 6px; font-size:.78rem; color:var(--text);}
.assistant-pref-row input[type=range]{flex:1; min-width:0; accent-color:var(--accent);}
.assistant-pref-row .assistant-pref-val{font-size:.72rem; color:var(--text-dim); min-width:34px; text-align:right;}
.assistant-pref-reset{
  width:100%; text-align:center; padding:6px 8px; margin-top:4px; border-radius:7px; cursor:pointer;
  background:var(--surface-2); border:1px solid var(--border); color:var(--text); font-size:.76rem; font-family:var(--sans);
}
.assistant-pref-reset:hover{border-color:var(--accent);}
.assistant-pref-check{display:flex; align-items:center; gap:7px; cursor:pointer;}
.assistant-pref-check input{accent-color:var(--accent);}

.assistant-head{
  display:flex; align-items:center; justify-content:space-between;
  padding:12px 14px; border-bottom:1px solid var(--border-soft);
  background:var(--bg-elev);
  background:color-mix(in srgb, var(--bg-elev) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
}
/* 헤더 아무 곳이나 잡아 창을 옮길 수 있다(app.js initAssistantHeadDrag).
   버튼·아이콘 위에서는 원래 커서/동작을 유지한다. */
.assistant-head{cursor:move;}
.assistant-head .assistant-gear, .assistant-head .assistant-close{cursor:pointer;}
.assistant-head .assistant-settings{cursor:default;}
/* 레이아웃 철칙: 패널이 좁아져도 제목·아이콘 줄이 재배치되거나 글자가 세로로
   접히지 않게 한다(넘치면 잘릴지언정 줄바꿈·재정렬은 하지 않는다). */
.assistant-head-title{display:flex; align-items:center; gap:8px; font-size:.86rem; flex:0 1 auto; min-width:0; white-space:nowrap; overflow:hidden;}
.assistant-head-title b{overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.assistant-head-title svg{width:17px; height:17px; color:var(--accent); flex:0 0 auto;}
.assistant-close{
  width:26px; height:26px; display:flex; align-items:center; justify-content:center;
  border-radius:7px; cursor:pointer; color:var(--text-dim);
}
.assistant-close:hover{background:var(--surface-2); color:var(--text);}
.assistant-close svg{width:15px; height:15px;}
.assistant-head{position:relative;}
.assistant-head-actions{display:flex; align-items:center; gap:2px; flex:0 0 auto; flex-wrap:nowrap;}
.assistant-gear{
  width:26px; height:26px; display:flex; align-items:center; justify-content:center;
  border-radius:7px; cursor:pointer; color:var(--text-dim);
}
.assistant-gear:hover{background:var(--surface-2); color:var(--text);}
.assistant-gear svg{width:15px; height:15px;}
.assistant-settings{
  position:absolute; top:44px; right:12px; z-index:5; width:220px;
  background:var(--surface);
  background:color-mix(in srgb, var(--surface) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
  border:1px solid var(--border); border-radius:var(--radius-sm);
  box-shadow:var(--shadow); padding:8px;
}
.assistant-settings-title{font-size:.72rem; color:var(--text-dim); padding:2px 6px 6px;}
/* 신고하기 팝오버 전용 — 설정 팝오버(220px)를 공유하면 텍스트 버튼 두 개가
   구겨져 글자가 세로로 쪼개지는 버그가 있었다. 폭을 넓히고 아이콘 버튼
   (📎/➤, 채팅 입력창과 동일한 클래스 재사용)로 교체. */
.assistant-feedback-pop{width:290px;}
.assistant-opacity-pop{width:200px;}
.assistant-opacity-pop .assistant-pref-row{padding:2px 6px;}
.assistant-feedback-actions{display:flex; align-items:center; justify-content:flex-end; gap:8px; margin-top:8px;}
.assistant-backend-list{display:flex; flex-direction:column; gap:3px;}
.assistant-backend-opt{
  text-align:left; padding:7px 9px; border-radius:7px; cursor:pointer;
  background:transparent; border:1px solid transparent; color:var(--text); font-size:.8rem;
}
.assistant-backend-opt:hover{background:var(--surface-2);}
.assistant-backend-opt.active{background:var(--surface-2); border-color:var(--accent); color:var(--text);}
.assistant-attach-strip{display:flex; gap:8px; flex-wrap:wrap; padding:0 12px 8px;}
.assistant-attach-btn{
  flex:0 0 auto; width:38px; height:38px; display:flex; align-items:center; justify-content:center;
  border-radius:var(--radius-sm); cursor:pointer; border:1px solid var(--border); color:var(--text-dim);
  background:var(--surface-2);
  background:color-mix(in srgb, var(--surface-2) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
}
.assistant-attach-btn:hover{border-color:var(--accent); color:var(--text);}
.assistant-attach-btn svg{width:17px; height:17px;}
.assistant-input-row.dragover{box-shadow:inset 0 0 0 2px var(--accent);}
.assistant-msg .chat-bubble-imgs img{width:52px; height:52px; object-fit:cover; border-radius:8px;}

.assistant-messages{
  flex:1; overflow-y:auto; padding:14px;
  display:flex; flex-direction:column; gap:9px;
}
.assistant-msg{
  max-width:82%; padding:9px 12px; border-radius:12px;
  font-size:.82rem; line-height:1.5; white-space:pre-wrap; word-break:break-word;
}
.assistant-msg.user{
  align-self:flex-end; color:var(--on-accent);
  background:var(--accent);
  background:color-mix(in srgb, var(--accent) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
  border-bottom-right-radius:4px;
}
.assistant-msg.model{
  align-self:flex-start; color:var(--text);
  background:var(--surface-2);
  background:color-mix(in srgb, var(--surface-2) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
  border:1px solid var(--border-soft); border-bottom-left-radius:4px;
}
.assistant-msg.notice{
  align-self:stretch; max-width:100%; text-align:center;
  background:transparent; color:var(--text-dim); font-size:.76rem; padding:6px 8px;
}
.assistant-msg.typing{display:flex; gap:5px; align-items:center;}
.assistant-msg.typing span{
  width:6px; height:6px; border-radius:50%; background:var(--text-faint);
  animation:assistant-blink 1s infinite ease-in-out;
}
.assistant-msg.typing span:nth-child(2){animation-delay:.2s;}
.assistant-msg.typing span:nth-child(3){animation-delay:.4s;}
@keyframes assistant-blink{0%,80%,100%{opacity:.25;}40%{opacity:1;}}
/* 도우미 채팅 텍스트 선택 가시성 — 사용자 말풍선이 라임(--accent) 배경이라 기본
   브라우저 선택색이 그 위에서 거의 안 보인다는 신고 반영. 말풍선 색은 그대로 두고
   ::selection만 대비색으로 지정한다. #assistantPanel 범위로 한정해 다른 화면의 선택
   표시에는 전혀 영향을 주지 않는다(전역 ::selection 변경 아님).
    · 사용자(라임 말풍선): 어두운 --on-accent 배경 + 라임 --accent 글자 → 라임 위에서 또렷.
    · 도우미(어두운 --surface-2 말풍선): 라임 --accent 배경 + 어두운 --on-accent 글자 → 어두운 위에서 또렷. */
#assistantPanel .assistant-msg.user::selection,
#assistantPanel .assistant-msg.user *::selection{ background:var(--on-accent); color:var(--accent); }
#assistantPanel .assistant-msg.model::selection,
#assistantPanel .assistant-msg.model *::selection{ background:var(--accent); color:var(--on-accent); }

.assistant-input-row{
  display:flex; gap:8px; align-items:flex-end;
  padding:10px 12px; border-top:1px solid var(--border-soft);
  background:var(--bg-elev);
  background:color-mix(in srgb, var(--bg-elev) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
}
.assistant-input{
  flex:1; resize:none; max-height:120px;
  color:var(--text);
  background:var(--surface-2);
  background:color-mix(in srgb, var(--surface-2) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
  border:1px solid var(--border); border-radius:var(--radius-sm);
  padding:9px 11px; font-size:.82rem; font-family:var(--sans); line-height:1.4;
}
.assistant-input:focus{outline:none; border-color:var(--accent);}
.assistant-send{
  flex:0 0 auto; width:38px; height:38px; border-radius:var(--radius-sm);
  display:flex; align-items:center; justify-content:center;
  color:var(--on-accent); border:none; cursor:pointer;
  background:var(--accent);
  background:color-mix(in srgb, var(--accent) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
}
.assistant-send:hover{filter:brightness(1.05);}
.assistant-send:disabled{opacity:.5; cursor:default;}
.assistant-send svg{width:17px; height:17px;}

/* ---- 팀 채팅 탭 (#164/#184) — AI 도우미와 같은 패널 안 탭 전환.
   .assistant-messages/.assistant-input-row는 기존 것 그대로 재사용하고,
   각 탭 콘텐츠를 감싸는 .assistant-tabpane만 새로 추가해 패널의
   flex-direction:column 안에서 남은 높이를 꽉 채우게 한다. ---- */
/* 좁아져도 탭이 아래로 접히지 않게(레이아웃 철칙) — 넘치면 가로 스크롤. */
.assistant-tabs{display:flex; gap:2px; padding:8px 12px 0; border-bottom:1px solid var(--border-soft); background:var(--bg-elev); background:color-mix(in srgb, var(--bg-elev) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent); flex-wrap:nowrap; overflow-x:auto; scrollbar-width:none;}
.assistant-tabs::-webkit-scrollbar{display:none;}
.assistant-tab{
  flex:1 0 auto; white-space:nowrap; text-align:center; padding:8px 6px; font-size:.78rem; font-weight:700;
  color:var(--text-faint); background:transparent; border:none; cursor:pointer;
  border-bottom:2px solid transparent; margin-bottom:-1px;
}
.assistant-tab:hover{color:var(--text-dim);}
.assistant-tab.active{color:var(--accent); border-bottom-color:var(--accent);}
/* 팀 탭에 안 읽음이 있고 지금 그 탭을 보고 있지 않을 때 점 표시(요청 #2). */
.assistant-tab.has-unread{position:relative;}
.assistant-tab.has-unread::after{
  content:''; position:absolute; top:5px; right:calc(50% - 26px);
  width:6px; height:6px; border-radius:50%; background:var(--danger);
}
.assistant-tabpane{flex:1; display:flex; flex-direction:column; min-height:0;}
/* (제거됨 2026-07-10) .assistant-jobbar — 도우미 채팅 안에 전역 진행상태를 중복
   표시하던 배너. 채팅 공간을 침범한다는 요청으로 제거하고 전역 jobTracker
   표시(상단바 배지·작업 목록·feedGrid 카드·토스트)로 일원화. */
.team-msg{max-width:82%; display:flex; flex-direction:column; gap:2px;}
.team-msg.own{align-self:flex-end; align-items:flex-end;}
.team-msg-meta{font-size:.68rem; color:var(--text-faint); padding:0 3px;}
/* 발신자 레벨 배지(게이미피케이션) — 이름 앞. 상단 사용자 칩의 레벨 토큰과 같은
   계열(accent-soft 배경 + accent 글자)로 통일. */
.team-msg-lv{
  display:inline-block; vertical-align:baseline; margin-right:5px;
  font-family:var(--display); font-weight:700; font-size:.62rem; letter-spacing:-.02em;
  line-height:1; padding:2px 5px; border-radius:999px;
  background:var(--accent-soft); color:var(--accent); border:1px solid var(--border-soft);
  white-space:nowrap;
}
.team-msg-bubble{
  padding:9px 12px; border-radius:12px; font-size:.82rem; line-height:1.5;
  white-space:pre-wrap; word-break:break-word;
  background:var(--surface-2); color:var(--text); border:1px solid var(--border-soft);
  border-bottom-left-radius:4px;
  background:color-mix(in srgb, var(--surface-2) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
}
.team-msg.own .team-msg-bubble{
  background:var(--accent); color:var(--on-accent); border-color:transparent; border-bottom-right-radius:4px; border-bottom-left-radius:12px;
  background:color-mix(in srgb, var(--accent) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
}
/* 메시지 본문 안의 URL을 클릭 가능한 링크로 렌더할 때 쓰는 스타일(teamMsgHtml → linkifyText). */
.team-msg-bubble .team-msg-link{
  color:var(--accent); text-decoration:underline; text-underline-offset:2px;
  word-break:break-all; cursor:pointer;
}
.team-msg.own .team-msg-bubble .team-msg-link{
  color:var(--on-accent); text-decoration:underline;
}
.team-msg-share{
  display:flex; align-items:center; gap:8px; margin-top:6px; padding:6px;
  background:var(--surface); border:1px solid var(--border-soft); border-radius:9px;
  text-decoration:none; color:var(--text); max-width:240px;
  background:color-mix(in srgb, var(--surface) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
}
.team-msg-share img, .team-msg-share video{width:40px; height:40px; object-fit:cover; border-radius:6px; flex:0 0 auto; background:#000;}
.team-msg-share-txt{display:flex; flex-direction:column; gap:1px; min-width:0;}
.team-msg-share-txt b{font-size:.74rem; font-weight:700; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.team-msg-share-txt span{font-size:.68rem; color:var(--text-faint);}
.team-msg-share:hover{border-color:var(--accent);}

/* 팀 채팅 v2 — 개별(DM) 수신자 선택, 삭제 버튼, 파일 첨부 */
.team-chat-recipient-row{display:flex; align-items:center; gap:8px; padding:8px 12px; border-bottom:1px solid var(--border-soft); background:var(--bg-elev); background:color-mix(in srgb, var(--bg-elev) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);}
.team-chat-recipient-sel{
  flex:1; min-width:0; background:var(--surface-2); border:1px solid var(--border-soft); color:var(--text);
  border-radius:8px; padding:6px 9px; font-size:.78rem; font-family:var(--sans); cursor:pointer;
  background:color-mix(in srgb, var(--surface-2) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
}
/* 그룹 채팅방을 볼 때만 나타나는 "채팅방 나가기" 버튼 — "+" 메뉴 안에 숨어
   있어 발견이 어렵다는 문제(사용자 보고)를 해결하려 헤더에 눈에 띄게 노출한다. */
.team-chat-leave-btn{
  flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center;
  width:30px; height:30px; border-radius:8px; cursor:pointer;
  background:var(--surface-2); border:1px solid var(--border-soft); color:var(--text-faint);
  transition:border-color .15s, color .15s, background .15s;
  background:color-mix(in srgb, var(--surface-2) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
}
/* display:inline-flex가 hidden 속성의 UA display:none과 동률 특이도라 소스 순서상
   이 규칙이 이겨서 "전체채팅"에서도 계속 보이던 버그 — 명시적으로 재정의. */
.team-chat-leave-btn[hidden]{display:none;}
.team-chat-leave-btn:hover{border-color:var(--danger); color:var(--danger);}
.team-chat-leave-btn svg{width:16px; height:16px;}
.team-msg-meta-row{display:flex; align-items:center; gap:6px;}
.team-msg.own .team-msg-meta-row{flex-direction:row-reverse;}
.team-msg-del{
  background:none; border:none; color:var(--text-faint); cursor:pointer; font-size:.68rem;
  padding:0 2px; line-height:1; opacity:.6;
}
.team-msg-del:hover{color:var(--danger); opacity:1;}
.team-msg-file{
  display:flex; align-items:center; gap:8px; margin-top:6px; padding:6px 10px;
  background:var(--surface); border:1px solid var(--border-soft); border-radius:9px;
  text-decoration:none; color:var(--text); max-width:240px; font-size:.76rem;
  background:color-mix(in srgb, var(--surface) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
}
.team-msg-file:hover{border-color:var(--accent);}
.team-msg-file img, .team-msg-file video{max-width:180px; max-height:180px; border-radius:8px; display:block;}
.team-msg-file-generic{display:flex; align-items:center; gap:6px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}

/* 팀 채팅 v3 — 아바타(프사), "+" 통합 기능 메뉴, 사용자 목록 팝업.
   전부 기존 디자인 토큰(--bg-elev/--surface/--border-soft/--accent 등)만 사용. */
.team-msg-row{display:flex; gap:8px; align-items:flex-start;}
.team-msg.own .team-msg-row{flex-direction:row-reverse;}
.team-msg-col{display:flex; flex-direction:column; gap:2px; min-width:0;}
.team-msg.own .team-msg-col{align-items:flex-end;}
.team-msg-avatar{
  width:30px; height:30px; border-radius:50%; flex:0 0 auto; overflow:hidden;
  display:flex; align-items:center; justify-content:center;
  font-size:11.5px; font-weight:700; color:#fff; user-select:none;
}
.team-msg-avatar img{width:100%; height:100%; object-fit:cover; display:block;}
.team-plus-wrap{position:relative; flex:0 0 auto;}
.team-plus-menu{
  position:absolute; bottom:calc(100% + 8px); left:0; z-index:60; min-width:188px;
  background:var(--bg-elev); border:1px solid var(--border-soft); border-radius:11px;
  padding:6px; box-shadow:0 10px 30px rgba(0,0,0,.34); display:flex; flex-direction:column; gap:2px;
  background:color-mix(in srgb, var(--bg-elev) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
}
.team-plus-item{
  display:flex; align-items:center; gap:10px; width:100%; text-align:left;
  background:none; border:none; border-radius:8px; padding:9px 10px;
  color:var(--text-dim); cursor:pointer; font-size:.8rem; font-family:var(--sans);
}
.team-plus-item:hover{background:var(--surface-2); color:var(--text);}
.team-plus-item svg{width:16px; height:16px; flex:0 0 auto;}

/* ── 반응(❤️ N) 배지 ─────────────────────────────────────────────────────
   메시지 아래에 붙는 반응 칩. 본인이 누른 반응(.mine)은 accent 로 강조. */
.team-msg-reactions{display:flex; flex-wrap:wrap; gap:4px; margin-top:5px;}
.team-msg-react{
  display:inline-flex; align-items:center; gap:3px; padding:2px 8px; cursor:pointer;
  font-size:.72rem; line-height:1.4; border-radius:999px; font-family:var(--sans);
  background:var(--surface-2); color:var(--text); border:1px solid var(--border-soft);
  background:color-mix(in srgb, var(--surface-2) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
  transition:border-color .12s, background .12s;
}
.team-msg-react:hover{border-color:var(--accent);}
.team-msg-react.mine{
  background:color-mix(in srgb, var(--accent) 22%, transparent);
  border-color:var(--accent); color:var(--text);
}
.team-msg-react-n{font-weight:700; font-variant-numeric:tabular-nums;}
.team-msg.own .team-msg-reactions{justify-content:flex-end;}

/* ── 채팅 미니게임 카드(봇/시스템 메시지) ──────────────────────────────
   슬래시 명령(/낚시·/홀짝·/야추)의 결과와 안내. 일반 대화 말풍선과 확실히
   구분되도록 폭을 넓게 쓰고 좌측 강조선 + 제목줄을 둔다. 색은 전부 기존
   토큰(--surface/--surface-2/--border-soft/--accent/--success/--warn/
   --danger/--text-dim)만 쓰고 새 색을 만들지 않는다.
   본문은 서버가 준 줄바꿈을 그대로 보이도록 pre-wrap 이고, 좁아져도 줄을
   재배치하지 않는다(레이아웃 철칙 — 넘치면 가로 스크롤). */
.team-msg.bot{max-width:100%; align-self:stretch; align-items:stretch;}
.team-msg.bot .team-msg-meta{display:flex; gap:6px; align-items:center;}
.team-game-card{
  border:1px solid var(--border-soft); border-left:3px solid var(--accent);
  border-radius:10px; padding:9px 11px;
  background:var(--surface);
  background:color-mix(in srgb, var(--surface) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
}
.team-game-title{
  font-family:var(--display); font-weight:700; font-size:.78rem; letter-spacing:-.02em;
  color:var(--text); margin-bottom:4px; white-space:nowrap;
}
.team-game-body{
  font-family:var(--mono); font-size:.72rem; line-height:1.6; color:var(--text-dim);
  white-space:pre; overflow-x:auto; scrollbar-width:thin;
}
/* 결과 성격별 강조선 — 등급이 올라갈수록 눈에 띄게. 배경은 건드리지 않아
   긴 대화 안에서도 시끄러워지지 않는다. */
.team-game-card.tone-miss,
.team-game-card.tone-lose{border-left-color:var(--border);}
.team-game-card.tone-common{border-left-color:var(--text-faint);}
.team-game-card.tone-fine{border-left-color:var(--success);}
.team-game-card.tone-win{border-left-color:var(--success);}
.team-game-card.tone-rare{border-left-color:var(--accent);}
.team-game-card.tone-epic,
.team-game-card.tone-myth{
  border-left-color:var(--accent);
  box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--accent) 30%, transparent);
}
.team-game-card.tone-warn{border-left-color:var(--warn);}
.team-game-card.tone-error{border-left-color:var(--danger);}
.team-game-card.tone-info,
.team-game-card.tone-neutral{border-left-color:var(--border);}
/* 본인에게만 보이는 임시 카드 — 저장되지 않는다는 것이 보이도록 살짝 흐리게. */
.team-msg.bot.local .team-game-card{
  border-style:dashed;
  background:var(--surface-2);
  background:color-mix(in srgb, var(--surface-2) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
}
.team-msg.bot.local .team-msg-meta{font-style:italic;}

/* ── 답장(회신) — 2026-08-05 ────────────────────────────────────────────
   메시지 위 인용 블록 / 메타줄의 [답장] 버튼 / 입력창 위 인용 미리보기.
   전부 기존 토큰(--surface-2/--border-soft/--accent/--text-faint)만 사용하고,
   데이·나이트 어느 테마에서도 같은 대비를 갖는다. */
.team-msg-quote{
  display:flex; align-items:baseline; gap:6px; max-width:100%; cursor:pointer;
  margin-bottom:4px; padding:4px 9px; border-radius:8px;
  border-left:3px solid var(--accent);
  background:var(--surface-2); color:var(--text-dim);
  background:color-mix(in srgb, var(--surface-2) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
  font-size:.7rem; line-height:1.45;
}
.team-msg-quote:hover{border-left-color:var(--accent); background:color-mix(in srgb, var(--accent) 12%, transparent);}
.team-msg-quote b{flex:0 0 auto; color:var(--accent); font-weight:700;}
.team-msg-quote span{min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.team-msg-quote.gone{cursor:default; border-left-color:var(--border); color:var(--text-faint);}
.team-msg-quote.gone:hover{background:var(--surface-2);}
.team-msg.own .team-msg-quote{align-self:flex-end;}
/* 인용 클릭으로 원본으로 이동했을 때의 짧은 강조. */
.team-msg.flash > .team-msg-row{
  border-radius:12px;
  animation:teamMsgFlash 1.4s ease-out 1;
}
@keyframes teamMsgFlash{
  0%   { background:color-mix(in srgb, var(--accent) 30%, transparent); }
  100% { background:transparent; }
}
/* 메타줄의 [답장] — 평소엔 흐리게, 메시지에 마우스를 올리면 또렷해진다.
   터치 기기(hover 없음)에서는 항상 보이게 둔다. */
.team-msg-reply-btn{
  background:none; border:none; padding:0 2px; margin-left:5px; cursor:pointer;
  color:var(--text-faint); opacity:0; line-height:1; vertical-align:middle;
  transition:opacity .12s, color .12s;
}
.team-msg-reply-btn svg{width:12px; height:12px; display:block;}
.team-msg:hover .team-msg-reply-btn, .team-msg-reply-btn:focus-visible{opacity:.75;}
.team-msg-reply-btn:hover{color:var(--accent); opacity:1;}
@media (hover: none){ .team-msg-reply-btn{opacity:.6;} }
/* 입력창 위 인용 미리보기 */
.team-reply-bar{
  display:flex; align-items:center; gap:8px; margin:0 12px 6px;
  padding:6px 8px; border-radius:9px;
  border:1px solid var(--border-soft); border-left:3px solid var(--accent);
  background:var(--surface-2);
  background:color-mix(in srgb, var(--surface-2) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
  font-size:.72rem; color:var(--text-dim);
}
.team-reply-bar-body{display:flex; align-items:baseline; gap:6px; min-width:0; flex:1;}
.team-reply-bar-label{flex:0 0 auto; font-weight:700; color:var(--accent);}
.team-reply-bar-body b{flex:0 0 auto; color:var(--text); font-weight:700;}
.team-reply-bar-txt{min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.team-reply-bar-x{
  flex:0 0 auto; background:none; border:none; cursor:pointer; padding:0 2px;
  color:var(--text-faint); font-size:.8rem; line-height:1; font-family:var(--sans);
}
.team-reply-bar-x:hover{color:var(--danger);}

/* ── GIF 메시지 ──────────────────────────────────────────────────────────*/
.team-msg-gif{
  position:relative; display:inline-block; margin-top:6px; max-width:220px;
  border-radius:10px; overflow:hidden; border:1px solid var(--border-soft); line-height:0;
}
.team-msg-gif img{display:block; width:100%; height:auto; max-height:240px; object-fit:cover; background:#000;}
.team-msg-gif-tag{
  position:absolute; left:6px; bottom:6px; padding:1px 6px; border-radius:5px;
  font-size:.6rem; font-weight:800; letter-spacing:.04em; line-height:1.5;
  background:rgba(0,0,0,.62); color:#fff; pointer-events:none;
}

/* ── 이모지 피커(입력창 위 팝오버) ───────────────────────────────────────*/
.team-emoji-panel{
  position:absolute; bottom:calc(100% + 8px); left:0; z-index:60; width:288px; max-width:78vw;
  max-height:300px; overflow-y:auto; padding:8px;
  background:var(--bg-elev); border:1px solid var(--border-soft); border-radius:11px;
  box-shadow:0 10px 30px rgba(0,0,0,.34);
  background:color-mix(in srgb, var(--bg-elev) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
}
.team-emoji-group-name{font-size:.66rem; color:var(--text-faint); margin:6px 2px 3px; font-weight:700;}
.team-emoji-group-name:first-child{margin-top:0;}
.team-emoji-grid{display:grid; grid-template-columns:repeat(8, 1fr); gap:2px;}
.team-emoji-btn{
  background:none; border:none; border-radius:7px; cursor:pointer; padding:4px 0;
  font-size:1.12rem; line-height:1.2; text-align:center;
}
.team-emoji-btn:hover{background:var(--surface-2);}

/* ── GIF 검색 모달 ───────────────────────────────────────────────────────*/
.team-gif-box{width:min(560px, 92vw);}
.team-gif-search-row{display:flex; gap:8px; margin:12px 0 10px;}
.team-gif-search{
  flex:1; min-width:0; background:var(--surface-2); border:1px solid var(--border-soft); color:var(--text);
  border-radius:8px; padding:8px 11px; font-size:.82rem; font-family:var(--sans);
}
.team-gif-search:focus{outline:none; border-color:var(--accent);}
.team-gif-grid{
  display:grid; grid-template-columns:repeat(auto-fill, minmax(120px, 1fr)); gap:6px;
  max-height:56vh; overflow-y:auto; min-height:120px;
}
.team-gif-item{
  padding:0; border:1px solid var(--border-soft); border-radius:8px; overflow:hidden; cursor:pointer;
  background:#000; line-height:0; aspect-ratio:1 / 1;
}
.team-gif-item img{width:100%; height:100%; object-fit:cover; display:block;}
.team-gif-item:hover{border-color:var(--accent);}
.team-gif-note{grid-column:1 / -1; padding:24px 8px; text-align:center; color:var(--text-faint); font-size:.82rem;}
.team-user-list{display:flex; flex-direction:column; gap:5px; margin-top:12px; max-height:48vh; overflow:auto;}
.team-user-pick{
  display:flex; align-items:center; gap:10px; width:100%; text-align:left;
  background:var(--surface); border:1px solid var(--border-soft); border-radius:9px;
  padding:8px 10px; color:var(--text); cursor:pointer; transition:border-color .12s, background .12s;
  background:color-mix(in srgb, var(--surface) calc(var(--assistant-panel-bg-alpha, 1) * 100%), transparent);
}
.team-user-pick:hover{border-color:var(--accent); background:var(--bg-elev);}
.team-user-pick .team-msg-avatar{width:32px; height:32px;}
.team-user-pick-name{font-size:.82rem; font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
/* 접속 상태 점 — 아바타 우측하단. 아바타(.team-msg-avatar)는 overflow:hidden이라
   점을 아바타 바깥 래퍼(.team-user-pick-av)에 얹어 잘리지 않게 한다. */
.team-user-pick-av{position:relative; flex:0 0 auto; display:inline-flex;}
.team-online-dot{
  position:absolute; right:-1px; bottom:-1px; width:10px; height:10px; border-radius:50%;
  border:2px solid var(--surface); box-sizing:border-box; pointer-events:none;
}
.team-online-dot.is-online{background:var(--success);}
.team-online-dot.is-offline{background:var(--text-faint, #8a8f98);}

/* Narrow screens: shrink the panel to fit and nudge the FAB in a touch so it
   doesn't crowd the edge. The mobile hamburger sits in the top bar, so the
   bottom-right widget never collides with it. */
@media (max-width: 560px){
  .assistant-fab{right:16px; bottom:16px; width:50px; height:50px;}
  .assistant-panel{
    right:12px; bottom:76px;
    width:calc(100vw - 24px);
    height:calc(100vh - 150px); max-height:calc(100vh - 150px);
  }
}

/* ==================== ComfyUI module ==================== */
.comfy-server-grid{
  display:grid; grid-template-columns:repeat(auto-fill, minmax(320px, 1fr));
  gap:16px; margin-top:6px;
}
.comfy-server-card{padding:16px 18px;}
.comfy-card-off{opacity:.62;}
.comfy-card-title{display:flex; align-items:flex-start; justify-content:space-between; gap:10px; margin-bottom:10px;}
.comfy-name{font-weight:700; font-size:15px; line-height:1.35; color:var(--text); word-break:break-word;}
.comfy-card-actions{display:flex; gap:4px; flex-shrink:0;}
.comfy-icon-btn{
  width:30px; height:30px; display:inline-flex; align-items:center; justify-content:center;
  border-radius:8px; background:var(--surface); border:1px solid var(--border-soft);
  color:var(--text-dim); cursor:pointer; padding:0;
}
.comfy-icon-btn:hover{background:var(--surface-2); color:var(--text);}
.comfy-icon-btn svg{width:16px; height:16px;}
.comfy-icon-btn.comfy-danger:hover{color:var(--danger); border-color:var(--danger);}
/* 비활성(OFF): 원형 슬래시 아이콘 + 중립 회색 — "꺼짐"을 표시(빨강 아님). */
.comfy-icon-btn.comfy-st-disabled{color:var(--text-faint);}
/* 활성(ON): 대기/작업중 모두 초록 칩(초록 글자+연초록 배경+초록 테두리)으로
   칠해, 중립 회색인 새로고침 버튼과 한눈에 확실히 구분되게 한다(요청 #6·#7). */
.comfy-icon-btn.comfy-st-active,
.comfy-icon-btn.comfy-st-idle{color:#25a35a; border-color:rgba(37,163,90,0.5); background:rgba(37,163,90,0.12);}
.comfy-icon-btn.comfy-st-active:hover,
.comfy-icon-btn.comfy-st-idle:hover{color:#1f8f4e; border-color:#25a35a; background:rgba(37,163,90,0.2);}
.comfy-line{font-size:12.5px; line-height:1.55; margin:2px 0;}
.comfy-line.mono, .mono{font-family:var(--mono, 'SFMono-Regular', ui-monospace, Menlo, Consolas, monospace);}
.comfy-line.muted, .muted{color:var(--text-faint);}
.comfy-url a{color:#6ab0ff; text-decoration:none; word-break:break-all;}
.comfy-url a:hover{text-decoration:underline;}
.comfy-owner{display:inline-flex; align-items:center; gap:4px; font-size:12px; margin-top:6px; font-weight:600;}
.comfy-owner-admin{color:#6ab0ff;}
.comfy-owner-shared{color:#4caf6d;}
.comfy-divider{height:1px; background:var(--border-soft); margin:12px 0 10px;}
.comfy-vram-row{display:flex; align-items:baseline; justify-content:space-between; font-size:12.5px;}
.comfy-vram-label{color:var(--text-dim); font-weight:600;}
.comfy-vram-val{color:var(--text);}
.comfy-vram-track{height:7px; border-radius:4px; background:var(--surface-2); overflow:hidden; margin:7px 0 2px;}
.comfy-vram-fill{height:100%; border-radius:4px; background:#5b9bff; transition:width .3s ease;}
.comfy-job{color:var(--text-dim); margin-top:8px;}
.comfy-lastconn{margin-top:6px;}
.comfy-empty{color:var(--text-faint); font-size:13px; padding:26px 4px;}

.comfy-wf-list{display:flex; flex-direction:column; gap:10px; margin-top:6px;}
.comfy-wf-card{display:flex; align-items:center; justify-content:space-between; gap:12px; padding:13px 16px; text-align:left; transition:border-color .14s ease, box-shadow .14s ease;}
.comfy-wf-card:hover{border-color:color-mix(in srgb, var(--accent) 50%, var(--border)); box-shadow:inset 3px 0 0 var(--accent);}
.comfy-wf-main{display:flex; align-items:center; gap:12px; min-width:0; flex:1; text-align:left;}
.comfy-wf-ico{width:22px; height:22px; color:var(--text-dim); flex-shrink:0; transition:color .14s ease;}
.comfy-wf-card:hover .comfy-wf-ico{color:var(--accent);}
.comfy-wf-label{font-weight:600; font-size:14px; color:var(--text); word-break:break-word; text-align:left;}
.comfy-wf-meta{font-size:11.5px; margin-top:3px; text-align:left;}

.comfy-modal-overlay{align-items:center; justify-content:center;}
.comfy-modal{
  width:min(460px, 92vw); background:var(--bg-elev); border:1px solid var(--border);
  border-radius:var(--radius); box-shadow:0 24px 60px rgba(0,0,0,.5); overflow:hidden;
}
.comfy-modal-head{display:flex; align-items:center; justify-content:space-between; padding:16px 18px; border-bottom:1px solid var(--border-soft); font-size:15px;}
.comfy-modal-body{padding:16px 18px; display:flex; flex-direction:column; gap:12px;}
.comfy-modal-body .field-row{display:flex; flex-direction:column; gap:5px;}
.comfy-modal-body .field-row label{font-size:12px; color:var(--text-dim); font-weight:600;}
.comfy-modal-body .field-row input, .comfy-modal-body .field-row select{
  background:var(--surface); border:1px solid var(--border-soft); color:var(--text);
  border-radius:var(--radius-sm); padding:9px 11px; font-size:13px; font-family:inherit;
}
.comfy-modal-body .field-row input:focus, .comfy-modal-body .field-row select:focus{outline:none; border-color:var(--accent);}
.comfy-modal-err{color:var(--danger); font-size:12.5px;}
.comfy-modal-foot{display:flex; justify-content:flex-end; gap:8px; padding:14px 18px; border-top:1px solid var(--border-soft);}

/* ── Phase 2: 웹 UI 열기 버튼 ── */
.comfy-webui-row{margin-top:10px;}
.comfy-webui-btn{width:100%; justify-content:center; display:inline-flex; align-items:center;}

/* ── Phase 2: 워크플로우 카드 액션 + 배지 ── */
.comfy-wf-actions{display:flex; align-items:center; gap:8px; flex-shrink:0;}
.comfy-wf-appbtn{font-size:12.5px; padding:6px 12px; white-space:nowrap;}
.comfy-wf-badge{display:inline-block; font-size:10.5px; font-weight:700; padding:1px 7px; border-radius:999px; margin-left:6px; vertical-align:middle;}
.comfy-wf-badge-cat{background:var(--accent-soft); color:var(--accent); border:1px solid color-mix(in srgb, var(--accent) 35%, transparent);}
.comfy-wf-badge-app{background:rgba(76,175,109,.15); color:#4caf6d;}
.comfy-wf-badge-app.comfy-wf-badge-off{background:rgba(150,150,150,.15); color:var(--text-faint);}
.comfy-wf-desc{font-size:12px; color:var(--text-dim); margin:2px 0 0;}
.comfy-wf-resetbtn{font-size:12.5px; padding:6px 12px; white-space:nowrap;}

/* ── Phase 2: 필드 디자이너 모달 ── */
.comfy-modal-wide{width:min(720px, 94vw); max-height:88vh; display:flex; flex-direction:column;}
.comfy-modal-wide .comfy-modal-body{flex:1; min-height:0; overflow-y:auto;}
.comfy-af-hint{font-size:12.5px; color:var(--text-dim); margin:0 0 4px;}
.comfy-af-nodes{display:flex; flex-direction:column; gap:12px; padding-right:4px;}
.comfy-af-nodes-toolbar{display:flex; align-items:center; gap:8px; margin-bottom:10px; flex-wrap:wrap;}
.comfy-af-nodes-toolbar .btn-ghost{font-size:12px; padding:5px 11px;}
.comfy-af-nodes-search{margin-top:-4px;}
.comfy-af-node-search-input{flex:1 1 260px; min-width:180px; max-width:420px;}
.comfy-af-node-hidden{display:none !important;}

/* ============ 노드 그래프 뷰어 (comfyGraph*) — ComfyUI 근사 시각화 ============ */
.comfy-af-viewswitch{display:flex; align-items:center; gap:6px; margin-bottom:10px; flex-wrap:wrap;}
.comfy-af-viewbtn{
  font-size:12px; padding:6px 14px; border-radius:999px; cursor:pointer;
  background:var(--bg-elev); border:1px solid var(--border-soft); color:var(--text-dim);
  transition:background .12s ease, color .12s ease, border-color .12s ease;
}
.comfy-af-viewbtn:hover{color:var(--text); border-color:var(--border);}
.comfy-af-viewbtn.is-active{background:var(--accent); color:var(--on-accent); border-color:var(--accent); font-weight:700;}

.comfy-graph-wrap{display:flex; flex-direction:column; gap:0;}
.comfy-graph-toolbar{display:flex; align-items:center; gap:8px; margin-bottom:8px; flex-wrap:wrap;}
.comfy-graph-toolbar .btn-ghost{font-size:12px; padding:5px 11px;}
.comfy-graph-legend{display:flex; align-items:center; gap:12px; font-size:11px; color:var(--text-dim);}
.comfy-graph-legend-item{display:inline-flex; align-items:center; gap:5px;}
.comfy-graph-legend .lg-dot{width:11px; height:11px; border-radius:3px; display:inline-block;}
.comfy-graph-legend .lg-field{background:var(--accent);}
.comfy-graph-legend .lg-toggle{background:#c9843d;}
.comfy-graph-legend .lg-group{background:transparent; border:2px solid #6ea8fe;}

.comfy-graph-viewport{
  position:relative; width:100%; height:min(62vh, 620px); overflow:hidden;
  border:1px solid var(--border); border-radius:var(--radius-sm);
  background:
    radial-gradient(circle at 1px 1px, color-mix(in srgb, var(--text-faint) 22%, transparent) 1px, transparent 0) 0 0 / 22px 22px,
    #131217;
  cursor:grab; touch-action:none; user-select:none;
}
:root[data-theme="light"] .comfy-graph-viewport,
.theme-light .comfy-graph-viewport{ background:
    radial-gradient(circle at 1px 1px, color-mix(in srgb, var(--text-faint) 30%, transparent) 1px, transparent 0) 0 0 / 22px 22px,
    #e9e9e2; }
.comfy-graph-viewport.is-panning{cursor:grabbing;}
.comfy-graph-canvas{position:absolute; top:0; left:0; width:1px; height:1px; transform-origin:0 0;}
.comfy-graph-links{position:absolute; top:0; left:0; overflow:visible; pointer-events:none; width:1px; height:1px;}
.comfy-graph-links path{fill:none; stroke:#7d7c86; stroke-width:2; opacity:.75; pointer-events:none; transition:stroke .1s ease, opacity .1s ease;}
.comfy-graph-links path.hot{stroke:var(--accent); opacity:1; stroke-width:3;}
.comfy-graph-groups{position:absolute; top:0; left:0; width:1px; height:1px;}
.comfy-graph-group{
  position:absolute; border:2px dashed #6ea8fe; border-radius:12px;
  background:color-mix(in srgb, #6ea8fe 8%, transparent); pointer-events:none;
}
.comfy-graph-group-label{
  position:absolute; top:-11px; left:12px; font-size:11px; font-weight:800;
  padding:1px 9px; border-radius:999px; background:#6ea8fe; color:#0b1220; white-space:nowrap;
}
.comfy-graph-nodes{position:absolute; top:0; left:0; width:1px; height:1px;}
.comfy-graph-node{
  position:absolute; width:220px; box-sizing:border-box;
  background:#26252b; border:1px solid #3a3941; border-radius:8px;
  box-shadow:0 3px 10px rgba(0,0,0,.35); overflow:hidden; font-size:12px;
}
.comfy-graph-node.has-field{border-color:var(--accent); box-shadow:0 0 0 1px var(--accent), 0 3px 10px rgba(0,0,0,.35);}
.comfy-graph-node.has-toggle{border-color:#c9843d;}
.comfy-graph-node.sel{outline:2px solid var(--accent); outline-offset:1px;}
.comfy-graph-node-head{
  display:flex; align-items:center; gap:6px; height:30px; padding:0 9px;
  background:linear-gradient(180deg,#34333b,#2b2a30); cursor:grab; color:#f2f1f5;
  border-bottom:1px solid #3a3941;
}
.comfy-graph-node-head:active{cursor:grabbing;}
.comfy-graph-node-title{font-weight:700; font-size:12px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; flex:1 1 auto;}
.comfy-graph-node-id{font-size:10px; color:#a9a8ad; flex:0 0 auto;}
.comfy-graph-badge{flex:0 0 auto; font-size:9px; font-weight:800; padding:1px 6px; border-radius:999px; color:#111008;}
.comfy-graph-badge.b-field{background:var(--accent);}
.comfy-graph-badge.b-toggle{background:#c9843d; color:#111008;}
.comfy-graph-ports{position:relative;}
.comfy-graph-portrow{display:flex; align-items:center; justify-content:space-between; height:22px; padding:0 4px; gap:6px;}
.comfy-graph-port{display:flex; align-items:center; gap:5px; min-width:0; max-width:50%;}
.comfy-graph-port.out{justify-content:flex-end; margin-left:auto;}
.comfy-graph-port .pdot{width:9px; height:9px; border-radius:50%; background:#8a8992; border:1px solid #1c1b1f; flex:0 0 auto;}
.comfy-graph-port.linked .pdot{background:#7fd08c;}
.comfy-graph-port.out .pdot{background:#e0a86b;}
.comfy-graph-port .plabel{font-size:11px; color:#c9c8cf; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.comfy-graph-params{border-top:1px dashed #3a3941; padding:5px 9px; display:flex; flex-direction:column; gap:2px;}
.comfy-graph-param{display:flex; gap:6px; font-size:10.5px; line-height:1.5;}
.comfy-graph-param .pk{color:#a9a8ad; flex:0 0 auto;}
.comfy-graph-param .pv{color:#d7d6dc; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.comfy-graph-empty{padding:24px; text-align:center; color:var(--text-dim); font-size:13px;}
.comfy-af-node-flash{border-color:var(--accent) !important; box-shadow:0 0 0 2px var(--accent); transition:box-shadow .2s ease, border-color .2s ease;}
/* 목록→그래프 이동 시 대상 그래프 노드를 잠깐 강조. */
.comfy-graph-node-flash{outline:2px solid var(--accent); outline-offset:2px; box-shadow:0 0 0 4px color-mix(in srgb, var(--accent) 35%, transparent) !important; z-index:5;}
@media (max-width:640px){
  .comfy-graph-viewport{height:min(56vh, 460px);}
  .comfy-graph-node{width:190px;}
}
.comfy-af-node{border:1px solid var(--border-soft); border-radius:var(--radius-sm); padding:10px 12px; transition:opacity .12s ease, border-color .12s ease, transform .12s ease, box-shadow .12s ease; position:relative;}
/* 드래그 중인 노드는 반투명 + 살짝 축소 + 그림자로 "들어올린" 느낌을 준다.
   포인터 기반 커스텀 드래그(핸들에서만 시작)에서 pointerdown 시 부여된다. */
.comfy-af-node-dragging{opacity:.5; transform:scale(.98); box-shadow:0 8px 24px rgba(0,0,0,.28); position:relative; z-index:5;}
/* 드롭 위치 미리보기 표시선 — drop 시점에 한 번만 실제로 옮기고, 그 전엔
   이 테두리로만 "여기에 놓입니다"를 보여준다(드래그 실동작 버그 수정). */
.comfy-af-node-dropbefore{box-shadow:0 -3px 0 0 var(--accent);}
.comfy-af-node-dropafter{box-shadow:0 3px 0 0 var(--accent);}
.comfy-af-node-head{font-size:12.5px; margin-bottom:8px; display:flex; gap:8px; align-items:center;}
/* 드래그 핸들. touch-action:none 으로 터치 드래그가 스크롤에 뺏기지 않게 하고,
   user-select:none 으로 텍스트 선택을 막는다(핸들에서만 드래그 시작). */
.comfy-af-node-drag{cursor:grab; color:var(--text-faint); font-size:15px; line-height:1; user-select:none; -webkit-user-select:none; touch-action:none; letter-spacing:-1px; padding:2px 4px; margin:-2px -2px -2px -4px; border-radius:4px;}
.comfy-af-node-drag:hover{color:var(--text-dim); background:color-mix(in srgb, var(--accent) 12%, transparent);}
.comfy-af-node-drag:active{cursor:grabbing;}
.comfy-af-node-collapse{
  background:none; border:none; color:var(--text-faint); cursor:pointer; font-size:11px;
  line-height:1; padding:2px 3px; transition:transform .15s ease; flex:0 0 auto;
}
.comfy-af-node.collapsed .comfy-af-node-collapse{transform:rotate(-90deg);}
/* 노드 이름(class_type)을 번호보다 앞·굵게 — 가시성 우선(요청: "노드 번호보다
   노드 이름이 앞으로 나오고... 노드 번호를 후순위로"). 번호는 옅게 뒤에 배치. */
.comfy-af-node-name{font-weight:700; color:var(--text); font-size:13px;}
.comfy-af-node-idtag{font-size:11px; color:var(--text-faint);}
/* "그래프에서 보기" 버튼 — 노드 헤더 우측. 이 노드를 그래프 뷰 중앙으로 이동. */
.comfy-af-node-tograph{margin-left:auto; flex:0 0 auto; background:none; border:1px solid var(--border-soft); color:var(--text-dim); border-radius:var(--radius-sm); font-size:11px; padding:3px 9px; cursor:pointer; white-space:nowrap;}
.comfy-af-node-tograph:hover{color:var(--text); border-color:var(--accent);}
/* 노드 표시 이름(라벨) 입력 행. */
.comfy-af-node-namerow{display:flex; align-items:center; gap:8px; margin:0 0 8px;}
.comfy-af-node-namerow-lbl{font-size:12px; color:var(--text-dim); flex:0 0 auto; font-weight:600;}
.comfy-af-node-displayname{flex:1 1 auto; min-width:0; background:var(--surface); border:1px solid var(--border-soft); color:var(--text); border-radius:var(--radius-sm); padding:6px 9px; font-size:12.5px; font-family:inherit;}
/* 편집기 안 "노드 X" → 그래프 이동 링크. */
.comfy-af-nodelink{color:var(--accent); cursor:pointer; text-decoration:underline dotted;}
.comfy-af-nodelink:hover{text-decoration:underline;}
.comfy-af-node.collapsed .comfy-af-node-body{display:none;}
/* 그룹 토글에 포함된 노드 — 개별 바이패스가 비활성됨을 시각적으로 안내(흐림 + 배지). */
.comfy-af-node.in-group{border-color:color-mix(in srgb, var(--accent) 40%, var(--border-soft)); background:color-mix(in srgb, var(--accent) 4%, transparent);}
.comfy-af-node.in-group .comfy-af-input,
.comfy-af-node.in-group .comfy-af-node-rewire{opacity:.45;}
.comfy-af-node-groupbadge{margin-left:auto; flex:0 0 auto; font-size:11px; font-weight:800; letter-spacing:.01em; padding:2px 9px; border-radius:999px; background:var(--accent); color:var(--on-accent); white-space:nowrap;}
/* 앱화 편집기 그룹 박스(#7): 그룹 토글에 묶인 멤버 노드 카드들을 테두리+헤더로
   한 덩어리로 보여준다. 헤더의 .comfy-af-group-drag 핸들로 그룹 전체를, 멤버 카드
   각각의 .comfy-af-node-drag 핸들로 그룹 내부 순서를 드래그 정렬한다. 드래그 상태
   표시(.comfy-af-node-dragging / -dropbefore / -dropafter)는 클래스 단독 셀렉터라
   그룹 박스에도 그대로 적용된다. 최상위 flex(.comfy-af-nodes gap:12px) 자식이므로
   일반 노드와 같은 간격으로 배치된다. */
.comfy-af-group-box{border:1px solid color-mix(in srgb, var(--accent) 45%, var(--border-soft)); border-radius:var(--radius-sm); background:color-mix(in srgb, var(--accent) 5%, transparent); padding:8px; display:flex; flex-direction:column; gap:10px; position:relative; transition:opacity .12s ease, transform .12s ease, box-shadow .12s ease;}
.comfy-af-group-box-head{display:flex; align-items:center; gap:8px; padding:2px 2px 0;}
.comfy-af-group-drag{cursor:grab; color:var(--text-faint); font-size:15px; line-height:1; user-select:none; -webkit-user-select:none; touch-action:none; letter-spacing:-1px; padding:2px 4px; margin:-2px 0 -2px -2px; border-radius:4px;}
.comfy-af-group-drag:hover{color:var(--text-dim); background:color-mix(in srgb, var(--accent) 12%, transparent);}
.comfy-af-group-drag:active{cursor:grabbing;}
.comfy-af-group-box-title{font-size:12.5px; font-weight:800; color:var(--text); letter-spacing:.01em; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.comfy-af-group-box-count{margin-left:auto; flex:0 0 auto; font-size:11px; font-weight:700; padding:2px 9px; border-radius:999px; background:var(--accent); color:var(--on-accent); white-space:nowrap;}
.comfy-af-group-box-members{display:flex; flex-direction:column; gap:10px;}
/* 노드 단위 숨김("이 노드 숨기기") — 편집기에서 흐림 + 점선 테두리 + 배지로 표시.
   생성 폼에서 이 노드가 노출되지 않고 값이 기본값으로 고정 전송됨을 관리자에게 알림.
   (검색 필터용 .comfy-af-node-hidden 과는 다른 클래스 — 그건 완전 숨김 display:none.) */
.comfy-af-node-boxhidden{opacity:.6; border-style:dashed;}
.comfy-af-node-hidebadge{display:none; flex:0 0 auto; font-size:11px; font-weight:800; letter-spacing:.01em; padding:2px 9px; border-radius:999px; background:var(--text-faint); color:var(--surface); white-space:nowrap;}
.comfy-af-node-boxhidden .comfy-af-node-hidebadge{display:inline-block;}
.comfy-af-node-bypass.group-locked .comfy-af-node-bypass-check,
.comfy-af-node-bypass.group-locked .comfy-af-node-bypass-cfg{opacity:.5;}
.comfy-af-node-bypass-grouplock{font-size:11px; line-height:1.55; padding:6px 9px; margin-bottom:8px; border-radius:var(--radius-sm); background:color-mix(in srgb, var(--accent) 10%, transparent); border:1px solid color-mix(in srgb, var(--accent) 30%, var(--border-soft)); color:var(--text-dim);}
.comfy-af-node-rewire{margin:2px 0 8px;}
.comfy-af-input{border-top:1px solid var(--border-soft); padding:8px 0;}
.comfy-af-input:first-of-type{border-top:none;}
.comfy-af-check{display:flex; align-items:center; gap:8px; font-size:13px; cursor:pointer;}
.comfy-af-check input{width:16px; height:16px;}
.comfy-af-curval{font-size:11px;}
.comfy-af-cfg{display:none; grid-template-columns:1fr 1fr auto; gap:8px; margin-top:8px;}
.comfy-af-input.on .comfy-af-cfg{display:grid;}
.comfy-af-cfg input, .comfy-af-cfg select{
  background:var(--surface); border:1px solid var(--border-soft); color:var(--text);
  border-radius:var(--radius-sm); padding:7px 9px; font-size:12.5px; font-family:inherit;
}
.comfy-af-cfg input:focus, .comfy-af-cfg select:focus{outline:none; border-color:var(--accent);}
/* D) 바이패스 설정 행 — "선택적(바이패스 가능)" 체크 시에만 대상 입력칸을 노출. */
.comfy-af-bypass-check{display:flex; align-items:center; gap:7px; font-size:12px; color:var(--text-dim); cursor:pointer;}
.comfy-af-bypass-check input{width:15px; height:15px; accent-color:var(--accent);}
/* 자동 추적된 바이패스 후보 체크박스 리스트 — "선택적(바이패스 가능)" 켜야 노출. */
.comfy-af-bypass-candidates, .comfy-af-bypass-empty{display:none;}
.comfy-af-cfg.bypass-on .comfy-af-bypass-candidates{display:flex; flex-direction:column; gap:5px; margin-top:2px;}
.comfy-af-cfg.bypass-on .comfy-af-bypass-empty{display:block; font-size:11px; line-height:1.5;}
.comfy-af-bypass-candidate{display:flex; align-items:center; gap:7px; font-size:12px; color:var(--text); cursor:pointer;
  padding:5px 8px; border:1px solid var(--border-soft); border-radius:var(--radius-sm);}
.comfy-af-bypass-candidate input{width:15px; height:15px; accent-color:var(--accent); cursor:pointer; flex:0 0 auto;}
.comfy-af-bypass-orphan{font-size:11px;}
/* 노드 단위 바이패스(파라미터별 바이패스와 별개) — 노드 박스 헤더 바로 아래에
   전용 박스로 표시. 켜면(.on) 라벨 입력 + 자동 추적 후보 목록이 펼쳐진다. */
.comfy-af-node-bypass{border:1px solid var(--border-soft); border-radius:var(--radius-sm); padding:8px 10px; margin-bottom:10px; background:color-mix(in srgb, var(--accent) 5%, transparent);}
.comfy-af-node-bypass-check{display:flex; align-items:center; gap:7px; font-size:12.5px; font-weight:700; color:var(--text); cursor:pointer;}
.comfy-af-node-bypass-check input{width:15px; height:15px; accent-color:var(--accent);}
.comfy-af-node-bypass-cfg{display:none; flex-direction:column; gap:7px; margin-top:8px;}
.comfy-af-node-bypass.on .comfy-af-node-bypass-cfg{display:flex;}
.comfy-af-node-bypass-label{
  background:var(--surface); border:1px solid var(--border-soft); color:var(--text);
  border-radius:var(--radius-sm); padding:7px 9px; font-size:12.5px; font-family:inherit;
}
.comfy-af-node-bypass-label:focus{outline:none; border-color:var(--accent);}
.comfy-af-node-bypass .comfy-af-bypass-candidates{display:flex; flex-direction:column; gap:5px;}
.comfy-af-node-bypass .comfy-af-bypass-empty{display:block; font-size:11px; line-height:1.5;}
.comfy-af-node-bypass-help{
  background:var(--surface); border:1px solid var(--border-soft); color:var(--text);
  border-radius:var(--radius-sm); padding:7px 9px; font-size:12.5px; font-family:inherit;
}
.comfy-af-node-bypass-help:focus{outline:none; border-color:var(--accent);}
.comfy-af-node-bypass-adv input, .comfy-af-advanced-check input, .comfy-af-hidden-check input{accent-color:var(--accent); cursor:pointer;}
/* 바이패스 검증 배지(앱화 편집기 — 저장 전 "단독으로 끄면 깨지는 토글" 사전 안내) */
.comfy-af-bp-badge{display:inline-flex; align-items:center; gap:6px; margin-top:8px; padding:5px 9px; border-radius:999px; font-size:11.5px; font-weight:700; border:1px solid var(--border-soft); line-height:1.3;}
.comfy-af-bp-badge.ok{color:var(--success); border-color:color-mix(in srgb, var(--success) 45%, var(--border-soft)); background:color-mix(in srgb, var(--success) 12%, transparent);}
.comfy-af-bp-badge.warn{color:var(--accent); border-color:color-mix(in srgb, var(--accent) 45%, var(--border-soft)); background:color-mix(in srgb, var(--accent) 12%, transparent);}
.comfy-af-bp-badge.bad{color:var(--danger); border-color:color-mix(in srgb, var(--danger) 45%, var(--border-soft)); background:color-mix(in srgb, var(--danger) 12%, transparent);}
/* 배지 아래 상세(제외 노드 목록 등) — 카드 폭을 넘지 않게 감싸고, 칩 목록과
   같은 들여쓰기로 맞춘다. 긴 노드 목록이 한 줄로 삐져나오던 문제를 없앤다. */
.comfy-af-bp-detail{
  margin-top:6px; padding:8px 10px; border-radius:var(--radius-sm);
  background:var(--bg-elev, var(--surface)); border:1px solid var(--border-soft);
  font-size:11px; line-height:1.65; color:var(--text-dim);
  overflow-wrap:anywhere; word-break:break-word; max-width:100%; box-sizing:border-box;
}
/* 배지와 상세를 세로로 쌓아 카드 안에서 정렬을 맞춘다(배지가 인라인이라 뒤 요소가
   같은 줄에 붙어 어긋나 보였다). */
.comfy-af-group-item-main .comfy-af-bp-badge,
.comfy-af-node-bypass .comfy-af-bp-badge{margin-top:8px;}
.comfy-af-group-item-main .comfy-af-bp-detail,
.comfy-af-node-bypass .comfy-af-bp-detail{display:block;}
.comfy-af-bp-actions{display:flex; flex-wrap:wrap; gap:6px; margin-top:6px;}
.comfy-af-bp-actions .btn-ghost{font-size:11px; padding:3px 9px;}
.comfy-af-validate-summary{border:1px solid var(--border-soft); border-radius:var(--radius-sm); padding:10px 12px; margin:0 0 10px; background:var(--bg-elev); font-size:12px; line-height:1.6;}
.comfy-af-validate-summary .vs-title{font-weight:800; margin-bottom:4px;}
.comfy-af-validate-summary .vs-combo{color:var(--danger); margin-top:4px;}
.comfy-af-validate-summary .vs-muted{color:var(--text-dim);}
/* 그룹 토글(여러 노드를 하나의 스위치로) 편집 패널 */
.comfy-af-group-panel{border:1px solid var(--border-soft); border-radius:var(--radius-sm); padding:10px 12px; margin-bottom:12px; background:color-mix(in srgb, var(--accent) 4%, transparent);}
.comfy-af-group-head{display:flex; align-items:baseline; gap:10px; flex-wrap:wrap; margin-bottom:8px;}
.comfy-af-group-list{display:flex; flex-direction:column; gap:6px; margin-bottom:10px;}
.comfy-af-group-item{display:flex; align-items:flex-start; justify-content:space-between; gap:10px; padding:7px 9px; border:1px solid var(--border-soft); border-radius:var(--radius-sm); background:var(--surface);}
.comfy-af-group-item-main{display:flex; flex-direction:column; gap:4px; min-width:0;}
.comfy-af-group-chips{display:flex; flex-wrap:wrap; gap:4px;}
.comfy-af-group-chip{font-size:11px; padding:2px 6px; border-radius:var(--radius-sm); background:color-mix(in srgb, var(--accent) 10%, transparent); color:var(--text-dim);}
.comfy-af-group-remove{flex:0 0 auto; font-size:12px; padding:4px 10px;}
/* 그룹 항목 우측 액션([멤버 편집]/[삭제]) — 좁아져도 아래로 접히지 않게 한 줄 고정.
   멤버 칩(.comfy-af-group-chips)만 wrap하고 버튼·라벨은 nowrap이다. */
.comfy-af-group-item-acts{flex:0 0 auto; display:flex; align-items:center; gap:6px; white-space:nowrap;}
.comfy-af-group-edit{flex:0 0 auto; font-size:12px; padding:4px 10px;}
.comfy-af-group-item.is-editing{border-color:var(--accent); background:color-mix(in srgb, var(--accent) 8%, var(--surface));}
.comfy-af-group-editing-tag{font-size:11px; font-weight:800; padding:3px 9px; border-radius:999px; background:var(--accent); color:var(--on-accent); white-space:nowrap;}
.comfy-af-group-add{display:flex; flex-direction:column; gap:8px;}
/* 편집 모드일 때의 하단 영역 강조(새 그룹 만들기와 시각적으로 구분). */
.comfy-af-group-add.is-editing{border:1px solid var(--accent); border-radius:var(--radius-sm); padding:10px; background:color-mix(in srgb, var(--accent) 5%, transparent);}
.comfy-af-group-edit-head{display:flex; align-items:baseline; gap:10px; flex-wrap:wrap;}
.comfy-af-group-editactions{display:flex; align-items:center; gap:8px; white-space:nowrap;}
.comfy-af-group-label{background:var(--surface); border:1px solid var(--border-soft); color:var(--text); border-radius:var(--radius-sm); padding:7px 9px; font-size:12.5px; font-family:inherit;}
.comfy-af-group-label:focus{outline:none; border-color:var(--accent);}
.comfy-af-group-picker-tools{display:flex; align-items:center; gap:8px; flex-wrap:wrap;}
.comfy-af-group-picker-tools .btn-ghost{font-size:12px; padding:4px 10px;}
.comfy-af-group-picker{display:flex; flex-direction:column; gap:4px; max-height:26vh; overflow-y:auto; overflow-x:auto; border:1px solid var(--border-soft); border-radius:var(--radius-sm); padding:6px;}
/* 한 줄 고정 — 노드 이름이 길어도 세로로 접히지 않고 목록이 가로로 스크롤된다. */
.comfy-af-group-pick{display:flex; align-items:center; gap:8px; font-size:12.5px; cursor:pointer; padding:4px 6px; border-radius:var(--radius-sm); white-space:nowrap;}
.comfy-af-group-pick:hover{background:color-mix(in srgb, var(--accent) 7%, transparent);}
.comfy-af-group-pick input{accent-color:var(--accent); cursor:pointer;}
.comfy-af-group-pick.is-rec{background:color-mix(in srgb, var(--accent) 6%, transparent);}
.comfy-af-group-recbadge{font-size:11px; font-weight:800; letter-spacing:.01em; padding:2px 8px; border-radius:999px; background:var(--accent); color:var(--on-accent);}
/* 이미 다른 그룹 토글에 속한 노드 — 중복 방지를 위해 선택 불가로 표시한다. */
.comfy-af-group-pick.is-owned{opacity:.55; cursor:not-allowed;}
.comfy-af-group-pick.is-owned:hover{background:transparent;}
.comfy-af-group-pick.is-owned input{cursor:not-allowed;}
.comfy-af-group-ownbadge{font-size:11px; font-weight:700; padding:2px 8px; border-radius:999px; background:color-mix(in srgb, var(--text-faint) 22%, transparent); color:var(--text-dim); white-space:nowrap;}
.comfy-af-group-addbtn{align-self:flex-start; font-size:12.5px; padding:7px 14px;}
/* 슬라이더 범위 설정(시작/기본값/최대/스텝) — 타입을 슬라이더(범위)로 고르면
   나타난다(인라인 style로 표시 여부 토글, 여기선 보일 때의 레이아웃만 정의). */
.comfy-af-slidercfg{display:grid; grid-template-columns:repeat(4, minmax(0,1fr)); gap:8px; margin-top:2px;}
.comfy-af-slidercfg label{display:flex; flex-direction:column; gap:4px; font-size:11px; color:var(--text-dim); min-width:0;}
.comfy-af-slidercfg input{
  background:var(--surface); border:1px solid var(--border-soft); color:var(--text);
  border-radius:var(--radius-sm); padding:7px 9px; font-size:12.5px; font-family:inherit;
  min-width:0; width:100%; box-sizing:border-box;
}
.comfy-af-slidercfg input:focus{outline:none; border-color:var(--accent);}
@media (max-width: 640px){ .comfy-af-slidercfg{grid-template-columns:repeat(2, minmax(0,1fr));} }
.comfy-af-schemarow{display:flex; gap:8px; align-items:center;}
.comfy-af-schemarow select{flex:1; min-width:0;}
.comfy-af-schemahint{font-size:11px; color:var(--text-dim); align-self:center;}
.comfy-af-gpulist{display:flex; flex-direction:column; gap:6px; margin:2px 0 6px; max-height:24vh; overflow-y:auto;}
.comfy-af-gpu-item{display:flex; align-items:center; gap:8px; font-size:13px; cursor:pointer; padding:6px 8px; border:1px solid var(--border-soft); border-radius:var(--radius-sm);}
.comfy-af-gpu-item input{accent-color:var(--accent); cursor:pointer;}
.comfy-af-gpu-off{cursor:not-allowed; opacity:.55;}
.comfy-af-gpu-off input{cursor:not-allowed;}

/* GPU 서버 선택 — 항상 펼쳐진 체크박스 목록 대신 접히는 멀티셀렉트 드롭다운.
   닫힘 상태에선 요약("전체 자동 할당" / "N개 서버 선택됨")만 보여 세로 공간을
   아낀다. 버튼을 누르면 같은 체크박스 목록이 패널로 펼쳐지고, 바깥을 클릭하면
   다시 접힌다. 자격 미달(3000번대 미만) 비활성 처리 로직은 그대로다. */
.comfy-af-gpudrop{position:relative; margin:2px 0 6px;}
.comfy-af-gpudrop-btn{display:flex; align-items:center; justify-content:space-between; gap:8px; width:100%; text-align:left; padding:9px 12px; background:var(--surface); border:1px solid var(--border-soft); border-radius:var(--radius-sm); color:var(--text); font-size:13px; cursor:pointer;}
.comfy-af-gpudrop-btn:hover{border-color:var(--accent);}
.comfy-af-gpudrop-caret{width:16px; height:16px; flex:none; transition:transform .15s ease;}
.comfy-af-gpudrop.open .comfy-af-gpudrop-caret{transform:rotate(180deg);}
.comfy-af-gpudrop-panel{display:none; position:absolute; z-index:30; top:calc(100% + 4px); left:0; right:0; margin:0; background:var(--surface-2); border:1px solid var(--border-soft); border-radius:var(--radius-sm); padding:6px; box-shadow:0 8px 24px rgba(0,0,0,.35);}
.comfy-af-gpudrop.open .comfy-af-gpudrop-panel{display:flex;}

/* "앱 노출 켜기" 토글 + "파라미터 초기화" 버튼을 한 줄에. */
.comfy-af-enable-row{display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap;}
.comfy-af-resetbtn{flex:none; display:inline-flex; align-items:center; font-size:12px;}

/* 대시보드 섹션 헤더의 버튼 묶음(새로고침 + 서버 추가). */
.comfy-head-actions{display:flex; align-items:center; gap:8px;}

/* 진행 중 버튼 — 상호작용 차단 + 아이콘 회전으로 로딩 표시. */
.is-loading{opacity:.6; pointer-events:none;}
@keyframes comfy-spin{to{transform:rotate(360deg);}}
.is-loading svg{animation:comfy-spin .8s linear infinite;}

/* ── Phase 2: 생성 서브뷰 (앱화된 워크플로우 실행) ── */
.comfy-gen-wrap{padding:2px;}
.comfy-gen-list{display:grid; grid-template-columns:repeat(auto-fill, minmax(220px, 1fr)); gap:12px; margin-bottom:18px;}
.comfy-gen-card{position:relative; text-align:left; cursor:pointer; padding:14px 16px; padding-right:52px; border:1px solid var(--border-soft); background:var(--surface);}
.comfy-gen-card:hover{background:var(--surface-2); border-color:var(--accent);}
.comfy-gen-card-label{font-weight:600; font-size:14px; color:var(--text);}
.comfy-gen-card-meta{font-size:11.5px; margin-top:4px;}
/* 생성 카드 즐겨찾기(좋아요) 별 — 카드 우상단 오버레이. 켜지면 라임(var(--accent))으로 채워진다. */
.comfy-gen-favbtn{position:absolute; top:9px; right:10px; display:inline-flex; flex-direction:column; align-items:center; gap:1px; color:var(--text-dim); cursor:pointer; padding:2px 3px; border-radius:8px; line-height:1; background:none; border:none; font:inherit; z-index:2;}
.comfy-gen-favbtn svg{width:17px; height:17px; transition:transform .12s ease;}
.comfy-gen-favbtn:hover{color:var(--accent); background:var(--accent-soft);}
.comfy-gen-favbtn:hover svg{transform:scale(1.12);}
.comfy-gen-favbtn.on{color:var(--accent);}
.comfy-gen-favcount{font-size:10px; font-weight:700;}
/* A) 실행 폼: 좁은 고정폭 카드 → 가용 가로폭을 쓰는 2단(폼 | 결과) 레이아웃.
   폼 필드는 반응형 그리드로 촘촘히 배치되고, 남는 공간은 결과 영역이 채운다
   (기존의 거대한 빈 공백 문제 해소). */
.comfy-gen-panel{width:100%; border:1px solid var(--border-soft); border-radius:var(--radius); padding:18px 20px; background:var(--bg-elev);}
/* 생성 폼 모달 오버레이 — 워크플로우 카드가 아무리 쌓여도 폼이 아래로 밀리지
   않도록, 폼을 그리드 아래 인라인이 아니라 화면 전체를 덮는 오버레이 안 중앙
   패널에 띄운다. z-index는 1100 — "수정"(앱필드 모달)·대기열 프롬프트(1200)가
   이 폼 위에 뜨고, 라이트박스(1000)보다는 위. 색/모서리는 기존 토큰만 사용. */
/* 비차단(non-modal) 창으로 전환(2026-08-04 지시): 예전에는 어두운 백드롭이 화면
   전체를 덮어 뒤의 작업 목록·플레이 리스트를 보지도 누르지도 못했다. 이제 오버레이
   자체는 투명하고 클릭을 통과시키며(pointer-events:none), 실제 창(.comfy-gen-panel)
   만 입력을 받는다. 닫기·접기 버튼이 이미 있으므로 백드롭 없이도 빠져나올 수 있다.
   창은 화면 중앙이 아니라 좌측에 붙여 우측(작업 목록 드로어가 열리는 쪽)을 가리지
   않게 한다. */
.comfy-gen-overlay{
  position:fixed; inset:0; z-index:2000; display:none;
  align-items:center; justify-content:center;
  background:none; pointer-events:none;
  padding:4vh 4vw; animation:comfyModalFade .12s ease-out;
}
.comfy-gen-overlay.show{display:flex;}
/* 작업 목록이 열려 있으면 그 폭만큼 오른쪽을 비워, 실행 창이 패널 밑으로 깔리지
   않게 한다(패널 350px + 여유 16px). 창은 남은 폭에 맞춰 알아서 좁아진다.
   좁은 화면에서는 패널이 원래 위로 덮이는 방식이라 이 보정을 하지 않는다. */
@media (min-width:901px){
  body.comfy-jobs-open .comfy-gen-overlay{padding-right:calc(4vw + 366px);}
}
/* 창 본체: 폭을 제한하고, 파라미터가 많은 긴 폼은 내부에서 세로 스크롤.
   백드롭이 없어진 만큼 테두리와 그림자로 뒷화면과의 경계를 분명히 한다. */
.comfy-gen-overlay .comfy-gen-panel{
  width:min(760px, 100%); max-height:92vh; overflow:auto;
  pointer-events:auto;
  border:1px solid var(--border);
  box-shadow:0 18px 60px rgba(0,0,0,.55);
}
html[data-theme="day"] .comfy-gen-overlay .comfy-gen-panel{box-shadow:0 18px 46px rgba(0,0,0,.22);}
/* 상단 줄(제목 · 수정 · 접기 · 닫기)은 스크롤해도 항상 남는다 — 긴 폼에서 아래로
   내려간 뒤 닫으려고 다시 맨 위까지 올라가야 하는 불편을 없앤다. 창 자신이 스크롤
   컨테이너이므로 sticky 로 충분하다. 구분선은 두지 않는다(지시) — 대신 창 배경과
   같은 색을 깔아 본문이 글자 뒤로 비쳐 보이지 않게만 한다. 창 padding(18px 20px)
   만큼 좌우로 넓혀 배경이 끝까지 덮이게 한다. */
.comfy-gen-panel-head{
  font-size:15px; display:flex; align-items:flex-start; justify-content:space-between; gap:12px;
  position:sticky; top:0; z-index:6;
  /* 세로 음수 margin 금지: sticky 는 margin 박스를 top:0 에 맞추므로 margin-top:-18px
     을 주면 실제 헤더가 18px 아래에 놓이고 그 틈으로 본문이 비쳐 보였다(2026-08-04).
     대신 창의 padding-top 을 0 으로 만들고 헤더가 그 여백을 직접 갖는다. */
  margin:0 -20px 14px; padding:18px 20px 12px;
  background:var(--bg-elev);
  /* 구분선도 그림자도 두지 않는다(지시) — 배경만 불투명하게 깔아 본문이 글자 뒤로
     비치지 않게 하는 것으로 충분하다. */
  cursor:move; user-select:none;
}
.comfy-gen-overlay .comfy-gen-panel{padding-top:0;}
/* 제목 줄은 창을 끄는 손잡이다. 버튼과 설명 글자는 예외로 두어 드래그가 아니라
   원래 동작(클릭·선택)을 하게 한다. */
.comfy-gen-panel-head button{cursor:pointer;}
.comfy-gen-panel-head-text{cursor:move;}
.comfy-gen-panel.dragging{transition:none;}
.comfy-gen-panel-head-text{display:flex; flex-direction:column; gap:3px; min-width:0;}
.comfy-gen-panel-desc{font-size:12.5px; font-weight:400; line-height:1.45;}
/* 닫기·접기 버튼: 앱 전역 모달 표준(.mstd-btn)과 같은 라임 톤으로 통일한다.
   회색이라 눈에 띄지 않는다는 지적(2026-08-04) — 두 모드 모두 --accent 위에
   --on-accent 를 쓰지 않고 테두리+글자색만 액센트로 두어 명암비를 확보한다. */
.comfy-gen-close,
.comfy-gen-min{
  display:inline-flex; align-items:center; justify-content:center;
  width:28px; height:28px; padding:0; border-radius:8px; cursor:pointer;
  background:color-mix(in srgb, var(--accent) 10%, transparent);
  border:1px solid color-mix(in srgb, var(--accent) 38%, transparent);
  color:var(--accent); font-size:14px; line-height:1; font-weight:700; font-family:inherit;
  transition:background .14s ease, border-color .14s ease;
}
.comfy-gen-close:hover,
.comfy-gen-min:hover{background:color-mix(in srgb, var(--accent) 24%, transparent); border-color:var(--accent);}
.comfy-gen-close:focus-visible,
.comfy-gen-min:focus-visible{outline:2px solid var(--accent); outline-offset:2px;}
.comfy-gen-min svg{width:16px; height:16px;}
/* 최소화 상태: 오버레이를 화면에서 완전히 치워 뒷화면을 자유롭게 조작하게 한다.
   show는 유지한 채 minimized만 얹혔을 때 display:none으로 덮어써, 복원 시 minimized만
   떼면 곧바로 다시 보인다. 폼 DOM은 그대로 살아 있어 입력값/진행이 보존된다. */
.comfy-gen-overlay.show.minimized{display:none;}
/* 최소화 핸들 도크 — 화면 하단 모서리에 카테고리별 핸들(필)이 쌓인다. 도크 자체는
   클릭을 통과시키고(뒷화면 조작 방해 금지), 핸들만 상호작용을 받는다. */
.comfy-gen-min-dock{position:fixed; left:0; right:0; bottom:0; z-index:1110; display:flex; flex-direction:column; align-items:flex-start; gap:8px; padding:12px 14px calc(12px + env(safe-area-inset-bottom, 0px)); pointer-events:none;}
.comfy-gen-min-handle{pointer-events:auto; display:inline-flex; align-items:center; gap:10px; max-width:min(360px, calc(100vw - 28px)); padding:9px 10px 9px 12px; border:1px solid var(--border); border-radius:999px; background:var(--bg-elev); box-shadow:0 6px 24px rgba(0,0,0,.32); cursor:pointer; color:var(--text); transition:border-color .12s ease, box-shadow .12s ease;}
.comfy-gen-min-handle:hover{border-color:var(--accent, var(--text-dim)); box-shadow:0 8px 30px rgba(0,0,0,.4);}
.comfy-gen-min-handle .cgmh-icon{display:inline-flex; flex-shrink:0; color:var(--text-dim);}
.comfy-gen-min-handle .cgmh-icon svg{width:18px; height:18px;}
.comfy-gen-min-handle .cgmh-body{display:flex; flex-direction:column; gap:1px; min-width:0;}
.comfy-gen-min-handle .cgmh-title{font-size:12.5px; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.comfy-gen-min-handle .cgmh-status{font-size:11px; color:var(--text-dim); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.comfy-gen-min-handle .cgmh-close{flex-shrink:0; display:inline-flex; align-items:center; justify-content:center; width:24px; height:24px; padding:0; border:none; border-radius:50%; background:none; color:var(--text-dim); cursor:pointer;}
.comfy-gen-min-handle .cgmh-close svg{width:14px; height:14px;}
.comfy-gen-min-handle .cgmh-close:hover{background:var(--surface-2); color:var(--text);}
/* 진행 상태별 강조: 실행 중=강조색 테두리+깜빡임 점, 완료=성공색, 실패=위험색. */
.comfy-gen-min-handle.is-running{border-color:var(--accent, #6ea8fe);}
.comfy-gen-min-handle.is-running .cgmh-icon{color:var(--accent, #6ea8fe); animation:comfyMinPulse 1.4s ease-in-out infinite;}
.comfy-gen-min-handle.is-done{border-color:var(--success, #3fb950);}
.comfy-gen-min-handle.is-done .cgmh-icon,.comfy-gen-min-handle.is-done .cgmh-status{color:var(--success, #3fb950);}
.comfy-gen-min-handle.is-error{border-color:var(--danger, #f85149);}
.comfy-gen-min-handle.is-error .cgmh-icon,.comfy-gen-min-handle.is-error .cgmh-status{color:var(--danger, #f85149);}
@keyframes comfyMinPulse{0%,100%{opacity:1;}50%{opacity:.4;}}
.comfy-gen-body{display:grid; grid-template-columns:minmax(320px, 1.1fr) minmax(280px, 1fr); gap:20px; align-items:start;}
@media (max-width:900px){ .comfy-gen-body{grid-template-columns:1fr;} }
/* 인라인 결과 컬럼 제거(결과는 우측 작업 목록 드로어에서만 본다). 한 번
   max-width를 아예 없앴더니 패널이 화면 전체로 퍼져버려 원치 않는다는
   피드백 — 원래보다 살짝만 넉넉한 폭으로 되돌린다(짧은 필드 몇 개는 여전히
   옆으로 나란히 붙지만, 이미지/텍스트영역 같은 "넓은" 필드가 화면 전체로
   늘어나지는 않게).*/
.comfy-gen-body-solo{grid-template-columns:1fr; max-width:720px;}
.comfy-gen-formcol{min-width:0;}
.comfy-gen-outputcol{min-width:0; display:flex; flex-direction:column; gap:8px;}
.comfy-gen-output-head{font-size:12px; font-weight:600; text-transform:uppercase; letter-spacing:.04em;}
.comfy-gen-output-empty{border:1px dashed var(--border-soft); border-radius:var(--radius-sm); padding:26px 16px; text-align:center; color:var(--text-dim); font-size:13px;}
/* 반응형 필드 그리드 — 여러 파라미터가 많아도 가로로 흐르며 채워진다. */
.comfy-gen-grid{display:grid; grid-template-columns:repeat(auto-fill, minmax(220px, 1fr)); gap:12px 14px; margin-bottom:14px;}
/* 같은 노드에 속한 필드들을 하나로 묶는 그룹 박스(정렬이 뒤섞이는 문제 수정).
   .comfy-gen-grid의 한 행을 통째로 차지하고, 내부에 자체 그리드를 이룬다. */
.comfy-gen-fieldgroup{
  grid-column:1 / -1; display:grid; grid-template-columns:repeat(auto-fill, minmax(220px, 1fr));
  gap:12px 14px; padding:12px 14px; border:1px solid var(--border-soft); border-radius:var(--radius-sm);
  background:color-mix(in srgb, var(--bg-elev) 55%, transparent);
}
/* 노드 그룹 헤더 — 이 그룹의 파라미터가 어느 노드(기능)에 속하는지 표시.
   그리드 한 행을 통째로 차지하고, 노드 표시 이름 + 옅은 노드 번호로 구성. */
.comfy-gen-group-head{grid-column:1 / -1; display:flex; align-items:baseline; gap:8px; margin:-1px 0 3px; min-width:0;}
.comfy-gen-group-title{font-size:13px; font-weight:700; color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.comfy-gen-group-id{font-size:11px; flex:0 0 auto;}
.comfy-gen-field{display:flex; flex-direction:column; gap:5px; min-width:0; margin:0;}
.comfy-gen-field-wide{grid-column:1 / -1;}
.comfy-gen-field-head{display:flex; align-items:center; justify-content:space-between; gap:10px;}
.comfy-gen-field label{font-size:12px; color:var(--text-dim); font-weight:600;}
/* 바이패스 OFF된 선택적 필드 — 회색 처리(제출 제외). */
.comfy-gen-field-off{opacity:.5;}
.comfy-gen-field-off .comfy-gen-input{pointer-events:none;}
/* 노드 단위 바이패스 토글(값 입력 컨트롤이 없는 필드) — 다른 필드들과 나란히
   있어도 허전해 보이지 않게 옅은 테두리 박스로 감싼다. */
.comfy-gen-nodetoggle{border:1px solid var(--border-soft); border-radius:var(--radius-sm); padding:9px 11px; background:color-mix(in srgb, var(--accent) 4%, transparent);}
.comfy-gen-nodetoggle .comfy-gen-field-head{gap:14px;}
.comfy-gen-bypass-toggle{font-size:11.5px; gap:6px; margin:0;}
/* 바이패스 토글 + 그 토글이 제어하는 기능(바로 뒤 필드)을 한 묶음으로 보이게 하는
   박스. 왼쪽 강조 테두리로 "이 토글이 이 기능에 붙어 있다"를 시각적으로 드러낸다.
   박스가 이미 프레임을 주므로 안쪽 토글의 자체 테두리/배경은 지운다(이중 박스 방지). */
.comfy-gen-togglegroup{border-left:3px solid color-mix(in srgb, var(--accent) 55%, transparent);}
.comfy-gen-togglegroup .comfy-gen-nodetoggle{border:0; padding:0; background:transparent;}
/* 토글 묶음이 통째로 OFF(바이패스)됐을 때: 강조 테두리를 옅게 죽이고 헤더에 '꺼짐'
   배지를 붙여 "이 기능 묶음이 실행에서 빠졌다"를 한눈에 보인다. 멤버 필드 카드
   각각은 comfy-gen-field-off(opacity)로 이미 흐려지므로 박스 자체엔 opacity를 겹치지
   않는다(이중 흐림 방지). comfyToggleBypassField가 토글 on/off 시 즉시 토글한다. */
.comfy-gen-togglegroup.comfy-gen-group-off{border-left-color:color-mix(in srgb, var(--text-dim) 40%, transparent); background:color-mix(in srgb, var(--text-dim) 5%, transparent);}
.comfy-gen-togglegroup.comfy-gen-group-off .comfy-gen-group-title{color:var(--text-dim);}
.comfy-gen-togglegroup.comfy-gen-group-off .comfy-gen-group-head::after{content:"꺼짐"; font-size:10.5px; font-weight:700; color:var(--text-dim); background:color-mix(in srgb, var(--text-dim) 16%, transparent); border-radius:999px; padding:1px 8px; margin-left:8px;}
/* 라벨 미입력 토글 안내(관리자에게만 노출) */
.comfy-gen-toggle-warn{color:var(--warn);}
/* 필드 도움말(관리자가 입력한 설명) — 라벨 아래 작은 글씨 */
.comfy-gen-field-hint{font-size:11.5px; line-height:1.45; margin:-2px 0 4px;}
/* "기능" 섹션(토글 스위치 모음) · "고급 설정"(접힘) 섹션 */
.comfy-gen-section{margin:4px 0 2px;}
.comfy-gen-section-title{font-size:12px; font-weight:700; color:var(--text-dim); margin:6px 2px 6px; letter-spacing:.02em;}
.comfy-gen-advanced{margin:6px 0 2px; border:1px solid var(--border-soft); border-radius:var(--radius-sm); padding:4px 10px; background:color-mix(in srgb, var(--accent) 3%, transparent);}
.comfy-gen-advanced-summary{cursor:pointer; font-size:12.5px; font-weight:700; color:var(--text-dim); padding:6px 2px; list-style:none; user-select:none;}
.comfy-gen-advanced-summary::-webkit-details-marker{display:none;}
.comfy-gen-advanced-summary::before{content:"▸ "; color:var(--accent);}
.comfy-gen-advanced[open] .comfy-gen-advanced-summary::before{content:"▾ ";}
.comfy-gen-advanced .comfy-gen-grid{margin:4px 0 8px;}
.comfy-gen-input{
  background:var(--surface); border:1px solid var(--border-soft); color:var(--text);
  border-radius:var(--radius-sm); padding:9px 11px; font-size:13px; font-family:inherit; width:100%;
}
.comfy-gen-input:focus{outline:none; border-color:var(--accent);}
/* 프롬프트 등 여러 줄 입력란 — "지금의 세배는 되어야 함" 요청 반영(rows도
   12로 늘렸다 — app.js). min-height도 그에 맞춰 3배 가까이 올린다. */
textarea.comfy-gen-input{resize:vertical; min-height:220px;}
/* 켜기/끄기 토글(예: Power Lora Loader의 lora_N.on) — 체크박스는 폭 100%가
   아니라 자기 크기만 차지해야 하므로 .comfy-gen-input의 기본 width:100%를
   여기서 되돌린다. */
.comfy-gen-toggle{
  display:flex; align-items:center; gap:8px; cursor:pointer; user-select:none;
  font-size:13px; color:var(--text-dim); width:fit-content;
}
.comfy-gen-toggle input.comfy-gen-input{
  width:auto; accent-color:var(--accent); cursor:pointer; padding:0;
}
.comfy-gen-toggle input:checked ~ span{color:var(--accent); font-weight:600;}
/* 실제 슬라이드 토글 스위치("켜짐/꺼짐 구분이 안 됨" 피드백) — 네이티브
   체크박스는 화면 밖으로 숨기고, 트랙+손잡이를 체크 상태에 따라 그린다.
   레이블 텍스트도 함께 켜짐/꺼짐이 바뀌도록 app.js의 onchange가 갱신한다. */
.comfy-gen-switch-input{position:absolute; width:1px; height:1px; opacity:0; margin:0; pointer-events:none;}
.comfy-gen-switch-track{
  position:relative; flex:0 0 auto; width:36px; height:20px; border-radius:999px;
  background:var(--border-soft); transition:background-color .15s ease;
}
.comfy-gen-switch-thumb{
  position:absolute; top:2px; left:2px; width:16px; height:16px; border-radius:50%;
  background:#fff; box-shadow:0 1px 2px rgba(0,0,0,.35); transition:transform .15s ease;
}
.comfy-gen-switch-input:checked ~ .comfy-gen-switch-track{background:var(--accent);}
.comfy-gen-switch-input:checked ~ .comfy-gen-switch-track .comfy-gen-switch-thumb{transform:translateX(16px);}
.comfy-gen-switch-input:focus-visible ~ .comfy-gen-switch-track{outline:2px solid var(--accent); outline-offset:2px;}
.comfy-gen-switch-label{font-size:12.5px; color:var(--text-dim); min-width:26px;}
.comfy-gen-switch-input:checked ~ .comfy-gen-switch-label{color:var(--accent); font-weight:600;}
/* 슬라이더(범위) 필드: range + 숫자 읽기 상자를 한 줄로. range가 남는 폭을 채우고
   숫자 상자는 고정 폭으로 현재 값을 보여준다(요청 #9). */
.comfy-gen-sliderwrap{display:flex; align-items:center; gap:10px; width:100%;}
.comfy-gen-sliderwrap input.comfy-gen-slider{flex:1 1 auto; width:auto; padding:0; accent-color:var(--accent); cursor:pointer;}
.comfy-gen-sliderwrap input.comfy-gen-slider-num{flex:0 0 78px; width:78px; background:var(--surface); border:1px solid var(--border-soft); color:var(--text); border-radius:var(--radius-sm); padding:6px 8px; font-size:12.5px; text-align:right;}
.comfy-gen-sliderwrap input.comfy-gen-slider-num:focus{outline:none; border-color:var(--accent);}
/* 시드 필드(요청): 숫자 입력 + 랜덤/고정/증가/감소 모드 드롭다운을 한 줄로.
   슬라이더 래퍼와 동일한 토큰만 사용해 나머지 폼과 일관되게 맞춘다. */
.comfy-gen-seedwrap{display:flex; align-items:center; gap:8px; width:100%;}
.comfy-gen-seedwrap input.comfy-gen-seed-input{flex:1 1 auto; width:auto; min-width:0; background:var(--surface); border:1px solid var(--border-soft); color:var(--text); border-radius:var(--radius-sm); padding:6px 8px; font-size:12.5px;}
.comfy-gen-seedwrap input.comfy-gen-seed-input:focus{outline:none; border-color:var(--accent);}
.comfy-gen-seedwrap input.comfy-gen-seed-locked{opacity:.6; cursor:not-allowed;}
.comfy-gen-seedwrap select.comfy-gen-seed-mode{flex:0 0 auto; background:var(--surface); border:1px solid var(--border-soft); color:var(--text); border-radius:var(--radius-sm); padding:6px 8px; font-size:12.5px; cursor:pointer;}
.comfy-gen-seedwrap select.comfy-gen-seed-mode:focus{outline:none; border-color:var(--accent);}
/* 모델 드롭다운 + '↻ 새로고침' 버튼 한 줄. GPU에 새로 넣은 모델을 이 워크플로우의
   모든 드롭다운에 최신으로 반영한다. 셸 토큰만 사용해 폼과 일관되게 맞춘다. */
.comfy-gen-selectrow{display:flex; align-items:center; gap:6px; width:100%;}
.comfy-gen-selectrow select.comfy-gen-select{flex:1 1 auto; min-width:0;}
.comfy-gen-refresh-btn{flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center; width:30px; height:30px; padding:0; border-radius:var(--radius-sm); font-size:15px; line-height:1; cursor:pointer;}
.comfy-gen-refresh-ico{display:inline-block; line-height:1;}
.comfy-gen-refresh-btn.is-loading .comfy-gen-refresh-ico,
.comfy-models-refresh.is-loading .comfy-gen-refresh-ico{animation:comfy-spin .8s linear infinite;}
/* 이미지 필드(요청 #2): 업로드 드롭존 + 미리보기 썸네일. 셸의 서피스/보더/
   액센트 토큰만 사용해 나머지 폼과 일관되게 맞춘다. */
.comfy-gen-imagefield{display:flex; flex-direction:column; gap:8px; width:100%;}
.comfy-gen-imagedrop{position:relative; display:flex; align-items:center; justify-content:center; min-height:120px; padding:12px; background:var(--surface); border:1px dashed var(--border-soft); border-radius:var(--radius-sm); cursor:pointer; transition:border-color .15s, background .15s;}
.comfy-gen-imagedrop:hover{border-color:var(--accent); background:var(--surface-2, var(--surface));}
.comfy-gen-imagepreview{max-width:100%; max-height:260px; border-radius:var(--radius-sm); display:block; object-fit:contain;}
.comfy-gen-imagehint{display:flex; flex-direction:column; align-items:center; gap:6px; color:var(--text-dim); font-size:12.5px; text-align:center;}
.comfy-gen-imagedefault{font-size:11px; word-break:break-all; max-width:240px;}
.comfy-gen-imageactions{display:flex; justify-content:flex-end; gap:6px; flex-wrap:wrap;}
.comfy-gen-imageclear{background:transparent; border:1px solid var(--border-soft); color:var(--text-dim); border-radius:var(--radius-sm); padding:4px 10px; font-size:12px; cursor:pointer;}
.comfy-gen-imageclear:hover{border-color:var(--accent); color:var(--text);}
/* 이미지 → 프롬프트 추천 버튼 — 강조 토큰(--accent/--on-accent)만 사용. */
.comfy-gen-suggestprompt{background:var(--accent); border:1px solid var(--accent); color:var(--on-accent); border-radius:var(--radius-sm); padding:4px 10px; font-size:12px; font-weight:600; cursor:pointer;}
.comfy-gen-suggestprompt:hover{filter:brightness(1.08);}
.comfy-gen-suggestprompt:disabled{opacity:.55; cursor:default;}
/* 이미지 필드 우클릭/길게누르기 → 마스크 편집 커스텀 메뉴(요청 #4). 네이티브 메뉴
   대신 셸 토큰만 사용한 작은 팝오버. 위치는 JS가 커서 좌표로 inline 지정한다. */
.comfy-img-ctxmenu{position:fixed; z-index:5300; min-width:132px; padding:4px; background:var(--bg-elev, var(--surface)); border:1px solid var(--border-soft); border-radius:var(--radius-sm); box-shadow:0 8px 28px rgba(0,0,0,.4);}
.comfy-img-ctxmenu-item{display:flex; align-items:center; gap:8px; width:100%; text-align:left; background:transparent; border:0; color:var(--text); border-radius:var(--radius-sm, 8px); padding:8px 10px; font-size:12.5px; cursor:pointer;}
.comfy-img-ctxmenu-item:hover{background:var(--accent-soft, var(--surface)); color:var(--text);}
/* 비디오 입력 필드(V2V) — 이미지 필드와 동일한 드롭존 스타일에 <video> 미리보기. */
.comfy-gen-videofield{display:flex; flex-direction:column; gap:8px; width:100%;}
.comfy-gen-videodrop{position:relative; display:flex; align-items:center; justify-content:center; min-height:120px; padding:12px; background:var(--surface); border:1px dashed var(--border-soft); border-radius:var(--radius-sm); cursor:pointer; transition:border-color .15s, background .15s;}
.comfy-gen-videodrop:hover{border-color:var(--accent); background:var(--surface-2, var(--surface));}
.comfy-gen-videopreview{max-width:100%; max-height:300px; border-radius:var(--radius-sm); display:block; background:#000;}
.comfy-gen-videohint{display:flex; flex-direction:column; align-items:center; gap:6px; color:var(--text-dim); font-size:12.5px; text-align:center;}
.comfy-gen-videoactions{display:flex; justify-content:flex-end;}
.comfy-gen-videoclear{background:transparent; border:1px solid var(--border-soft); color:var(--text-dim); border-radius:var(--radius-sm); padding:4px 10px; font-size:12px; cursor:pointer;}
.comfy-gen-videoclear:hover{border-color:var(--accent); color:var(--text);}
/* 포인트 에디터(캔버스) — KJNodes PointsEditor류 대화형 점/박스 편집. */
.comfy-gen-pointsfield{display:flex; flex-direction:column; gap:8px; width:100%;}
.comfy-gen-points-toolbar{display:flex; align-items:center; justify-content:space-between; gap:10px; flex-wrap:wrap;}
.comfy-gen-points-counter{font-size:12.5px; color:var(--text); font-variant-numeric:tabular-nums;}
.comfy-gen-points-tools{display:flex; gap:6px; flex-wrap:wrap;}
.comfy-gen-points-btn{background:transparent; border:1px solid var(--border-soft); color:var(--text-dim); border-radius:var(--radius-sm); padding:4px 10px; font-size:12px; cursor:pointer;}
.comfy-gen-points-btn:hover{border-color:var(--accent); color:var(--text);}
.comfy-gen-points-canvaswrap{position:relative; width:100%; background:#1a1a1e; border:1px solid var(--border-soft); border-radius:var(--radius-sm); overflow:hidden;}
.comfy-gen-points-canvas{display:block; width:100%; height:380px; cursor:crosshair; touch-action:none;}
.comfy-gen-points-empty{position:absolute; inset:0; display:flex; align-items:center; justify-content:center; color:var(--text-dim); font-size:13px; pointer-events:none; text-align:center; padding:16px;}
.comfy-gen-points-help{font-size:11px; line-height:1.6;}
/* 포인트 에디터 편집 모드 버튼(모바일/무수식어 클릭용) + 의미 힌트. */
.comfy-gen-points-modes{display:flex; gap:6px; flex-wrap:wrap;}
.comfy-gen-points-mode{background:transparent; border:1px solid var(--border-soft); color:var(--text-dim); border-radius:var(--radius-sm); padding:5px 11px; font-size:12px; cursor:pointer; transition:border-color .12s, background .12s, color .12s;}
.comfy-gen-points-mode:hover{border-color:var(--accent); color:var(--text);}
.comfy-gen-points-mode.is-active{border-color:var(--accent); background:var(--accent-soft, color-mix(in srgb, var(--accent) 16%, transparent)); color:var(--text);}
.comfy-gen-points-semantic{font-size:12px; color:var(--text); line-height:1.5;}
/* 마스크(인페인트) 필드: 이미지 드롭존 + 썸네일 위 마스크 오버레이. 셸 토큰만 사용. */
.comfy-gen-maskfield{display:flex; flex-direction:column; gap:8px; width:100%;}
.comfy-gen-maskdrop{position:relative; display:flex; align-items:center; justify-content:center; min-height:120px; padding:12px; background:var(--surface); border:1px dashed var(--border-soft); border-radius:var(--radius-sm); cursor:pointer; transition:border-color .15s, background .15s;}
.comfy-gen-maskdrop:hover{border-color:var(--accent); background:var(--surface-2, var(--surface));}
/* 드래그앤드롭 하이라이트(image/mask/video 드롭존 공통) — 파일을 끌어다 올린
   동안 앱 셸 --accent 색으로 테두리를 강조하고 얇은 안쪽 링을 더해 "여기에 놓으면
   된다"를 분명히 보여준다. 어시스턴트 입력창의 .dragover 인셋 링 패턴과 동일 톤. */
.comfy-gen-imagedrop.comfy-gen-dropactive,
.comfy-gen-videodrop.comfy-gen-dropactive,
.comfy-gen-maskdrop.comfy-gen-dropactive{
  border-color:var(--accent); border-style:solid;
  background:color-mix(in srgb, var(--accent) 12%, var(--surface));
  box-shadow:inset 0 0 0 2px var(--accent);
}
.comfy-gen-mask-thumbwrap{position:relative; display:inline-block; max-width:100%; line-height:0;}
.comfy-gen-mask-thumbwrap .comfy-gen-imagepreview{max-width:100%; max-height:260px; display:block;}
.comfy-gen-mask-thumboverlay{position:absolute; inset:0; width:100%; height:100%; pointer-events:none; border-radius:var(--radius-sm);}
.comfy-gen-maskactions{display:flex; justify-content:flex-end; gap:8px;}
.comfy-gen-maskpaint{background:var(--accent); border:1px solid var(--accent); color:var(--on-accent, #10140a); font-weight:700; border-radius:var(--radius-sm); padding:4px 12px; font-size:12px; cursor:pointer;}
.comfy-gen-maskpaint:hover{filter:brightness(1.05);}
/* 마스크 에디터 전체화면 모달 — comfy-modal-overlay를 확장해 큰 편집 박스로. */
.comfy-maske-overlay{z-index:2300; padding:3vh 3vw;}   /* 모달 밴드 — 생성 오버레이(2000) 위 */
.comfy-maske-overlay.show{display:flex;}
.comfy-maske-box{display:flex; flex-direction:column; gap:12px; width:min(1000px, 96vw); max-width:96vw; max-height:94vh; background:var(--bg-elev, var(--surface)); border:1px solid var(--border-soft); border-radius:var(--radius-md, 14px); padding:16px; box-shadow:0 10px 44px rgba(0,0,0,.5);}
.comfy-maske-head{display:flex; align-items:center; justify-content:space-between; gap:12px;}
.comfy-maske-head b{font-size:15px; color:var(--text);}
.comfy-maske-toolbar{display:flex; align-items:center; gap:8px; flex-wrap:wrap;}
.comfy-maske-tool,.comfy-maske-btn{background:transparent; border:1px solid var(--border-soft); color:var(--text-dim); border-radius:var(--radius-sm); padding:5px 12px; font-size:12.5px; cursor:pointer; transition:border-color .12s, background .12s, color .12s;}
.comfy-maske-tool:hover,.comfy-maske-btn:hover{border-color:var(--accent); color:var(--text);}
.comfy-maske-tool.is-active{border-color:var(--accent); background:var(--accent-soft, color-mix(in srgb, var(--accent) 16%, transparent)); color:var(--text);}
.comfy-maske-brush{display:flex; align-items:center; gap:8px; font-size:12.5px; color:var(--text-dim);}
.comfy-maske-brush input[type="range"]{width:120px;}
.comfy-maske-canvaswrap{position:relative; flex:1 1 auto; min-height:300px; height:60vh; background:#141417; border:1px solid var(--border-soft); border-radius:var(--radius-sm); overflow:hidden;}
.comfy-maske-canvas{display:block; width:100%; height:100%; cursor:crosshair; touch-action:none;}
/* 브러시/지우개 호버 원(Task 103) — 마우스 위치에 실제 칠해질 크기의 원을
   표시한다. 별도 오버레이 div라 그리기 캔버스를 다시 그리지 않는다(성능). */
.comfy-maske-cursor{position:absolute; left:0; top:0; border-radius:50%; pointer-events:none; transform:translate(-50%,-50%); display:none; box-sizing:border-box; z-index:3;}
.comfy-maske-cursor.is-brush{border:2px solid rgba(255,255,255,.92); background:rgba(229,72,77,.28); box-shadow:0 0 0 1px rgba(0,0,0,.45);}
.comfy-maske-cursor.is-eraser{border:2px dashed rgba(255,255,255,.92); background:transparent; box-shadow:0 0 0 1px rgba(0,0,0,.45);}
.comfy-maske-help{font-size:11px; line-height:1.6;}
.comfy-maske-actions{display:flex; justify-content:flex-end; gap:8px;}
/* 개요(살펴보기) 최근 생성물 카드 그리드(요청 #6). 셸 토큰만 사용. */
.comfy-ov-head{display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:16px;}
.comfy-ov-heading{font-size:20px; font-weight:700; margin:0; color:var(--text);}
.comfy-ov-refresh{background:transparent; border:1px solid var(--border-soft); color:var(--text-dim); border-radius:var(--radius-sm); padding:6px 14px; font-size:12.5px; cursor:pointer;}
.comfy-ov-refresh:hover{border-color:var(--accent); color:var(--text);}
.comfy-ov-grid{display:grid; grid-template-columns:repeat(auto-fill, minmax(200px, 1fr)); gap:16px;}
.comfy-ov-card{display:flex; flex-direction:column; background:var(--surface); border:1px solid var(--border-soft); border-radius:var(--radius); overflow:hidden;}
.comfy-ov-mediabox{position:relative; width:100%; aspect-ratio:1/1; background:var(--surface-2, #000); display:flex; align-items:center; justify-content:center; overflow:hidden;}
.comfy-ov-thumb{width:100%; height:100%; object-fit:cover; display:block;}
.comfy-ov-textwrap,.comfy-ov-audiowrap{width:100%; height:100%; padding:12px; display:flex; align-items:center; justify-content:center; box-sizing:border-box;}
.comfy-ov-text{margin:0; font-size:11.5px; color:var(--text-dim); white-space:pre-wrap; overflow:hidden; max-height:100%;}
.comfy-ov-badge{position:absolute; right:8px; bottom:8px; background:rgba(0,0,0,.66); color:#fff; font-size:11px; padding:2px 7px; border-radius:999px;}
/* 미확인 생성물 "NEW" 배지 — 갤러리 미디어박스/작업목록 썸네일(둘 다 position:relative)
   좌상단에 얹는다. 앱 accent 토큰 사용, 24h 경과 또는 상세 열람 시 렌더/DOM에서 제거됨. */
.gen-new-badge{
  position:absolute; left:6px; top:6px; z-index:3;
  padding:2px 7px; border-radius:999px;
  background:var(--accent); color:var(--on-accent);
  font-size:9.5px; font-weight:800; letter-spacing:.05em; line-height:1.35;
  pointer-events:none; box-shadow:0 1px 4px rgba(0,0,0,.4);
}
/* 결과물 카드 삭제 버튼(호버 시 노출) */
.comfy-ov-del{
  position:absolute; right:8px; top:8px; width:30px; height:30px; padding:0;
  display:flex; align-items:center; justify-content:center; cursor:pointer;
  border-radius:8px; border:1px solid rgba(255,255,255,.25);
  background:rgba(0,0,0,.55); color:#fff; opacity:0; transition:opacity .12s ease, background .12s ease;
}
.comfy-ov-card:hover .comfy-ov-del{opacity:1;}
.comfy-ov-del:hover{background:var(--danger); border-color:var(--danger);}
/* 카드 1장 빠른 분류 버튼 — 삭제 버튼 왼쪽, 라임 호버 */
.comfy-ov-asg{right:44px;}
.comfy-ov-asg:hover{background:var(--accent); border-color:var(--accent); color:var(--on-accent,#10140a);}
/* 드래그 앤 드롭 분류 — 끄는 카드는 흐리게, 놓을 수 있는 헤더는 라임 강조 */
.comfy-ov-card.gal-dragging{opacity:.45;}
.comfy-ov-card img{-webkit-user-drag:none; user-select:none;}
.gal-drop-hover{outline:2px dashed var(--accent); outline-offset:-2px;
  background:color-mix(in srgb, var(--accent) 10%, transparent);}
/* 갤러리 공유 토글(결과물 카드) — 켜져 있으면 라임으로 고정 표시해 호버 없이도
   공유 중임을 알 수 있게 한다. 상위 폴더 공유로 잠긴 상태(bylock)는 살짝 흐리게. */
.comfy-ov-shr.on{opacity:1; background:var(--accent); border-color:var(--accent); color:var(--on-accent,#10140a);}
.comfy-ov-shr:hover{background:var(--accent); border-color:var(--accent); color:var(--on-accent,#10140a);}
.comfy-ov-shr.bylock{opacity:.85; cursor:default;}
/* 작성자 배지 / 공유중 배지 — 기존 카운트 배지와 같은 알약 모양·토큰만 사용 */
.gal-owner-badge{
  /* 우하단 "+N" 배지(.comfy-ov-badge)와 절대 겹치지 않게 폭 상한을 둔다.
     이름이 길면 줄바꿈 대신 말줄임(레이아웃 철칙: 세로 접힘 금지). */
  position:absolute; left:8px; bottom:8px; max-width:calc(100% - 64px);
  background:rgba(0,0,0,.66); color:#fff; font-size:10.5px; padding:2px 8px; border-radius:999px;
  white-space:nowrap; word-break:keep-all; overflow:hidden; text-overflow:ellipsis; pointer-events:none;
}
.gal-owner-badge.inline{
  position:static; display:inline-block; vertical-align:middle; margin-left:6px; max-width:140px;
  background:var(--surface-2, rgba(255,255,255,.08)); color:var(--text-dim);
  border:1px solid var(--border-soft); font-size:10.5px;
}
.gal-shared-badge{
  position:absolute; left:8px; top:8px;
  background:var(--accent); color:var(--on-accent,#10140a);
  font-size:10.5px; font-weight:700; padding:2px 8px; border-radius:999px;
  white-space:nowrap; word-break:keep-all; pointer-events:none;
}
.gal-shared-badge.inline{
  position:static; display:inline-block; vertical-align:middle; margin-left:6px;
}
/* 폴더 카드/행의 공유 버튼 — 공유 중이면 라임 고정 */
/* 공유 상태 3분기 — 색으로도 구분(글자만으로는 판별이 어렵다는 지적, 2026-07-30) */
.gal-sharefact{white-space:nowrap; word-break:keep-all;}
.gal-sharefact.on{background:var(--accent); color:var(--on-accent); border-color:var(--accent);}
.gal-sharefact.bydefault{background:transparent; color:var(--accent); border-color:var(--accent);}
.gal-sharefact.isoff{background:var(--surface-2, rgba(255,255,255,.06)); color:var(--text-dim); border-color:var(--border-soft);}
/* 남의 공유본 카드 — 테두리로만 구분(레이아웃은 그대로) */
.comfy-ov-card.gal-readonly{border-style:dashed;}
.comfy-ov-del svg{width:15px; height:15px;}
.comfy-ov-more{display:flex; justify-content:center; margin-top:18px;}
.comfy-ov-meta{padding:10px 12px; display:flex; flex-direction:column; gap:3px; min-width:0;}
.comfy-ov-cardtitle{font-size:13px; font-weight:600; color:var(--text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.comfy-ov-cardsub{font-size:11px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}

/* ── 갤러리 개편: 작품 > 씬 > 분할 폴더 + 개인화 보기 ─────────────────────────
   레이아웃 철칙(재배치 금지): 툴바·폴더 헤더는 화면이 좁아져도 줄바꿈/세로 접힘
   없이 가로 스크롤로 처리한다(white-space:nowrap + overflow-x:auto). 색·모서리는
   전부 기존 토큰(--surface/--border-soft/--accent/--radius…)만 쓴다. */
.gal-toolbar{margin-bottom:14px; overflow-x:auto; overflow-y:hidden;}
.gal-toolbar-inner{display:inline-flex; align-items:center; gap:8px; white-space:nowrap; padding-bottom:2px;}
.gal-tlabel{font-size:11.5px; font-weight:600; color:var(--text-faint); letter-spacing:.02em;}
.gal-tsep{display:inline-block; width:1px; height:18px; background:var(--border-soft); margin:0 2px;}
.gal-tbtn{
  background:transparent; border:1px solid var(--border-soft); color:var(--text-dim);
  border-radius:var(--radius-sm); padding:6px 13px; font-size:12.5px; cursor:pointer; white-space:nowrap;
}
.gal-tbtn:hover{border-color:var(--accent); color:var(--text);}
.gal-tbtn.active{border-color:var(--accent); color:var(--text); background:var(--accent-soft);}
.gal-tbtn.accent{border-color:var(--accent); color:var(--accent);}
.gal-tbtn.accent:hover{background:var(--accent); color:var(--on-accent);}
.gal-seg{display:inline-flex; border:1px solid var(--border-soft); border-radius:var(--radius-sm); overflow:hidden;}
.gal-segbtn{
  background:transparent; border:0; border-right:1px solid var(--border-soft); color:var(--text-dim);
  padding:6px 12px; font-size:12.5px; cursor:pointer; white-space:nowrap;
}
.gal-segbtn:last-child{border-right:0;}
.gal-segbtn:hover{color:var(--text);}
.gal-segbtn.active{background:var(--accent); color:var(--on-accent); font-weight:600;}
.gal-tselect{
  background:var(--surface); border:1px solid var(--border-soft); color:var(--text);
  border-radius:var(--radius-sm); padding:6px 10px; font-size:12.5px; cursor:pointer;
}
.gal-tselect:focus{outline:none; border-color:var(--accent);}

/* 선택 바 — 자리(높이)는 항상 차지하고 내용만 visibility 로 숨긴다.
   display 토글이었을 땐 선택할 때마다 바가 생기며 화면이 아래로 밀려 눈이 아팠다. */
.gal-selbar{
  display:flex; align-items:center; gap:10px; white-space:nowrap; overflow-x:auto;
  background:var(--accent-soft); border:1px solid var(--accent); border-radius:var(--radius-sm);
  padding:8px 12px; margin-bottom:14px;
  visibility:hidden;
}
.gal-selbar.on{visibility:visible;}
.gal-selcount{font-size:12.5px; font-weight:600; color:var(--text);}

.gal-layout{display:block;}
.gal-ibtn{
  background:transparent; border:1px solid var(--border-soft); color:var(--text-dim);
  border-radius:var(--radius-sm); padding:4px 10px; font-size:11.5px; cursor:pointer; white-space:nowrap;
}
.gal-ibtn:hover{border-color:var(--accent); color:var(--text);}
.gal-ibtn.danger:hover{border-color:var(--danger); color:var(--danger);}

/* ── 폴더 카드 그리드(1층: 작품 / 2층: 씬) ───────────────────────────────────
   대표 이미지가 보이는 "폴더" 실루엣 — 위쪽에 탭이 살짝 튀어나온 형태를 CSS로만
   만든다. 그리드는 auto-fill minmax 라 좁아지면 줄 수가 줄 뿐, 카드 내부 구성은
   어떤 폭에서도 재배치되지 않는다(재배치 금지 원칙). */
/* 폴더 카드 그리드도 크기 설정(작게~아주 크게)을 따른다 — 1층·모달 공통.
   (예전엔 결과 카드 그리드만 크기가 바뀌고 폴더 카드는 210px 고정이었다.) */
.gal-fgrid{display:grid; grid-template-columns:repeat(auto-fill, minmax(210px, 1fr)); gap:16px; align-items:start;}
.gal-size-s .gal-fgrid{grid-template-columns:repeat(auto-fill, minmax(150px, 1fr)); gap:12px;}
.gal-size-m .gal-fgrid{grid-template-columns:repeat(auto-fill, minmax(210px, 1fr)); gap:16px;}
.gal-size-l .gal-fgrid{grid-template-columns:repeat(auto-fill, minmax(290px, 1fr)); gap:18px;}
.gal-size-xl .gal-fgrid{grid-template-columns:repeat(auto-fill, minmax(390px, 1fr)); gap:20px;}
.gal-fcard{position:relative; padding-top:13px; cursor:pointer; outline:none;}
.gal-fcard-tab{
  position:absolute; left:0; top:0; width:46%; height:14px;
  background:var(--accent); border:none;
  border-radius:10px 10px 0 0;
}
/* 폴더 본체 — 탭·표지(이미지 영역)는 라임, 제목·설명 헤더는 기존 색(사용자 지시
   2026-07-28: "배경과 구분이 안 된다", 점선 금지). 표지에 이미지가 있으면 이미지가
   덮고, 라임은 탭·테두리·빈 표지에서 보인다. */
.gal-fcard-body{
  position:relative; background:var(--surface); border:2px solid var(--accent);
  border-radius:0 var(--radius) var(--radius) var(--radius); overflow:hidden;
  box-shadow:var(--shadow, 0 2px 10px rgba(0,0,0,.18)); transition:filter .12s ease, transform .12s ease;
}
.gal-fcard:hover .gal-fcard-body, .gal-fcard:focus-visible .gal-fcard-body{filter:brightness(1.08); transform:translateY(-1px);}
.gal-fcard-cover{position:relative; width:100%; aspect-ratio:4/3; background:color-mix(in srgb, var(--accent) 82%, #000); display:flex; align-items:center; justify-content:center; overflow:hidden;}
.gal-fcard-img{width:100%; height:100%; object-fit:cover; display:block; -webkit-user-drag:none;}
.gal-fcard-glyph{display:flex; align-items:center; justify-content:center; color:rgba(0,0,0,.55);}
.gal-fcard-glyph svg{width:44px; height:44px;}
.gal-fcard-plus{font-size:34px; line-height:1; color:rgba(0,0,0,.6); font-weight:400;}
.gal-fcard-count{
  position:absolute; right:8px; bottom:8px; background:rgba(0,0,0,.66); color:#fff;
  font-size:11px; padding:2px 8px; border-radius:999px;
}
.gal-fcard-info{padding:9px 11px 11px; display:flex; flex-direction:column; gap:3px; min-width:0;}
.gal-fcard-name{
  display:flex; align-items:center; gap:7px;
  font-size:13.5px; font-weight:700; color:var(--text);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.gal-fcard-num{color:var(--accent);}
.gal-fcard-desc{font-size:11.5px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.gal-fcard-acts{
  position:absolute; right:6px; top:19px; z-index:3; display:flex; gap:4px;
  opacity:0; transition:opacity .12s ease;
}
.gal-fcard:hover .gal-fcard-acts, .gal-fcard:focus-within .gal-fcard-acts{opacity:1;}
.gal-fact{
  background:rgba(0,0,0,.62); border:1px solid transparent; color:#fff;
  border-radius:var(--radius-sm); padding:3px 8px; font-size:11px; cursor:pointer; white-space:nowrap;
}
.gal-fact:hover{background:var(--accent); color:var(--on-accent);}
.gal-fact.danger:hover{background:var(--danger); color:#fff;}
/* 카드 크기 "작게"(폴더 카드 최소 150px)에서 액션 버튼 수가 늘어도 한 줄에 들어가게
   여백만 좁힌다 — 줄바꿈/세로 접힘은 금지(레이아웃 철칙). 공유 버튼이 추가되면서
   씬 카드는 최대 5개(공유·분할+·분할−·✎·✕)가 된다. */
.gal-size-s .gal-fact{padding:3px 6px; font-size:10.5px;}
/* (새 작품 폴더 카드 제거 — 툴바 [+ 새 작품] 버튼으로 대체. 점선 규칙도 함께 삭제) */
.gal-fcard-new .gal-fcard-cover{background:transparent;}

/* ── 폴더 모달(2층) ──────────────────────────────────────────────────────────
   z-index 900 — 결과 상세 라이트박스(1000)와 폼/확인 모달(1200)이 이 위에 뜬다.
   배경 클릭으로는 닫지 않는다(✕ / Esc 만). */
.gal-modal-overlay{
  position:fixed; inset:0; z-index:2100; display:flex; align-items:center; justify-content:center;   /* 모달 밴드 */
  background:color-mix(in srgb, var(--bg) 55%, black 45%); padding:3vh 3vw;
  animation:comfyModalFade .12s ease-out;
}
html[data-theme="day"] .gal-modal-overlay{background:color-mix(in srgb, var(--bg) 72%, black 28%);}
.gal-modal-box{
  position:relative; width:min(1320px, 96vw); max-height:94vh; display:flex; flex-direction:column;
  background:var(--bg-elev, var(--surface)); border:1px solid var(--border-soft);
  border-radius:var(--radius-md, 14px); box-shadow:0 18px 60px rgba(0,0,0,.5); overflow:hidden;
}
.gal-modal-head{
  display:flex; align-items:center; gap:12px; white-space:nowrap; overflow-x:auto; overflow-y:hidden;
  padding:16px 54px 14px 20px; border-bottom:1px solid var(--border-soft);
}
.gal-modal-titles{min-width:0; flex:1 0 auto;}
.gal-modal-name{font-size:16px; font-weight:700; color:var(--text); white-space:nowrap;}
.gal-modal-num{color:var(--accent);}
.gal-modal-count{font-size:12px; font-weight:400;}
.gal-modal-desc{font-size:12px; margin-top:3px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.gal-modal-acts{display:flex; align-items:center; gap:6px; white-space:nowrap;}
.gal-modal-back{
  flex:0 0 auto; width:30px; height:30px; border-radius:50%; border:1px solid var(--border-soft);
  background:var(--surface); color:var(--text); font-size:15px; line-height:1; cursor:pointer;
}
.gal-modal-back:hover{border-color:var(--accent); color:var(--accent);}
.gal-modal-close{
  position:absolute; top:14px; right:14px; z-index:4; width:32px; height:32px; border-radius:50%;
  border:1px solid var(--border-soft); background:var(--surface); color:var(--text); font-size:15px; cursor:pointer;
}
.gal-modal-close:hover{border-color:var(--accent); color:var(--accent);}
.gal-modal-box .gal-selbar{margin:12px 20px 0;}
.gal-modal-body{padding:16px 20px 20px; overflow:auto; min-height:0;}
.gal-modal-section{margin-bottom:20px;}
.gal-modal-section:last-child{margin-bottom:0;}
.gal-modal-sechead{
  display:flex; align-items:center; gap:8px; white-space:nowrap; overflow-x:auto;
  font-size:12px; font-weight:700; color:var(--text-faint); letter-spacing:.03em; margin-bottom:9px;
}
.gal-scene-splitbadge{
  font-size:10.5px; color:var(--text-dim); border:1px solid var(--border-soft);
  border-radius:999px; padding:1px 8px;
}
.gal-split{margin:0 0 12px 0; padding-left:10px; border-left:2px solid var(--border-soft);}
.gal-split-head{display:flex; align-items:center; gap:8px; white-space:nowrap; overflow-x:auto; margin-bottom:7px;}
.gal-split-name{font-size:12px; font-weight:600; color:var(--text-dim);}
.gal-split-count{font-size:11px;}
.gal-empty{font-size:12.5px; padding:10px 2px;}

/* 카드 크기 4단계 — grid의 minmax만 바꾼다(카드 내부 구조는 그대로). */
.gal-size-s .comfy-ov-grid{grid-template-columns:repeat(auto-fill, minmax(130px, 1fr)); gap:11px;}
.gal-size-m .comfy-ov-grid{grid-template-columns:repeat(auto-fill, minmax(200px, 1fr)); gap:16px;}
.gal-size-l .comfy-ov-grid{grid-template-columns:repeat(auto-fill, minmax(290px, 1fr)); gap:18px;}
.gal-size-xl .comfy-ov-grid{grid-template-columns:repeat(auto-fill, minmax(400px, 1fr)); gap:20px;}
/* 보기 모드 — 간단히는 썸네일+제목만. */
.gal-mode-simple .comfy-ov-cardsub{display:none;}
.gal-mode-simple .comfy-ov-meta{padding:7px 10px;}

/* 카드 다중 선택 체크박스 */
.comfy-ov-pick{
  position:absolute; left:6px; bottom:6px; z-index:3; display:flex; align-items:center; justify-content:center;
  width:22px; height:22px; border-radius:6px; background:rgba(0,0,0,.5); cursor:pointer;
  opacity:0; transition:opacity .12s ease;
}
.comfy-ov-card:hover .comfy-ov-pick, .comfy-ov-card.selected .comfy-ov-pick{opacity:1;}
.comfy-ov-pick input{margin:0; cursor:pointer; accent-color:var(--accent);}
.comfy-ov-card.selected{border-color:var(--accent); box-shadow:0 0 0 1px var(--accent) inset;}

/* 숨긴 작품 관리 모달 */
.gal-hidden-list{display:flex; flex-direction:column; gap:7px; margin-top:14px; max-height:44vh; overflow-y:auto;}
.gal-hidden-row{
  display:flex; align-items:center; justify-content:space-between; gap:10px;
  border:1px solid var(--border-soft); border-radius:var(--radius-sm); padding:8px 11px;
}
.gal-hidden-name{font-size:12.5px; color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}

/* 실행 폼의 "갤러리 분류(선택)" 한 줄 */
/* 갤러리 분류 줄 — 라임 테두리로 강조(사용자 지시 2026-07-28). 어디로 분류돼
   들어가는지가 중요한 정보라 다른 접이식 줄과 구분되게 한다. */
.comfy-gen-gallery{margin-top:10px; border:1px solid color-mix(in srgb, var(--accent) 55%, transparent);
  border-radius:var(--radius-sm); padding:8px 11px;
  box-shadow:0 0 0 1px color-mix(in srgb, var(--accent) 14%, transparent) inset;}
.comfy-gen-gallery .comfy-gen-gallery-summary{color:var(--text);}
.comfy-gen-gallery .comfy-gen-gallery-summary::before{color:var(--accent);}
.comfy-gen-gallery .comfy-gen-gallery-cur{color:var(--accent);}
.comfy-gen-gallery-summary{
  cursor:pointer; font-size:12.5px; font-weight:600; color:var(--text-dim);
  list-style:none; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.comfy-gen-gallery-summary::-webkit-details-marker{display:none;}
.comfy-gen-gallery-summary::before{content:'▸ '; color:var(--text-faint);}
.comfy-gen-gallery[open] .comfy-gen-gallery-summary::before{content:'▾ ';}
.comfy-gen-gallery-cur{font-size:11.5px; font-weight:400; margin-left:6px;}
.comfy-gen-gallery-row{
  display:flex; align-items:center; gap:8px; margin-top:9px;
  white-space:nowrap; overflow-x:auto; overflow-y:hidden;
}
/* .comfy-gen-input 은 width:100% 라, 가로 한 줄에 나란히 두려면 여기서 되돌린다. */
.comfy-gen-gal-sel{width:auto; min-width:150px; max-width:260px; flex:0 0 auto; padding:7px 10px; font-size:12.5px;}
.comfy-gen-gal-refresh{flex:0 0 auto; font-size:12px; padding:6px 12px;}
.comfy-gen-gal-hint{font-size:12px;}
.comfy-gen-card-desc{font-size:12px; color:var(--text-dim); margin-top:3px;}
.comfy-gen-actions{display:flex; align-items:center; gap:12px; margin-top:6px; flex-wrap:wrap;}
.comfy-gen-repeat{display:flex; align-items:center; gap:6px;}
.comfy-gen-repeat label{font-size:12px; color:var(--text-dim); font-weight:600;}
.comfy-gen-repeat-input{
  width:56px; background:var(--surface); border:1px solid var(--border-soft); color:var(--text);
  border-radius:var(--radius-sm); padding:7px 8px; font-size:13px; text-align:center;
}
/* 동적 LoRA 슬롯(lora_add): (1) 관리자 필드 설정의 "Add Lora" 잔재 활성화 패널,
   (2) 생성 화면의 "+ LoRA 추가" UI. 기존 comfy-gen/comfy-af 패턴과 같은 토큰
   (surface/border-soft/accent/radius)만 재사용해 새 디자인 언어를 만들지 않는다. */
.comfy-af-loraadd-cfg{display:none; flex-direction:column; gap:8px; margin-top:8px;}
.comfy-af-input.comfy-af-loraadd-row.on .comfy-af-loraadd-cfg{display:flex;}
.comfy-af-loraadd-note{font-size:11.5px; line-height:1.5;}
.comfy-af-loraadd-cfg input[type=text], .comfy-af-loraadd-cfg input[type=number]{
  background:var(--surface); border:1px solid var(--border-soft); color:var(--text);
  border-radius:var(--radius-sm); padding:7px 9px; font-size:12.5px; font-family:inherit;
}
.comfy-af-loraadd-cfg input:focus{outline:none; border-color:var(--accent);}
.comfy-af-loraadd-max{display:flex; align-items:center; gap:8px; font-size:12px; color:var(--text-dim);}
.comfy-af-loraadd-max input{width:80px; text-align:center;}
.comfy-gen-lora-add{display:flex; flex-direction:column; gap:8px;}
.comfy-gen-lora-add-head{font-size:12px; color:var(--text-dim); font-weight:600;}
.comfy-gen-lora-add-list{display:flex; flex-direction:column; gap:8px;}
.comfy-gen-lora-add-row{display:flex; align-items:center; gap:10px; flex-wrap:wrap; padding:8px; background:var(--surface); border:1px solid var(--border-soft); border-radius:var(--radius-sm);}
.comfy-gen-lora-add-toggle{display:flex; align-items:center; gap:6px; font-size:12px; color:var(--text-dim); white-space:nowrap;}
.comfy-gen-lora-add-model{flex:1 1 180px; min-width:140px; background:var(--surface); border:1px solid var(--border-soft); color:var(--text); border-radius:var(--radius-sm); padding:7px 9px; font-size:12.5px; font-family:inherit;}
.comfy-gen-lora-add-model:focus{outline:none; border-color:var(--accent);}
.comfy-gen-lora-add-strwrap{display:flex; align-items:center; gap:8px; flex:1 1 200px;}
.comfy-gen-lora-add-str{flex:1 1 auto; accent-color:var(--accent); cursor:pointer;}
.comfy-gen-lora-add-strnum{flex:0 0 70px; width:70px; background:var(--surface); border:1px solid var(--border-soft); color:var(--text); border-radius:var(--radius-sm); padding:6px 8px; font-size:12.5px; text-align:right;}
.comfy-gen-lora-add-strnum:focus{outline:none; border-color:var(--accent);}
.comfy-gen-lora-add-del{background:transparent; border:1px solid var(--border-soft); color:var(--text-dim); border-radius:var(--radius-sm); padding:4px 9px; cursor:pointer; font-size:13px; line-height:1;}
.comfy-gen-lora-add-del:hover{border-color:var(--accent); color:var(--text);}
.comfy-gen-lora-add-btn{align-self:flex-start;}
.comfy-gen-status{font-size:12.5px; color:var(--text-dim);}
.comfy-gen-status-run{color:#6ab0ff;}
.comfy-gen-status-done{color:#4caf6d;}
.comfy-gen-status-err{color:var(--danger);}
/* B) 결과 영역: 이미지=썸네일 그리드(클릭 확대), 영상/오디오=인라인 플레이어,
   텍스트=선택 가능한 블록. 출력 칼럼 안에서 자동으로 채워진다. */
.comfy-gen-results{display:grid; grid-template-columns:repeat(auto-fill, minmax(150px, 1fr)); gap:12px; margin-top:0;}
.comfy-gen-results .comfy-gen-output-empty{grid-column:1 / -1;}
.comfy-gen-result{width:100%; border-radius:var(--radius-sm); border:1px solid var(--border-soft);}
.comfy-gen-result-wrap{position:relative; display:block; width:100%;}
.comfy-gen-result-wrap .comfy-gen-result{display:block;}
.comfy-gen-result-media{width:100%; height:auto; background:var(--surface);}
img.comfy-gen-result-media{aspect-ratio:1 / 1; object-fit:cover;}
video.comfy-gen-result-media{aspect-ratio:auto;}
/* 영상/오디오/텍스트는 한 줄을 다 쓰게(작은 썸네일로 뭉개지지 않도록). */
.comfy-gen-result-wrap:has(video.comfy-gen-result-media){grid-column:1 / -1;}
.comfy-gen-result-textwrap, .comfy-gen-result-audiowrap{grid-column:1 / -1;}
.comfy-gen-text{margin:0; padding:12px 14px; background:var(--surface); border:1px solid var(--border-soft); border-radius:var(--radius-sm); font-size:13px; line-height:1.6; color:var(--text); white-space:pre-wrap; word-break:break-word; user-select:text; max-height:340px; overflow:auto;}
.comfy-gen-result-audio{width:100%;}
.comfy-gen-result-fn{font-size:11px; margin-top:4px;}

/* 생성 정보 패널(프롬프트/시드/샘플러/스케줄러/CFG/스텝) — 결과 그리드 아래
   전체 폭으로 붙는다. */
.comfy-gen-meta{grid-column:1 / -1; margin-top:6px; border:1px solid var(--border-soft); border-radius:var(--radius-sm); padding:10px 14px; background:var(--surface); display:flex; flex-direction:column; gap:6px;}
.comfy-gen-meta-row{display:flex; gap:10px; align-items:flex-start; font-size:12px; line-height:1.5;}
.comfy-gen-meta-k{flex:0 0 64px; color:var(--text-dim); font-weight:600;}
.comfy-gen-meta-v{flex:1; color:var(--text); word-break:break-word; user-select:text;}
.comfy-gen-meta-vlong{white-space:pre-wrap;}

/* 결과 이미지 확대 라이트박스(BytePlus 갤러리와 독립).
   배경을 고정 rgba(0,0,0,.82)로 두면 나이트 모드에선 적당해도 데이 모드에서
   갑자기 화면이 확 어두워져 눈이 아프다는 피드백 — 기존 .lightbox-overlay와
   동일하게 var(--bg) 기준 color-mix로 테마에 맞춰 밝기를 조절한다. */
/* 결과 상세(라이트박스) — 상세 라이트박스 밴드. 작업 드로어(1010)·생성 오버레이(2000)
   위에 떠야 하므로 3020. 이전에는 1000 이라 드로어/생성 오버레이에 가려졌다. */
.comfy-lightbox-overlay{position:fixed; inset:0; z-index:3020; display:none; align-items:center; justify-content:center; background:color-mix(in srgb, var(--bg) 40%, black 60%); padding:4vh 4vw; cursor:zoom-out;}
html[data-theme="day"] .comfy-lightbox-overlay{background:color-mix(in srgb, var(--bg) 62%, black 38%);}
.comfy-lightbox-overlay.show{display:flex;}
.comfy-lightbox-overlay img{max-width:100%; max-height:92vh; border-radius:var(--radius-sm); box-shadow:0 8px 40px rgba(0,0,0,.5);}

/* 개요 카드 상세보기 — 전체 결과물(이미지/영상/오디오/텍스트) + 생성 정보 패널. */
.comfy-ov-detail{position:relative; width:min(1180px, 96vw); max-height:94vh; overflow:auto; background:var(--bg-elev, var(--surface)); border:1px solid var(--border-soft); border-radius:var(--radius-md); padding:24px; cursor:default;}
.comfy-ov-detail-close{position:absolute; top:12px; right:12px; width:32px; height:32px; border-radius:50%; border:1px solid var(--border-soft); background:var(--surface); color:var(--text); font-size:15px; cursor:pointer; z-index:6;}
.comfy-ov-detail-title{font-size:16px; font-weight:700; padding-right:40px;}
.comfy-ov-detail-sub{font-size:12px; margin-top:4px; margin-bottom:16px;}
/* 결과 상세 — 좌: 미디어 캐러셀 / 우: 파일정보+생성파라미터. 예전엔 이미지 한
   장을 화면 중앙에 띄우기만 해서 세로로 긴 이미지일 때 좌우가 텅 비어 보인다는
   피드백이 있었다 — 정보 패널로 그 공간을 채우고, 여러 출력물은 슬라이드처럼
   넘겨보게 했다. */
.comfy-ov-detail-body{display:flex; gap:22px; align-items:flex-start; flex-wrap:wrap;}
.comfy-ov-media-col{flex:1 1 460px; max-width:620px; display:flex; flex-direction:column; gap:10px;}
/* 고정 높이(vh) — 예전엔 콘텐츠(이미지/영상)의 실제 크기에 맞춰 스테이지가
   늘었다 줄었다 해서, 캐러셀에서 다음 슬라이드로 넘길 때마다 모달 전체 높이가
   출렁이는 문제가 있었다("카드를 넘기면 UI 크기가 달라짐"). 슬라이드가 뭐든
   스테이지 자체는 항상 같은 높이를 유지하고, 그 안에서 object-fit:contain으로
   비율만 맞춰 레터박스 처리한다. */
.comfy-ov-media-stage{position:relative; display:flex; align-items:center; justify-content:center; background:#000; border-radius:var(--radius-sm); overflow:hidden; height:56vh;}
/* 카드 넘기기(comfyOvNav/Goto)는 모달 전체가 아니라 이 래퍼의 innerHTML만
   교체한다 — 예전엔 넘길 때마다 모달 전체를 다시 그려서 깜빡였다(flicker). */
.comfy-ov-media-wrap{width:100%; height:100%; display:flex; align-items:center; justify-content:center;}
.comfy-ov-detail-media{width:100%; height:100%; object-fit:contain; display:block; background:#000;}
.comfy-ov-media-count{position:absolute; top:10px; left:10px; z-index:5; background:rgba(0,0,0,.62); color:#fff; font-size:11.5px; font-weight:700; padding:4px 9px; border-radius:999px;}
.comfy-ov-nav{position:absolute; top:50%; transform:translateY(-50%); z-index:5; width:34px; height:34px; border-radius:50%; border:none; background:rgba(0,0,0,.55); color:#fff; font-size:19px; line-height:1; cursor:pointer; display:flex; align-items:center; justify-content:center;}
.comfy-ov-nav:hover{background:var(--accent); color:var(--on-accent);}
.comfy-ov-nav-prev{left:8px;}
.comfy-ov-nav-next{right:8px;}
/* 결과 간(작업 간) 이전/다음 전환(Task 105) — 모달 창 바깥 좌/우, 라임(--accent)색,
   세로 중앙. 캐러셀 내부 ‹ ›(.comfy-ov-nav)와 구분되는 큼직한 버튼. */
.comfy-ov-jobnav{position:fixed; top:50%; transform:translateY(-50%); z-index:3030; width:54px; height:54px; border-radius:50%; border:1px solid color-mix(in srgb, var(--accent) 55%, transparent); background:color-mix(in srgb, var(--accent) 16%, transparent); color:var(--accent); font-size:34px; line-height:1; cursor:pointer; display:flex; align-items:center; justify-content:center; transition:background .12s, color .12s, opacity .12s;}
.comfy-ov-jobnav:hover{background:var(--accent); color:var(--on-accent);}
.comfy-ov-jobnav:disabled{opacity:.3; cursor:default; pointer-events:none;}
/* 화면 양 끝이 아니라 "상세창 바로 옆"에 붙인다. 넓은 모니터에서 뷰포트 가장자리에
   고정하면 모달과 수백 px 떨어져 손이 멀고 시선도 분리된다.
   상세창 폭(.comfy-ov-detail = min(1180px,96vw))을 그대로 써서 그 바깥 여백을 계산하고,
   여백이 모자라면 max() 로 최소 간격만 남겨 화면 밖으로 나가지 않게 한다. */
.comfy-ov-jobnav{--ovw:min(1180px, 96vw); --ovbtn:54px;
  --ovgap:max(6px, calc(50vw - var(--ovw) / 2 - var(--ovbtn) - 12px));}
.comfy-ov-jobnav-prev{left:var(--ovgap);}
.comfy-ov-jobnav-next{right:var(--ovgap);}
@media (max-width:768px){
  .comfy-ov-jobnav{width:40px; height:40px; font-size:26px; --ovbtn:40px;}
  .comfy-ov-jobnav-prev{left:max(4px, var(--ovgap));}
  .comfy-ov-jobnav-next{right:max(4px, var(--ovgap));}
}
.comfy-ov-dl-current{position:absolute; top:10px; right:10px; z-index:5; width:30px; height:30px; border-radius:50%; border:none; background:rgba(0,0,0,.62); color:#fff; cursor:pointer; display:flex; align-items:center; justify-content:center;}
.comfy-ov-dl-current svg{width:16px; height:16px;}
.comfy-ov-dl-current:hover{background:var(--accent); color:var(--on-accent);}
.comfy-ov-share-current{right:48px;}
/* 갤러리 공유 토글(상세) — 메신저 공유 버튼 왼쪽. 공유 중이면 라임 고정. */
.comfy-ov-galshare{right:86px;}
.comfy-ov-galshare.on{background:var(--accent); color:var(--on-accent);}
.comfy-ov-dl-all{display:flex; align-items:center; justify-content:center; gap:6px; width:100%; margin-top:8px; padding:8px 10px; border-radius:var(--radius-sm); border:1px solid var(--border); background:var(--surface); color:var(--text); font-size:12.5px; font-weight:600; cursor:pointer;}
.comfy-ov-dl-all svg{width:14px; height:14px;}
.comfy-ov-dl-all:hover{border-color:var(--accent); color:var(--accent);}
.comfy-ov-thumbstrip{display:flex; gap:8px; overflow-x:auto; padding-bottom:2px;}
/* 이름 주의: 기존 .comfy-ov-thumb는 "최근 생성물" 그리드 카드의 큰 정사각형
   썸네일(width:100%;height:100%)에 이미 쓰이고 있었다 — 캐러셀 썸네일에 같은
   클래스명을 재사용했다가 카드 위주 그리드까지 54x54로 쪼그라드는 회귀가
   있었다. 별도 클래스(comfy-ov-carousel-thumb)로 분리해 재발을 막는다. */
.comfy-ov-carousel-thumb{position:relative; flex:0 0 auto; width:54px; height:54px; border-radius:8px; overflow:hidden; cursor:pointer; border:2px solid transparent; background:var(--surface);}
.comfy-ov-carousel-thumb.active{border-color:var(--accent);}
.comfy-ov-carousel-thumb img, .comfy-ov-carousel-thumb video{width:100%; height:100%; object-fit:cover; display:block;}
.comfy-ov-thumb-play{position:absolute; inset:0; display:flex; align-items:center; justify-content:center; color:#fff; font-size:12px; background:rgba(0,0,0,.28);}
.comfy-ov-thumb-generic{display:flex; align-items:center; justify-content:center; width:100%; height:100%; font-size:18px; color:var(--text-faint);}

.comfy-ov-info-col{flex:1 1 300px; min-width:270px; display:flex; flex-direction:column; gap:14px;}
.comfy-ov-info-section{border:1px solid var(--border-soft); border-radius:var(--radius-sm); overflow:hidden;}
.comfy-ov-info-heading{font-size:11px; font-weight:700; letter-spacing:.03em; color:var(--text-faint); text-transform:uppercase; padding:8px 12px; background:var(--surface);}
.comfy-ov-info-row{display:flex; align-items:flex-start; gap:8px; padding:8px 12px; border-top:1px solid var(--border-soft); font-size:12.5px;}
.comfy-ov-info-section .comfy-ov-info-row:first-of-type{border-top:none;}
.comfy-ov-info-k{flex:0 0 74px; color:var(--text-faint); font-weight:600;}
.comfy-ov-info-v{flex:1; word-break:break-word;}
.comfy-ov-info-row-long{flex-direction:column; align-items:stretch; gap:4px;}
.comfy-ov-info-row-long-head{display:flex; align-items:center; justify-content:space-between;}
.comfy-ov-info-v-long{white-space:pre-wrap; word-break:break-word; line-height:1.5; color:var(--text);}
.comfy-ov-info-copy{flex:0 0 auto; width:22px; height:22px; border:none; background:transparent; color:var(--text-faint); cursor:pointer; display:flex; align-items:center; justify-content:center; border-radius:6px;}
.comfy-ov-info-copy:hover{color:var(--accent); background:var(--surface);}
.comfy-ov-info-copy svg{width:14px; height:14px;}

/* 입력 소스 섹션(결과 상세) — 제출 시 만든 입력 썸네일들을 라벨과 함께 보여준다. */
.comfy-ov-inputs{display:grid; grid-template-columns:repeat(auto-fill, minmax(84px, 1fr)); gap:10px; padding:10px 12px;}
.comfy-ov-input{margin:0; display:flex; flex-direction:column; gap:4px; min-width:0;}
.comfy-ov-input-imgwrap{position:relative; width:100%; aspect-ratio:1/1; border-radius:var(--radius-sm); overflow:hidden; background:var(--surface); border:1px solid var(--border-soft); cursor:zoom-in;}
.comfy-ov-input-imgwrap img{width:100%; height:100%; object-fit:cover; display:block;}
.comfy-ov-input-kind{position:absolute; left:4px; bottom:4px; font-size:9.5px; font-weight:700; line-height:1; padding:2px 5px; border-radius:5px; background:rgba(0,0,0,.6); color:#fff;}
/* 확대 가능함을 알리는 돋보기 힌트(우상단, 호버 시 표시) — #162 */
.comfy-ov-input-zoomhint{position:absolute; right:4px; top:4px; font-size:11px; line-height:1; padding:2px 5px; border-radius:5px; background:rgba(0,0,0,.55); color:#fff; opacity:0; transition:opacity .15s; pointer-events:none;}
.comfy-ov-input-imgwrap:hover .comfy-ov-input-zoomhint{opacity:1;}
.comfy-ov-input figcaption{font-size:11px; color:var(--text-faint); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}

/* 입력 이미지 확대 오버레이(#162) — 결과 상세 라이트박스(z-index:3020)보다 위(3100). */
.comfy-inputzoom-overlay{position:fixed; inset:0; z-index:3100; display:none; align-items:center; justify-content:center; background:color-mix(in srgb, var(--bg) 30%, black 70%); padding:5vh 5vw;}
.comfy-inputzoom-overlay.show{display:flex;}
.comfy-inputzoom-inner{position:relative; max-width:92vw; max-height:90vh; display:flex; flex-direction:column; align-items:center; gap:12px;}
.comfy-inputzoom-inner img{max-width:92vw; max-height:78vh; object-fit:contain; border-radius:var(--radius-sm); background:var(--surface); box-shadow:0 12px 48px rgba(0,0,0,.5);}
.comfy-inputzoom-close{position:absolute; top:-14px; right:-14px; width:34px; height:34px; border-radius:50%; border:1px solid var(--border-soft); background:var(--bg-elev); color:var(--text); font-size:15px; line-height:1; cursor:pointer; box-shadow:0 4px 14px rgba(0,0,0,.4);}
.comfy-inputzoom-close:hover{color:var(--danger);}
.comfy-inputzoom-nav{position:absolute; top:50%; transform:translateY(-50%); width:40px; height:40px; border-radius:50%; border:none; background:rgba(0,0,0,.5); color:#fff; font-size:22px; line-height:1; cursor:pointer; display:flex; align-items:center; justify-content:center;}
.comfy-inputzoom-nav:hover{background:rgba(0,0,0,.72);}
.comfy-inputzoom-nav.prev{left:-14px;}
.comfy-inputzoom-nav.next{right:-14px;}
.comfy-inputzoom-caption{font-size:.82rem; color:#fff; text-align:center; max-width:92vw; line-height:1.4;}
.comfy-inputzoom-kind{font-weight:700; background:rgba(255,255,255,.18); padding:2px 7px; border-radius:6px; margin-right:6px;}
.comfy-inputzoom-idx{color:rgba(255,255,255,.7);}
.comfy-inputzoom-note{color:#ffe9b0; opacity:.9; font-size:.74rem;}

/* 작업 카드의 입력 소스 미니 썸네일(과하지 않게 — 작게). */
/* (제거됨) 카드 내 입력 소스 미니 썸네일 — 상세에서만 표시(2026-07-29) */

/* C) 목록 검색 툴바 + 내부 스크롤 컨테이너 (워크플로우가 수십·수백 개여도 안정). */
.comfy-list-toolbar{margin-bottom:12px; display:flex; align-items:center; gap:8px; flex-wrap:wrap;}
.comfy-list-search{
  width:100%; max-width:340px; background:var(--surface); border:1px solid var(--border-soft);
  color:var(--text); border-radius:var(--radius-sm); padding:9px 12px; font-size:13px; font-family:inherit;
}
.comfy-list-search:focus{outline:none; border-color:var(--accent);}
.comfy-list-sort{
  background:var(--surface); border:1px solid var(--border-soft); color:var(--text);
  border-radius:var(--radius-sm); padding:9px 10px; font-size:13px; font-family:inherit; cursor:pointer;
}
.comfy-list-sort:focus{outline:none; border-color:var(--accent);}
/* 워크플로우 카드 즐겨찾기(좋아요) 별 토글 — 정렬 기준(좋아요순)을 카드에서 바로 조작. */
.comfy-wf-favbtn{
  display:flex; flex-direction:column; align-items:center; gap:1px; flex:0 0 auto; order:2;
  background:none; border:none; color:var(--text-faint); cursor:pointer; padding:2px 4px;
}
.comfy-wf-favbtn svg{width:15px; height:15px;}
.comfy-wf-favbtn.on{color:var(--accent);}
.comfy-wf-favbtn:hover{color:var(--accent);}
.comfy-wf-favcount{font-size:9.5px; line-height:1;}
.comfy-scroll-list{max-height:56vh; overflow-y:auto; padding-right:2px;}
.comfy-scroll-list.comfy-gen-list{max-height:52vh;}

/* ── 설치된 모델 브라우징 뷰 (동기화 서버 모델 라이브러리) ── */
.comfy-models-toolbar{display:flex; align-items:center; gap:14px; flex-wrap:wrap; margin-bottom:12px;}
.comfy-models-hint{font-size:12px; line-height:1.5; margin:0 0 12px;}
.comfy-models-serverrow{display:flex; align-items:center; gap:8px;}
.comfy-models-serverlabel{font-size:12px; color:var(--text-dim); font-weight:600;}
.comfy-models-server{
  background:var(--surface); border:1px solid var(--border-soft); color:var(--text);
  border-radius:var(--radius-sm); padding:8px 11px; font-size:13px; font-family:inherit; min-width:220px; max-width:360px;
}
.comfy-models-server:focus{outline:none; border-color:var(--accent);}
.comfy-models-search{max-width:280px; margin-left:auto;}

/* ===== ComfyUI 오류 수집(관리) — 앱 토큰 재사용, PC/모바일 공용 ===== */
.ce-summary{background:var(--surface); border:1px solid var(--border-soft); border-radius:var(--radius-md, 14px); padding:16px 18px; margin-bottom:14px;}
.ce-summary-top{display:flex; align-items:flex-start; gap:20px; flex-wrap:wrap;}
.ce-summary-total{display:flex; flex-direction:column; gap:2px; min-width:96px;}
.ce-total-n{font-size:30px; font-weight:800; line-height:1; color:var(--text);}
.ce-total-l{font-size:12px; color:var(--text-dim); font-weight:600;}
.ce-sum-chips{display:flex; gap:8px; flex-wrap:wrap; flex:1;}
.ce-sum-chip{display:flex; flex-direction:column; align-items:center; gap:2px; min-width:78px; padding:8px 12px; border-radius:var(--radius-sm); border:1px solid var(--border-soft); background:var(--surface-2);}
.ce-sum-chip.ce-sum-zero{opacity:.5;}
.ce-sum-n{font-size:19px; font-weight:800; color:var(--text); line-height:1;}
.ce-sum-l{font-size:11px; color:var(--text-dim); font-weight:600; text-align:center;}
.ce-sum-chip.ce-t-node{border-color:color-mix(in srgb, #e0663a 55%, var(--border-soft));}
.ce-sum-chip.ce-t-exc{border-color:color-mix(in srgb, #d64b4b 55%, var(--border-soft));}
.ce-sum-chip.ce-t-out{border-color:color-mix(in srgb, #d89b2a 55%, var(--border-soft));}
.ce-sum-chip.ce-t-zero{border-color:color-mix(in srgb, #4b8dd6 55%, var(--border-soft));}
.ce-sum-chip.ce-t-prompt{border-color:color-mix(in srgb, #9b5bd6 55%, var(--border-soft));}

.ce-repeats{margin-top:14px; padding-top:14px; border-top:1px solid var(--border-soft);}
.ce-repeats-head{font-size:12px; font-weight:700; color:var(--text-dim); margin-bottom:8px;}
.ce-repeats-list{display:flex; flex-direction:column; gap:6px;}
.ce-repeat-row{display:flex; align-items:center; gap:10px; font-size:12.5px; flex-wrap:wrap;}
.ce-repeat-count{font-weight:800; color:var(--text); background:var(--surface-2); border:1px solid var(--border-soft); border-radius:999px; padding:2px 9px; font-size:11.5px;}
.ce-repeat-wf{font-weight:600; color:var(--text); max-width:260px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.ce-repeat-srv{color:var(--text-dim);}
.ce-repeat-time{color:var(--text-faint); margin-left:auto; font-size:11.5px;}

.ce-filterbar{display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:14px;}
.ce-filter{min-width:150px; max-width:220px;}
.ce-refresh{margin-left:auto;}

.ce-list{display:flex; flex-direction:column; gap:8px;}
.ce-card{background:var(--surface); border:1px solid var(--border-soft); border-radius:var(--radius-sm); overflow:hidden;}
.ce-card[open]{border-color:color-mix(in srgb, var(--accent) 40%, var(--border-soft));}
.ce-card-head{display:flex; align-items:center; gap:10px; padding:11px 14px; cursor:pointer; list-style:none; flex-wrap:wrap;}
.ce-card-head::-webkit-details-marker{display:none;}
.ce-card-head:hover{background:var(--surface-2);}
.ce-card-wf{font-weight:600; color:var(--text); font-size:13.5px; max-width:340px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.ce-card-meta{color:var(--text-dim); font-size:12px;}
.ce-card-time{color:var(--text-faint); font-size:12px; margin-left:auto;}
.ce-badge{display:inline-flex; align-items:center; font-size:11px; font-weight:700; padding:3px 9px; border-radius:999px; color:var(--text); background:var(--surface-2); border:1px solid var(--border-soft); white-space:nowrap;}
.ce-badge.ce-t-node{color:#e0663a; border-color:color-mix(in srgb, #e0663a 45%, var(--border-soft));}
.ce-badge.ce-t-exc{color:#d64b4b; border-color:color-mix(in srgb, #d64b4b 45%, var(--border-soft));}
.ce-badge.ce-t-out{color:#c98a1e; border-color:color-mix(in srgb, #d89b2a 45%, var(--border-soft));}
.ce-badge.ce-t-zero{color:#4b8dd6; border-color:color-mix(in srgb, #4b8dd6 45%, var(--border-soft));}
.ce-badge.ce-t-prompt{color:#9b5bd6; border-color:color-mix(in srgb, #9b5bd6 45%, var(--border-soft));}
.ce-card-body{padding:4px 14px 14px; border-top:1px solid var(--border-soft);}
.ce-detail-row{display:flex; gap:12px; padding:8px 0; border-bottom:1px solid var(--border-soft); font-size:12.5px;}
.ce-detail-row:last-child{border-bottom:none;}
.ce-detail-k{flex:0 0 92px; color:var(--text-dim); font-weight:600;}
.ce-detail-v{flex:1; color:var(--text); min-width:0; word-break:break-word;}
.ce-detail-v code{background:var(--surface-2); border:1px solid var(--border-soft); border-radius:4px; padding:1px 5px; font-size:11.5px; margin:0 1px;}
.ce-detail-v.ce-pre{white-space:pre-wrap; font-family:var(--mono, ui-monospace, monospace); font-size:11.5px; color:var(--text-dim);}
.ce-pne{margin:2px 0;}
.ce-card-actions{display:flex; justify-content:flex-end; margin-top:10px;}
.ce-del{font-size:12px; padding:6px 12px;}
@media (max-width:640px){
  .ce-card-time{margin-left:0; width:100%;}
  .ce-detail-k{flex-basis:72px;}
  .ce-filter{flex:1; max-width:none;}
}
/* 카테고리 칩(전체/체크포인트/LoRA/VAE) — 앱의 무톤 배지/탭 룩. */
.comfy-models-cats{display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-bottom:14px;}
.comfy-models-chip{
  display:inline-flex; align-items:center; gap:6px; font-size:12.5px; font-weight:600;
  color:var(--text-dim); background:var(--surface); border:1px solid var(--border-soft);
  border-radius:999px; padding:6px 13px; cursor:pointer; font-family:inherit;
}
.comfy-models-chip:hover{color:var(--text); border-color:color-mix(in srgb, var(--accent) 45%, var(--border-soft));}
.comfy-models-chip.active{background:var(--accent); color:var(--on-accent); border-color:transparent;}
.comfy-models-chip-n{font-size:11px; opacity:.7;}
.comfy-models-chip.active .comfy-models-chip-n{opacity:.85;}
.comfy-models-status{padding:22px 4px; font-size:13px;}
/* 반응형 카드 그리드 — 참조 스크린샷의 밀도를 앱 고유 토큰으로 재현. */
.comfy-models-grid{display:grid; grid-template-columns:repeat(auto-fill, minmax(180px, 1fr)); gap:14px;}
.comfy-model-card{padding:0; overflow:hidden; display:flex; flex-direction:column;}
.comfy-model-thumb{
  position:relative; width:100%; aspect-ratio:1 / 1; background:var(--surface-2);
  display:flex; align-items:center; justify-content:center; overflow:hidden;
}
/* 플레이스홀더(파일 종류 아이콘) — 뒤에 깔리고, 미리보기 img가 있으면 그 위를 덮는다. */
.comfy-model-thumb-ph{position:absolute; inset:0; display:flex; align-items:center; justify-content:center; color:var(--text-faint);}
.comfy-model-thumb-ph svg{width:44px; height:44px; opacity:.55;}
.comfy-model-thumb img{position:absolute; inset:0; width:100%; height:100%; object-fit:cover; background:var(--surface-2); z-index:1;}
.comfy-model-info{padding:10px 12px 12px; display:flex; flex-direction:column; gap:7px; min-width:0;}
.comfy-model-badge{margin-left:0; align-self:flex-start; background:var(--accent-soft); color:var(--accent);}
.comfy-model-name{
  font-size:12.5px; color:var(--text); font-weight:600; min-width:0;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}

/* ---------- civitai 모델 검색·설치 패널 (설치된 모델 뷰 내부) ---------- */
.comfy-civitai-toggle{margin-left:auto;}
.comfy-civitai-panel{
  margin:0 0 14px; padding:14px; border:1px solid var(--border);
  border-radius:12px; background:var(--surface-2);
}
.comfy-civitai-searchbar{display:flex; gap:10px; flex-wrap:wrap; align-items:center;}
.comfy-civitai-q{flex:1 1 260px; min-width:180px;}
.comfy-civitai-type{flex:0 0 auto; min-width:120px;}
.comfy-civitai-hint{margin:8px 2px 0; font-size:12px; line-height:1.5;}
.comfy-civitai-results{margin-top:12px;}
.comfy-civitai-grid{display:grid; grid-template-columns:repeat(auto-fill, minmax(170px, 1fr)); gap:14px;}
.comfy-civitai-card{padding:0; overflow:hidden; display:flex; flex-direction:column;}
.comfy-civitai-thumb{
  position:relative; width:100%; aspect-ratio:1 / 1; background:var(--surface-1);
  display:flex; align-items:center; justify-content:center; overflow:hidden;
}
.comfy-civitai-thumb img{width:100%; height:100%; object-fit:cover;}
.comfy-civitai-nsfw{
  position:absolute; top:8px; left:8px; z-index:2;
  padding:2px 7px; border-radius:6px; font-size:10.5px; font-weight:700; letter-spacing:.03em;
  background:var(--danger); color:#fff;
}
.comfy-civitai-meta{padding:10px 12px 12px; display:flex; flex-direction:column; gap:6px; min-width:0;}
.comfy-civitai-name{
  font-size:12.5px; color:var(--text); font-weight:600; min-width:0;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.comfy-civitai-sub{font-size:11.5px; min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.comfy-civitai-pager{display:flex; gap:10px; justify-content:center; margin-top:14px;}
.comfy-civitai-install-form{display:flex; flex-direction:column; gap:6px; text-align:left; min-width:min(360px, 72vw);}
.comfy-civitai-install-name{font-size:13.5px; font-weight:700; color:var(--text); margin-bottom:6px;}
.comfy-civitai-flabel{margin-top:6px; font-size:12px; font-weight:600; color:var(--text-muted, var(--text));}
.comfy-civitai-install-form select{width:100%;}
/* 검색 카드 하단 액션(설치 버튼 + civitai 링크)과 civitai 모델 페이지 링크. */
.comfy-civitai-cardactions{display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-top:2px;}
.comfy-civitai-link{
  font-size:11.5px; font-weight:600; color:var(--accent); text-decoration:none;
  white-space:nowrap;
}
.comfy-civitai-link:hover{text-decoration:underline;}
.comfy-civitai-install-form .comfy-civitai-link{display:inline-block; margin-bottom:6px;}

/* civitai 필터 바(정렬/기간/종류/베이스/NSFW/설치숨김) — 상단 검색바 아래 한 줄. */
.comfy-civitai-filters{
  display:flex; flex-wrap:wrap; gap:10px 14px; align-items:flex-end;
  margin-top:10px;
}
.comfy-civitai-fl{
  display:flex; flex-direction:column; gap:3px;
  font-size:11px; font-weight:600; color:var(--text-muted, var(--text));
}
.comfy-civitai-fl select{min-width:120px;}
.comfy-civitai-togglel{
  display:flex; align-items:center; gap:6px;
  font-size:12px; font-weight:600; color:var(--text); cursor:pointer;
  padding-bottom:4px;
}
.comfy-civitai-togglel input{accent-color:var(--accent); cursor:pointer;}
/* 설치됨 배지(썸네일 우상단) + 카드 강조. */
.comfy-civitai-installed{
  position:absolute; top:8px; right:8px; z-index:2;
  padding:2px 7px; border-radius:6px; font-size:10.5px; font-weight:700; letter-spacing:.02em;
  background:var(--accent); color:var(--on-accent, #10140a);
}
.comfy-civitai-card.is-installed{outline:1.5px solid var(--accent); outline-offset:-1px;}
/* 카드 통계/업로더/버전 라인. */
.comfy-civitai-stats{display:flex; gap:10px; font-size:11px; align-items:center;}
.comfy-civitai-uploader,.comfy-civitai-ver{
  font-size:11px; min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}

/* ---------- 생산물 복사 버튼 (앱 전역 hover-reveal copy) ---------- */
/* 이미지·영상·오디오·텍스트 결과 컨테이너 위에 마우스를 올리면 나타나는
   작은 복사 버튼. 기존 마크업/클래스는 건드리지 않고, 결과 컨테이너에
   .hover-copy-host 클래스만 추가로 붙여 위치 기준(position:relative)만 잡는다. */
.hover-copy-host{position:relative;}
.hover-copy-btn{
  position:absolute; top:8px; right:8px; z-index:6;
  width:30px; height:30px; padding:0;
  display:inline-flex; align-items:center; justify-content:center;
  border-radius:8px; cursor:pointer;
  background:rgba(18,17,22,.62); border:1px solid rgba(255,255,255,.16); color:#f4f3ef;
  -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px);
  opacity:0; transform:translateY(-2px); pointer-events:none;
  transition:opacity .15s ease, transform .15s ease, background .15s ease, color .15s ease, border-color .15s ease;
}
.hover-copy-host:hover .hover-copy-btn,
.hover-copy-btn:focus-visible{opacity:1; transform:translateY(0); pointer-events:auto;}
.hover-copy-btn:hover{background:var(--accent); color:var(--on-accent); border-color:var(--accent);}
.hover-copy-btn.copied{background:var(--accent); color:var(--on-accent); border-color:var(--accent);}
.hover-copy-btn svg{width:15px; height:15px; pointer-events:none;}
/* hover가 없는 터치 기기에서는 은은하게 항상 노출 */
@media (hover:none){ .hover-copy-btn{opacity:.85; transform:none; pointer-events:auto;} }

/* 피드 썸네일 hover 다운로드 버튼 — 복사 버튼(우상단) 왼쪽에 나란히 위치 */
.hover-dl-btn{
  position:absolute; top:8px; right:44px; z-index:6;
  width:30px; height:30px; padding:0;
  display:inline-flex; align-items:center; justify-content:center;
  border-radius:8px; cursor:pointer;
  background:rgba(18,17,22,.62); border:1px solid rgba(255,255,255,.16); color:#f4f3ef;
  -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px);
  opacity:0; transform:translateY(-2px); pointer-events:none;
  transition:opacity .15s ease, transform .15s ease, background .15s ease, color .15s ease, border-color .15s ease;
}
.hover-copy-host:hover .hover-dl-btn,
.hover-dl-btn:focus-visible{opacity:1; transform:translateY(0); pointer-events:auto;}
.hover-dl-btn:hover{background:var(--accent); color:var(--on-accent); border-color:var(--accent);}
.hover-dl-btn svg{width:15px; height:15px; pointer-events:none;}
@media (hover:none){ .hover-dl-btn{opacity:.85; transform:none; pointer-events:auto;} }

/* 복사 완료/실패 토스트 (앱 전역 공용) — 하단 중앙 단발 알림.
   copyToast()는 이제 공용 showToast()의 얇은 래퍼로, 이 스타일은 유지된다. */
.copy-toast{
  position:fixed; left:50%; bottom:34px; transform:translateX(-50%) translateY(12px);
  z-index:6500; max-width:80vw;   /* 토스트 밴드 */
  padding:10px 16px; border-radius:10px;
  background:var(--surface-2); color:var(--text); border:1px solid var(--border);
  box-shadow:0 8px 30px rgba(0,0,0,.35);
  font-size:13px; font-weight:600; letter-spacing:-.01em;
  opacity:0; pointer-events:none; transition:opacity .2s ease, transform .2s ease;
}
.copy-toast.show{opacity:1; transform:translateX(-50%) translateY(0);}
.copy-toast.error{border-color:var(--danger); color:var(--danger);}

/* ── 전역 생성 토스트 스택 (우측 하단, 자동 소멸, 비차단) ──
   생성 시작/완료/실패 알림에 사용. 셸 디자인 토큰만 사용한다.
   bottom을 스튜디오 도우미 FAB(.assistant-fab, right:22px/bottom:22px)과
   똑같이 22px로 겹쳐 두면 토스트가 도우미 버튼 위에 얹혀 지저분해 보인다 —
   FAB 위쪽으로 띄워 겹치지 않게 한다(.assistant-panel이 이미 bottom:88px로
   FAB 위에 뜨는 것과 같은 간격 규칙). */
.toast-stack{
  position:fixed; right:22px; bottom:90px; z-index:6600;   /* 토스트 밴드 최상단 */
  display:flex; flex-direction:column; gap:10px; align-items:flex-end;
  pointer-events:none; max-width:min(360px, 86vw);
}
.app-toast{
  pointer-events:auto; display:flex; align-items:flex-start; gap:10px;
  padding:11px 14px; border-radius:var(--radius-sm);
  background:var(--surface-2); color:var(--text); border:1px solid var(--border);
  box-shadow:var(--shadow);
  font-size:13px; font-weight:600; letter-spacing:-.01em; line-height:1.35;
  opacity:0; transform:translateY(10px); transition:opacity .22s ease, transform .22s ease;
  max-width:100%; word-break:break-word;
}
.app-toast.show{opacity:1; transform:translateY(0);}
.app-toast .toast-ico{flex:0 0 auto; width:16px; height:16px; margin-top:1px;}
.app-toast .toast-ico svg{width:16px; height:16px; display:block;}
.app-toast.type-success{border-color:color-mix(in srgb, var(--success) 55%, var(--border)); }
.app-toast.type-success .toast-ico{color:var(--success);}
.app-toast.type-error{border-color:color-mix(in srgb, var(--danger) 55%, var(--border));}
.app-toast.type-error .toast-ico{color:var(--danger);}
.app-toast.type-info .toast-ico{color:var(--accent);}
@media (max-width:640px){ .toast-stack{ left:12px; right:12px; bottom:78px; max-width:none; align-items:stretch; } }

/* ==================== AI 채팅 · Seed 모드 ====================
   생성(create) 뷰의 세 번째 모드. 이미지/영상 도크와 형제로, setMode('chat')일
   때만 표시된다(.chat-panel display:flex). 셸 디자인 토큰만 사용한다. */
.chat-panel{flex:1; min-height:0; display:flex; flex-direction:column; gap:12px; padding-bottom:6px;}
.chat-head{display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap; flex-shrink:0;}
.chat-head-left{display:flex; align-items:center; gap:10px; min-width:0;}
.chat-title{font-size:15px; font-weight:700; color:var(--text);}
.chat-badge{font-size:11px; font-weight:600; padding:2px 10px; border-radius:999px; background:var(--surface-2); color:var(--text-dim); border:1px solid var(--border-soft);}
.chat-badge.ready{background:var(--accent-soft); color:var(--accent); border-color:transparent;}
.chat-head-right{display:flex; align-items:center; gap:10px; flex-wrap:wrap;}
.chat-tier{display:flex; align-items:center; gap:8px; font-size:12px; color:var(--text-dim); font-weight:600;}
.chat-tier select{
  background:var(--surface); border:1px solid var(--border-soft); color:var(--text);
  border-radius:var(--radius-sm); padding:8px 11px; font-size:13px; font-family:inherit; max-width:280px;
}
.chat-tier select:focus{outline:none; border-color:var(--accent);}
.chat-clear{
  background:transparent; border:1px solid var(--border-soft); color:var(--text-dim);
  border-radius:var(--radius-sm); padding:7px 13px; font-size:12.5px; cursor:pointer; font-family:inherit;
}
.chat-clear:hover{border-color:var(--accent); color:var(--text);}
.chat-tier-hint{font-size:12px; color:var(--text-dim); flex-shrink:0; min-height:1px;}
.chat-thread{
  flex:1; min-height:0; overflow-y:auto; padding:16px;
  display:flex; flex-direction:column; gap:11px;
  background:var(--bg-elev); border:1px solid var(--border-soft); border-radius:var(--radius);
}
.chat-empty{margin:auto; text-align:center; color:var(--text-faint); font-size:13px; padding:26px 8px;}
.chat-msg{max-width:78%; padding:10px 13px; border-radius:14px; font-size:13px; line-height:1.55; white-space:pre-wrap; word-break:break-word;}
.chat-msg.user{align-self:flex-end; background:var(--accent); color:var(--on-accent); border-bottom-right-radius:4px;}
.chat-msg.assistant{align-self:flex-start; background:var(--surface-2); color:var(--text); border:1px solid var(--border-soft); border-bottom-left-radius:4px;}
.chat-msg.assistant.error{border-color:var(--danger); color:var(--danger);}
/* 응답 대기 타이핑 표시 — 스튜디오 도우미(.assistant-msg.typing)와 동일한
   점 3개 깜빡임 애니메이션(assistant-blink 키프레임 재사용). */
.chat-msg.typing{display:flex; gap:5px; align-items:center; padding:12px 15px;}
.chat-msg.typing span{width:6px; height:6px; border-radius:50%; background:var(--text-faint); animation:assistant-blink 1s infinite ease-in-out;}
.chat-msg.typing span:nth-child(2){animation-delay:.2s;}
.chat-msg.typing span:nth-child(3){animation-delay:.4s;}
.chat-msg-text{min-width:0;}
.chat-bubble-imgs{display:flex; gap:6px; flex-wrap:wrap; margin-bottom:7px;}
.chat-bubble-imgs img{width:60px; height:60px; object-fit:cover; border-radius:9px; cursor:pointer;}
/* 실제 생성된 이미지(진짜 Seedream 결과) — 첨부 썸네일보다 훨씬 크게 보여줘
   가짜 링크가 아닌 진짜 결과물임을 시각적으로도 분명히 한다. */
.chat-bubble-imgs-generated img{width:220px; height:220px;}
.chat-bubble-vid{margin-bottom:7px;}
.chat-bubble-vid video{width:260px; max-width:100%; border-radius:11px; display:block;}
.chat-bubble-genrow{display:flex; gap:8px; margin-top:9px; flex-wrap:wrap;}
.chat-bubble-genimg{
  display:inline-flex; align-items:center; gap:6px;
  padding:6px 12px; border-radius:9px; border:1px solid var(--border-soft);
  background:var(--surface-2); color:var(--text-dim); font-size:12px; cursor:pointer;
  transition:border-color .15s,color .15s,background .15s;
}
.chat-bubble-genimg:hover{border-color:var(--accent); color:var(--accent); background:var(--accent-soft);}
.chat-bubble-genimg:disabled{opacity:.5; cursor:not-allowed;}
.chat-attach-strip{display:flex; gap:8px; flex-wrap:wrap; flex-shrink:0;}
.chat-attach-thumb{position:relative; width:56px; height:56px;}
.chat-attach-thumb img{width:100%; height:100%; object-fit:cover; border-radius:9px; border:1px solid var(--border-soft);}
.chat-attach-thumb button{
  position:absolute; top:-6px; right:-6px; width:20px; height:20px; padding:0; line-height:1;
  border-radius:50%; border:1px solid var(--border); background:var(--surface-2); color:var(--text);
  cursor:pointer; font-size:13px; display:flex; align-items:center; justify-content:center;
}
.chat-attach-thumb button:hover{background:var(--danger); color:#fff; border-color:var(--danger);}
/* 비이미지 파일 첨부 칩(파일명 표시) */
.chat-attach-file{width:auto; height:auto; min-height:34px; display:flex; align-items:center; padding:6px 26px 6px 10px;
  border:1px solid var(--border-soft); border-radius:9px; background:var(--surface-2); max-width:180px;}
.chat-attach-fname{font-size:12px; color:var(--text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.chat-bubble-files{display:flex; gap:6px; flex-wrap:wrap; margin-bottom:7px;}
.chat-bubble-file{font-size:12px; color:var(--text-dim); background:var(--surface-2); border:1px solid var(--border-soft); border-radius:8px; padding:3px 8px;}
.chat-input-wrap{
  display:flex; align-items:flex-end; gap:9px; flex-shrink:0;
  padding:10px 12px; background:var(--surface); border:1px solid var(--border-soft); border-radius:var(--radius);
  transition:border-color .15s, box-shadow .15s;
}
.chat-input-wrap.dragover{border-color:var(--accent); box-shadow:0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);}
.chat-attach-btn, .chat-send, .chat-quick-btn{
  flex:0 0 auto; width:38px; height:38px; display:flex; align-items:center; justify-content:center;
  border-radius:var(--radius-sm); cursor:pointer; border:1px solid var(--border-soft); background:var(--surface-2); color:var(--text-dim);
}
.chat-attach-btn:hover, .chat-quick-btn:hover{border-color:var(--accent); color:var(--text);}
.chat-attach-btn svg, .chat-send svg, .chat-quick-btn svg{width:18px; height:18px;}
/* "+" 빠른 생성 메뉴 — 입력창에 아직 타이핑 중인 텍스트를 LLM 왕복 없이
   바로 이미지/영상 생성 요청으로 보낸다(quickChatGenerate, app.js). ChatGPT류
   입력창의 "+" 첨부 메뉴와 같은 위치/패턴. */
.chat-quick-wrap{position:relative; flex:0 0 auto;}
.chat-quick-btn.active{border-color:var(--accent); color:var(--accent);}
.chat-quick-menu{
  position:absolute; left:0; bottom:46px; z-index:20; width:230px;
  background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-sm);
  box-shadow:var(--shadow); padding:6px; display:flex; flex-direction:column; gap:2px;
}
.chat-quick-menu button{
  display:flex; flex-direction:column; align-items:flex-start; gap:1px; text-align:left;
  padding:8px 10px; border-radius:7px; border:1px solid transparent; background:transparent;
  color:var(--text); font-size:.82rem; font-weight:600; cursor:pointer;
}
.chat-quick-menu button:hover{background:var(--surface-2); border-color:var(--border-soft);}
.chat-quick-menu button small{color:var(--text-faint); font-weight:400; font-size:.72rem;}
.chat-send{background:var(--accent); color:var(--on-accent); border-color:transparent;}
.chat-send:hover{filter:brightness(1.05);}
.chat-send:disabled{opacity:.5; cursor:default; filter:none;}
.chat-input-wrap textarea{
  flex:1; min-width:0; resize:none; max-height:120px;
  background:transparent; border:none; color:var(--text); font-size:14px; font-family:inherit; line-height:1.5;
  padding:8px 4px;
}
.chat-input-wrap textarea:focus{outline:none;}
@media (max-width:640px){
  .chat-msg{max-width:88%;}
  .chat-tier select{max-width:180px;}
}

/* ── 상단바 진행 중 작업 인디케이터(전역) ── */
.gt-jobpill{
  position:relative; display:none; align-items:center; gap:7px;
  height:34px; padding:0 12px; border-radius:999px; cursor:pointer;
  background:var(--surface-2); border:1px solid var(--border); color:var(--text);
  font-size:12.5px; font-weight:700; letter-spacing:-.01em;
  transition:border-color .15s ease, background .15s ease;
}
.gt-jobpill.active{display:inline-flex;}
.gt-jobpill:hover{border-color:var(--accent);}
.gt-jobpill .jobpill-dot{
  width:8px; height:8px; border-radius:50%; background:var(--accent);
  box-shadow:0 0 0 0 var(--accent-soft); animation:jobpulse 1.5s ease-out infinite;
}
@keyframes jobpulse{ 0%{box-shadow:0 0 0 0 var(--accent-soft);} 70%{box-shadow:0 0 0 7px transparent;} 100%{box-shadow:0 0 0 0 transparent;} }
.gt-jobpill .jobpill-count{color:var(--accent);}
.gt-jobmenu{
  position:absolute; top:46px; right:0; z-index:5200; width:min(340px, 90vw);   /* 앵커드 메뉴 밴드 */
  background:var(--surface); border:1px solid var(--border); border-radius:var(--radius);
  box-shadow:var(--shadow); padding:8px; display:none;
}
.gt-jobmenu.open{display:block;}
.gt-jobmenu-head{font-size:11px; font-weight:700; letter-spacing:.1em; text-transform:uppercase; color:var(--text-faint); padding:6px 8px 8px;}
.gt-jobmenu-empty{padding:12px 8px; color:var(--text-dim); font-size:12.5px;}
.gt-jobmenu-item{display:flex; align-items:center; gap:10px; padding:9px 8px; border-radius:var(--radius-sm);}
.gt-jobmenu-item + .gt-jobmenu-item{border-top:1px solid var(--border-soft);}
.gt-jobmenu-item .ji-spin{width:14px; height:14px; flex:0 0 auto; color:var(--accent); animation:jobspin 1s linear infinite;}
@keyframes jobspin{to{transform:rotate(360deg);}}
.gt-jobmenu-item .ji-tx{min-width:0; flex:1;}
.gt-jobmenu-item .ji-label{font-size:13px; font-weight:600; color:var(--text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.gt-jobmenu-item .ji-sub{font-size:11.5px; color:var(--text-faint); margin-top:1px;}
/* 진행 중 작업 취소 버튼(상단바 드롭다운) */
.gt-jobcancel{
  flex:0 0 auto; margin-left:auto; font-size:11.5px; font-weight:600;
  padding:4px 10px; border-radius:999px; cursor:pointer;
  color:var(--danger); background:transparent;
  border:1px solid color-mix(in srgb, var(--danger) 45%, var(--border));
}
.gt-jobcancel:hover{background:color-mix(in srgb, var(--danger) 12%, transparent);}

/* ── 알림(벨) 백그라운드 작업 배지 + 드롭다운 (Task 97) — 셸 토큰 재사용, jobmenu 톤 ── */
#notifBtn{position:relative;}
.notif-badge{
  position:absolute; top:-5px; right:-5px; min-width:17px; height:17px; padding:0 4px;
  display:none; align-items:center; justify-content:center; box-sizing:border-box;
  border-radius:999px; background:var(--accent); color:var(--on-accent);
  font-size:10.5px; font-weight:800; line-height:1;
}
.notif-badge.show{display:flex;}
.notif-panel{
  position:absolute; top:46px; right:0; z-index:5210; width:min(320px, 90vw);   /* 앵커드 메뉴 밴드 */
  background:var(--surface); border:1px solid var(--border); border-radius:var(--radius);
  box-shadow:var(--shadow); padding:8px; display:none; cursor:default; text-align:left;
}
.notif-panel.open{display:block;}
.notif-panel-head{font-size:11px; font-weight:700; letter-spacing:.1em; text-transform:uppercase; color:var(--text-faint); padding:6px 8px 8px;}
.notif-panel-empty{padding:12px 8px; color:var(--text-dim); font-size:12.5px;}
.notif-job-item{display:flex; align-items:center; gap:10px; padding:9px 8px; border-radius:var(--radius-sm);}
.notif-job-item + .notif-job-item{border-top:1px solid var(--border-soft);}
.notif-job-item .nj-spin{width:14px; height:14px; flex:0 0 auto; color:var(--accent); animation:jobspin 1s linear infinite;}
.notif-job-item .nj-idle{width:8px; height:8px; flex:0 0 auto; border-radius:50%; background:var(--border);}
.notif-job-item .nj-tx{min-width:0; flex:1;}
.notif-job-item .nj-label{font-size:13px; font-weight:600; color:var(--text);}
.notif-job-item .nj-sub{font-size:11.5px; color:var(--text-faint); margin-top:1px;}

/* ── ComfyUI 대시보드 통계 행 + 작업 목록 + 최근 오류 (셸 .card 톤) ── */
.comfy-stat-row{display:grid; grid-template-columns:repeat(4, 1fr); gap:12px; margin:2px 0 20px;}
.comfy-stat-card{
  background:var(--surface); border:1px solid var(--border-soft); border-radius:var(--radius);
  padding:14px 16px; display:flex; flex-direction:column; gap:6px;
}
.comfy-stat-card .cs-label{font-size:11.5px; font-weight:700; letter-spacing:.06em; color:var(--text-faint);}
.comfy-stat-card .cs-val{font-family:var(--display); font-size:26px; font-weight:700; letter-spacing:-.02em; color:var(--text); line-height:1;}
.comfy-stat-card.accent .cs-val{color:var(--accent);}
.comfy-stat-card.danger .cs-val{color:var(--danger);}
.comfy-dash-section-title{font-size:12px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--text-faint); margin:6px 0 12px;}
.comfy-errlog{display:flex; flex-direction:column; gap:8px; margin-bottom:8px;}
.comfy-errlog-item{
  display:flex; align-items:flex-start; gap:10px; padding:11px 13px;
  background:var(--surface); border:1px solid var(--border-soft); border-left:3px solid var(--danger);
  border-radius:var(--radius-sm);
}
.comfy-errlog-item .el-ico{flex:0 0 auto; width:15px; height:15px; color:var(--danger); margin-top:1px;}
.comfy-errlog-item .el-tx{min-width:0; flex:1;}
.comfy-errlog-item .el-top{display:flex; gap:8px; align-items:baseline; flex-wrap:wrap;}
.comfy-errlog-item .el-wf{font-size:13px; font-weight:600; color:var(--text);}
.comfy-errlog-item .el-time{font-size:11.5px; color:var(--text-faint);}
.comfy-errlog-item .el-msg{font-size:12px; color:var(--text-dim); margin-top:3px; word-break:break-word;}
.comfy-errlog-empty{padding:14px; color:var(--text-dim); font-size:12.5px; border:1px dashed var(--border-soft); border-radius:var(--radius-sm);}

/* 작업 목록(전체 작업) 화면 — 필터 바 + 잡 카드 */
.comfy-jobs-filters{display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin-bottom:16px;}
.comfy-jobs-search{
  flex:1 1 200px; min-width:160px; height:36px; padding:0 12px;
  background:var(--surface-2); border:1px solid var(--border); border-radius:var(--radius-sm);
  color:var(--text); font-size:13px; font-family:var(--sans);
}
.comfy-jobs-chips{display:flex; flex-wrap:wrap; gap:6px;}
.comfy-jobchip{
  height:30px; padding:0 12px; border-radius:999px; cursor:pointer;
  background:var(--surface-2); border:1px solid var(--border); color:var(--text-dim);
  font-size:12px; font-weight:600; display:inline-flex; align-items:center;
  transition:all .12s ease;
  /* 레이아웃 철칙: 좁아져도 칩 안에서 글자가 세로로 접히지 않게 한다. */
  white-space:nowrap; word-break:keep-all;
}
/* 남의 공유 작업 카드 — 갤러리 카드와 같은 규칙으로 점선 테두리로만 구분한다
   (레이아웃은 그대로 — 폭·행 구조를 바꾸지 않는다). */
.comfy-job-card.gal-readonly{border-style:dashed;}
.comfy-jobchip:hover{color:var(--text); border-color:var(--text-faint);}
.comfy-jobchip.active{background:var(--accent); color:var(--on-accent); border-color:var(--accent);}
.comfy-jobs-list{display:flex; flex-direction:column; gap:10px;}
/* 카드 높이 축소(2026-07-29) — 입력 소스 미니 썸네일 제거와 함께 패딩·행간을 줄였다. */
.comfy-job-card{
  display:flex; align-items:flex-start; gap:9px; padding:7px 10px;
  background:var(--surface); border:1px solid var(--border-soft); border-radius:var(--radius);
}
.comfy-job-card.is-clickable{cursor:pointer;}
.comfy-job-card.is-clickable:hover{border-color:var(--accent);}
.comfy-job-thumb{flex:0 0 44px; width:44px; height:44px; border-radius:var(--radius-sm); overflow:hidden; background:var(--bg-elev, #000); position:relative;}
.comfy-job-thumb img, .comfy-job-thumb video{width:100%; height:100%; object-fit:cover; display:block;}
.comfy-job-thumb-video .comfy-job-thumb-playico{position:absolute; inset:0; display:flex; align-items:center; justify-content:center; font-size:14px; color:#fff; text-shadow:0 1px 3px rgba(0,0,0,.6); pointer-events:none;}
.comfy-job-thumb-generic{display:flex; align-items:center; justify-content:center; font-size:16px; color:var(--text-faint);}
.comfy-job-card .jc-main{min-width:0; flex:1 1 auto; display:flex; flex-direction:column; gap:2px;}
/* 1행: 카테고리 배지 + 제목(한 줄 말줄임). nowrap로 배지 옆에서 제목만 줄어든다. */
.comfy-job-card .jc-top{display:flex; align-items:center; gap:7px; flex-wrap:nowrap; min-width:0;}
.comfy-job-card .jc-label{flex:1 1 auto; min-width:0; font-size:13px; font-weight:600; color:var(--text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
/* 2행: 상태 배지 · 시간 · 요청자 — 한 줄 유지(요청자만 남는 폭에서 말줄임). */
.comfy-job-card .jc-line2{display:flex; align-items:center; gap:8px; min-width:0; font-size:11.5px; color:var(--text-faint); white-space:nowrap;}
.comfy-job-card .jc-line2 .jc-time{flex:0 0 auto;}
.comfy-job-card .jc-line2 .jc-user{min-width:0; overflow:hidden; text-overflow:ellipsis;}
/* 남의 공유 작업의 작성자 배지 — 요청자 칩과 같은 자리를 쓰며, 폭이 부족하면
   줄바꿈 대신 말줄임으로 줄어든다(레이아웃 철칙: 재배치·세로접힘 금지). */
.comfy-job-card .jc-line2 .gal-owner-badge{flex:0 1 auto; min-width:0; max-width:140px;}
/* 3행: GPU 서버명(축약, 전체는 title) · 결과 수. */
.comfy-job-card .jc-line3{display:flex; align-items:center; gap:8px; min-width:0; font-size:11px; color:var(--text-faint); white-space:nowrap;}
.comfy-job-card .jc-line3 .jc-server{min-width:0; overflow:hidden; text-overflow:ellipsis;}
.comfy-job-card .jc-line3 .jc-outcount{flex:0 0 auto; color:var(--text-dim);}
/* 액션 버튼 행 — 본문 하단 우측 정렬 한 줄(썸네일과 가로로 다투지 않게 열 안에 둔다). */
.comfy-job-card .jc-actions{display:flex; align-items:center; justify-content:flex-end; gap:5px; flex-wrap:nowrap; overflow-x:auto; margin-top:0;}
.comfy-job-card .jc-meta{display:flex; gap:12px; flex-wrap:wrap; font-size:11.5px; color:var(--text-faint);}
.comfy-job-card .jc-meta .mono{font-family:var(--mono);}
.comfy-cat-badge{
  font-size:10.5px; font-weight:700; letter-spacing:.04em; text-transform:uppercase;
  padding:2px 7px; border-radius:5px; background:var(--ink-soft); color:var(--ink); flex:0 0 auto;
}
.comfy-status-badge{
  font-size:11px; font-weight:700; padding:3px 10px; border-radius:999px; flex:0 0 auto;
  border:1px solid var(--border);
}
.comfy-status-badge.st-running{color:var(--accent); border-color:color-mix(in srgb, var(--accent) 50%, var(--border)); background:var(--accent-soft);}
.comfy-status-badge.st-done{color:var(--success); border-color:color-mix(in srgb, var(--success) 45%, var(--border));}
.comfy-status-badge.st-failed{color:var(--danger); border-color:color-mix(in srgb, var(--danger) 45%, var(--border));}
/* 대기열: 실행 대기 상태 배지 + 대기 카드 + 상단 "실행 중 M · 대기 N" 요약 바.
   기존 상태 배지 토큰을 그대로 재사용하고, 대기는 중립 강조(accent) 톤으로 구분. */
.comfy-status-badge.st-queued{color:var(--warning, var(--accent)); border-color:color-mix(in srgb, var(--warning, var(--accent)) 45%, var(--border)); background:color-mix(in srgb, var(--warning, var(--accent)) 12%, transparent);}
.comfy-job-card.comfy-job-card-queued{border-style:dashed; border-color:color-mix(in srgb, var(--warning, var(--accent)) 40%, var(--border-soft));}
.comfy-queue-summary{
  display:flex; align-items:center; gap:8px; padding:8px 14px; margin-bottom:2px;
  font-size:12px; color:var(--text-dim);
  background:var(--accent-soft); border:1px solid color-mix(in srgb, var(--accent) 30%, var(--border-soft));
  border-radius:var(--radius);
}
.comfy-queue-summary b{color:var(--text); font-weight:700;}
.comfy-queue-summary .cqs-sep{color:var(--text-faint);}
.comfy-jobs-empty{padding:20px; color:var(--text-dim); font-size:13px; text-align:center; border:1px dashed var(--border-soft); border-radius:var(--radius);}
/* 작업 목록 카드의 실시간 진행 상태(단계명 + 퍼센트 막대) — 인라인 생성 폼의
   상태 표시와 같은 정보를 처리중 카드에도 보여달라는 요청 반영. */
.comfy-job-progress{margin-top:2px;}
.comfy-job-progress-head{display:flex; align-items:center; justify-content:space-between; gap:8px; font-size:11px; color:var(--text-dim); margin-bottom:3px;}
.comfy-job-progress-head b{color:var(--accent); font-weight:700;}
.comfy-job-progress-track{width:100%; height:5px; border-radius:999px; background:var(--border-soft); overflow:hidden;}
.comfy-job-progress-fill{height:100%; background:var(--accent); border-radius:999px; transition:width .3s ease;}
/* 작업 목록 카드 취소 버튼(진행 중 작업만) */
.comfy-job-cancel{
  flex:0 0 auto; font-size:12px; font-weight:600; padding:5px 12px; border-radius:8px; cursor:pointer;
  color:var(--danger); background:transparent;
  border:1px solid color-mix(in srgb, var(--danger) 45%, var(--border));
}
.comfy-job-cancel:hover{background:color-mix(in srgb, var(--danger) 12%, transparent);}
/* 작업 카드 체크박스(선택 다운로드용) + 개별 다운로드 버튼(완료 카드만) */
.comfy-job-check-wrap{flex:0 0 auto; display:flex; align-items:center; justify-content:center; cursor:pointer;}
.comfy-job-check{width:16px; height:16px; cursor:pointer; accent-color:var(--accent); margin:0;}
.comfy-job-dl{
  flex:0 0 auto; display:flex; align-items:center; justify-content:center; width:26px; height:26px;
  border-radius:7px; cursor:pointer; color:var(--text-dim); background:transparent; border:1px solid var(--border);
}
.comfy-job-dl svg{width:14px; height:14px;}
.comfy-job-dl:hover{border-color:var(--accent); color:var(--accent); background:var(--accent-soft);}
/* 작업 카드 삭제 버튼(요청 #3) — 다운로드 버튼과 같은 크기, hover 시 위험색. */
.comfy-job-del{
  flex:0 0 auto; display:flex; align-items:center; justify-content:center; width:30px; height:30px;
  border-radius:8px; cursor:pointer; color:var(--text-dim); background:transparent; border:1px solid var(--border);
}
.comfy-job-del svg{width:16px; height:16px;}
.comfy-job-regen{
  flex:0 0 auto; display:flex; align-items:center; justify-content:center; width:30px; height:30px;
  border-radius:8px; cursor:pointer; color:var(--text-dim); background:transparent; border:1px solid var(--border);
}
.comfy-job-regen:hover{color:var(--accent); border-color:var(--accent);}
.comfy-job-regen svg{width:16px; height:16px;}
.comfy-job-playlist{
  flex:0 0 auto; display:flex; align-items:center; justify-content:center; width:30px; height:30px;
  border-radius:8px; cursor:pointer; color:var(--text-dim); background:transparent; border:1px solid var(--border);
}
.comfy-job-playlist:hover{color:var(--accent); border-color:var(--accent);}
.comfy-job-playlist svg{width:16px; height:16px;}
.comfy-job-del:hover{border-color:color-mix(in srgb, var(--danger) 55%, var(--border)); color:var(--danger); background:color-mix(in srgb, var(--danger) 12%, transparent);}
/* 작업 목록 상단 — 선택 항목 일괄 다운로드 바 */
.comfy-jobs-bulkbar{
  display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:12px;
  padding:9px 12px; border-radius:var(--radius-sm); border:1px solid color-mix(in srgb, var(--accent) 40%, var(--border));
  background:var(--accent-soft);
}
.comfy-jobs-bulkbar .cjb-count{font-size:12.5px; font-weight:600; color:var(--text);}
.comfy-jobs-bulkbar .cjb-dl{
  display:flex; align-items:center; gap:6px; margin-left:auto; padding:7px 12px; border-radius:8px;
  border:none; cursor:pointer; font-size:12.5px; font-weight:600; background:var(--accent); color:var(--on-accent);
}
.comfy-jobs-bulkbar .cjb-dl svg{width:14px; height:14px;}
.comfy-jobs-bulkbar .cjb-dl:hover{filter:brightness(1.05);}
.comfy-jobs-bulkbar .cjb-clear{
  padding:7px 10px; border-radius:8px; cursor:pointer; font-size:12.5px; font-weight:600;
  background:transparent; color:var(--text-dim); border:1px solid var(--border);
}
.comfy-jobs-bulkbar .cjb-clear:hover{border-color:var(--accent); color:var(--accent);}
/* 일괄 삭제 버튼(요청 #3) — 위험색 계열 */
.comfy-jobs-bulkbar .cjb-del{
  display:flex; align-items:center; gap:6px; padding:7px 12px; border-radius:8px; cursor:pointer;
  font-size:12.5px; font-weight:600; background:transparent; color:var(--danger);
  border:1px solid color-mix(in srgb, var(--danger) 45%, var(--border));
}
.comfy-jobs-bulkbar .cjb-del svg{width:14px; height:14px;}
.comfy-jobs-bulkbar .cjb-del:hover{background:color-mix(in srgb, var(--danger) 14%, transparent); border-color:var(--danger);}
/* ── 작업 목록 하단 페이지 바(2026-08-05) ─────────────────────────────────────
   `‹ 1 / 23,385 · 총 233,846개 ›`. 레이아웃 철칙: 드로어가 좁아져도 세로로 접거나
   글자를 세로로 흘리지 않는다 — nowrap 을 유지하고 넘치면 가로 스크롤. */
.comfy-jobs-pager{margin-top:14px;}
.comfy-jobs-pager-bar{
  display:flex; align-items:center; justify-content:center; gap:8px; flex-wrap:nowrap;
  overflow-x:auto; white-space:nowrap; padding:8px 10px;
  border-top:1px solid var(--border);
}
.comfy-jobs-pager-bar::-webkit-scrollbar{height:6px;}
.comfy-jobs-pager-bar::-webkit-scrollbar-thumb{background:var(--border); border-radius:3px;}
.comfy-jobs-pager-bar .cjp-arrow{
  flex:0 0 auto; width:30px; height:30px; line-height:1; font-size:17px; cursor:pointer;
  border-radius:8px; border:1px solid var(--border); background:var(--surface);
  color:var(--text-dim); font-family:inherit;
}
.comfy-jobs-pager-bar .cjp-arrow:hover:not([disabled]){border-color:var(--accent); color:var(--accent);}
.comfy-jobs-pager-bar .cjp-arrow[disabled]{opacity:.35; cursor:default;}
.comfy-jobs-pager-bar .cjp-pos{flex:0 0 auto; display:flex; align-items:center; gap:6px;}
.comfy-jobs-pager-bar .cjp-input{
  width:64px; padding:5px 6px; text-align:center; font-size:12.5px; font-weight:600;
  font-family:inherit; color:var(--text); background:var(--bg-elev);
  border:1px solid var(--border); border-radius:7px;
}
.comfy-jobs-pager-bar .cjp-input:focus{outline:none; border-color:var(--accent);}
.comfy-jobs-pager-bar .cjp-of,
.comfy-jobs-pager-bar .cjp-total{flex:0 0 auto; font-size:12.5px; color:var(--text-dim);}
.comfy-jobs-pager-bar .cjp-sep{flex:0 0 auto; color:var(--text-faint);}
/* 2페이지 이후에서 목록을 얼려둔 동안의 안내 — 누르면 1페이지(최신)로 간다. */
.comfy-jobs-pager .cjp-newbar{
  display:flex; align-items:center; justify-content:center; gap:8px; width:100%;
  margin-bottom:8px; padding:9px 12px; cursor:pointer; white-space:nowrap; overflow-x:auto;
  font-size:12.5px; font-weight:600; font-family:inherit; color:var(--accent);
  background:var(--accent-soft); border-radius:var(--radius-sm);
  border:1px solid color-mix(in srgb, var(--accent) 40%, var(--border));
}
.comfy-jobs-pager .cjp-newbar:hover{filter:brightness(1.06);}
.comfy-jobs-pager .cjp-newdot{
  flex:0 0 auto; width:7px; height:7px; border-radius:50%; background:var(--accent);
}
/* Gmail식 대량 선택 프리셋 툴바(요청 #2) — 체크박스 헤더 옆 작은 ▾ 드롭다운 */
.comfy-jobs-selecttools{position:relative; margin-bottom:10px;}
.cjs-preset-btn{
  display:inline-flex; align-items:center; gap:6px; padding:6px 11px; border-radius:8px; cursor:pointer;
  font-size:12.5px; font-weight:600; font-family:inherit; color:var(--text-dim);
  background:var(--surface); border:1px solid var(--border);
}
.cjs-preset-btn svg{width:15px; height:15px;}
.cjs-preset-btn .cjs-caret{width:13px; height:13px; opacity:.7;}
.cjs-preset-btn:hover{border-color:var(--accent); color:var(--accent);}
.comfy-jobs-preset-menu{
  position:absolute; top:calc(100% + 4px); left:0; z-index:30; min-width:180px; padding:6px;
  background:var(--bg-elev); border:1px solid var(--border); border-radius:var(--radius-sm);
  box-shadow:0 14px 40px rgba(0,0,0,.45);
}
.comfy-jobs-preset-menu button{
  display:block; width:100%; text-align:left; padding:8px 10px; border-radius:7px; cursor:pointer;
  font-size:13px; font-family:inherit; color:var(--text); background:transparent; border:none;
}
.comfy-jobs-preset-menu button:hover{background:var(--accent-soft); color:var(--accent);}
.cjs-preset-label{padding:6px 10px 2px; font-size:10.5px; font-weight:700; letter-spacing:.05em; text-transform:uppercase; color:var(--text-faint);}
.cjs-preset-sep{height:1px; margin:5px 6px; background:var(--border-soft);}
/* 결과 상세 라이트박스 썸네일의 선택 체크박스 */
.comfy-ov-thumb-check{position:absolute; top:2px; left:2px; z-index:3; display:flex; align-items:center; justify-content:center; cursor:pointer;}
.comfy-ov-thumb-check input{width:15px; height:15px; cursor:pointer; accent-color:var(--accent); margin:0;}

/* 작업 목록 — 우측 슬라이드 패널(오프캔버스). 여닫기는 라임 엣지 핸들
   (.comfy-jobs-handle)이 전담한다. (구 .comfy-jobs-toggle / 상단바 버튼은
   중복이라 제거 — 사용자 지시) */
.comfy-jobs-drawer-backdrop{
  display:none; position:fixed; inset:0; z-index:1000;   /* 드로어 밴드 */
  background:color-mix(in srgb, var(--bg) 55%, transparent); backdrop-filter:blur(3px);
}
.comfy-jobs-drawer-backdrop.open{display:block;}
/* "우측이 항상 있으면 좋겠다" — ComfyUI 모듈에서는 패널이 배경을 덮는 모달이
   아니라 본문 옆에 도킹된 패널처럼 느껴져야 한다. 패널 자체는 여전히
   position:fixed(아래)라 그리드 트랙을 새로 만들지 않고도 붙일 수 있지만, 그
   대신 본문(.main)에 패널 폭만큼 padding-right를 줘서 밑에 깔리지 않게 한다.
   좁은 화면(태블릿 이하)에서는 다시 기존처럼 위에 슬라이드 오버되는 편이
   낫다(본문을 밀어내면 너무 좁아짐). */
@media (min-width:901px){
  .app-shell.comfy-jobs-docked .main{padding-right:452px; transition:padding-right .22s cubic-bezier(.4,0,.2,1);}
}
.comfy-jobs-drawer{
  /* top:0 대신 상단바 높이만큼 내려서 시작 — 안 그러면 패널이 화면 맨 위부터
     차지해 상단바(모듈 타이틀·검색 등)를 덮어버린다("작업목록이 너무 위까지
     다 차지"). */
  position:fixed; top:var(--topbar-h); right:0; bottom:0; z-index:1010;   /* 드로어 밴드 */
  /* 폭 350px — NAI 작업 목록과 규격 통일(과하게 넓다는 지적 2026-07-29). */
  width:min(350px, 92vw); display:flex; flex-direction:column;
  background:var(--surface); border-left:1px solid var(--border);
  box-shadow:var(--shadow); transform:translateX(100%);
  transition:transform .22s cubic-bezier(.4,0,.2,1);
}
.comfy-jobs-drawer.open{transform:translateX(0);}
.comfy-jobs-drawer-head{
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  padding:16px 18px; border-bottom:1px solid var(--border-soft); flex:0 0 auto;
}
.comfy-jobs-drawer-head .cjd-title{font-size:15px; font-weight:700; color:var(--text);}
/* 접기(✕)가 작업 목록을 닫는 유일한 출구이므로 라임으로 명확히 강조한다
   (NAI 작업 목록과 동일한 규칙). 호버 시 라임 배경으로 반전. */
.comfy-jobs-drawer-close{
  width:32px; height:32px; display:inline-flex; align-items:center; justify-content:center;
  background:var(--surface-2); border:1px solid var(--accent); border-radius:8px;
  color:var(--accent); cursor:pointer; font-weight:800; transition:all .12s;
}
.comfy-jobs-drawer-close:hover{background:var(--accent); color:var(--on-accent,#10140a); border-color:var(--accent);}

/* ── 작업 목록 엣지 핸들(드로어 핸들 / pull tab) ──────────────────────────
   접힌 상태에서 우측 가장자리에 세로 라임 라벨로 떠서 어디서든 바로 열 수 있게 한다.
   ComfyUI 모듈이 활성이고 드로어가 닫혀 있을 때만 보인다. */
.comfy-jobs-handle{
  position:fixed; right:0; top:50%; transform:translateY(-50%);
  z-index:1020; display:none; align-items:center; gap:6px;   /* 드로어 밴드(핸들) */
  padding:14px 7px; cursor:pointer;
  background:var(--accent); color:var(--on-accent,#10140a);
  border:none; border-radius:10px 0 0 10px;
  box-shadow:-4px 0 16px -4px rgba(0,0,0,.5);
  writing-mode:vertical-rl; text-orientation:mixed;
  font-family:inherit; font-size:11px; font-weight:800; letter-spacing:.14em;
  transition:padding .12s, filter .12s;
}
/* ComfyUI 모듈이 활성일 때만 노출(다른 모듈에서 떠 있으면 방해된다) */
body.comfy-module-active .comfy-jobs-handle{display:flex;}
/* 열려 있어도 숨기지 않는다 — 드로어 왼쪽 가장자리에 붙어 있다가 오른쪽으로
   밀면 다시 접힌다(사용자 요청). 열림 상태 기본 오프셋 = 드로어 폭. */
body.comfy-module-active.comfy-jobs-open .comfy-jobs-handle .cjh-badge{display:none;}
/* right 는 JS(syncComfyJobsHandlePos)가 드로어 실제 위치를 재서 인라인으로 준다.
   CSS 값(min(440px,92vw))으로 짐작하면 드로어 폭·위치와 어긋나 손잡이만 따로 떠 보였다.
   트랜지션도 걸지 않는다 — 매 프레임 따라가므로 있으면 오히려 한 박자 늦게 끌려온다. */
.comfy-jobs-handle:hover{padding-right:11px; filter:brightness(1.08);}
.comfy-jobs-handle .cjh-badge{
  writing-mode:horizontal-tb; min-width:16px; height:16px; padding:0 4px; border-radius:99px;
  background:rgba(0,0,0,.28); color:inherit; font-size:9px; font-weight:800;
  display:inline-flex; align-items:center; justify-content:center;
}
/* 당기는 동안에는 트랜지션을 끄고 JS가 transform 을 직접 갱신한다. */
body.comfy-jobs-dragging .comfy-jobs-drawer{transition:none !important;}
@media (min-width:901px){
  body.comfy-jobs-dragging .app-shell.comfy-jobs-docked .main{transition:none !important;}
}
/* 아이콘을 SVG(》)에서 ✕ 글리프로 바꿨다 — NAI 작업 목록과 동일한 모양·크기. */
.comfy-jobs-drawer-close{font-size:14px; line-height:1;}
.comfy-jobs-drawer-body{padding:16px 18px 24px; overflow-y:auto; flex:1 1 auto;}
@media (max-width:720px){ .comfy-stat-row{grid-template-columns:repeat(2,1fr);} }

/* 작업 목록 드로어 안의 "작업 목록 / 대기열" 탭 전환 */
.comfy-jobs-drawer-tabs{display:flex; gap:2px; padding:8px 18px 0; border-bottom:1px solid var(--border-soft); flex:0 0 auto;}
.cjd-tab{
  flex:1; text-align:center; padding:9px 6px; font-size:12.5px; font-weight:700;
  color:var(--text-faint); background:transparent; border:none; cursor:pointer;
  border-bottom:2px solid transparent; margin-bottom:-1px;
}
.cjd-tab:hover{color:var(--text-dim);}
.cjd-tab.active{color:var(--accent); border-bottom-color:var(--accent);}

/* 대기열(그룹) 패널 */
.comfy-groups-toolbar{margin-bottom:12px;}
.comfy-groups-list{display:flex; flex-direction:column; gap:12px;}
.comfy-group-card{border:1px solid var(--border-soft); border-radius:var(--radius); background:var(--surface); padding:13px 15px;}
.comfy-group-head{display:flex; align-items:center; justify-content:space-between; gap:10px; margin-bottom:8px;}
.comfy-group-name{font-size:13.5px; font-weight:700; color:var(--text);}
.comfy-group-head-actions{display:flex; align-items:center; gap:6px; flex:0 0 auto;}
/* 이름 변경은 파괴적이지 않으므로 삭제(빨강)와 달리 차분한 톤으로 둔다. */
.comfy-group-rename{
  background:var(--surface-2); border:1px solid var(--border); color:var(--text-dim);
  border-radius:var(--radius-sm); padding:3px 9px; font-size:11.5px; font-weight:600;
  cursor:pointer; font-family:inherit; white-space:nowrap;
}
.comfy-group-rename:hover{color:var(--text); border-color:var(--text-faint);}
.comfy-group-items{display:flex; flex-direction:column; gap:5px; margin-bottom:10px;}
/* 항목 한 줄 = [번호+썸네일+이름+펼침 화살표](버튼) + [앱 실행] + [제거]. 펼치면
   그 아래로 상세가 붙기 때문에 wrap을 허용하고, 상세 블록만 100% 폭을 차지하게
   한다. 좁아져도 이름은 한 줄 말줄임으로 유지한다(레이아웃 재배치 금지 규칙). */
.comfy-group-item{
  display:flex; align-items:center; justify-content:space-between; gap:8px; flex-wrap:wrap;
  background:var(--bg-elev); border:1px solid var(--border-soft); border-radius:7px;
  padding:6px 9px; font-size:12px; color:var(--text-dim);
}
.comfy-group-item.open{border-color:var(--border);}
.comfy-group-item-main{
  display:flex; align-items:center; gap:7px; min-width:0; flex:1 1 0;
  background:none; border:none; padding:0; margin:0; cursor:pointer;
  font:inherit; color:inherit; text-align:left;
  /* 썸네일 유무와 무관하게 줄 높이를 고정 — 섞여 있어도 어긋나지 않는다. */
  min-height:34px;
}
.comfy-group-item-main:hover{color:var(--text);}
.comfy-group-item-no{
  flex:0 0 auto; min-width:16px; height:16px; line-height:16px; text-align:center;
  border-radius:4px; background:var(--surface); color:var(--text-faint); font-size:10.5px; font-weight:700;
  align-self:center;
}
.comfy-group-item-caret{flex:0 0 auto; color:var(--text-faint); font-size:10px; align-self:center;}
/* 접힌 줄 요약: [번호][썸네일][앱 이름 / 프롬프트 한 줄][화살표]. 두 줄 모두
   한 줄 말줄임이라 좁아져도 세로로 접히지 않는다(레이아웃 재배치 금지 규칙).
   썸네일이 없는 항목(t2i 등)도 같은 크기의 빈 자리를 차지해, 썸네일이 있는 줄과
   높이·좌측 정렬이 정확히 맞는다. */
.comfy-group-item-thumb{
  flex:0 0 auto; width:34px; height:34px; object-fit:cover;
  border-radius:5px; border:1px solid var(--border-soft); background:var(--surface);
  align-self:center;
}
.comfy-group-item-thumb-empty{
  display:flex; align-items:center; justify-content:center;
  color:var(--text-faint); font-size:13px; line-height:1;
  border-style:dashed;
}
.comfy-group-item-txt{display:flex; flex-direction:column; justify-content:center; gap:1px; min-width:0; flex:1 1 auto; align-self:center;}
.comfy-group-item-label{overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.comfy-group-item-sub{
  font-size:10.5px; color:var(--text-faint);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}

/* ── 잘린 글자 흐르기(마퀴) ──────────────────────────────────────────────────
   `.mq-wrap > .mq-in` 두 겹 구조. 평소에는 말줄임으로 두고, JS(comfyMarqueeScan)가
   실제로 넘치는 요소에만 `.is-over` 와 이동 거리(--mq-shift)를 심는다. 마우스를
   올린 동안에만 좌우로 오가며(alternate) 전체를 보여 준다 — 가만히 있을 때도
   움직이면 목록 전체가 산만해진다. 넘치지 않는 글자에는 아무 일도 일어나지 않는다. */
.mq-wrap{overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.mq-wrap > .mq-in{display:inline-block; will-change:transform;}
.mq-wrap.is-over{text-overflow:clip;}          /* 흐르는 동안 말줄임 점은 방해된다 */
.comfy-group-item:hover .mq-wrap.is-over > .mq-in{
  animation:mqScroll var(--mq-dur, 4s) ease-in-out .35s infinite alternate;
}
@keyframes mqScroll{
  from{transform:translateX(0);}
  to  {transform:translateX(var(--mq-shift, 0px));}
}
/* 동작 최소화를 선호하는 사용자에게는 움직이지 않고 말줄임 그대로 둔다. */
@media (prefers-reduced-motion: reduce){
  .comfy-group-item:hover .mq-wrap.is-over > .mq-in{animation:none;}
  .mq-wrap.is-over{text-overflow:ellipsis;}
}

/* 앱 실행(▶) — 담아 둔 설정 그대로 생성 창을 연다. 상세 펼치기(▸)·제거(✕)와
   헷갈리지 않도록 아이콘과 title 로 구분하고, 세로 중앙에 맞춘다. */
.comfy-group-item-open{
  background:none; border:1px solid var(--border-soft); border-radius:5px;
  color:var(--text-faint); cursor:pointer; font-size:10.5px; line-height:1;
  flex:0 0 auto; align-self:center; padding:5px 7px;
}
.comfy-group-item-open:hover{color:var(--accent); border-color:var(--accent);}
/* 별칭 바꾸기 — 실행(▶)과 제거(✕) 사이의 중립 동작이라 테두리 없이 조용하게. */
.comfy-group-item-rename{
  background:none; border:none; color:var(--text-faint); cursor:pointer;
  font-size:11px; line-height:1; flex:0 0 auto; align-self:center; padding:0 3px;
}
.comfy-group-item-rename:hover{color:var(--text);}
.comfy-group-item-del{background:none; border:none; color:var(--text-faint); cursor:pointer; font-size:12px; flex:0 0 auto; padding:0 2px; align-self:center;}
.comfy-group-item-del:hover{color:var(--danger);}
/* 펼친 상세 — 항목이 실제로 제출할 값 그대로. 값이 길어도 카드가 무한정 늘어나지
   않도록 4줄까지만 보여주고 전문은 title(마우스 오버)로 넘긴다. */
.comfy-group-item-detail{
  flex:1 1 100%; margin-top:7px; padding-top:7px; border-top:1px solid var(--border-soft);
  display:flex; flex-direction:column; gap:5px;
}
.comfy-gi-row{display:flex; gap:8px; align-items:flex-start; font-size:11.5px;}
.comfy-gi-k{flex:0 0 88px; color:var(--text-faint); word-break:keep-all;}
.comfy-gi-v{flex:1 1 auto; min-width:0; color:var(--text-dim); overflow:hidden; overflow-wrap:anywhere;
  display:-webkit-box; -webkit-line-clamp:4; -webkit-box-orient:vertical;}
.comfy-gi-thumb{max-width:112px; max-height:112px; border-radius:6px; border:1px solid var(--border-soft); display:block;}
.comfy-group-empty{font-size:12px; color:var(--text-faint); padding:8px 0;}
.comfy-group-schedule-tag{
  display:flex; align-items:center; gap:6px; font-size:11.5px; color:var(--accent);
  background:var(--accent-soft); border-radius:999px; padding:4px 10px; width:fit-content; margin-bottom:10px;
}
.comfy-group-actions{display:flex; gap:8px; align-items:stretch; flex-wrap:wrap;}
.comfy-group-actions .btn-primary, .comfy-group-actions .btn-ghost{flex:1 1 auto; font-size:12.5px; padding:8px 10px;}
/* 대기열 반복 재생 횟수 입력 — "전체 재생" 옆에 붙는 작은 스텝 필드. */
.comfy-group-repeat-field{
  display:flex; align-items:center; gap:5px; flex:0 0 auto;
  background:var(--bg-elev); border:1px solid var(--border-soft); border-radius:7px; padding:2px 8px;
}
.comfy-group-repeat-lbl{font-size:11.5px; color:var(--text-dim); font-weight:600;}
.comfy-group-repeat-unit{font-size:11.5px; color:var(--text-faint);}
.comfy-group-repeat{
  width:38px; text-align:center; background:transparent; border:none; color:var(--text);
  font-size:13px; font-weight:700; padding:5px 0; -moz-appearance:textfield;
}
.comfy-group-repeat:focus{outline:none;}
.comfy-group-repeat:disabled{opacity:.5;}

/* 작업 목록 카드의 "대기열 대기 중" 문구 — 아직 GPU 큐에서 순서를 기다리는
   작업(실시간 진행 정보 없음)에만 노출. 색은 강조 토큰(accent)만 사용. */
.comfy-job-queued{
  margin-top:3px; font-size:11px; color:var(--accent); font-weight:600;
  display:flex; align-items:center; gap:6px;
}
.comfy-job-queued::before{
  content:""; width:6px; height:6px; border-radius:50%; background:var(--accent);
  animation:comfyQueuedPulse 1.4s ease-in-out infinite;
}
@keyframes comfyQueuedPulse{0%,100%{opacity:.35;}50%{opacity:1;}}

/* 정체 워치독 배지(요청 #155): GPU 대기/지연을 경고색으로 알린다. 기존 디자인
   토큰(--warn)만 사용. 사유는 title 속성으로 hover 시 전체 노출된다. */
.comfy-job-stall{
  margin-top:4px; font-size:11px; font-weight:600; line-height:1.4;
  display:flex; align-items:center; gap:6px; color:var(--warn);
  padding:3px 8px; border-radius:8px;
  background:color-mix(in srgb, var(--warn) 12%, transparent);
  border:1px solid color-mix(in srgb, var(--warn) 34%, transparent);
}
.comfy-job-stall::before{
  content:""; flex:0 0 auto; width:6px; height:6px; border-radius:50%; background:var(--warn);
}
.comfy-job-stall.is-slow::before{animation:comfyQueuedPulse 1.4s ease-in-out infinite;}

/* ── 범용 모달(대기열 그룹: 이름 입력 / 대기열 선택 / 예약 시각 / 확인) ──
   네이티브 prompt()/confirm()를 앱 톤으로 대체. 어떤 모달/라이트박스 위에서 띄워도
   보여야 하므로 다이얼로그 밴드(4000) — 토스트·팝오버만 이보다 위다.
   색·모서리·경계선은 전부 기존 디자인 토큰만 사용. */
.comfy-modal-overlay{
  position:fixed; inset:0; z-index:4000; display:flex; align-items:center; justify-content:center;
  /* "나가기 창 뜰때 배경이 전부 검은색으로 처리될 필요 있을까" — 60% 검정은
     확인 하나 띄우기엔 화면 전체를 너무 무겁게 가린다는 피드백으로 완화. */
  background:color-mix(in srgb, var(--bg) 62%, black 38%); padding:5vh 5vw;
  animation:comfyModalFade .12s ease-out;
}
html[data-theme="day"] .comfy-modal-overlay{background:color-mix(in srgb, var(--bg) 76%, black 24%);}
@keyframes comfyModalFade{from{opacity:0;}to{opacity:1;}}
.comfy-modal-box{
  width:min(440px, 94vw); max-height:88vh; overflow:auto;
  background:var(--bg-elev, var(--surface)); border:1px solid var(--border-soft);
  border-radius:var(--radius-md, 14px); padding:20px 20px 18px; box-shadow:0 10px 44px rgba(0,0,0,.45);
}
/* 사용자 선택 팝업을 채팅창(어시스턴트 패널) 옆에 앵커링할 때 — 화면 전체를
   무겁게 가리지 않도록 배경을 더 옅게(팝오버에 가깝게) 만든다. 실제 위치는
   JS가 패널의 getBoundingClientRect() 기준으로 매번 계산해서 inline style로
   지정한다(패널이 드래그로 움직일 수 있으므로). */
.comfy-modal-overlay.team-picker-anchored{background:color-mix(in srgb, var(--bg) 12%, transparent 88%);}
.team-user-picker-box{max-height:70vh;}
.comfy-modal-title{font-size:15px; font-weight:700; color:var(--text);}
.comfy-modal-msg{font-size:12.5px; color:var(--text-dim); margin-top:7px; line-height:1.55;}
.comfy-modal-input{
  width:100%; margin-top:14px; box-sizing:border-box;
  background:var(--bg-elev); border:1px solid var(--border); color:var(--text);
  border-radius:var(--radius-sm); padding:10px 12px; font-size:13.5px;
}
.comfy-modal-input:focus{outline:none; border-color:color-mix(in srgb, var(--accent) 55%, var(--border));}
/* 여러 입력을 세로로 나열하는 모달(회원 정보 수정 등)에서 각 입력 위 필드 라벨. */
.comfy-modal-field-label{display:block; font-size:11.5px; font-weight:600; color:var(--text-dim); margin-top:14px; letter-spacing:.01em;}
.comfy-modal-field-label + .comfy-modal-input{margin-top:6px;}
.comfy-modal-actions{display:flex; justify-content:flex-end; gap:8px; margin-top:18px;}
.comfy-modal-actions .btn-primary, .comfy-modal-actions .btn-ghost{font-size:12.5px; padding:8px 16px;}
/* 워크플로우 파일 교체 결과 리포트 모달 — 기존 모달 토큰만 재사용한다.
   좁아져도 항목을 재배치하지 않고(레이아웃 철칙) 세로 스크롤로만 처리한다. */
.comfy-wfrep-box{width:min(620px, 94vw);}
.comfy-wfrep-body{margin-top:14px; max-height:52vh; overflow:auto; display:flex; flex-direction:column; gap:10px;}
.comfy-wfrep-alert{
  background:var(--surface); border:1px solid color-mix(in srgb, var(--danger, #ff6b6b) 45%, var(--border));
  border-radius:var(--radius-sm); padding:10px 12px; font-size:12.5px; color:var(--text); line-height:1.55;
}
.comfy-wfrep-sec{
  background:var(--surface); border:1px solid var(--border-soft);
  border-radius:var(--radius-sm); padding:10px 12px;
}
.comfy-wfrep-sec.comfy-wfrep-bad{border-color:color-mix(in srgb, var(--danger, #ff6b6b) 40%, var(--border-soft));}
.comfy-wfrep-sec.comfy-wfrep-warn{border-color:color-mix(in srgb, var(--accent) 40%, var(--border-soft));}
.comfy-wfrep-sec-h{font-size:12.5px; font-weight:700; color:var(--text); display:flex; align-items:center; gap:7px;}
.comfy-wfrep-count{
  font-size:11px; font-weight:700; color:var(--text-dim);
  background:var(--bg-elev); border:1px solid var(--border-soft);
  border-radius:999px; padding:1px 7px;
}
.comfy-wfrep-note{font-size:11.5px; margin-top:5px; line-height:1.5;}
.comfy-wfrep-list{list-style:none; margin:7px 0 0; padding:0; display:flex; flex-direction:column; gap:5px;}
.comfy-wfrep-list li{font-size:12px; color:var(--text-dim); line-height:1.5; white-space:nowrap; overflow-x:auto;}
.comfy-wfrep-list li b{color:var(--text); font-weight:600;}
.comfy-wfrep-move{font-size:11.5px; color:var(--accent);}
.comfy-modal-choice-list{display:flex; flex-direction:column; gap:7px; margin-top:14px;}
/* 선택 모달의 부가 입력(플레이 리스트 담기의 작업 별칭) — 목록 아래에 붙는
   선택 입력란. 새 색·새 스타일 없이 기존 모달 토큰만 재사용한다. */
.comfy-modal-extra{margin-top:14px;}
.comfy-modal-extra-lbl{display:block; font-size:11.5px; font-weight:600; color:var(--text-dim); letter-spacing:.01em;}
.comfy-modal-extra-lbl + .comfy-modal-input{margin-top:6px;}
.comfy-modal-extra-hint{font-size:11px; color:var(--text-faint); margin-top:5px; line-height:1.5;}
.comfy-modal-choice-item{
  display:flex; align-items:center; gap:11px; width:100%; text-align:left; cursor:pointer;
  background:var(--surface); border:1px solid var(--border-soft); border-radius:var(--radius-sm);
  padding:11px 13px; color:var(--text-dim); transition:border-color .12s, background .12s;
}
.comfy-modal-choice-item:hover{border-color:var(--border); background:var(--bg-elev);}
.comfy-modal-choice-item.selected{
  border-color:color-mix(in srgb, var(--accent) 55%, var(--border)); background:var(--accent-soft); color:var(--text);
}
.comfy-modal-choice-radio{
  flex:0 0 auto; width:15px; height:15px; border-radius:50%;
  border:2px solid var(--border); background:transparent; position:relative;
}
.comfy-modal-choice-item.selected .comfy-modal-choice-radio{border-color:var(--accent);}
.comfy-modal-choice-item.selected .comfy-modal-choice-radio::after{
  content:""; position:absolute; inset:2.5px; border-radius:50%; background:var(--accent);
}
.comfy-modal-choice-text{display:flex; flex-direction:column; gap:2px; min-width:0;}
.comfy-modal-choice-label{font-size:13px; font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.comfy-modal-choice-sub{font-size:11px; color:var(--text-faint);}

/* ============================================================
   ComfyUI 대비(contrast) 개선 패스 — 눈 피로 완화 (요청)
   기존 comfy 규칙 "뒤"에 두어 동일 선택자를 소스 순서로 덮어쓴다. 색은
   전부 디자인 토큰만 사용하므로 night/day 테마가 각자의 값으로 자동 적용된다.
   색 계열(다크+라임 아이덴티티)은 그대로 두고 legibility만 끌어올린다:
   · 배경에 거의 묻히던 --border-soft 경계선을 --border로 강화(카드/입력/구분선)
   · 너무 흐리던 보조 텍스트 --text-faint 를 --text-dim 으로 상향
   · 필드 라벨 등 중요 텍스트는 --text 로 또렷하게
   ============================================================ */

/* 경계선 강화: 카드·입력·구분선·버튼 테두리가 배경에 묻히지 않도록 */
.comfy-divider{background:var(--border);}
.comfy-icon-btn{border-color:var(--border);}
.comfy-gen-card{border-color:var(--border);}
.comfy-gen-panel{border-color:var(--border);}
.comfy-gen-input{border-color:var(--border);}
.comfy-gen-output-empty{border-color:var(--border);}
.comfy-gen-imagedrop{border-color:var(--border);}
.comfy-gen-imageclear{border-color:var(--border);}
.comfy-gen-result{border-color:var(--border);}
.comfy-gen-text{border-color:var(--border);}
.comfy-gen-sliderwrap input.comfy-gen-slider-num{border-color:var(--border);}
.comfy-gen-seedwrap input.comfy-gen-seed-input, .comfy-gen-seedwrap select.comfy-gen-seed-mode{border-color:var(--border);}
.comfy-modal-body .field-row input, .comfy-modal-body .field-row select{border-color:var(--border);}
.comfy-af-cfg input, .comfy-af-cfg select{border-color:var(--border);}
.comfy-af-input{border-top-color:var(--border);}
.comfy-af-gpu-item{border-color:var(--border);}
.comfy-af-gpudrop-btn{border-color:var(--border);}
.comfy-af-gpudrop-panel{border-color:var(--border);}
.comfy-list-search{border-color:var(--border);}
.comfy-models-server{border-color:var(--border);}
.comfy-models-chip{border-color:var(--border);}
.comfy-ov-card{border-color:var(--border);}
.comfy-ov-refresh{border-color:var(--border);}
.comfy-stat-card{border-color:var(--border);}
.comfy-errlog-item{border-color:var(--border);}
.comfy-job-card{border-color:var(--border);}

/* 흐린 보조 텍스트 상향: --text-faint → --text-dim (가독성 확보) */
.comfy-line.muted{color:var(--text-dim);}
.comfy-empty{color:var(--text-dim);}
.comfy-wf-meta{color:var(--text-dim);}
.comfy-gen-card-meta{color:var(--text-dim);}
.comfy-stat-card .cs-label{color:var(--text-dim);}
.comfy-dash-section-title{color:var(--text-dim);}
.comfy-errlog-item .el-time{color:var(--text-dim);}
.comfy-job-card .jc-meta{color:var(--text-dim);}

/* 필드 라벨 등 중요 텍스트는 본문급으로 또렷하게 (--text-dim → --text) */
.comfy-gen-field label,
.comfy-modal-body .field-row label{color:var(--text);}

/* 항목 1: 앱 파라미터 설정 모달의 닫기(✕) 버튼이 헤더 배경(--bg-elev)과 같은
   색이라 안 보이던 문제 — 표면 톤 + 뚜렷한 테두리로 분명한 버튼처럼 보이게 한다.
   (comfy 모달에만 적용; 마스크 에디터의 동일 클래스는 건드리지 않는다.) */
.comfy-modal-head .mask-editor-close{background:var(--surface-2); border-color:var(--border); color:var(--text);}
.comfy-modal-head .mask-editor-close:hover{border-color:var(--accent); color:var(--accent);}

/* 항목 2: 실행 폼 헤더의 "수정"(앱 파라미터 설정) 버튼 + 버튼 묶음 */
.comfy-gen-head-actions{display:flex; align-items:center; gap:8px; flex-shrink:0;}
.comfy-gen-editbtn{background:var(--surface-2); border:1px solid var(--border); color:var(--text-dim); border-radius:var(--radius-sm); padding:5px 13px; font-size:12.5px; font-weight:600; cursor:pointer; font-family:inherit; white-space:nowrap;}
.comfy-gen-editbtn:hover{border-color:var(--accent); color:var(--accent);}

/* ============================================================
   포탈(로그인 후 착지: 모듈 런처 + 팀 공유 피드) — 셸의 기존 디자인
   토큰만 사용(네오 라임 다크/데이). 목업 변수는 베끼지 않고 실제
   :root/테마 변수를 재사용한다.
   ============================================================ */
.portal{max-width:1180px; margin:0 auto; padding:6px 4px 60px;}
.portal-hero{display:flex; align-items:flex-end; justify-content:space-between; gap:20px; flex-wrap:wrap; margin-bottom:22px;}
/* 히어로 텍스트 컬럼은 flex 자식이므로 기본 min-width:auto가 콘텐츠 최소폭을
   보장해 좁은 폭에서 형제(역할 pill)와 함께 오버플로를 유발할 수 있다. min-width:0
   + flex:1로 자연스러운 줄바꿈을 보장(방어). 데스크톱 레이아웃에는 영향 없음. */
.portal-hero-tx{flex:1 1 auto; min-width:0;}
.portal-hello{font-family:var(--display); font-size:30px; font-weight:700; letter-spacing:-.02em; margin:0 0 6px; color:var(--text);}
.portal-sub{margin:0; color:var(--text-dim); font-size:14px;}
.portal-rolepill{display:inline-flex; align-items:center; gap:8px; background:var(--surface); border:1px solid var(--border); border-radius:999px; padding:8px 15px; font-size:12.5px; font-weight:600; color:var(--text-dim); white-space:nowrap;}
.portal-rolepill b{color:var(--accent); font-weight:700;}

.portal-banner{display:flex; align-items:center; gap:13px; background:var(--accent-soft); border:1px solid color-mix(in srgb, var(--accent) 40%, var(--border)); border-radius:var(--radius); padding:14px 18px; margin-bottom:20px; color:var(--text);}
.portal-banner svg{width:22px; height:22px; flex:0 0 auto; color:var(--accent);}
.portal-banner-tx{flex:1; font-size:13.5px; line-height:1.5;}
.portal-banner-link{color:var(--accent); font-weight:700; font-size:13px; cursor:pointer; white-space:nowrap;}
.portal-banner-link:hover{text-decoration:underline;}

.portal-wait{display:flex; flex-direction:column; align-items:center; text-align:center; gap:12px; background:var(--surface); border:1px solid var(--border); border-radius:var(--radius); padding:44px 28px; margin-bottom:24px;}
.portal-wait-icon{width:58px; height:58px; border-radius:16px; background:var(--accent-soft); display:flex; align-items:center; justify-content:center; color:var(--accent);}
.portal-wait-icon svg{width:30px; height:30px;}
.portal-wait-title{font-family:var(--display); font-size:19px; font-weight:700; color:var(--text);}
.portal-wait-desc{color:var(--text-dim); font-size:13.5px; max-width:440px; line-height:1.6;}
.portal-wait-actions{display:flex; gap:10px; margin-top:6px; flex-wrap:wrap; justify-content:center;}

.portal-admband{background:var(--surface); border:1px solid var(--border); border-radius:var(--radius); padding:18px 18px 20px; margin-bottom:26px;}
.portal-admband-head{display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:14px;}
.portal-admband-tag{background:var(--ink-soft); color:var(--ink); border-radius:999px; padding:4px 12px; font-size:11.5px; font-weight:700; letter-spacing:.02em; white-space:nowrap;}
.portal-admband-note{color:var(--text-faint); font-size:12px; line-height:1.4;}
.portal-admband-grid{display:grid; grid-template-columns:repeat(3,1fr); gap:12px;}

.portal-sec-head{display:flex; align-items:center; justify-content:space-between; margin:28px 2px 13px;}
.portal-sec-title{font-family:var(--display); font-size:15px; font-weight:700; text-transform:uppercase; letter-spacing:.06em; color:var(--text-dim);}
.portal .section-link{cursor:pointer; color:var(--text-faint); font-size:12.5px; font-weight:600;}
.portal .section-link:hover{color:var(--accent);}

.portal-modgrid{display:grid; grid-template-columns:repeat(3,1fr); gap:14px;}
.portal-commongrid{display:grid; grid-template-columns:repeat(4,1fr); gap:12px;}

/* 모듈/공통/관리 카드 공통 */
.pcard{position:relative; display:flex; flex-direction:column; gap:10px; background:var(--surface); border:1px solid var(--border); border-radius:var(--radius); padding:18px; cursor:pointer; transition:border-color .13s, transform .13s, background .13s; text-align:left; overflow:hidden;}
.pcard:hover{border-color:color-mix(in srgb, var(--accent) 55%, var(--border)); transform:translateY(-2px);}
.pcard-icon{width:40px; height:40px; border-radius:11px; background:var(--surface-2); display:flex; align-items:center; justify-content:center; color:var(--accent); flex:0 0 auto;}
.pcard-icon svg{width:22px; height:22px;}
/* 한국어 라벨은 기본(word-break:normal)에서 CJK가 글자 단위로 아무 데서나 줄바꿈돼
   "스토리보드 메이" / "커"처럼 어절 중간이 끊긴다. keep-all로 공백(어절) 경계에서만
   줄바꿈해 "스토리보드" / "메이커"처럼 단어 단위로 접히게 한다. 포탈 카드 이름은
   모두 짧은 어절이라(프롬프트 메이커·3D 시뮬레이터 등) 오버플로 위험이 없다. */
.pcard-name{font-family:var(--display); font-size:16px; font-weight:700; color:var(--text); word-break:keep-all;}
.pcard-desc{font-size:12.5px; color:var(--text-dim); line-height:1.5; flex:1;}
.pcard-foot{display:flex; align-items:center; justify-content:space-between; gap:8px; margin-top:2px;}
.pcard-pill{font-size:11px; font-weight:700; padding:3px 10px; border-radius:999px; white-space:nowrap;}
.pcard-pill.ok{background:var(--accent-soft); color:var(--accent);}
.pcard-pill.locked{background:var(--surface-2); color:var(--text-faint);}
.pcard-meta{font-size:11.5px; color:var(--text-faint);}
.pcard-open{font-size:12px; font-weight:700; color:var(--accent);}
/* 잠긴 모듈 카드(dim) */
.pcard.locked{cursor:default; opacity:.62;}
.pcard.locked:hover{border-color:var(--border); transform:none;}
.pcard.locked .pcard-icon{color:var(--text-faint);}
.pcard-reqbtn{background:transparent; border:1px solid var(--border); color:var(--text-dim); border-radius:var(--radius-sm); padding:4px 11px; font-size:11.5px; font-weight:600; cursor:pointer; font-family:inherit;}
.pcard-reqbtn:hover{border-color:var(--accent); color:var(--accent);}
.pcard-lockmark{position:absolute; top:14px; right:14px; color:var(--text-faint);}
.pcard-lockmark svg{width:15px; height:15px;}
/* 관리 콘솔 밴드 안 카드는 조금 더 낮은 높이 */
.portal-admband-grid .pcard{padding:15px 16px;}
/* 공통 그리드 카드(4열)는 컴팩트하게 */
.portal-commongrid .pcard{padding:15px 15px;}
.portal-commongrid .pcard-desc{display:none;}

/* 내 작업물 */
.portal-works{display:grid; grid-template-columns:repeat(4,1fr); gap:12px;}
.pwork{position:relative; aspect-ratio:1/1; border-radius:var(--radius-sm); overflow:hidden; background:var(--surface-2); border:1px solid var(--border-soft); cursor:pointer;}
.pwork img, .pwork video{width:100%; height:100%; object-fit:cover; display:block;}
.pwork.is-private img, .pwork.is-private video{filter:blur(7px) brightness(.7);}
.pwork-vis{position:absolute; top:7px; right:7px; display:inline-flex; align-items:center; gap:4px; background:rgba(0,0,0,.62); color:#fff; border:none; border-radius:999px; padding:4px 9px; font-size:10.5px; font-weight:700; cursor:pointer; backdrop-filter:blur(4px); z-index:2;}
.pwork-vis.pub{background:color-mix(in srgb, var(--accent) 82%, #000); color:var(--on-accent);}
.pwork-vis svg{width:11px; height:11px;}
/* 라이트박스(대시보드/갤러리/피드 공용) 안의 공개/비공개 토글 — "내 작업물"
   타일의 .pwork-vis(절대배치 코너 뱃지)와 달리 여기는 usage 태그 줄 안에 인라인으로
   들어가므로 .feed-tag를 베이스로 쓰되 클릭 가능하게 커서/호버만 얹는다. */
.lightbox-vis-btn{cursor:pointer; font-family:var(--sans); transition:.12s;}
.lightbox-vis-btn:hover{border-color:var(--border);}
.lightbox-vis-btn.pub{background:color-mix(in srgb, var(--accent) 16%, var(--bg-elev)); border-color:color-mix(in srgb, var(--accent) 40%, transparent); color:var(--accent);}
.lightbox-share-btn{cursor:pointer; font-family:var(--sans); transition:.12s;}
.lightbox-share-btn:hover{border-color:var(--border); color:var(--text-dim);}
/* 공유 모달의 대상 목록 항목 — 사용자 선택 팝업(.team-user-pick)과 톤을 통일. */
.share-target{cursor:pointer;}
.pwork-badge{position:absolute; left:7px; bottom:7px; background:rgba(0,0,0,.6); color:#fff; border-radius:6px; padding:2px 7px; font-size:10px; font-weight:600; backdrop-filter:blur(4px);}

/* 팀 공유 피드 그리드(포탈 프리뷰 8열 + 전용화면 3열) */
.portal-feedgrid{display:grid; grid-template-columns:repeat(8,1fr); gap:9px;}
.feed-empty{grid-column:1/-1; text-align:center; color:var(--text-faint); font-size:13px; padding:34px 10px; border:1px dashed var(--border); border-radius:var(--radius);}
.ftile{position:relative; aspect-ratio:1/1; border-radius:var(--radius-sm); overflow:hidden; background:var(--surface-2); border:1px solid var(--border-soft); cursor:pointer;}
.ftile img, .ftile video{width:100%; height:100%; object-fit:cover; display:block;}
.ftile-author{position:absolute; top:6px; left:6px; width:22px; height:22px; border-radius:50%; background:color-mix(in srgb, var(--accent) 80%, #000); color:var(--on-accent); font-size:10px; font-weight:800; display:flex; align-items:center; justify-content:center; z-index:2;}
.ftile-play{position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); color:#fff; opacity:.9; pointer-events:none;}
.ftile-play svg{width:26px; height:26px; filter:drop-shadow(0 1px 3px rgba(0,0,0,.6));}
.ftile-lab{position:absolute; left:6px; bottom:6px; background:rgba(0,0,0,.6); color:#fff; border-radius:5px; padding:1px 6px; font-size:9.5px; font-weight:600; backdrop-filter:blur(3px);}
.ftile-stats{position:absolute; right:6px; bottom:6px; display:flex; gap:7px; background:rgba(0,0,0,.6); color:#fff; border-radius:6px; padding:2px 7px; font-size:10px; font-weight:600; backdrop-filter:blur(3px);}
.ftile-stats span{display:inline-flex; align-items:center; gap:3px;}
.ftile-stats svg{width:10px; height:10px;}

/* 전용 피드 화면 */
.feedpage{display:grid; grid-template-columns:1fr 390px; gap:18px; height:100%; align-items:start;}
.feedpage-main{min-width:0;}
.feedpage-toolbar{display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:14px;}
.feed-filter-chips{display:flex; gap:8px; flex-wrap:wrap;}
.fchip{background:var(--surface); border:1px solid var(--border); color:var(--text-dim); border-radius:999px; padding:6px 15px; font-size:12.5px; font-weight:600; cursor:pointer; font-family:inherit;}
.fchip:hover{border-color:var(--accent); color:var(--text);}
.fchip.active{background:var(--accent); border-color:var(--accent); color:var(--on-accent);}
.feedpage-grid{display:grid; grid-template-columns:repeat(3,1fr); gap:12px;}
.feedpage-grid .ftile{aspect-ratio:1/1;}
.feedpage-grid .ftile.selected{outline:2px solid var(--accent); outline-offset:1px;}

.feedpage-detail{position:sticky; top:0; background:var(--surface); border:1px solid var(--border); border-radius:var(--radius); padding:16px; max-height:calc(100vh - 130px); overflow:auto;}
.feed-detail-empty{color:var(--text-faint); font-size:13px; text-align:center; padding:60px 12px;}
/* 모바일 전용 피드 상세 닫기 버튼 — 데스크톱에서는 숨김. 모바일(≤768px)에서 상세가
   전체화면 오버레이로 뜰 때만 노출된다(위 Phase 2 미디어쿼리 참조). */
.fd-mobile-close{
  display:none; position:absolute; top:10px; right:12px; z-index:3;
  align-items:center; gap:5px; background:var(--surface-2);
  border:1px solid var(--border); color:var(--text-dim);
  border-radius:999px; padding:8px 14px; font-size:12.5px; font-weight:700;
  font-family:inherit; cursor:pointer;
}
.fd-mobile-close svg{width:14px; height:14px;}
.fd-media{width:100%; border-radius:var(--radius-sm); overflow:hidden; background:var(--surface-2); margin-bottom:13px;}
.fd-media img, .fd-media video{width:100%; display:block; max-height:340px; object-fit:contain; background:#000;}
.fd-title{font-family:var(--display); font-size:16px; font-weight:700; margin-bottom:4px; color:var(--text);}
.fd-meta{font-size:12px; color:var(--text-dim); margin-bottom:12px;}
.fd-prompt{position:relative; background:var(--surface-2); border:1px solid var(--border-soft); border-radius:var(--radius-sm); padding:11px 12px; font-size:12.5px; color:var(--text-dim); line-height:1.55; margin-bottom:12px; white-space:pre-wrap; word-break:break-word; max-height:160px; overflow:auto;}
.fd-copy{position:absolute; top:8px; right:8px; background:var(--surface); border:1px solid var(--border); color:var(--text-dim); border-radius:6px; padding:3px 9px; font-size:11px; font-weight:600; cursor:pointer;}
.fd-copy:hover{border-color:var(--accent); color:var(--accent);}
.fd-params{display:grid; grid-template-columns:repeat(3,1fr); gap:8px; margin-bottom:14px;}
.fd-param{background:var(--surface-2); border:1px solid var(--border-soft); border-radius:var(--radius-sm); padding:8px 10px;}
.fd-param-k{font-size:10px; color:var(--text-faint); text-transform:uppercase; letter-spacing:.04em;}
.fd-param-v{font-size:12.5px; font-weight:600; color:var(--text); margin-top:2px; word-break:break-word;}
.fd-actions{display:flex; gap:8px; margin-bottom:16px; flex-wrap:wrap;}
.fd-actbtn{display:inline-flex; align-items:center; gap:6px; background:var(--surface-2); border:1px solid var(--border); color:var(--text-dim); border-radius:var(--radius-sm); padding:7px 12px; font-size:12px; font-weight:600; cursor:pointer; font-family:inherit;}
.fd-actbtn:hover{border-color:var(--accent); color:var(--accent);}
.fd-actbtn.liked{background:var(--accent-soft); border-color:color-mix(in srgb, var(--accent) 45%, var(--border)); color:var(--accent);}
.fd-actbtn svg{width:14px; height:14px;}
.fd-comments-head{font-size:12px; font-weight:700; color:var(--text-dim); text-transform:uppercase; letter-spacing:.04em; margin-bottom:10px;}
.fd-comment{display:flex; gap:8px; margin-bottom:11px;}
.fd-comment-av{width:26px; height:26px; border-radius:50%; background:var(--surface-2); color:var(--text-dim); font-size:10px; font-weight:800; display:flex; align-items:center; justify-content:center; flex:0 0 auto;}
.fd-comment-body{flex:1; min-width:0;}
.fd-comment-top{display:flex; align-items:center; gap:7px;}
.fd-comment-name{font-size:12px; font-weight:700; color:var(--text);}
.fd-comment-time{font-size:10.5px; color:var(--text-faint);}
.fd-comment-del{margin-left:auto; background:none; border:none; color:var(--text-faint); font-size:11px; cursor:pointer; padding:0;}
.fd-comment-del:hover{color:var(--danger);}
.fd-comment-text{font-size:12.5px; color:var(--text-dim); line-height:1.5; margin-top:2px; word-break:break-word;}
.fd-comment-input{display:flex; gap:8px; margin-top:10px;}
.fd-comment-input input{flex:1; background:var(--surface-2); border:1px solid var(--border); color:var(--text); border-radius:var(--radius-sm); padding:8px 11px; font-size:12.5px; font-family:inherit;}
.fd-comment-input input:focus{outline:none; border-color:var(--accent);}
.fd-comment-input button{background:var(--accent); border:none; color:var(--on-accent); border-radius:var(--radius-sm); padding:0 14px; font-size:12.5px; font-weight:700; cursor:pointer; font-family:inherit;}

/* 반응형 */
@media (max-width:1100px){
  .portal-feedgrid{grid-template-columns:repeat(5,1fr);}
  .feedpage{grid-template-columns:1fr;}
  .feedpage-detail{position:static; max-height:none;}
}
@media (max-width:820px){
  .portal-modgrid{grid-template-columns:repeat(2,1fr);}
  .portal-commongrid{grid-template-columns:repeat(2,1fr);}
  .portal-admband-grid{grid-template-columns:1fr;}
  .portal-works{grid-template-columns:repeat(3,1fr);}
  .portal-feedgrid{grid-template-columns:repeat(4,1fr);}
  .feedpage-grid{grid-template-columns:repeat(2,1fr);}
}
@media (max-width:520px){
  .portal-modgrid{grid-template-columns:1fr;}
  .portal-works{grid-template-columns:repeat(2,1fr);}
  .portal-feedgrid{grid-template-columns:repeat(3,1fr);}
}

/* ── 갤러리 게시물형 세로 목록(배열: 목록형) ─────────────────────────────────
   폴더 카드의 대안 배열 — 게시판처럼 작품 한 줄씩. 클릭·드롭·관리 버튼은 카드와 동일.
   한 줄 안에서는 재배치·줄바꿈 금지: 설명이 말줄임되고, 그래도 좁으면 가로 스크롤. */
.gal-lrows{display:flex; flex-direction:column; gap:8px;}
.gal-lrow{display:flex; align-items:center; gap:12px; white-space:nowrap; overflow-x:auto;
  background:var(--surface); border:1px solid var(--border-soft); border-left:4px solid var(--accent);
  border-radius:var(--radius-sm); padding:8px 12px; cursor:pointer; outline:none;
  transition:border-color .12s ease, background .12s ease;}
.gal-lrow:hover, .gal-lrow:focus-visible{border-color:var(--accent); background:var(--surface-2);}
.gal-lrow-thumb{flex:0 0 auto; width:64px; height:44px; border-radius:7px; overflow:hidden;
  background:color-mix(in srgb, var(--accent) 82%, #000); display:flex; align-items:center; justify-content:center;}
.gal-lrow-thumb img, .gal-lrow-thumb video{width:100%; height:100%; object-fit:cover; display:block;}
.gal-lrow-thumb .gal-fcard-glyph svg{width:22px; height:22px;}
.gal-lrow-name{font-size:13.5px; font-weight:700; color:var(--text); flex:0 0 auto;}
.gal-lrow-desc{font-size:12px; overflow:hidden; text-overflow:ellipsis; max-width:40%;}
/* 목록 행의 여백 채움 요소(스페이서) — 개수·액션 영역을 오른쪽 끝으로 밀어낸다.
   대응 규칙이 없어서 0폭으로 접혀 있었고, 그 결과 개수·액션이 설명 바로 뒤에
   붙어 있었다. 좁은 폭에서는 .gal-lrow 의 overflow-x:auto 로 가로 스크롤되고
   (재배치·세로접힘 없음), 남는 공간이 0 이면 스페이서도 0 으로 줄어들어 다른
   칸을 밀어내지 않는다. */
.gal-head-spacer{flex:1 1 auto; min-width:0;}
.gal-lrow-count{font-size:11.5px; flex:0 0 auto;}
.gal-lrow-acts{display:flex; gap:6px; flex:0 0 auto; opacity:0; transition:opacity .12s ease;}
.gal-lrow:hover .gal-lrow-acts, .gal-lrow:focus-visible .gal-lrow-acts{opacity:1;}
.gal-lrow.gal-drop-hover{outline:2px dashed var(--accent); outline-offset:-2px;}

/* ── 갤러리 윈도우식 다중 선택 ─────────────────────────────────────────────
   빈 공간 드래그로 선택 사각형(마퀴)을 그려 카드들을 한 번에 선택하고,
   선택된 카드 하나를 끌면 전체가 함께 이동한다. */
.gal-marquee{position:fixed; z-index:5000; pointer-events:none;
  border:1px solid var(--accent); background:color-mix(in srgb, var(--accent) 14%, transparent);
  border-radius:2px;}
.gal-drag-ghost{position:fixed; left:-9999px; top:-9999px; z-index:5010; pointer-events:none;
  background:var(--accent); color:var(--on-accent,#10140a); font-size:12px; font-weight:800;
  padding:6px 12px; border-radius:999px; box-shadow:var(--shadow);}
/* 미분류 인라인 섹션 — 폴더 카드가 아니라 그리드로 바로 나열(사용자 지시) */
.gal-unfiled-head{display:flex; align-items:center; gap:10px; margin:18px 0 10px;
  white-space:nowrap; overflow-x:auto; padding:6px 4px; border-radius:8px;}
.gal-unfiled-head .t{font-size:14px; font-weight:700; color:var(--text);}
.gal-unfiled-head.gal-drop-hover{outline:2px dashed var(--accent); outline-offset:-2px;}

/* ── 태그 자동완성 · 태그 검색기 · 문서 뷰어 (2026-07-31) ──────────────────
   ComfyUI 생성 폼의 프롬프트 성격 입력칸에 붙는 자동완성 드롭다운과, 라벨 줄의
   🔍 버튼이 여는 태그 검색기 모달, 튜토리얼에서 여는 md 문서 뷰어. 색·반경·그림자는
   전부 셸 토큰(--bg2/--border/--accent/--shadow)을 그대로 쓴다. */
.tagac-wrap{position:relative; display:block; min-width:0;}
.tagac-wrap > .comfy-gen-input{width:100%;}
.tagac-drop{
  position:absolute; left:0; right:0; top:calc(100% + 3px); z-index:60;
  display:none; max-height:230px; overflow-y:auto;
  background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-sm);
  box-shadow:var(--shadow);
}
.tagac-drop.open{display:block;}
.tagac-drop.up{top:auto; bottom:calc(100% + 3px);}
.tagac-item{
  display:flex; align-items:center; gap:8px; padding:6px 10px; cursor:pointer;
  font-family:var(--mono); font-size:11.5px; color:var(--text);
  border-bottom:1px solid var(--border-soft); white-space:nowrap; overflow-x:auto;
}
.tagac-item:last-child{border-bottom:none;}
.tagac-item:hover, .tagac-item.active{background:color-mix(in srgb, var(--accent) 14%, transparent);}
.tagac-item em{font-style:normal; color:var(--accent); font-weight:700;}
.tagac-tag{flex:1 1 auto; min-width:0;}
.tagac-count{flex:0 0 auto; font-size:10.5px; color:var(--text-faint);}
.tagac-badge{
  flex:0 0 auto; font-family:var(--sans); font-size:9.5px; font-weight:700; letter-spacing:.04em;
  padding:1px 6px; border-radius:999px; text-transform:uppercase;
  background:color-mix(in srgb, var(--text-dim) 18%, transparent); color:var(--text-dim);
}
/* 카테고리 배지 — Danbooru 사전의 category 코드(0 일반 / 1 작가 / 3 작품 / 4 캐릭터 / 5 메타).
   기본형(.tagac-badge)이 "일반"이고 나머지만 색을 달리한다. text-transform:uppercase 는
   한글에 영향이 없으므로 그대로 둔다. */
.tagac-badge-artist{background:color-mix(in srgb, var(--accent) 22%, transparent); color:var(--accent);}
.tagac-badge-char{background:var(--ink-soft); color:var(--ink);}
.tagac-badge-copy{background:color-mix(in srgb, var(--gold) 22%, transparent); color:var(--gold);}
.tagac-badge-meta{background:color-mix(in srgb, var(--text-faint) 18%, transparent); color:var(--text-faint);}
.tagac-hint{padding:8px 10px; font-size:11.5px; color:var(--text-faint);}
/* 라벨 줄의 태그 검색기 버튼 — 라벨과 같은 줄 오른쪽 끝(.comfy-gen-field-head 는
   이미 space-between). 좁아져도 줄바꿈하지 않는다(레이아웃 철칙). */
.comfy-gen-tagsearch-btn{
  flex:0 0 auto; white-space:nowrap; cursor:pointer;
  font-family:inherit; font-size:11px; font-weight:600; padding:3px 9px;
  border-radius:999px; border:1px solid var(--border);
  background:transparent; color:var(--text-dim); transition:all .12s ease;
}
.comfy-gen-tagsearch-btn:hover{border-color:var(--accent); color:var(--accent);}

.tagsearch-box{width:min(560px, 94vw); max-height:86vh; display:flex; flex-direction:column;}
.tagsearch-input{margin-top:14px;}
.tagsearch-chips{display:flex; gap:6px; margin-top:10px; white-space:nowrap; overflow-x:auto; padding-bottom:2px;}
.tagsearch-chip{
  flex:0 0 auto; cursor:pointer; font-family:inherit; font-size:11.5px; font-weight:600;
  padding:4px 11px; border-radius:999px; border:1px solid var(--border);
  background:transparent; color:var(--text-dim); transition:all .12s ease;
}
.tagsearch-chip:hover{border-color:var(--accent); color:var(--accent);}
.tagsearch-chip.active{background:var(--accent); border-color:var(--accent); color:var(--on-accent,#10140a);}
.tagsearch-list{
  flex:1 1 auto; min-height:120px; max-height:46vh; overflow-y:auto; margin-top:12px;
  border:1px solid var(--border); border-radius:var(--radius-sm); background:var(--bg-elev);
}
.tagsearch-empty{padding:16px 12px; font-size:12px; color:var(--text-faint); text-align:center; line-height:1.6;}
.tagsearch-row{display:flex; align-items:stretch; gap:0; border-bottom:1px solid var(--border-soft);}
.tagsearch-row:last-child{border-bottom:none;}
.tagsearch-pick{
  flex:1 1 auto; min-width:0; display:flex; align-items:center; gap:8px;
  padding:7px 10px; cursor:pointer; text-align:left;
  background:transparent; border:none; color:var(--text);
  font-family:var(--mono); font-size:11.5px;
  white-space:nowrap; overflow-x:auto;
}
.tagsearch-pick:hover{background:color-mix(in srgb, var(--accent) 12%, transparent);}
.tagsearch-pick em{font-style:normal; color:var(--accent); font-weight:700;}
.tagsearch-tag{flex:0 0 auto;}
.tagsearch-meta{flex:0 0 auto; font-family:var(--sans); font-size:10.5px; color:var(--text-faint); margin-left:auto; padding-left:10px;}
.tagsearch-copy{
  flex:0 0 auto; cursor:pointer; border:none; border-left:1px solid var(--border-soft);
  background:transparent; color:var(--text-dim); font-family:inherit; font-size:11px; padding:0 12px;
}
.tagsearch-copy:hover{color:var(--accent);}

.docview-box{width:min(760px, 95vw); max-height:88vh; display:flex; flex-direction:column;}
.docview-body{
  flex:1 1 auto; min-height:0; overflow-y:auto; margin-top:14px; padding-right:6px;
  font-size:13px; line-height:1.7; color:var(--text-dim);
}
.docview-body h2{font-size:17px; color:var(--text); margin:20px 0 8px;}
.docview-body h3{font-size:14.5px; color:var(--text); margin:18px 0 6px;}
.docview-body h4, .docview-body h5{font-size:13px; color:var(--text); margin:14px 0 5px;}
.docview-body p{margin:0 0 10px;}
.docview-body ul{margin:0 0 12px; padding-left:20px;}
.docview-body li{margin:0 0 4px;}
.docview-body code{font-family:var(--mono); font-size:11.5px; background:var(--bg-elev); padding:1px 5px; border-radius:4px;}
.docview-callout{
  margin:10px 0 14px; padding:10px 12px; border-radius:var(--radius-sm);
  border:1px solid var(--border); background:var(--bg-elev); font-size:12.5px; line-height:1.65;
}
/* 표는 좁아져도 재배치·세로접힘 없이 가로 스크롤로 처리한다(레이아웃 철칙). */
.docview-tablewrap{overflow-x:auto; margin:8px 0 16px; border:1px solid var(--border); border-radius:var(--radius-sm);}
.docview-tablewrap table{border-collapse:collapse; width:100%; min-width:max-content;}
.docview-tablewrap th, .docview-tablewrap td{
  padding:7px 11px; border-bottom:1px solid var(--border-soft); text-align:left;
  font-size:12px; vertical-align:top; white-space:nowrap;
}
.docview-tablewrap th{background:var(--bg-elev); color:var(--text); font-weight:700; font-size:11.5px;}
.docview-tablewrap tr:last-child td{border-bottom:none;}

/* ============================================================
   모달 표준화 (2026-08-03) — 셸 전 모달 공통 규칙
   ------------------------------------------------------------
   구현부는 app.js의 "모달 표준화(modalStd)" 절. 세 가지를 강제한다.

   1) 배경(바깥) 클릭으로는 절대 닫히지 않는다.
      실수로 눌러 작업 중이던 입력이 통째로 날아간다는 지적. 인라인
      onclick="if(event.target===this)..." 패턴은 전부 삭제했고, 남은/새로
      생기는 모달은 캡처 단계 가드가 배경 클릭을 삼킨다.
      닫기는 ✕ · Esc · 명시적 [취소]/[닫기] 버튼으로만.

   2) 우측 상단 라임(--accent) ✕.
      기존 ✕가 있는 모달은 그것을 숨기고 표준 ✕가 대신 눌러 준다 — 닫기
      로직(폼 리셋·프라미스 resolve 등)은 원래 것을 그대로 재사용한다.
      순수 뷰어(라이트박스·갤러리 모달 등)는 이미 우측 상단에 있는 ✕를
      라임으로 물들이기만 한다(레이아웃 손대지 않음).

   3) ✕ 왼쪽에 최소화(—).
      누르면 본문만 감추고 헤더 칩만 좌하단에 남는다. DOM은 그대로라
      입력값·캔버스·스크롤 상태가 전부 보존된다. 접힌 동안 백드롭은
      투명 + pointer-events:none 이라 뒤 화면을 그대로 조작할 수 있다.
      여러 개를 접으면 --mstd-slot 으로 세로로 쌓아 겹치지 않게 한다.

   좁은 폭에서도 헤더가 세로로 접히지 않도록 표준 버튼 그룹은 absolute
   배치 + nowrap 이다(레이아웃 철칙: 재배치 금지).
   ============================================================ */
.mstd-host{position:relative;}
/* 배치 규칙(2026-08-03 재정비): "언제나 창 안쪽 우상단 10px".
   이전 구현은 0높이 sticky + 음수 margin 이라, padding 이 0인 창(.comfy-modal,
   .cmdk-panel, .mask-editor-panel, .sim3d-tut-modal, .gal-modal-box …)에서
   버튼 바가 창 위쪽 바깥으로 6px 튀어나오고 overflow:hidden 에 잘렸다.
   이제는 창(.mstd-host = position:relative)을 기준으로 절대배치해 창 padding·
   border-radius·overflow 설정과 무관하게 항상 안쪽에 놓인다. */
.mstd-actions{
  position:absolute; top:var(--mstd-top, 10px); right:var(--mstd-right, 10px); z-index:6;
  height:auto; margin:0; pointer-events:none;
  display:flex; align-items:center; justify-content:flex-end;
  gap:6px; white-space:nowrap; flex-wrap:nowrap;
}
.mstd-actions > *{pointer-events:auto;}
/* 창 본체 자신이 스크롤 컨테이너인 경우(.comfy-modal-box 처럼 max-height +
   overflow:auto)만 예외적으로 sticky 를 쓴다 — 절대배치는 본문과 함께 스크롤
   되어 긴 모달에서 ✕가 사라지기 때문이다. 음수 margin 이 없으므로 sticky 여도
   바깥으로 나가지 않는다(정지 상태=본문 상단, 스크롤 시=스크롤포트에서 10px).
   가로(row) flex·grid 창에는 붙이지 않는다(한 칸을 차지해 본문을 밀어버린다). */
.mstd-actions.mstd-actions-sticky{
  position:sticky; top:var(--mstd-top, 10px); right:auto; height:0; align-items:flex-start;
}
/* 박스가 없는 오버레이(이미지 확대 뷰 등)에서는 화면 우측 상단에 띄운다. */
.mstd-actions.mstd-actions-float{position:fixed; top:18px; right:22px; z-index:10;}
.mstd-btn{
  flex:0 0 auto; width:28px; height:28px; padding:0; border-radius:8px; cursor:pointer;
  display:flex; align-items:center; justify-content:center;
  background:color-mix(in srgb, var(--accent) 10%, transparent);
  border:1px solid color-mix(in srgb, var(--accent) 38%, transparent);
  color:var(--accent); font-size:13px; line-height:1; font-weight:700;
  font-family:inherit; letter-spacing:0;
  transition:background .14s ease, border-color .14s ease;
}
.mstd-btn:hover{background:color-mix(in srgb, var(--accent) 24%, transparent); border-color:var(--accent);}
.mstd-btn:focus-visible{outline:2px solid var(--accent); outline-offset:2px;}
/* 표준 ✕가 대신 눌러 주는 기존 닫기 버튼 — 중복 노출만 막고 DOM은 남긴다. */
.mstd-legacy-x{display:none !important;}
/* 레이아웃을 건드리지 않는 뷰어류: 기존 ✕를 라임으로 통일만 한다. */
.mstd-lime-x{color:var(--accent) !important; border-color:color-mix(in srgb, var(--accent) 45%, var(--border-soft)) !important;}
.mstd-lime-x:hover{background:color-mix(in srgb, var(--accent) 18%, transparent) !important; border-color:var(--accent) !important;}
.mstd-min-title{display:none;}

/* 표준 버튼 그룹이 제목을 덮지 않도록 헤더 우측에 자리를 비운다.
   버튼이 두 개(최소화+닫기)면 62px + 우측 여백 10px + 여유 6px = 78px,
   ✕ 하나뿐이면 28 + 10 + 10 = 48px 만 빼면 된다. 창마다 실제 버튼 수가
   다르므로 JS(mstdBuildBar)가 --mstd-pad 를 창에 심어 준다 — 고정 78px 는
   ✕만 있는 창에서 제목 오른쪽에 40px 짜리 빈 구멍을 만들었다. */
.mstd-host > .comfy-modal-title,
.mstd-host > .comfy-modal-head,
.mstd-host > .mask-editor-head,
.mstd-host > .sim3d-tut-modal-hd,
.mstd-host > .mup-head,
.mstd-host > .cmdk-input-row{padding-right:var(--mstd-pad, 78px);}
/* 버튼(28px)이 제목 줄보다 커서 아래 본문 첫 줄을 덮는 문제 — 헤더 줄에 버튼과
   같은 최소 높이를 주고 세로 중앙 정렬해 버튼이 헤더 안에서만 머물게 한다. */
.mstd-host > .comfy-modal-title,
.mstd-host > .comfy-modal-head,
.mstd-host > .mask-editor-head,
.mstd-host > .sim3d-tut-modal-hd,
.mstd-host > .mup-head{min-height:28px; display:flex; align-items:center;}
/* 버튼 바가 세로로 덮는 범위에 걸치는 요소(제목·설명 문단 등)에 JS가 붙인다. */
.mstd-host > .mstd-clear{padding-right:var(--mstd-pad, 78px);}

/* ---- 비차단(non-modal) 창 · 이동 손잡이 (2026-08-04) ----------------------
   ComfyUI 앱 실행 창(.comfy-gen-overlay)에서 먼저 검증한 방식을 앱 전체 모달로
   일반화한다. 오버레이는 배경도 블러도 없이 클릭을 통과시키고(pointer-events:
   none) 창 본체만 입력을 받는다 — 창이 떠 있어도 뒤의 버튼·패널을 그대로 쓸 수
   있다. 창의 "제자리"는 각 오버레이가 원래 갖고 있던 정렬(대부분 flex 중앙)이며,
   드래그는 그 자리 기준의 상대 이동(transform)이라 창 크기가 바뀌어도 어긋나지
   않는다.
   예외(딤 배경 유지)는 JS 의 MSTD_KEEP_BACKDROP_SEL 로만 관리한다 — 개별 모달
   CSS 를 하나씩 고치지 않는다. 수동 옵트아웃이 필요하면 오버레이에
   .mstd-keep-backdrop / .mstd-no-drag 클래스를 붙이면 된다. */
.mstd-nonblock{
  background:none !important;
  -webkit-backdrop-filter:none !important; backdrop-filter:none !important;
  pointer-events:none !important;
}
/* 창 본체만 입력을 받는다. 최소화 상태는 자체 규칙(.mstd-min)이 따로 있으므로
   제외해 두 규칙이 서로 덮어쓰지 않게 한다. */
.mstd-nonblock:not(.mstd-min) > *{pointer-events:auto;}
/* 창 본체가 오버레이의 직계 자식이 아닌 모달(래퍼가 한 겹 더 있는 경우)에서도
   창이 입력을 받도록 하는 안전망. */
.mstd-nonblock:not(.mstd-min) .mstd-host{pointer-events:auto;}
/* 백드롭이 사라지면 밝은 배경(데이 테마) 위에서 창 경계가 묻힌다 — 테두리와
   그림자로 뒷화면과의 경계를 분명히 한다. 색은 기존 토큰만 사용. */
.mstd-nonblock .mstd-host{
  border:1px solid var(--border);
  box-shadow:0 18px 60px rgba(0,0,0,.55);
}
html[data-theme="day"] .mstd-nonblock .mstd-host{box-shadow:0 18px 46px rgba(0,0,0,.22);}
/* 창을 끄는 손잡이(제목 줄). touch-action:none 은 손잡이에만 걸어 창 본문의
   세로 스크롤(모바일)은 그대로 살려 둔다. */
.mstd-handle{cursor:move; user-select:none; -webkit-user-select:none; touch-action:none;}
/* 손잡이 안의 버튼·입력은 원래 동작(클릭·선택)을 해야 하므로 커서를 되돌린다. */
.mstd-handle button, .mstd-handle a, .mstd-handle input,
.mstd-handle select, .mstd-handle textarea{cursor:pointer; user-select:auto; -webkit-user-select:auto;}
.mstd-handle input, .mstd-handle textarea{cursor:text;}
/* 창 자신이 스크롤 컨테이너면 손잡이를 상단에 고정한다(긴 폼에서 제목·✕가 함께
   사라지지 않도록). 세로 음수 margin 은 절대 쓰지 않는다 — sticky 는 margin
   박스를 top 에 맞추므로 위쪽에 틈이 생겨 본문이 비친다(2026-08-04 실제 버그).
   대신 JS 가 창의 padding-top 을 0 으로 옮기고 손잡이가 그 여백을 직접 갖는다.
   구분선·드롭섀도우는 두지 않는다(지시) — 불투명 배경만으로 충분하다. */
.mstd-handle.mstd-handle-stick{position:sticky; top:0; z-index:5;}
.mstd-dragging{transition:none !important;}

/* ---- 최소화(접어두기) 상태 ---- */
.mstd-min{
  background:transparent !important; -webkit-backdrop-filter:none !important; backdrop-filter:none !important;
  padding:0 !important; pointer-events:none !important; animation:none !important;
}
.mstd-min > *{pointer-events:none;}
/* 접힌 칩은 좌하단에 쌓인다. 생성 최소화 도크(.comfy-gen-min-dock)도 같은
   자리를 쓰므로, JS(mstdReflowMin)가 도크 높이를 재서 --mstd-base 를 올려 준다. */
.mstd-min-box{
  pointer-events:auto !important;
  position:fixed !important; left:16px !important; right:auto !important; top:auto !important;
  bottom:calc(var(--mstd-base, 16px) + var(--mstd-slot, 0) * 50px) !important;
  width:auto !important; min-width:0 !important; max-width:min(400px, 92vw) !important;
  height:auto !important; min-height:0 !important; max-height:none !important;
  padding:0 !important; margin:0 !important; overflow:hidden !important; display:block !important;
  border-radius:12px !important; box-shadow:0 8px 26px rgba(0,0,0,.45) !important;
  transform:none !important;
}
.mstd-min-box > *{display:none !important;}
.mstd-min-box > .mstd-actions{
  display:flex !important; position:static !important; pointer-events:auto !important;
  top:auto !important; right:auto !important;
  height:auto !important; width:auto !important; margin:0 !important;
  padding:8px 10px 8px 13px; gap:8px;
  align-items:center; justify-content:flex-start;
}
.mstd-min-box .mstd-min-title{
  display:block; flex:1 1 auto; min-width:0; max-width:250px; cursor:pointer;
  font-size:12.5px; font-weight:700; color:var(--text);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
/* 모바일: 하단 탭 바(1100)를 피해 위로 띄운다. */
@media (max-width:820px){
  .mstd-min-box{
    left:12px !important; max-width:min(290px, 88vw) !important;
    bottom:calc(var(--mstd-base, 80px) + var(--mstd-slot, 0) * 48px) !important;
  }
  .mstd-min-box .mstd-min-title{max-width:170px;}
  .mstd-actions.mstd-actions-float{top:12px; right:12px;}
}
