/* ============================================================================
   인터랙션 프리셋 (움직임 토큰 겹)
   ============================================================================
   색 테마(theme.css·calm.css)와 완전히 따로 논다. 색은 손대지 않고 "어떻게 움직이나"만 정한다.

     <html data-theme="light" data-effect="soft-business">

   effects.js 가 html 에 data-effect 를 붙이면 아래 토큰이 살고, 공통 컴포넌트 규칙이 그 토큰을 읽는다.
   미리보기 화면은 칸마다 data-effect 를 붙여 다섯 프리셋을 나란히 본다(토큰은 상속되므로 칸 안에서만 산다).

   프리셋을 하나 더 만들려면 [data-effect="이름"] 토큰 묶음만 추가한다. 컴포넌트 규칙은 그대로다.
   로그인 화면은 effects.js 를 싣지 않으므로 이 파일과 무관하다.

   컴포넌트 규칙이 화면마다 인라인으로 적은 .btn:hover 같은 규칙보다 이기도록 [data-effect] 를 앞에 둔다.
   ============================================================================ */

/* ── 토큰: 기본값 = Soft Business ─────────────────────────────────────── */
[data-effect] {
  --motion-fast: 160ms;
  --motion-normal: 200ms;
  --motion-slow: 220ms;
  --motion-ease: cubic-bezier(.25, .46, .45, .94);   /* ease-out */
  --motion-ease-enter: var(--motion-ease);

  --hover-y: -1px;
  --hover-scale: 1;
  --hover-brightness: 1;
  --press-scale: .98;
  --shadow-hover: 0 4px 12px rgba(0, 0, 0, .10);

  --card-hover-y: -2px;
  --card-hover-scale: 1;
  --card-hover-border: var(--line-2, var(--line));
  --card-shadow-hover: 0 6px 16px rgba(0, 0, 0, .08);

  --focus-ring-size: 3px;
  --focus-glow: 0 0 0 0 transparent;
  --input-focus-scale: 1;

  --menu-hover-x: 0px;
  --menu-panel-scale: 1;
  --menu-shadow-hover: none;

  --modal-enter-ms: var(--motion-slow);
  --modal-enter-y: 0px;
  --modal-enter-scale: .98;
  --modal-enter-opacity: 0;
  --backdrop-opacity: 1;

  --toast-enter-y: -8px;
  --toast-enter-x: 0px;

  --stagger-delay: 0ms;
  --page-enter-y: 0px;
  --page-enter-ms: 0ms;
  --page-leave-ms: 0ms;

  --tooltip-y: -4px;
  --dropdown-y: -6px;
  --dropdown-scale: .98;
}

/* Clean Minimal: 색·테두리 변화만. 이동 없음. */
[data-effect="clean-minimal"] {
  --motion-fast: 100ms; --motion-normal: 130ms; --motion-slow: 160ms;
  --motion-ease: ease;
  --hover-y: 0px; --press-scale: 1; --shadow-hover: none;
  --card-hover-y: 0px; --card-hover-border: var(--accent); --card-shadow-hover: none;
  --focus-ring-size: 2px;
  --modal-enter-y: 0px; --modal-enter-scale: 1;
  --toast-enter-y: 0px;
  --tooltip-y: 0px; --dropdown-y: 0px; --dropdown-scale: 1;
}

/* Floating: 살짝 떠오르고 그림자가 커진다. */
[data-effect="floating"] {
  --motion-fast: 200ms; --motion-normal: 230ms; --motion-slow: 260ms;
  --motion-ease: cubic-bezier(.22, 1, .36, 1);
  --hover-y: -2px; --shadow-hover: 0 10px 24px rgba(0, 0, 0, .14);
  --card-hover-y: -5px; --card-shadow-hover: 0 14px 30px rgba(0, 0, 0, .12);
  --focus-glow: 0 0 0 5px var(--accent-10);
  --menu-panel-scale: 1.02; --menu-shadow-hover: 0 6px 16px rgba(0, 0, 0, .10);
  --modal-enter-y: 16px; --modal-enter-scale: 1;
  --toast-enter-y: 12px;
  --tooltip-y: -6px; --dropdown-y: -8px; --dropdown-scale: 1;
}

/* Dynamic: 빠르고 또렷하다. 이동·크기·그림자 변화가 가장 크다. 튀지는 않는다. */
[data-effect="dynamic"] {
  --motion-fast: 180ms; --motion-normal: 220ms; --motion-slow: 280ms;
  --motion-ease: cubic-bezier(.2, .8, .2, 1);
  --motion-ease-enter: cubic-bezier(.34, 1.25, .64, 1);   /* 약한 overshoot */
  --hover-y: -2px; --hover-scale: 1.02; --press-scale: .96; --shadow-hover: 0 12px 28px rgba(0, 0, 0, .18);
  --card-hover-y: -4px; --card-hover-scale: 1.015; --card-shadow-hover: 0 16px 34px rgba(0, 0, 0, .16);
  --focus-ring-size: 4px; --input-focus-scale: 1.005;
  --menu-hover-x: 5px;
  --modal-enter-y: 24px; --modal-enter-scale: .94;
  --toast-enter-y: 0px; --toast-enter-x: 24px;
  --tooltip-y: -8px; --dropdown-y: -10px; --dropdown-scale: .96;
}

/* Login Motion: 로그인 화면의 움직임 언어를 대시보드용으로 옮겼다.
   감속 곡선(.2,.7,.3,1), 240~340ms, 1~2px 떠오름 + 색 있는 부드러운 그림자, 테두리와 빛이 같이 번지는 focus,
   scale .96→1 + 살짝 올라오며 나타나는 창, 늦게 안착하는 알림, 20~50ms 로 차례로 뜨는 첫 화면. */
[data-effect="login-motion"] {
  --motion-fast: 240ms; --motion-normal: 300ms; --motion-slow: 340ms;
  --motion-ease: cubic-bezier(.2, .7, .3, 1);
  --hover-y: -2px; --hover-scale: 1.005; --hover-brightness: 1.04; --press-scale: .985;
  --shadow-hover: 0 8px 24px var(--accent-30);
  --card-hover-y: -3px; --card-hover-scale: 1.005; --card-hover-border: var(--accent-30); --card-shadow-hover: 0 12px 28px var(--accent-20);
  --focus-ring-size: 3px; --focus-glow: 0 0 0 5px var(--accent-10), 0 0 18px var(--accent-20);
  --menu-panel-scale: 1;
  --modal-enter-y: 12px; --modal-enter-scale: .96;
  --toast-enter-y: 10px;
  --stagger-delay: 30ms;
  --page-enter-y: 6px; --page-enter-ms: 240ms; --page-leave-ms: 160ms;
  --tooltip-y: -6px; --dropdown-y: -8px; --dropdown-scale: .97;
}

/* 터치 기기는 hover 대신 눌림·창 등장으로 차이를 느끼게 한다. PC 토큰은 그대로 둔다. */
@media (hover: none) and (pointer: coarse) {
  [data-effect] {
    --press-scale: .97;
    --modal-enter-y: 8px;
    --modal-enter-ms: 260ms;
  }
  [data-effect="clean-minimal"] {
    --press-scale: 1;
    --modal-enter-y: 0px;
    --modal-enter-ms: 160ms;
  }
  [data-effect="floating"] {
    --modal-enter-y: 22px;
    --modal-enter-ms: 300ms;
  }
  [data-effect="dynamic"] {
    --press-scale: .95;
    --modal-enter-y: 28px;
    --modal-enter-ms: 320ms;
  }
  [data-effect="login-motion"] {
    --press-scale: .96;
    --modal-enter-y: 20px;
    --modal-enter-ms: 360ms;
    --page-enter-y: 12px;
    --page-enter-ms: 300ms;
    --stagger-delay: 20ms;
  }
}

/* effects.js 는 이 표식으로 누를 대상을 찾는다. 단추 안 글씨·아이콘에는 상속하지 않는다. */
[data-effect] * { --fx-pressable: 0; }

/* ── 버튼 ────────────────────────────────────────────────────────────────
   공통 단추와 실제 업무 화면의 저장·모달·사진·주 이동·탭·불러오기 단추. hover 는 마우스가 있는 기기에서만.
   calm.css 와 theme.css(낮)는 .btn 의 transform·box-shadow·transition 을 !important 로 막아 두었다(예전 보라 테마의 들림을 지우려고).
   그래서 여기 움직임 선언도 !important 이고, [data-effect] 를 두 번 적어 우선순위를 그보다 높인다. Clean Minimal 은 값이 0 이라 지금과 같다. */
[data-effect][data-effect] :is(.btn, button[type="submit"], .fx-btn, .submit-btn, .modal-btn, .dp-photo-button, .week-nav-btn, .tab-btn, .load-btn, .btn-this-week):not(:disabled):not([disabled]):not([aria-disabled="true"]) {
  --fx-pressable: 1;
  transition: transform var(--motion-fast) var(--motion-ease), box-shadow var(--motion-fast) var(--motion-ease),
    background-color var(--motion-fast) var(--motion-ease), border-color var(--motion-fast) var(--motion-ease),
    color var(--motion-fast) var(--motion-ease), filter var(--motion-fast) var(--motion-ease) !important;
}
@media (hover: hover) {
  [data-effect][data-effect] :is(.btn, button[type="submit"], .fx-btn, .submit-btn, .modal-btn, .dp-photo-button, .week-nav-btn, .tab-btn, .load-btn, .btn-this-week):not(:disabled):not([disabled]):not([aria-disabled="true"]):hover {
    transform: translateY(var(--hover-y)) scale(var(--hover-scale)) !important;
    box-shadow: var(--shadow-hover) !important;
    filter: brightness(var(--hover-brightness));
  }
}
@media (hover: hover) {
  [data-effect][data-effect] :is(.btn, button[type="submit"], .fx-btn, .submit-btn, .modal-btn, .dp-photo-button, .week-nav-btn, .tab-btn, .load-btn, .btn-this-week):not(:disabled):not([disabled]):not([aria-disabled="true"]):active {
    transform: translateY(0) scale(var(--press-scale)) !important;
    box-shadow: none !important;
    transition-duration: 60ms !important;
  }
}
[data-effect][data-effect] :is(.btn, button[type="submit"], .fx-btn, .submit-btn, .modal-btn, .dp-photo-button, .week-nav-btn, .tab-btn, .load-btn, .btn-this-week):not(:disabled):not([disabled]):not([aria-disabled="true"]).fx-touch-pressed {
  transform: translateY(0) scale(var(--press-scale)) !important;
  box-shadow: none !important;
  transition-duration: 60ms !important;
}
[data-effect] :is(.btn, button[type="submit"], .fx-btn, .submit-btn, .modal-btn, .dp-photo-button, .week-nav-btn, .tab-btn, .load-btn, .btn-this-week):not(:disabled):not([disabled]):not([aria-disabled="true"]):focus-visible {
  outline: var(--focus-ring-size) solid var(--accent);
  outline-offset: 2px;
}

/* ── 카드 ────────────────────────────────────────────────────────────────
   누를 수 있는 카드만 움직인다. 정보만 보여주는 카드는 그대로 둔다. (calm.css 의 :hover{transform:none!important} 를 위와 같은 방법으로 이긴다) */
[data-effect][data-effect] :is(.card, .tile, .bi, .kpi, .summary-card, .fx-card):is(a, button, [onclick], [role="button"], [tabindex], .fx-clickable, .kpi[data-x], .summary-card[data-x]):not(:disabled):not([disabled]):not([aria-disabled="true"]) {
  --fx-pressable: 1;
  transition: transform var(--motion-normal) var(--motion-ease), box-shadow var(--motion-normal) var(--motion-ease),
    border-color var(--motion-normal) var(--motion-ease), background-color var(--motion-normal) var(--motion-ease) !important;
}
@media (hover: hover) {
  [data-effect][data-effect] :is(.card, .tile, .bi, .kpi, .summary-card, .fx-card):is(a, button, [onclick], [role="button"], [tabindex], .fx-clickable, .kpi[data-x], .summary-card[data-x]):not(:disabled):not([disabled]):not([aria-disabled="true"]):hover {
    transform: translateY(var(--card-hover-y)) scale(var(--card-hover-scale)) !important;
    box-shadow: var(--card-shadow-hover) !important;
    border-color: var(--card-hover-border) !important;
  }
}
@media (hover: hover) {
  [data-effect][data-effect] :is(.card, .tile, .bi, .kpi, .summary-card, .fx-card):is(a, button, [onclick], [role="button"], [tabindex], .fx-clickable, .kpi[data-x], .summary-card[data-x]):not(:disabled):not([disabled]):not([aria-disabled="true"]):active {
    transform: translateY(0) scale(var(--press-scale)) !important;
    transition-duration: 60ms !important;
  }
}
[data-effect][data-effect] :is(.card, .tile, .bi, .kpi, .summary-card, .fx-card):is(a, button, [onclick], [role="button"], [tabindex], .fx-clickable, .kpi[data-x], .summary-card[data-x]):not(:disabled):not([disabled]):not([aria-disabled="true"]).fx-touch-pressed {
  transform: translateY(0) scale(var(--press-scale)) !important;
  transition-duration: 60ms !important;
}
[data-effect] :is(.card, .tile, .bi, .kpi, .summary-card, .fx-card):is(a, button, [onclick], [role="button"], [tabindex], .fx-clickable, .kpi[data-x], .summary-card[data-x]):not(:disabled):not([disabled]):not([aria-disabled="true"]):focus-visible {
  outline: var(--focus-ring-size) solid var(--accent);
  outline-offset: 2px;
}

/* 터치 뒤 남는 기존 화면의 :hover·:active 확대를 지운다. 누르는 동안에는 위 상태가 그대로 이긴다. */
@media (hover: none) and (pointer: coarse) {
  [data-effect][data-effect] :is(.btn, button[type="submit"], .fx-btn, .submit-btn, .modal-btn, .dp-photo-button, .week-nav-btn, .tab-btn, .load-btn, .btn-this-week):not(:disabled):not([disabled]):not([aria-disabled="true"]):is(:hover, :active):not(.fx-touch-pressed),
  [data-effect][data-effect] :is(.card, .tile, .bi, .kpi, .summary-card, .fx-card):is(a, button, [onclick], [role="button"], [tabindex], .fx-clickable, .kpi[data-x], .summary-card[data-x]):not(:disabled):not([disabled]):not([aria-disabled="true"]):is(:hover, :active):not(.fx-touch-pressed) {
    transform: none !important;
  }
}

/* ── 입력칸 ──────────────────────────────────────────────────────────────
   focus 는 테두리·고리·빛으로만. 체크박스·라디오는 뺀다. */
[data-effect] :is(input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]), select, textarea) {
  transition: border-color var(--motion-normal) var(--motion-ease), box-shadow var(--motion-normal) var(--motion-ease),
    outline-color var(--motion-normal) var(--motion-ease), background-color var(--motion-normal) var(--motion-ease),
    transform var(--motion-normal) var(--motion-ease);
}
/* calm.css 가 입력칸 focus 고리를 2px !important 로 정해 두었다. 같은 방법으로 토큰 값이 이긴다. */
[data-effect][data-effect] :is(input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]), select, textarea):focus-visible {
  outline: var(--focus-ring-size) solid var(--accent) !important;
  outline-offset: 0;
  border-color: var(--accent);
  box-shadow: var(--focus-glow);
  transform: scale(var(--input-focus-scale));
}
[data-effect] :is(input, select, textarea)[aria-invalid="true"] {
  border-color: var(--error, #d9534f);
}
[data-effect][data-effect] :is(input, select, textarea)[aria-invalid="true"]:focus-visible {
  outline-color: var(--error, #d9534f) !important;
  box-shadow: 0 0 0 4px rgba(217, 83, 79, .15);
}

/* ── 메뉴 (사이드바·미리보기 메뉴) ──────────────────────────────────────── */
[data-effect] :is(.hanul-menu-item, .fx-menu-item) {
  transition: background-color var(--motion-normal) var(--motion-ease), color var(--motion-normal) var(--motion-ease),
    transform var(--motion-normal) var(--motion-ease), box-shadow var(--motion-normal) var(--motion-ease);
}
@media (hover: hover) {
  [data-effect] :is(.hanul-menu-item, .fx-menu-item):not(.active):not([aria-current]):hover {
    transform: translateX(var(--menu-hover-x)) scale(var(--menu-panel-scale));
    box-shadow: var(--menu-shadow-hover);
  }
}
[data-effect] :is(.hanul-menu-item, .fx-menu-item):focus-visible {
  outline: var(--focus-ring-size) solid var(--accent);
  outline-offset: -2px;
}
/* 하위 메뉴가 펼쳐질 때 (사이드바 묶음은 calm.css 가 grid-rows 로 연다) */
[data-effect] .fx-submenu[hidden] { display: none; }
[data-effect] .fx-submenu { animation: fx-drop var(--motion-normal) var(--motion-ease) both; }

/* ── 모달 ────────────────────────────────────────────────────────────────
   화면들이 쓰는 세 가지: .modal.active > .modal-content, <dialog open>, .sheet-bg > .sheet. 미리보기는 .fx-modal. */
[data-effect] :is(.modal.active, .sheet-bg:not([hidden]), .fx-modal.open) {
  animation: fx-backdrop var(--motion-normal) var(--motion-ease) both;
}
[data-effect] :is(.modal.active .modal-content, .sheet-bg:not([hidden]) .sheet, dialog[open], .fx-modal.open .fx-modal-panel) {
  animation: fx-modal-in var(--modal-enter-ms) var(--motion-ease-enter) both;
}
[data-effect] dialog[open]::backdrop { animation: fx-backdrop var(--motion-normal) var(--motion-ease) both; }
@keyframes fx-backdrop { from { opacity: 0; } to { opacity: var(--backdrop-opacity, 1); } }
@keyframes fx-modal-in {
  from { opacity: var(--modal-enter-opacity); transform: translateY(var(--modal-enter-y)) scale(var(--modal-enter-scale)); }
  to { opacity: 1; transform: none; }
}
/* 닫힘: 화면 코드가 .closing 을 잠깐 붙여 줄 때만 (미리보기 모달이 그렇게 한다) */
[data-effect] :is(.fx-modal.closing .fx-modal-panel) { animation: fx-modal-out var(--motion-normal) var(--motion-ease) both; }
[data-effect] .fx-modal.closing { animation: fx-backdrop var(--motion-normal) var(--motion-ease) both reverse; }
@keyframes fx-modal-out { to { opacity: var(--modal-enter-opacity); transform: translateY(calc(var(--modal-enter-y) / 2)) scale(var(--modal-enter-scale)); } }

/* ── 토스트 (shared/toast.js 의 .toast 와 미리보기 .fx-toast) ─────────────── */
[data-effect] :is(.toast, .fx-toast) { animation: fx-toast-in var(--motion-slow) var(--motion-ease) both; }
[data-effect] :is(.toast.hiding, .fx-toast.hiding) { animation: fx-toast-out var(--motion-normal) var(--motion-ease) both; }
@keyframes fx-toast-in { from { opacity: 0; transform: translate(var(--toast-enter-x), var(--toast-enter-y)); } to { opacity: 1; transform: none; } }
@keyframes fx-toast-out { to { opacity: 0; transform: translate(var(--toast-enter-x), var(--toast-enter-y)); } }

/* ── 드롭다운 · 팝오버 · 툴팁 (구조만 마련: 화면이 .fx-dropdown / .fx-tooltip 을 쓰면 붙는다) ── */
[data-effect] :is(.fx-dropdown, .fx-popover, .sow-search-dropdown) { animation: fx-drop var(--motion-normal) var(--motion-ease) both; transform-origin: top; }
@keyframes fx-drop { from { opacity: 0; transform: translateY(var(--dropdown-y)) scale(var(--dropdown-scale)); } to { opacity: 1; transform: none; } }
[data-effect] .fx-tooltip { animation: fx-tip var(--motion-fast) var(--motion-ease) both; }
@keyframes fx-tip { from { opacity: 0; transform: translateY(calc(var(--tooltip-y) * -1)); } to { opacity: 1; transform: none; } }

/* ── 스켈레톤 (구조만) ──────────────────────────────────────────────────── */
[data-effect] .fx-skeleton { background: linear-gradient(90deg, var(--surface-2, rgba(127,127,127,.12)) 25%, var(--surface-3, rgba(127,127,127,.2)) 50%, var(--surface-2, rgba(127,127,127,.12)) 75%); background-size: 200% 100%; animation: fx-skeleton 1.4s ease-in-out infinite; }
[data-effect="clean-minimal"] .fx-skeleton { animation: none; }
@keyframes fx-skeleton { from { background-position: 200% 0; } to { background-position: -200% 0; } }

/* ── 첫 화면 등장 · 화면 전환 (Login Motion 만 값이 있다) ──────────────────
   effects.js 가 html 에 fx-enter 를 붙였다 뗀다. 사이드바(sidebar.js 가 div 로 감싼다)·알림·스크립트는 뺀다. */
[data-effect].fx-enter body > :not(.hanul-sidebar):not(:has(.hanul-sidebar)):not(.hanul-calm-backdrop):not(#toastContainer):not(script) {
  animation: fx-page-in var(--page-enter-ms) var(--motion-ease) both;
}
[data-effect].fx-leave body > :not(.hanul-sidebar):not(:has(.hanul-sidebar)):not(.hanul-calm-backdrop):not(#toastContainer):not(script) {
  opacity: 0; transform: translateY(calc(var(--page-enter-y) * -1));
  transition: opacity var(--page-leave-ms) var(--motion-ease), transform var(--page-leave-ms) var(--motion-ease);
}
@keyframes fx-page-in { from { opacity: 0; transform: translateY(var(--page-enter-y)); } to { opacity: 1; transform: none; } }
/* 카드가 20~50ms 차이로 차례로 뜬다. 다섯째부터는 같이. */
[data-effect].fx-enter :is(.card, .tile, .kpi, .bi, .fx-card), [data-effect].fx-enter.fx-stagger > :is(.card, .tile, .kpi, .bi, .fx-card) {
  animation: fx-rise calc(var(--page-enter-ms) * 1.5) var(--motion-ease) both;   /* 등장 값이 0 인 프리셋은 0ms = 아무것도 안 보인다 */
  animation-delay: calc(var(--stagger-delay) * var(--fx-i, 0));
}
[data-effect].fx-enter :is(.card, .tile, .kpi, .bi, .fx-card):nth-child(2) { --fx-i: 1; }
[data-effect].fx-enter :is(.card, .tile, .kpi, .bi, .fx-card):nth-child(3) { --fx-i: 2; }
[data-effect].fx-enter :is(.card, .tile, .kpi, .bi, .fx-card):nth-child(4) { --fx-i: 3; }
[data-effect].fx-enter :is(.card, .tile, .kpi, .bi, .fx-card):nth-child(n+5) { --fx-i: 4; }
@keyframes fx-rise { from { opacity: 0; transform: translateY(var(--page-enter-y)); } to { opacity: 1; transform: none; } }
/* 등장·전환 값이 0 인 프리셋에는 effects.js 가 fx-enter / fx-leave 를 아예 붙이지 않는다. */

/* ── 동작 줄이기: JS 가 먼저 data-effect 를 떼지만, 남은 것도 멈춘다 ─────── */
@media (prefers-reduced-motion: reduce) {
  [data-effect] *, [data-effect] *::before, [data-effect] *::after { animation: none !important; transition-duration: 1ms !important; }
}
