/* ---------- 테마 변수 (라이트/다크) ----------
   실제 색상 값은 여기(:root)와 다크 오버라이드 두 곳에만 있고, 나머지 스타일시트 전체는 이 변수만
   참조한다 - 다크 모드 지원을 켜면서 하드코딩된 색을 개별로 다 뜯어고치는 대신, 변수 하나만 바꾸면
   그 변수를 쓰는 모든 곳이 한 번에 따라오게 하기 위해서다(이미 스타일시트 전역에 var(--color-*)가
   300곳 넘게 쓰이고 있어 이 구조로 가는 게 자연스러웠다).
   테마 적용 우선순위: (1) 사용자가 명시적으로 고른 테마(:root[data-theme])가 최우선 - 라이트를
   명시적으로 골랐으면 OS가 다크여도 라이트를 쓴다. (2) 명시적 선택이 없으면 OS 설정
   (prefers-color-scheme)을 따른다. 실제 적용/저장/토글 UI는 js/theme.js가 담당하고, 화면이 밝았다가
   어두워지는 깜빡임(FOUC)을 막기 위해 각 HTML의 <head> 인라인 스크립트가 CSS보다 먼저 data-theme을
   심어둔다. */
:root {
  --color-bg: #f2f1f6;
  --color-surface: #ffffff;
  --color-border: #e5e7f0;
  --color-text: #201f2c;
  --color-text-muted: #6b7086;
  --color-primary: #3a3552;
  --color-primary-dark: #2b273f;
  --color-primary-hover: #2b273f;
  --color-primary-soft: #eceaf3;

  --color-pending: #b45309;
  --color-pending-bg: #fef3c7;
  --color-progress: #1d4ed8;
  --color-progress-bg: #dbeafe;
  --color-completed: #15803d;
  --color-completed-bg: #dcfce7;
  --color-cancelled: #6b7280;
  --color-cancelled-bg: #eceef2;

  --color-danger: #b91c1c;
  --color-danger-bg: #fef2f2;
  --color-danger-border: #fecaca;
  --color-input-bg: #fbfbfe;
  --color-auth-gradient: linear-gradient(160deg, #eef0fd 0%, #f5f6fb 45%, #ffffff 100%);

  --radius-md: 10px;
  --radius-lg: 16px;
  --shadow-card: 0 1px 2px rgba(20, 23, 45, 0.04), 0 4px 12px rgba(20, 23, 45, 0.06);
}

/* 다크 값은 두 군데에 똑같이 적어둔다: media query 쪽은 "명시적 선택이 없을 때 OS 설정을 따르는" 기본
   동작, [data-theme="dark"] 쪽은 "사용자가 직접 골랐을 때" - 두 선택자를 CSS 하나로 합칠 방법이 없어
   부득이하게 값을 중복시켰다(변경 시 두 곳 다 고쳐야 함). :not([data-theme="light"])는 OS는 다크인데
   사용자가 라이트를 명시적으로 고른 경우 media query 쪽이 이기지 않게 막는다 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --color-bg: #16161c;
    --color-surface: #1f1f27;
    --color-border: #34343f;
    --color-text: #eeedf6;
    --color-text-muted: #a2a1b2;
    --color-primary: #7266dd;
    --color-primary-dark: #c1b8f2;
    --color-primary-hover: #5b4ecb;
    --color-primary-soft: #2b2740;

    --color-pending: #f5ad42;
    --color-pending-bg: #3a2c14;
    --color-progress: #6ea3ff;
    --color-progress-bg: #182a4a;
    --color-completed: #52dd8e;
    --color-completed-bg: #113322;
    --color-cancelled: #9fa1b0;
    --color-cancelled-bg: #2b2b34;

    --color-danger: #f2837e;
    --color-danger-bg: #3c1a1a;
    --color-danger-border: #5c2727;
    --color-input-bg: #26262f;
    --color-auth-gradient: linear-gradient(160deg, #1d1d27 0%, #17171e 45%, #101013 100%);

    --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.35), 0 4px 16px rgba(0, 0, 0, 0.4);
  }
}

:root[data-theme="dark"] {
  --color-bg: #16161c;
  --color-surface: #1f1f27;
  --color-border: #34343f;
  --color-text: #eeedf6;
  --color-text-muted: #a2a1b2;
  --color-primary: #7266dd;
  --color-primary-dark: #c1b8f2;
  --color-primary-hover: #5b4ecb;
  --color-primary-soft: #2b2740;

  --color-pending: #f5ad42;
  --color-pending-bg: #3a2c14;
  --color-progress: #6ea3ff;
  --color-progress-bg: #182a4a;
  --color-completed: #52dd8e;
  --color-completed-bg: #113322;
  --color-cancelled: #9fa1b0;
  --color-cancelled-bg: #2b2b34;

  --color-danger: #f2837e;
  --color-danger-bg: #3c1a1a;
  --color-danger-border: #5c2727;
  --color-input-bg: #26262f;
  --color-auth-gradient: linear-gradient(160deg, #1d1d27 0%, #17171e 45%, #101013 100%);

  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.35), 0 4px 16px rgba(0, 0, 0, 0.4);
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: "Pretendard", "Apple SD Gothic Neo", "Segoe UI", Roboto, sans-serif;
  background: var(--color-bg);
  color: var(--color-text);
  -webkit-font-smoothing: antialiased;
}

/* 명언 배너 + 로그아웃 버튼을 한 줄에 배치하는 바 - 배경/구분선은 여기로 옮기고,
   quote-banner 자체는 텍스트 스타일만 갖는다(quotes.js 가 innerHTML 로 통째로 갈아끼우는 대상은
   quote-banner 뿐이라, 로그아웃 버튼은 형제 요소로 둬야 정시마다 다시 그릴 때 사라지지 않는다) */
.quote-bar {
  display: flex;
  align-items: center;
  gap: 16px;
  background: var(--color-primary-soft);
  border-bottom: 1px solid var(--color-border);
  padding-right: 20px;
}

/* 사이드바 접기/펼치기 토글(js/sidebar.js) - .sidebar-brand-row 안에서 "Schedule Manager"
   로고 오른쪽 끝에 배치된다. Claude 앱의 사이드바 토글 아이콘과 같은 느낌으로 테두리 없는
   아이콘 버튼 스타일을 쓴다 */
.sidebar-toggle-btn {
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 7px;
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
}

.sidebar-toggle-btn svg {
  width: 18px;
  height: 18px;
}

.sidebar-toggle-btn:hover {
  background: rgba(58, 53, 82, 0.09);
  color: var(--color-primary-dark);
}

.quote-banner {
  flex: 1;
  min-width: 0;
  padding: 10px 24px;
  text-align: center;
  font-size: 13px;
  line-height: 1.5;
}

.quote-banner:empty {
  display: none;
}

/* quote-banner가 비어서(:empty) 접히면 flex:1로 공간을 채워주던 요소가 사라져 뒤따르는 아이콘/버튼들이
   기본 정렬(flex-start)대로 왼쪽 끝(0,0)에 붙어버린다 - 예전엔 #logout-btn 하나만 margin-left:auto를
   줬는데, 그 앞에 테마 토글 버튼이 새로 생기면서(리포트/설정 페이지는 quotes.js를 안 불러와 배너가
   항상 비어있음) 그 버튼만 왼쪽 위에 뜨는 문제가 생겼다 - #logout-btn을 못박는 대신, "비어있는
   quote-banner 바로 다음에 오는 요소가 뭐든" 그 요소에 margin-left:auto를 주면 로그아웃 앞에 아이콘이
   몇 개든 항상 오른쪽 끝 무리 전체가 통째로 붙는다(명언이 있는 페이지는 :empty가 매치되지 않아 이
   규칙 자체가 적용되지 않고, quote-banner의 flex:1이 그대로 같은 효과를 낸다) */
.quote-banner:empty + * {
  margin-left: auto;
}

.quote-banner .quote-text {
  color: var(--color-text);
  font-style: italic;
}

.quote-banner .quote-author {
  margin-left: 8px;
  color: var(--color-text-muted);
}

a {
  color: inherit;
  text-decoration: none;
}

/* color: inherit가 없으면 브라우저 기본 버튼 글자색(다크 모드와 무관한 시스템 색, 대체로 검정)이
   그대로 남아 - .achievement-headline처럼 자기 색을 따로 안 정하고 부모(<button class="achievement-widget">)
   에게서 물려받으려던 텍스트가 다크 모드에서도 계속 검게 보이는 버그가 있었다 */
button {
  font-family: inherit;
  color: inherit;
  cursor: pointer;
}

/* select도 button과 같은 이유(브라우저 기본 폼 컨트롤 글자색이 다크 모드와 무관하게 검정 계열로
   고정돼있는 경우가 있음)로 color: inherit가 필요하다 - 특히 .card-actions select(보드 카드의 상태
   변경 드롭다운)는 자기 색을 따로 안 정하고 부모에게서 물려받으려던 거라 다크 모드에서 선택된 상태
   텍스트("대기"/"진행중" 등)가 계속 검게 보이는 문제가 있었다 */
select {
  font-family: inherit;
  color: inherit;
}

/* ---------- Auth pages (login / signup) ---------- */

.auth-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: var(--color-auth-gradient);
}

.auth-card {
  width: 100%;
  max-width: 380px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: 36px 32px 28px;
}

.auth-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 28px;
}

.auth-brand .logo-mark {
  width: 34px;
  height: 34px;
  border-radius: 9px;
  object-fit: cover;
  display: block;
}

.auth-brand .logo-text {
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.auth-card h1 {
  font-size: 21px;
  margin: 0 0 6px;
  letter-spacing: -0.02em;
}

.auth-card .subtitle {
  margin: 0 0 24px;
  color: var(--color-text-muted);
  font-size: 13.5px;
}

.field {
  margin-bottom: 16px;
}

.field label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 6px;
  color: var(--color-text-muted);
}

.field input,
.field select,
.field textarea {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font-size: 14px;
  background: var(--color-input-bg);
  color: var(--color-text);
  transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
}

.checkbox-field label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 500;
  color: var(--color-text);
  cursor: pointer;
}

.checkbox-field input[type="checkbox"] {
  width: auto;
}

#schedule-time-row.single-col {
  grid-template-columns: 1fr;
}

.datetime-24h-group {
  display: flex;
  align-items: center;
  gap: 4px;
}

.datetime-24h-group input[type="date"] {
  flex: 1;
  min-width: 0;
}

.datetime-24h-group input[type="number"] {
  width: 48px;
  flex-shrink: 0;
  padding-left: 6px;
  padding-right: 6px;
  text-align: center;
}

.datetime-24h-group .dt-colon {
  color: var(--color-text-muted);
  font-weight: 600;
  flex-shrink: 0;
}

.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px var(--color-primary-soft);
  background: var(--color-surface);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: none;
  border-radius: var(--radius-md);
  padding: 10px 16px;
  font-size: 14px;
  font-weight: 600;
  transition: background 0.15s, transform 0.05s;
}

.btn:active {
  transform: translateY(1px);
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
}

.btn-primary {
  background: var(--color-primary);
  color: #fff;
  width: 100%;
  padding: 11px 16px;
}

.btn-primary:hover {
  background: var(--color-primary-hover);
}

.btn-ghost {
  background: transparent;
  color: var(--color-text-muted);
  border: 1px solid var(--color-border);
}

.btn-ghost:hover {
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
}

.btn-sm {
  padding: 6px 10px;
  font-size: 12.5px;
  border-radius: 8px;
}

/* 상단 quote-bar의 "리포트"/"설정" 링크 - 텍스트 라벨 없이 아이콘만 보여준다(hover 시 title 툴팁으로
   설명). 로그아웃 버튼은 텍스트를 그대로 유지한다(오탐/오클릭 방지가 더 중요한 액션이라 라벨이 남아있는
   쪽이 안전하다는 판단) */
.quote-bar-icon-btn {
  width: 32px;
  height: 32px;
  padding: 0;
  flex-shrink: 0;
}

.quote-bar-icon-btn svg {
  width: 17px;
  height: 17px;
}

.auth-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 18px 0;
  color: var(--color-text-muted);
  font-size: 12.5px;
}

.auth-divider::before,
.auth-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--color-border);
}

.google-signin-button {
  display: flex;
  justify-content: center;
}

.auth-footer {
  margin-top: 18px;
  text-align: center;
  font-size: 13px;
  color: var(--color-text-muted);
}

.auth-footer a {
  color: var(--color-primary);
  font-weight: 600;
}

.form-alert {
  display: none;
  background: var(--color-danger-bg);
  color: var(--color-danger);
  border: 1px solid var(--color-danger-border);
  border-radius: var(--radius-md);
  padding: 10px 12px;
  font-size: 13px;
  margin-bottom: 16px;
}

.form-alert.show {
  display: block;
}

/* ---------- App shell ---------- */

/* quote-bar + app-shell 를 감싸는 뷰포트 높이 컨테이너 - body 자체에 height:100vh/overflow:hidden 을
   걸면 로그인/회원가입 페이지(.auth-page, 이 스타일시트를 같이 쓴다)의 세로 중앙 정렬까지 깨지므로,
   대시보드/만다라트 화면에만 쓰는 이 wrapper 로 범위를 좁힌다. quote-bar 높이가 몇 px인지 몰라도
   되도록(폰트 로딩·줄바꿈에 따라 달라질 수 있음) position:sticky + 고정 vh 계산 대신, flex 컬럼으로
   "명언바는 원래 크기, 나머지(app-shell)는 남는 공간 전부"를 자연스럽게 나누는 방식을 쓴다.
   실제 스크롤은 app-shell 내부의 .main 이 담당한다(main-header/사이드바는 페이지 스크롤과 무관하게
   항상 보인다) */
.shell-page {
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow: hidden;
}

.app-shell {
  display: flex;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

/* 카테고리/만다라트 목록처럼 길어질 수 있는 영역만 .sidebar-section-scroll 로 내부 스크롤을 따로 두고,
   그 위아래(브랜드·폼·사용자 정보)는 항상 고정된 크기로 유지된다 - 사이드바 자체는 이제 부모
   (.app-shell, 뷰포트에 맞게 이미 높이가 고정됨)를 그대로 채우기만 하면 되므로 별도의
   position/height 계산이 필요 없다.
   너비는 grid track이 아니라 sidebar 자신의 width로 갖고 있어야 접기/펼치기 애니메이션(width
   transition)이 가능하다 - flex-shrink:0으로 .main이 늘어나도 밀리지 않게 고정폭을 유지한다 */
.sidebar {
  width: 240px;
  flex-shrink: 0;
  background: var(--color-surface);
  border-right: 1px solid var(--color-border);
  padding: 22px 18px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transition: width 0.22s ease, padding 0.22s ease;
}

/* 사이드바 접힘 상태 - 토글 버튼(#sidebar-toggle-btn, js/sidebar.js)이 .app-shell에 이 클래스를
   토글한다. 완전히 감추는 대신(width:0) Claude 앱처럼 좁은 아이콘 레일(64px)만 남기고, 텍스트/폼처럼
   좁은 폭에 안 맞는 요소는 숨긴 채 로고·카테고리 아이콘(.cat-icon)·아바타만 보여준다 */
.app-shell.sidebar-collapsed .sidebar {
  width: 64px;
  padding: 22px 10px;
  align-items: center;
}

.app-shell.sidebar-collapsed .sidebar-brand-row {
  flex-direction: column;
  gap: 10px;
}

/* 접으면 로고(S 마크 + 텍스트)까지 통째로 숨긴다 - 좁은 레일엔 토글 버튼만 남는다 */
.app-shell.sidebar-collapsed .auth-brand,
.app-shell.sidebar-collapsed .sidebar-section h3,
.app-shell.sidebar-collapsed .add-category-form,
.app-shell.sidebar-collapsed .admin-add-category-form,
.app-shell.sidebar-collapsed .user-chip .who {
  display: none;
}

/* 만다라트 페이지의 "← 대시보드로", 설정 페이지의 설정 메뉴 탭처럼 아이콘으로 대체할 수 없는
   텍스트 전용 섹션(.sidebar-section-scroll이 아닌 것)은 좁은 레일에서 통째로 숨긴다 - 로고(S)
   자체가 이미 /dashboard 링크라 대시보드 복귀 동선은 남아있다 */
.app-shell.sidebar-collapsed .sidebar-section:not(.sidebar-section-scroll) {
  display: none;
}

.app-shell.sidebar-collapsed .category-list li {
  justify-content: center;
  padding: 8px 0;
}

.app-shell.sidebar-collapsed .category-list li > span:not(.cat-icon) {
  display: none;
}

.app-shell.sidebar-collapsed .category-list li .cat-icon {
  display: flex;
}

.app-shell.sidebar-collapsed .user-chip {
  justify-content: center;
}

.sidebar-brand-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 30px;
}

.sidebar .auth-brand {
  margin-bottom: 0;
}

.sidebar-section-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

.sidebar-section {
  margin-bottom: 26px;
}

.sidebar-section h3 {
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-muted);
  margin: 0 0 10px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

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

.category-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 10px;
  border-radius: 8px;
  font-size: 13.5px;
  color: var(--color-text);
  cursor: pointer;
}

.category-list li:hover {
  background: var(--color-bg);
}

.category-list li.active {
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
  font-weight: 600;
}

.category-list li .dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--color-primary);
  margin-right: 8px;
  flex-shrink: 0;
}

/* 사이드바가 접힌 아이콘 레일 모드에서만 보인다(.app-shell.sidebar-collapsed) - 카테고리 이름
   첫 글자를 로고/아바타와 같은 뱃지 스타일로 보여줘 좁은 폭에서도 어떤 카테고리인지 구분되게 한다 */
.category-list li .cat-icon {
  display: none;
  width: 26px;
  height: 26px;
  border-radius: 8px;
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 700;
  flex-shrink: 0;
}

.category-list li.active .cat-icon {
  background: var(--color-primary);
  color: #fff;
}

.category-list li .cat-count {
  font-size: 11px;
  font-weight: 600;
  color: var(--color-text-muted);
  flex-shrink: 0;
}

.category-list li.active .cat-count {
  color: var(--color-primary-dark);
}

.category-list li .cat-right {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.category-list li[draggable="true"] {
  cursor: grab;
}

.category-list li.dragging {
  opacity: 0.4;
}

.category-list li.drag-over-top {
  box-shadow: inset 0 2px 0 var(--color-primary);
}

.category-list li.drag-over-bottom {
  box-shadow: inset 0 -2px 0 var(--color-primary);
}

.category-list li .drag-handle {
  display: inline-block;
  width: 10px;
  margin-right: 4px;
  letter-spacing: -2px;
  color: var(--color-text-muted);
  opacity: 0;
}

.category-list li:hover .drag-handle {
  opacity: 1;
}

.category-list li .cat-actions {
  display: flex;
  align-items: center;
  gap: 2px;
  opacity: 0;
}

.category-list li:hover .cat-actions {
  opacity: 1;
}

.category-list li .remove-cat {
  color: var(--color-text-muted);
  font-size: 15px;
  line-height: 1;
  padding: 2px 4px;
}

.category-list li .remove-cat:hover {
  color: var(--color-danger);
}

.category-list li .apply-board {
  font-size: 11px;
  font-weight: 600;
  padding: 3px 8px;
  border-radius: 999px;
  color: var(--color-text-muted);
  background: var(--color-bg);
  white-space: nowrap;
}

.category-list li .apply-board:hover {
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
}

.category-list li .apply-board.applied {
  background: var(--color-primary);
  color: #fff;
}

.add-category-form {
  display: flex;
  gap: 6px;
  margin-top: 10px;
}

/* input에 별도 width 지정이 없으면 브라우저 기본 size(약 20자 폭)를 차지해 좁은 사이드바(240px)
   안에서 버튼과 합쳐 한 줄에 다 안 들어가고, flex-shrink 기본값(1) 때문에 버튼이 눌리면서 "추가"
   글자가 두 줄로 줄바꿈된다 - input이 남는 폭만큼만 줄어들게(flex:1) 하고 버튼은 내용 크기 그대로
   고정해(flex-shrink:0) 한 줄을 유지한다 */
.add-category-form input {
  flex: 1;
  min-width: 0;
}

.add-category-form button {
  flex-shrink: 0;
  white-space: nowrap;
}

.admin-add-category-form {
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  margin-top: 14px;
  padding-top: 10px;
  border-top: 1px dashed var(--color-border);
}

.admin-add-category-form .admin-caption {
  font-size: 11px;
  font-weight: 600;
  color: var(--color-text-muted);
}

.admin-add-category-form input,
.admin-add-category-form button {
  width: 100%;
}

.add-category-form input {
  flex: 1;
  padding: 7px 9px;
  border: 1px solid var(--color-border);
  border-radius: 8px;
  font-size: 12.5px;
}

.sidebar-footer {
  margin-top: auto;
  padding-top: 16px;
  border-top: 1px solid var(--color-border);
}

.user-chip {
  display: flex;
  align-items: center;
  gap: 10px;
}

.user-chip .avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 13px;
}

.user-chip .who {
  overflow: hidden;
}

.user-chip .who .name {
  font-size: 13.5px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.user-chip .who .email {
  font-size: 11.5px;
  color: var(--color-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------- Main content ---------- */

.main {
  flex: 1;
  padding: 26px 32px 40px;
  min-width: 0;
  overflow-y: auto;
}

/* 제목/날짜가 board-title-center 로 옮겨가고 나니 이 안엔 header-widgets 하나만 남는다 -
   원래 오른쪽에 있던 위치 그대로 유지되도록 오른쪽 정렬한다 */
.main-header {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  margin-bottom: 22px;
  flex-wrap: wrap;
  gap: 12px;
}

/* 전체 일정 / 대기 / 진행중 / 완료 통계 카드 바로 위 중앙에 놓이는 제목+날짜 - 제목/날짜 자체는
   그대로 가운데 정렬해서 보여주고, "새 일정" 버튼만 이 줄의 오른쪽 끝에 절대 위치로 붙인다(레이아웃
   흐름에서 빼야 h1/.today의 중앙 정렬 계산에 버튼 너비가 끼어들지 않는다) */
.board-title-center {
  position: relative;
  text-align: center;
  margin-bottom: 22px;
}

.board-title-center h1 {
  font-size: 21px;
  margin: 0;
  letter-spacing: -0.02em;
}

.board-title-center .today {
  color: var(--color-text-muted);
  font-size: 13px;
}

/* 보드 뷰에서는 #today-nav(실제 오늘/viewDate 기준 전날·다음날 이동), 일/주/월/년 뷰에서는
   #view-nav(현재 보고 있는 날짜/주/월/년 단위 이동)가 이 자리에서 서로 자리를 바꿔가며 나온다
   (switchView가 .show 토글) - 같은 자리·같은 스타일을 공유하도록 둘 다 .today-nav 클래스를 쓴다 */
.today-nav {
  display: none;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 4px;
}

.today-nav.show {
  display: flex;
}

.today-nav .icon-btn {
  padding: 2px 8px;
  font-size: 13px;
  line-height: 1.4;
}

.board-title-actions {
  position: absolute;
  right: 0;
  top: 0;
  display: flex;
  gap: 6px;
}

.board-title-actions .btn {
  width: auto;
}

/* 만다라트 화면 제목 + "AI로 채우기" 버튼을 한 줄에 배치한다 */
/* max-width를 .mandalart-grid(900px)와 맞춰야 "AI로 채우기" 버튼이 표 바로 위 오른쪽 끝에 붙는다 -
   안 맞추면 헤더가 .main 전체 폭을 다 차지해 버튼이 900px짜리 표보다 훨씬 오른쪽(페이지 끝)에 떨어져
   보인다 */
.mandalart-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  max-width: 900px;
  margin: 0 0 22px;
}

.mandalart-page-title {
  font-size: 21px;
  margin: 0;
  letter-spacing: -0.02em;
}

/* ---------- 설정 페이지 ---------- */

/* 사이드바에 놓이는 설정 메뉴 - 항목이 늘어날 걸 대비한 구조(settings.js의 showSettingsPanel 참고) */
.settings-menu {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.settings-menu-item {
  display: block;
  width: 100%;
  text-align: left;
  border: none;
  background: transparent;
  padding: 10px 12px;
  border-radius: 8px;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--color-text);
  cursor: pointer;
}

.settings-menu-item:hover {
  background: var(--color-bg);
}

.settings-menu-item.active {
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
  font-weight: 600;
}

.settings-content {
  flex: 1;
  min-width: 0;
}

.settings-panel {
  display: none;
}

.settings-panel.active {
  display: block;
}

.settings-section {
  max-width: 640px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 22px 24px;
  box-shadow: var(--shadow-card);
}

.settings-section h2 {
  font-size: 15px;
  margin: 0 0 4px;
}

.settings-section-desc {
  font-size: 12.5px;
  color: var(--color-text-muted);
  margin: 0 0 6px;
}

.settings-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  padding: 16px 0;
  border-top: 1px solid var(--color-border);
}

.settings-row-text {
  min-width: 0;
}

.settings-row-title {
  font-size: 14px;
  font-weight: 600;
}

.settings-row-desc {
  font-size: 12.5px;
  color: var(--color-text-muted);
  line-height: 1.5;
  margin-top: 4px;
}

.settings-switch {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  font-size: 12.5px;
  color: var(--color-text-muted);
  cursor: pointer;
  padding-top: 2px;
}

/* 네이티브 체크박스 대신 흔히 보는 슬라이딩 토글 모양으로 - appearance:none으로 기본 UI를 지우고
   트랙(배경)은 input 자체에, 움직이는 동그란 손잡이는 ::after 가상 요소로 그린다(별도 마크업 불필요) */
.settings-switch input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  width: 34px;
  height: 20px;
  flex-shrink: 0;
  margin: 0;
  border-radius: 999px;
  background: var(--color-border);
  cursor: pointer;
  transition: background 0.15s ease;
}

.settings-switch input[type="checkbox"]::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
  transition: transform 0.15s ease;
}

.settings-switch input[type="checkbox"]:checked {
  background: var(--color-primary);
}

.settings-switch input[type="checkbox"]:checked::after {
  transform: translateX(14px);
}

.settings-switch input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.recurring-schedule-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.recurring-schedule-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 0;
  border-top: 1px solid var(--color-border);
}

.recurring-schedule-item:first-child {
  border-top: none;
}

.recurring-schedule-title {
  font-size: 14px;
  font-weight: 600;
}

.recurring-schedule-meta {
  font-size: 12.5px;
  color: var(--color-text-muted);
  margin-top: 2px;
}

.recurring-schedule-empty {
  font-size: 12.5px;
  color: var(--color-text-muted);
  padding: 8px 0;
}

/* ---------- Header widgets (month radar + today clock) ---------- */

.header-widgets {
  display: flex;
  align-items: center;
  gap: 14px;
  flex: 1;
}

/* ---------- 워드클라우드 위젯 (내 일정 제목 키워드 빈도) ---------- */
/* .main-header가 justify-content:flex-end라 오른쪽 4개 위젯(고정폭)을 다 채우고도 왼쪽에 빈 공간이
   남는데, 그 자리를 채우려고 header-widgets의 첫 번째 자식으로 넣었다 - flex:1로 남는 폭을 그대로
   흡수하고, 다른 위젯들과 시각적으로 맞추려 높이(226px)·배경·테두리·radius를 mandalart-widget과 통일했다 */
.wordcloud-widget {
  position: relative;
  flex: 1 1 240px;
  min-width: 200px;
  height: 226px;
  box-sizing: border-box;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 12px;
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

.wordcloud-canvas {
  position: relative;
  width: 100%;
  height: 100%;
}

/* 마우스를 따라다니는 원형 돋보기(볼록렌즈) - .wordcloud-canvas는 매 렌더마다 innerHTML을 통째로
   새로 그려서(dashboard.js renderWordCloud) 그 안에 넣으면 렌더될 때마다 사라진다. .wordcloud-widget의
   형제로 두면(부모가 position:relative) canvas와 같은 패딩 기준 좌표계를 공유해, canvas 기준으로 잰
   마우스 좌표를 그대로 이 요소의 left/top에 써도 맞아떨어진다. dashboard.js가 mousemove마다
   left/top을 이 요소에 직접 옮기고(centered via transform), "렌즈 반경 안 단어만 확대"는 단어별
   커서 거리 계산이 필요해 CSS만으로는 안 되므로 같은 mousemove 핸들러가 .wordcloud-word 각각의
   --wc-scale도 함께 갱신한다 - 이 요소 자체는 렌즈의 "유리알" 시각 효과만 담당한다 */
.wordcloud-lens {
  position: absolute;
  top: 0;
  left: 0;
  width: 130px;
  height: 130px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  background:
    radial-gradient(circle at 30% 22%, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0.12) 45%, rgba(255, 255, 255, 0) 68%),
    radial-gradient(circle at 72% 80%, rgba(20, 23, 45, 0.1) 0%, rgba(20, 23, 45, 0) 55%);
  border: 1px solid rgba(255, 255, 255, 0.35);
  box-shadow:
    0 6px 16px rgba(20, 23, 45, 0.2),
    inset 0 -4px 8px rgba(20, 23, 45, 0.1),
    inset 0 4px 7px rgba(255, 255, 255, 0.45);
  opacity: 0;
  transition: opacity 160ms ease;
}

.wordcloud-lens.show {
  opacity: 1;
}

/* 라이트 모드용 흰색 기반 유리 그라데이션을 다크 카드 위에 그대로 쓰면 뿌옇고 밝은 회색 공처럼
   튀어 보인다(실제로 그렇게 보인다는 피드백) - 하이라이트/테두리 불투명도를 낮추고, 그림자 쪽은
   흰색이 아니라 검정 계열로 바꿔 어두운 배경에 자연스럽게 녹아드는 유리알로 다시 조정한다 */
:root[data-theme="dark"] .wordcloud-lens {
  background:
    radial-gradient(circle at 30% 22%, rgba(255, 255, 255, 0.16) 0%, rgba(255, 255, 255, 0.05) 45%, rgba(255, 255, 255, 0) 68%),
    radial-gradient(circle at 72% 80%, rgba(0, 0, 0, 0.35) 0%, rgba(0, 0, 0, 0) 55%);
  border: 1px solid rgba(255, 255, 255, 0.12);
  box-shadow:
    0 6px 16px rgba(0, 0, 0, 0.45),
    inset 0 -4px 8px rgba(0, 0, 0, 0.3),
    inset 0 4px 7px rgba(255, 255, 255, 0.12);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .wordcloud-lens {
    background:
      radial-gradient(circle at 30% 22%, rgba(255, 255, 255, 0.16) 0%, rgba(255, 255, 255, 0.05) 45%, rgba(255, 255, 255, 0) 68%),
      radial-gradient(circle at 72% 80%, rgba(0, 0, 0, 0.35) 0%, rgba(0, 0, 0, 0) 55%);
    border: 1px solid rgba(255, 255, 255, 0.12);
    box-shadow:
      0 6px 16px rgba(0, 0, 0, 0.45),
      inset 0 -4px 8px rgba(0, 0, 0, 0.3),
      inset 0 4px 7px rgba(255, 255, 255, 0.12);
  }
}

.wordcloud-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  color: var(--color-text-muted);
  font-size: 12.5px;
  text-align: center;
  padding: 0 12px;
}

/* 달 이동 컨트롤 - 캔버스 흐름에 끼워 넣으면(dashboard.js가 매 렌더마다 canvas.innerHTML을 통째로
   갈아치우고, 렌즈 좌표는 canvas가 위젯 패딩 박스를 꽉 채운다는 전제로 계산돼 있어) 캔버스 높이가
   줄어 그 전제가 깨진다 - 대신 캔버스는 그대로 두고 모서리에 겹쳐 띄운다(⚙ 필터 버튼들과 같은 패턴) */
.wordcloud-month-nav {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 2px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 999px;
  padding: 1px 4px;
  box-shadow: var(--shadow-card);
}

.wordcloud-month-nav-btn {
  width: 16px;
  height: 16px;
  border: none;
  background: transparent;
  color: var(--color-text-muted);
  font-size: 12px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  cursor: pointer;
  border-radius: 50%;
}

.wordcloud-month-nav-btn:hover {
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
}

.wordcloud-month-label {
  font-size: 10.5px;
  color: var(--color-text-muted);
  padding: 0 2px;
  white-space: nowrap;
}

/* wordcloud.js가 단어마다 절대 좌표(중심점)·글자 크기·색·회전 여부를 인라인 style로 계산해 배치한다
   (레이아웃 자체가 JS 쪽 타원형 스파이럴 배치 알고리즘의 결과라 CSS만으로는 재현 불가) - 회전은
   --wc-rotate 커스텀 프로퍼티로 받아 기본/호버 transform 양쪽에서 공통으로 쓴다(안 그러면 호버 시
   scale만 있는 transform으로 덮어써져 회전이 풀려버린다) */
/* 확대/그림자는 더 이상 :hover(그 단어 자기 영역 위일 때만)로 켜지 않는다 - 마우스를 원형 렌즈로
   보고 그 반경 안에 있는 모든 단어가 거리에 비례해 같이 부풀어야 하므로, dashboard.js가 mousemove마다
   각 단어~커서 거리를 계산해 --wc-scale/--wc-shadow-t를 인라인으로 갱신한다(.wordcloud-lens가 그
   "렌즈" 자체의 유리알 시각 효과를 담당). transition을 짧게 둬 커서를 따라 자연스럽게 뒤쫓아오게 한다 */
.wordcloud-word {
  --wc-rotate: 0deg;
  --wc-scale: 1;
  position: absolute;
  top: 0;
  left: 0;
  transform: translate(-50%, -50%) rotate(var(--wc-rotate)) scale(var(--wc-scale));
  white-space: nowrap;
  font-weight: 700;
  line-height: 1;
  cursor: default;
  user-select: none;
  transition: transform 45ms linear, filter 45ms linear;
  animation: wordcloudWordIn 260ms ease-out backwards;
  animation-delay: calc(var(--stagger-index, 0) * 30ms);
}

@keyframes wordcloudWordIn {
  from {
    opacity: 0;
    transform: translate(-50%, -50%) rotate(var(--wc-rotate)) scale(0.5);
  }
  to {
    opacity: 1;
    transform: translate(-50%, -50%) rotate(var(--wc-rotate)) scale(1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .wordcloud-word {
    animation: none;
    opacity: 1;
  }
}

/* ---------- Mandalart widget (dashboard 미리보기, 클릭 시 /mandalart 이동) ---------- */

.mandalart-widget {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 228px;
  height: 226px;
  box-sizing: border-box;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 10px 16px;
  box-shadow: var(--shadow-card);
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  transition: border-color 0.15s, transform 0.05s;
}

.mandalart-widget:hover {
  border-color: var(--color-primary);
}

/* ---------- 오늘 성취도 위젯 (랜덤 비유: 진행률/계절/월/산 높이) ---------- */

.achievement-widget {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 228px;
  height: 226px;
  box-sizing: border-box;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 14px 16px;
  box-shadow: var(--shadow-card);
  cursor: pointer;
  font-family: inherit;
  transition: border-color 0.15s;
}

.achievement-widget:hover {
  border-color: var(--color-primary);
}

.achievement-track {
  position: relative;
  width: 56px;
  height: 108px;
  border-radius: 28px;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  overflow: hidden;
  flex-shrink: 0;
}

.achievement-fill {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 0%;
  background: linear-gradient(180deg, var(--color-primary) 0%, var(--color-primary-dark) 100%);
  border-radius: 28px 28px 0 0;
  transition: height 0.7s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.achievement-emoji {
  position: absolute;
  left: 50%;
  bottom: 0%;
  transform: translate(-50%, 50%);
  font-size: 22px;
  line-height: 1;
  transition: bottom 0.7s cubic-bezier(0.34, 1.56, 0.64, 1);
  animation: achievement-bob 2.4s ease-in-out infinite;
}

@keyframes achievement-bob {
  0%, 100% { margin-top: 0; }
  50% { margin-top: -4px; }
}

.achievement-headline {
  font-size: 21px;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.achievement-detail {
  font-size: 11.5px;
  color: var(--color-text-muted);
  text-align: center;
  line-height: 1.4;
}

.achievement-widget.complete .achievement-headline {
  color: var(--color-completed);
}

.achievement-widget.complete .achievement-fill {
  background: linear-gradient(180deg, var(--color-completed) 0%, #0f5c2e 100%);
}

.mandalart-widget:active {
  transform: scale(0.98);
}

.mandalart-widget-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--color-text-muted);
}

/* 이미 만든 만다라트가 있으면(가장 최근 것) 제목을 작은 글씨로 보여준다 - 없으면 비어서 렌더링 안 됨 */
.mandalart-widget-subtitle {
  max-width: 140px;
  font-size: 11px;
  color: var(--color-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mandalart-widget-subtitle:empty {
  display: none;
}

@media (max-width: 940px) {
  .mandalart-widget {
    display: none;
  }
}

/* ---------- Month radar (pentagon) ---------- */

.month-radar {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 228px;
  height: 226px;
  box-sizing: border-box;
  /* 내부 차트가 이 박스보다 크게 그려지므로, 잘리지 않고 카드 밖으로 자연스럽게 넘치도록 둔다 */
  overflow: visible;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 10px 16px;
  box-shadow: var(--shadow-card);
}

.month-radar-face {
  position: relative;
  line-height: 0;
}

.month-radar-face svg {
  display: block;
}

.radar-grid-ring {
  fill: none;
  stroke: var(--color-border);
  stroke-width: 1;
}

.radar-axis-line {
  stroke: var(--color-border);
  stroke-width: 1;
}

.radar-axis-label {
  font-size: 13px;
  font-weight: 600;
  fill: var(--color-text-muted);
}

.radar-data-shape {
  fill: #f9a8d4;
  stroke: #db2777;
  stroke-width: 1;
  stroke-linejoin: round;
  fill-opacity: 0.45;
  /* transform-origin 은 렌더링 시 JS 가 RADAR_CENTER 좌표로 직접 지정한다 (중심 기준으로 퍼지는 모션이어야 하므로) */
  animation: radar-grow 0.5s ease-out;
}

@keyframes radar-grow {
  from {
    transform: scale(0);
    opacity: 0;
  }
  to {
    transform: scale(1);
    opacity: 1;
  }
}

@media (max-width: 940px) {
  .month-radar {
    display: none;
  }
}

/* ---------- Today 24h clock ---------- */

.today-clock {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 228px;
  height: 226px;
  box-sizing: border-box;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 10px 16px;
  box-shadow: var(--shadow-card);
}

.clock-filter-btn {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 2;
  width: 22px;
  height: 22px;
  border: 1px solid var(--color-border);
  border-radius: 50%;
  background: var(--color-surface);
  color: var(--color-text-muted);
  font-size: 11px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}

.clock-filter-btn:hover,
.clock-filter-btn.active {
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
  border-color: var(--color-primary-soft);
}

.clock-filter-popover {
  display: none;
  position: absolute;
  top: 32px;
  right: 8px;
  z-index: 3;
  width: 168px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  padding: 10px 12px;
}

.clock-filter-popover.show {
  display: block;
}

.clock-filter-popover-title {
  font-size: 11px;
  font-weight: 600;
  color: var(--color-text-muted);
  margin-bottom: 8px;
}

.clock-filter-options {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 160px;
  overflow-y: auto;
}

.clock-filter-option {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 12.5px;
  cursor: pointer;
}

.clock-filter-option input[type="checkbox"] {
  width: auto;
  margin: 0;
}

.clock-filter-option .swatch {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

.today-clock-face {
  position: relative;
  flex-shrink: 0;
  line-height: 0;
}

.today-clock-face svg {
  display: block;
}

/* 낮/밤을 한눈에 보여주는 원판 배경 - #today-clock-svg.is-night 는 renderTodayClock()이
   06~18시가 아닐 때 svg 루트에 붙이는 클래스다. 그 아래(눈금 라벨/바늘)도 어두운 배경에서
   읽히도록 같이 톤을 바꾼다 */
.clock-face-bg {
  fill: #eaf4fd;
  transition: fill 0.4s ease;
}

#today-clock-svg.is-night .clock-face-bg {
  fill: #161d3a;
}

.clock-tick {
  stroke: var(--color-border);
  stroke-width: 1;
}

#today-clock-svg.is-night .clock-tick {
  stroke: #38416b;
}

.clock-tick-label {
  font-size: 9px;
  fill: var(--color-text-muted);
}

#today-clock-svg.is-night .clock-tick-label {
  fill: #aab0d6;
}

.clock-face-ring {
  fill: none;
  stroke: var(--color-border);
  stroke-width: 1;
}

#today-clock-svg.is-night .clock-face-ring {
  stroke: #38416b;
}

.clock-center-icon {
  font-size: 26px;
}

.clock-arc {
  fill: none;
  stroke-linecap: round;
  transition: stroke-width 0.1s;
}

.clock-arc-hit {
  fill: none;
  stroke: transparent;
  stroke-linecap: round;
  /* stroke 가 transparent 라 기본 visiblePainted 히트테스트로는 안 잡히므로 명시적으로 켠다 */
  pointer-events: stroke;
  cursor: pointer;
}

.clock-arc-hit:focus {
  outline: none;
}

.clock-arc-hit:hover + .clock-arc,
.clock-arc-hit:focus + .clock-arc {
  stroke-width: 10;
}

.clock-now-line {
  stroke: var(--color-primary);
  stroke-width: 2;
  stroke-linecap: round;
}

.clock-now-dot {
  fill: var(--color-primary);
  stroke: var(--color-surface);
  stroke-width: 2;
}

.today-clock-legend {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px 10px;
  font-size: 11px;
  color: var(--color-text-muted);
  white-space: nowrap;
}

.today-clock-legend-item {
  display: flex;
  align-items: center;
  gap: 6px;
}

.today-clock-legend-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
}

.today-clock-legend-dot.PENDING { background: var(--color-pending); }
.today-clock-legend-dot.IN_PROGRESS { background: var(--color-progress); }
.today-clock-legend-dot.COMPLETED { background: var(--color-completed); }
.today-clock-legend-dot.CANCELLED { background: var(--color-cancelled); }

.clock-tooltip {
  position: absolute;
  min-width: 130px;
  max-width: 210px;
  background: #1f2333;
  color: #fff;
  padding: 8px 10px;
  border-radius: 8px;
  font-size: 11.5px;
  line-height: 1.45;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -100%);
  transition: opacity 0.12s;
  z-index: 60;
  top: 0;
  left: 0;
}

.clock-tooltip.show {
  opacity: 1;
}

.clock-tooltip .tt-title {
  font-weight: 700;
  margin-bottom: 3px;
  white-space: normal;
  word-break: break-word;
}

.clock-tooltip .tt-time,
.clock-tooltip .tt-meta {
  color: #c7c9d9;
  font-size: 11px;
  white-space: normal;
}

@media (max-width: 720px) {
  .today-clock {
    display: none;
  }
}

.board {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px;
  align-items: start;
}

.board-column {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  min-height: 160px;
  display: flex;
  flex-direction: column;
  transition: background 0.1s, border-color 0.1s;
}

.board-column.drop-target {
  background: var(--color-primary-soft);
  border-color: var(--color-primary);
}

.board-column-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 14px 10px;
  border-bottom: 1px solid var(--color-border);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}

/* 컬럼 헤더에 상태별 파스텔 톤을 은은하게 깔아 한눈에 대기/진행중/완료/취소를 구분하게 한다 */
.board-column.PENDING .board-column-header { background: var(--color-pending-bg); }
.board-column.IN_PROGRESS .board-column-header { background: var(--color-progress-bg); }
.board-column.COMPLETED .board-column-header { background: var(--color-completed-bg); }
.board-column.CANCELLED .board-column-header { background: var(--color-cancelled-bg); }

.board-column-header .title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13.5px;
  font-weight: 700;
}

.status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
}

.status-dot.PENDING { background: var(--color-pending); }
.status-dot.IN_PROGRESS { background: var(--color-progress); }
.status-dot.COMPLETED { background: var(--color-completed); }
.status-dot.CANCELLED { background: var(--color-cancelled); }

.count-badge {
  background: var(--color-bg);
  color: var(--color-text-muted);
  font-size: 11.5px;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: 20px;
}

.board-column-header-right {
  display: flex;
  align-items: center;
  gap: 6px;
}

/* 컬럼별 "+" - 상단의 전체 "+ 새 일정"과 달리, 여기서 열면 그 컬럼 상태로 미리 선택된 채로 새
   일정 모달이 뜬다(openCreateModal(col.key), dashboard.js) */
.board-column-add-btn {
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 5px;
  background: transparent;
  color: var(--color-text-muted);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}

.board-column-add-btn:hover {
  background: rgba(0, 0, 0, 0.08);
  color: var(--color-text);
}

.board-column-body {
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1;
}

.board-more-btn {
  margin: 0 12px 12px;
  padding: 8px 10px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--color-text-muted);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  cursor: pointer;
}

.board-more-btn:hover {
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
}

.empty-hint {
  color: var(--color-text-muted);
  font-size: 12.5px;
  text-align: center;
  padding: 18px 6px;
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
}

.schedule-card {
  border: 1px solid var(--color-border);
  border-left: 3px solid transparent;
  border-radius: var(--radius-md);
  padding: 12px 13px;
  background: var(--color-input-bg);
  cursor: grab;
  transition: box-shadow 0.15s, border-color 0.15s;
}

.schedule-card.dragging {
  opacity: 0.4;
}

.schedule-card:hover {
  box-shadow: var(--shadow-card);
  border-color: var(--color-border);
}

/* 상태별 파스텔 톤 - 카드 배경은 옅게, 왼쪽 테두리는 살짝 진하게 줘서 한눈에 상태가 구분되게 한다 */
.schedule-card.PENDING { background: var(--color-pending-bg); border-left-color: var(--color-pending); }
.schedule-card.IN_PROGRESS { background: var(--color-progress-bg); border-left-color: var(--color-progress); }
.schedule-card.COMPLETED { background: var(--color-completed-bg); border-left-color: var(--color-completed); }
.schedule-card.CANCELLED { background: var(--color-cancelled-bg); border-left-color: var(--color-cancelled); }

.schedule-card .card-top {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 8px;
  margin-bottom: 6px;
}

/* 전체 접기(#board-compact-toggle-btn)와 카드별 접기(.card-collapse-btn) 둘 다 dashboard.js가
   렌더링 시점에 카드별로 .collapsed 클래스 부여 여부를 계산해서(isCardVisuallyCollapsed) 반영한다 -
   전체 접기가 켜져 있어도 개별로 펼친 카드는 .collapsed가 안 붙으므로, 아래 규칙 하나로 두 모드가
   자연히 함께 처리되고 "전체는 접혀 있지만 이 카드만 펼침" 같은 예외도 그대로 표현된다 */
.schedule-card.collapsed {
  padding: 9px 13px;
}

.schedule-card.collapsed .card-top {
  margin-bottom: 0;
}

.schedule-card.collapsed .card-content,
.schedule-card.collapsed .card-meta,
.schedule-card.collapsed .card-time,
.schedule-card.collapsed .card-actions {
  display: none;
}

.card-collapse-btn {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 5px;
  background: transparent;
  color: var(--color-text-muted);
  font-size: 10px;
  cursor: pointer;
}

.card-collapse-btn:hover {
  background: rgba(0, 0, 0, 0.06);
  color: var(--color-text);
}

.schedule-card .card-title {
  font-size: 13.5px;
  font-weight: 700;
  line-height: 1.35;
}

.schedule-card .card-content {
  font-size: 12.5px;
  color: var(--color-text-muted);
  margin: 0 0 10px;
  line-height: 1.5;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.tag {
  display: inline-flex;
  align-items: center;
  font-size: 11px;
  font-weight: 700;
  padding: 3px 8px;
  border-radius: 20px;
  white-space: nowrap;
}

.tag.PENDING { color: var(--color-pending); background: var(--color-pending-bg); }
.tag.IN_PROGRESS { color: var(--color-progress); background: var(--color-progress-bg); }
.tag.COMPLETED { color: var(--color-completed); background: var(--color-completed-bg); }
.tag.CANCELLED { color: var(--color-cancelled); background: var(--color-cancelled-bg); }

.tag.category-tag {
  color: var(--color-primary-dark);
  background: var(--color-primary-soft);
}

.card-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 10px;
}

.card-time {
  font-size: 11.5px;
  color: var(--color-text-muted);
  margin-bottom: 10px;
}

.card-actions {
  display: flex;
  align-items: center;
  gap: 6px;
}

.card-actions select {
  flex: 1;
  font-size: 11.5px;
  padding: 5px 6px;
  border-radius: 7px;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
}

.icon-btn {
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  border-radius: 7px;
  padding: 5px 8px;
  font-size: 12px;
  color: var(--color-text-muted);
}

.icon-btn:hover {
  color: var(--color-danger);
  border-color: var(--color-danger-border);
  background: var(--color-danger-bg);
}

/* ---------- View toolbar (보드 / 일 / 주 / 월 / 년) ---------- */

.view-toolbar {
  display: flex;
  align-items: center;
  margin-bottom: 16px;
}

.view-switcher {
  display: inline-flex;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: 10px;
  padding: 3px;
  gap: 2px;
}

.view-tab {
  border: none;
  background: transparent;
  color: var(--color-text-muted);
  font-size: 13px;
  font-weight: 600;
  padding: 6px 14px;
  border-radius: 8px;
}

.view-tab:hover {
  color: var(--color-text);
}

.view-tab.active {
  background: var(--color-surface);
  color: var(--color-primary-dark);
  box-shadow: var(--shadow-card);
}

/* ---------- Calendar views (일 / 주 / 월 / 년) ---------- */

.calendar-view {
  display: none;
}

.calendar-view.show {
  display: block;
}

.board.hide {
  display: none;
}

/* -- 월간: 7x6 날짜 그리드 -- */

.cal-weekday-row,
.cal-month-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
}

.cal-weekday-row {
  margin-bottom: 4px;
}

.cal-weekday-row .cal-weekday {
  text-align: center;
  font-size: 11.5px;
  font-weight: 700;
  color: var(--color-text-muted);
  padding: 4px 0;
}

.cal-month-grid {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--color-border);
  gap: 1px;
}

.cal-month-cell {
  background: var(--color-surface);
  min-height: 96px;
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.cal-month-cell.other-month {
  background: var(--color-bg);
}

.cal-month-cell.other-month .cal-date-num {
  color: var(--color-text-muted);
  opacity: 0.7;
}

.cal-date-num {
  font-size: 12px;
  font-weight: 700;
  color: var(--color-text);
  cursor: pointer;
  width: fit-content;
  padding: 1px 6px;
  border-radius: 20px;
}

.cal-date-num:hover {
  background: var(--color-primary-soft);
}

.cal-date-num.is-today {
  background: var(--color-primary);
  color: #fff;
}

.cal-event-chip {
  font-size: 10.5px;
  font-weight: 600;
  padding: 2px 6px;
  border-radius: 6px;
  border-left: 3px solid transparent;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}

.cal-more-link {
  font-size: 10px;
  color: var(--color-text-muted);
  cursor: pointer;
  padding: 0 2px;
}

.cal-more-link:hover {
  color: var(--color-primary-dark);
}

/* -- 주간 / 일간: 0~24시 타임그리드 -- */

.cal-time-grid-wrap {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  overflow: hidden;
}

.cal-time-header {
  display: flex;
  border-bottom: 1px solid var(--color-border);
}

.cal-time-header-spacer {
  width: 46px;
  flex-shrink: 0;
}

.cal-time-header-day {
  flex: 1;
  text-align: center;
  padding: 10px 4px;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--color-text-muted);
  border-left: 1px solid var(--color-border);
}

.cal-time-header-day.is-today {
  color: var(--color-primary-dark);
}

.cal-time-header-day .dow {
  display: block;
  font-size: 10.5px;
  font-weight: 600;
}

.cal-time-body {
  display: flex;
  max-height: 560px;
  overflow-y: auto;
  position: relative;
}

.cal-time-axis {
  width: 46px;
  flex-shrink: 0;
  position: relative;
}

.cal-hour-label {
  position: absolute;
  left: 0;
  width: 40px;
  text-align: right;
  padding-right: 6px;
  font-size: 10.5px;
  color: var(--color-text-muted);
  transform: translateY(-6px);
}

.cal-time-columns {
  display: flex;
  flex: 1;
  position: relative;
}

.cal-day-column {
  flex: 1;
  position: relative;
  border-left: 1px solid var(--color-border);
}

.cal-hour-line {
  position: absolute;
  left: 0;
  right: 0;
  border-top: 1px solid var(--color-border);
}

/* 겹치는 일정이 많아 레인이 상한(MAX_DAY_LANES_BEFORE_SCROLL)을 넘으면 이 레이어만 가로 스크롤된다 -
   시간 그리드선(.cal-hour-line)/현재 시각선(.cal-now-line)은 .cal-day-column의 직계 자식으로 남아있어
   스크롤과 무관하게 항상 그 자리에 고정된다 */
.cal-day-column-lanes {
  position: absolute;
  inset: 0;
  overflow-x: auto;
  overflow-y: hidden;
}

.cal-day-column-lanes-inner {
  position: relative;
  height: 100%;
  min-width: 100%;
}

.cal-event-block {
  position: absolute;
  border-radius: 6px;
  border-left: 3px solid transparent;
  padding: 3px 6px;
  font-size: 10.5px;
  font-weight: 600;
  line-height: 1.3;
  overflow: hidden;
  cursor: pointer;
  box-sizing: border-box;
}

.cal-event-block .cal-event-time {
  font-weight: 500;
  opacity: 0.85;
  font-size: 9.5px;
  display: block;
}

.cal-now-line {
  position: absolute;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--color-primary);
  z-index: 5;
}

.cal-now-line::before {
  content: "";
  position: absolute;
  left: -4px;
  top: -3px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-primary);
}

/* -- 연간: 12개월 미니 달력 -- */

.cal-year-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}

.cal-mini-month {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 12px;
  background: var(--color-surface);
}

.cal-mini-month-title {
  font-size: 15px;
  font-weight: 700;
  margin-bottom: 8px;
  cursor: pointer;
}

.cal-mini-month-title:hover {
  color: var(--color-primary-dark);
}

.cal-mini-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
}

.cal-mini-cell {
  font-size: 12.5px;
  text-align: center;
  color: var(--color-text);
  padding: 5px 0;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
}

.cal-mini-cell:hover {
  background: var(--color-primary-soft);
}

.cal-mini-cell.other-month {
  visibility: hidden;
}

.cal-mini-cell.is-today {
  background: var(--color-primary);
  color: #fff;
  font-weight: 700;
}

.cal-mini-cell.has-events::after {
  content: "";
  position: absolute;
  bottom: 2px;
  left: 50%;
  transform: translateX(-50%);
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--color-primary);
}

.cal-mini-cell.is-today.has-events::after {
  background: #fff;
}

@media (max-width: 1200px) {
  .cal-year-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 720px) {
  .cal-year-grid {
    grid-template-columns: 1fr;
  }
}

/* ---------- Modal ---------- */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(20, 23, 45, 0.45);
  display: none;
  align-items: center;
  justify-content: center;
  padding: 20px;
  z-index: 50;
}

.modal-overlay.show {
  display: flex;
}

.modal {
  width: 100%;
  max-width: 440px;
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  padding: 24px 24px 20px;
  box-shadow: 0 20px 50px rgba(20, 23, 45, 0.25);
}

.modal h2 {
  font-size: 17px;
  margin: 0 0 18px;
}

/* 반복 일정이 많아지면 모달이 화면 밖으로 늘어나지 않도록 목록만 스크롤한다 */
.recurring-list-modal .recurring-schedule-list {
  max-height: 340px;
  overflow-y: auto;
}

/* 새 일정 모달 상단의 "새 일정"/"반복 일정" 탭 - 두 폼이 같은 모달 안에 같이 있고, 이 탭으로만
   전환한다(schedule-form과 recurring-form 중 하나만 display:block, dashboard.js의 setModalTab) */
.modal-tabs {
  display: flex;
  gap: 4px;
  margin: -4px 0 18px;
  border-bottom: 1px solid var(--color-border);
}

.modal-tab {
  flex: 1;
  padding: 10px 0 9px;
  border: none;
  background: transparent;
  font-size: 14px;
  font-weight: 600;
  color: var(--color-text-muted);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color 0.15s ease, border-color 0.15s ease;
}

.modal-tab:hover {
  color: var(--color-text);
}

.modal-tab.active {
  color: var(--color-primary);
  border-bottom-color: var(--color-primary);
}

.mandalart-preview-modal {
  max-width: 360px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.mandalart-preview-modal h2 {
  margin: 0 0 16px;
}

.mandalart-preview-link {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
}

/* 만다라트를 가장 잘 요약하는 중앙 3x3 블록(핵심 목표 + 세부목표 8개)의 실제 텍스트를 그대로
   보여준다(dashboard.js buildMandalartGoalPreview) - 나머지 64개 실행항목은 여기선 생략한다.
   대시보드 홈 위젯(.compact, 228px 카드 안에 들어가야 함)과 미리보기 모달(공간이 넉넉함) 둘 다
   같은 구조를 쓰고, .compact 여부로 칸 크기/글자 크기만 다르게 조정한다 */
.mandalart-goal-preview-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  gap: 6px;
  width: 280px;
  height: 280px;
}

.mandalart-goal-preview-grid.compact {
  width: 150px;
  height: 150px;
  gap: 3px;
}

/* 세부목표 칸 호버 팝오버(.mandalart-subblock-popover) 전용 크기 - compact(150px)보다 뚜렷하게 크게,
   부모 미리보기 모달(280px, max-width:360px)과 거의 맞먹는 크기로 잡아 내용이 잘 읽히게 한다 */
.mandalart-goal-preview-grid.popover-size {
  width: 260px;
  height: 260px;
  gap: 5px;
}

.mandalart-goal-preview-grid.popover-size .mandalart-goal-preview-cell {
  padding: 6px 5px;
  border-radius: 8px;
  font-size: 11.5px;
  line-height: 1.3;
}

.mandalart-goal-preview-grid.popover-size .mandalart-goal-preview-text {
  -webkit-line-clamp: 4;
}

.mandalart-goal-preview-grid.popover-size .mandalart-goal-preview-cell.main-goal {
  font-size: 12px;
}

/* 칸 자체는 flex로 가운데 정렬만 담당하고(가로/세로 모두), 줄임(line-clamp, .mandalart-goal-preview-text)은
   display:-webkit-box가 필요해 같은 요소에 flex와 함께 줄 수 없으므로 텍스트를 감싸는 안쪽 span에
   따로 적용한다(dashboard.js buildMandalartGoalPreview) */
.mandalart-goal-preview-cell {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px 6px;
  border-radius: 10px;
  background: var(--color-primary-soft);
  font-size: 12px;
  line-height: 1.35;
  color: var(--color-text);
  text-align: center;
}

.mandalart-goal-preview-text {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 5;
  overflow: hidden;
}

.mandalart-goal-preview-grid.compact .mandalart-goal-preview-cell {
  padding: 3px 2px;
  border-radius: 5px;
  font-size: 8px;
  line-height: 1.2;
}

.mandalart-goal-preview-grid.compact .mandalart-goal-preview-text {
  -webkit-line-clamp: 3;
}

.mandalart-goal-preview-cell.main-goal {
  background: var(--color-primary);
  color: #fff;
  font-weight: 700;
  font-size: 13px;
}

.mandalart-goal-preview-grid.compact .mandalart-goal-preview-cell.main-goal {
  font-size: 8.5px;
}

.mandalart-goal-preview-cell.empty {
  background: var(--color-bg);
}

.mandalart-preview-link:hover .mandalart-goal-preview-cell {
  box-shadow: inset 0 0 0 1px var(--color-primary);
}

.mandalart-preview-hint {
  margin: 14px 0 0;
  font-size: 12px;
  color: var(--color-text-muted);
}

/* 세부목표 칸 호버 시 뜨는 바깥 블록(실행항목 9칸) 팝오버 - modal-overlay(z-index 50)보다 위에 떠야
   하고, 마우스가 벗어나면 바로 사라지는 가벼운 미리보기라 배경 오버레이/닫기 버튼 없이 카드 하나만 fixed로
   띄운다. 위치(left/top)는 dashboard.js가 호버한 칸 기준으로 매번 계산해서 인라인 style로 준다 */
.mandalart-subblock-popover {
  position: fixed;
  z-index: 60;
  display: none;
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  box-shadow: 0 20px 50px rgba(20, 23, 45, 0.25);
  padding: 10px;
  pointer-events: none;
}

.mandalart-subblock-popover.show {
  display: block;
}

.mandalart-subblock-popover-title {
  width: 260px;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--color-text-muted);
  text-align: center;
  margin-bottom: 8px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.detail-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}

.detail-top .category-tag:empty {
  display: none;
}

.detail-status {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  color: var(--color-text-muted);
  font-weight: 600;
}

.modal #detail-title {
  margin: 0 0 8px;
}

.detail-time {
  font-size: 13px;
  color: var(--color-text-muted);
  margin-bottom: 14px;
}

.detail-content {
  font-size: 14px;
  line-height: 1.6;
  color: var(--color-text);
  white-space: pre-wrap;
  word-break: break-word;
  background: var(--color-bg);
  border-radius: var(--radius-md);
  padding: 12px 14px;
  margin: 0 0 12px;
  max-height: 220px;
  overflow-y: auto;
}

.detail-content:empty::before {
  content: "내용이 없습니다.";
  color: var(--color-text-muted);
}

.detail-meta {
  font-size: 12.5px;
  color: var(--color-text-muted);
}

.detail-meta:empty {
  display: none;
}

.modal-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

/* 그리드 아이템은 기본적으로 min-width: auto 라, 내용(예: 한글 로케일의 "오전/오후"가 붙는
   datetime-local)이 트랙 폭보다 넓으면 줄어들지 않고 그리드/모달 밖으로 튀어나온다.
   min-width: 0 으로 그 자동 최소폭을 없애야 1fr 1fr 이 실제로 균등하게 나뉜다 */
.modal-row .field {
  min-width: 0;
}

/* 반복 일정 추가 폼의 요일 선택 - 체크박스 7개 대신 토글 버튼으로 눌러서 선택/해제한다 */
.weekday-picker {
  display: flex;
  gap: 6px;
}

.weekday-btn {
  flex: 1;
  padding: 8px 0;
  border: 1px solid var(--color-border);
  border-radius: 8px;
  background: var(--color-surface);
  color: var(--color-text-muted);
  font-size: 13px;
  cursor: pointer;
}

.weekday-btn.active {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: #fff;
  font-weight: 600;
}

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 18px;
}

/* 오른쪽 하단 고정 챗봇 캐릭터 - "새 일정 추가"가 FAB에서 보드 제목 줄의 버튼으로 옮겨가면서 이
   자리(우측 하단)를 그대로 차지한다.
   right:32px는 바로 위에 쌓이는 도움말 버튼(.help-widget, 44px, right:32px)과 같은 값이다 - 아바타도
   같은 44px 지름이라 오른쪽 "끝"만 맞추면 두 원의 중심이 같은 수직선에 자연히 맞는다(지름이 다를 땐
   차이의 절반만큼 보정이 필요하지만 지금은 필요 없다) */
.ai-chatbot-widget {
  position: fixed;
  bottom: 32px;
  right: 32px;
  z-index: 40;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
}

.ai-chatbot-bubble {
  position: relative;
  max-width: 190px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 14px;
  padding: 10px 22px 10px 14px;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--color-text);
  box-shadow: var(--shadow-card);
  animation: ai-bubble-in 0.35s ease-out;
}

.ai-chatbot-bubble-close {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 18px;
  height: 18px;
  border: none;
  background: transparent;
  color: var(--color-text-muted);
  font-size: 14px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  cursor: pointer;
}

.ai-chatbot-bubble-close:hover {
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
}

.ai-chatbot-bubble-tail {
  position: absolute;
  right: 18px;
  bottom: -7px;
  width: 12px;
  height: 12px;
  background: var(--color-surface);
  border-right: 1px solid var(--color-border);
  border-bottom: 1px solid var(--color-border);
  transform: rotate(45deg);
}

.ai-chatbot-avatar {
  width: 44px;
  height: 44px;
  border: none;
  border-radius: 50%;
  background: transparent;
  padding: 0;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: var(--shadow-card);
  overflow: hidden;
  /* 계속 움직이면 시선을 뺏고 거슬리므로(클릭 대상이 끊임없이 흔들리는 것도 문제) 페이지를 열었을 때
     인사하듯 몇 번만 통통 튀고 멈춘다 - infinite 로 두지 않는다 */
  animation: ai-bot-bounce 1.4s ease-in-out 3;
}

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

.ai-chatbot-avatar:hover {
  animation-play-state: paused;
}

@keyframes ai-bubble-in {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes ai-bot-bounce {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-5px); }
}

@media (prefers-reduced-motion: reduce) {
  .ai-chatbot-avatar {
    animation: none;
  }
}

/* ---------- 도움말 버튼 (AI 챗봇 아이콘 위, js/onboarding.js) ----------
   bottom:100px(아바타 바로 위, .ai-chat-panel과 같은 간격)로는 안 되는 게, 아직 안 닫은 인사
   말풍선(#ai-chatbot-bubble)이 그 자리에도 떠서 겹친다(아바타 클릭으로 패널을 열 때만 dashboard.js가
   말풍선을 숨기지, 평소엔 계속 떠 있다) - 말풍선까지 확실히 위로 넘어간 165px를 bottom으로 써서
   말풍선을 닫기 전/후 어느 쪽이든 겹치지 않게 한다.
   right:32px는 이 버튼(44px)의 가로 중심 기준값이다 - .ai-chatbot-widget 쪽을 이 중심에 맞춰
   옮겼다(아래 주석 참고), 지름이 다른 두 원을 오른쪽 "끝"만 맞추면 중심축이 어긋나 보이기 때문 */
.help-widget {
  position: fixed;
  bottom: 100px;
  right: 32px;
  z-index: 40;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
}

.help-widget-tooltip {
  max-width: 190px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 14px;
  padding: 10px 14px;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--color-text);
  box-shadow: var(--shadow-card);
  opacity: 0;
  visibility: hidden;
  transform: translateY(4px);
  transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;
  pointer-events: none;
}

.help-widget:hover .help-widget-tooltip,
.help-widget:focus-within .help-widget-tooltip {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.help-widget-btn {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  font-size: 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: var(--shadow-card);
  transition: background 0.15s, transform 0.15s;
}

.help-widget-btn:hover {
  background: var(--color-primary-soft);
}

.help-widget-btn:active {
  transform: translateY(1px);
}

/* ---------- AI 추천 챗봇 (로봇 아이콘에서 펼쳐지는 채팅창) ---------- */

/* 전체 화면을 덮는 .modal-overlay와 달리 배경이 없는 작은 패널이라, 열어둔 채로 뒤의 보드/사이드바를
   그대로 클릭·조작할 수 있다. .ai-chatbot-widget(bottom:32px)의 아바타 바로 위에 오도록 고정 위치를
   맞춘다: 아바타 위쪽 끝이 32+44=76px 지점이므로, 거기서 12px 띄운 88px를 패널의 bottom으로 쓴다 */
.ai-chat-panel {
  position: fixed;
  bottom: 88px;
  right: 32px;
  z-index: 42;
  width: 380px;
  max-width: calc(100vw - 48px);
  height: min(70vh, 640px);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: 0 20px 50px rgba(20, 23, 45, 0.25);
  padding: 18px 18px 14px;
  display: none;
  flex-direction: column;
  animation: ai-chat-panel-in 0.2s ease-out;
}

.ai-chat-panel.show {
  display: flex;
}

@keyframes ai-chat-panel-in {
  from { opacity: 0; transform: translateY(12px) scale(0.98); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

@media (max-width: 480px) {
  .ai-chat-panel {
    right: 16px;
    left: 16px;
    width: auto;
  }
}

.ai-chat-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}

.ai-chat-header-actions {
  position: relative;
  display: flex;
  align-items: center;
  gap: 4px;
}

/* ⚙️ 버튼 위로 펼쳐지는 작은 팝오버 - clock-filter-popover와 같은 패턴(전체 화면 오버레이 대신
   버튼 그룹(.ai-chat-header-actions)의 relative 부모 기준 절대 위치). bottom:100%로 위쪽에 펼치는
   이유 - 헤더 바로 아래는 대화 메시지 목록이라, 아래로 펼치면 방금 나눈 대화를 가린다. 그룹 자체가
   헤더에서 이미 오른쪽 끝에 붙어 있으므로(justify-content:space-between) 팝오버도 오른쪽 끝(right:0)에
   맞춰 왼쪽으로 펼친다 */
.ai-chat-settings-popover {
  display: none;
  position: absolute;
  bottom: 100%;
  right: 0;
  margin-bottom: 8px;
  z-index: 3;
  width: 200px;
  max-width: calc(100vw - 64px);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  padding: 10px 12px;
}

.ai-chat-settings-popover.show {
  display: block;
}

.ai-chat-settings-popover .settings-row {
  align-items: center;
  padding: 0;
  border-top: none;
}

.ai-chat-close-btn {
  width: 26px;
  height: 26px;
  border: none;
  background: transparent;
  color: var(--color-text-muted);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  border-radius: 6px;
}

.ai-chat-close-btn:hover {
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
}

.ai-chat-header h2 {
  margin: 0;
}

.ai-chat-messages {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 4px 2px;
}

.ai-chat-empty {
  margin: auto;
  text-align: center;
  color: var(--color-text-muted);
  font-size: 13px;
  line-height: 1.6;
}

.ai-chat-bubble {
  max-width: 82%;
  padding: 10px 13px;
  border-radius: 14px;
  font-size: 13.5px;
  line-height: 1.5;
}

.ai-chat-bubble-user {
  align-self: flex-end;
  background: var(--color-primary);
  color: #fff;
  border-bottom-right-radius: 4px;
  white-space: pre-wrap;
}

.ai-chat-bubble-assistant {
  align-self: flex-start;
  background: var(--color-input-bg);
  border: 1px solid var(--color-border);
  border-bottom-left-radius: 4px;
}

/* SCHEDULE_UPDATE(기존 일정 수정 제안) 말풍선임을 알려주는 작은 라벨 - 새 일정 추천과 헷갈리지 않도록 */
.ai-chat-bubble-tag {
  display: inline-block;
  font-size: 11px;
  font-weight: 600;
  color: var(--color-primary);
  margin-bottom: 3px;
}

.ai-chat-bubble-title {
  font-weight: 700;
}

.ai-chat-bubble-time {
  font-size: 12px;
  color: var(--color-text-muted);
  margin-top: 2px;
}

.ai-chat-bubble-content {
  white-space: pre-wrap;
  margin-top: 6px;
}

/* 제목이 있는(구조화된 추천) 말풍선에서만 추천 이유를 구분선 아래 보조 설명으로 둔다 -
   제목 없이 그냥 텍스트로만 답한 말풍선은 이 텍스트 자체가 본문이라 구분선이 필요 없다 */
.ai-chat-bubble-assistant.has-suggestion .ai-chat-bubble-reason {
  font-size: 12px;
  color: var(--color-text-muted);
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid var(--color-border);
  white-space: pre-wrap;
}

.ai-chat-bubble-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 10px;
  flex-wrap: wrap;
}

/* .btn-primary는 전역적으로 width: 100%라, 여길 override하지 않으면 옆의 ghost 버튼이 남는 공간으로
   찌그러져 텍스트가 세로로 줄바꿈된다 - 두 버튼 모두 내용 크기만큼만 차지하는 필(pill) 모양으로 통일한다 */
.ai-chat-bubble-actions .btn {
  width: auto;
  flex: 0 0 auto;
  padding: 7px 16px;
  font-size: 12.5px;
  border-radius: 999px;
  line-height: 1.2;
}

.ai-chat-bubble-actions .btn-ghost {
  background: var(--color-surface);
}

.ai-chat-bubble-actions .btn-primary {
  box-shadow: 0 2px 6px rgba(79, 70, 229, 0.28);
}

.ai-chat-bubble-actions .btn-primary:hover {
  box-shadow: 0 3px 9px rgba(79, 70, 229, 0.38);
}

.ai-chat-registered-badge {
  font-size: 12px;
  color: var(--color-completed);
  font-weight: 600;
}

/* SCHEDULE_RECOMMENDATION이 한 번에 여러 일정을 제안했을 때, 항목마다 카드 하나씩 세로로 쌓아 보여준다 */
.ai-chat-suggestion-items {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.ai-chat-suggestion-item {
  padding: 8px 10px;
  border: 1px solid var(--color-border);
  border-radius: 10px;
  background: var(--color-surface);
}

.ai-chat-suggestion-item .ai-chat-bubble-actions {
  margin-top: 6px;
  justify-content: flex-start;
}

.ai-chat-bulk-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 10px;
}

.ai-chat-input-row {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  margin-top: 12px;
}

.ai-chat-input-row textarea {
  flex: 1;
  resize: none;
  max-height: 90px;
  padding: 9px 12px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font-size: 13.5px;
  font-family: inherit;
}

/* AI 추천 응답을 기다리는 동안 보여주는 인디케이터 - 실제 진행률을 알 수 없는 단발성 요청이라
   퍼센트 바 대신, 채팅 상대가 입력 중일 때 흔히 쓰는 "아바타 + 점 세 개" 타이핑 인디케이터로
   보여준다. .ai-chat-bubble-assistant와 같은 모양의 말풍선을 써서 곧 도착할 답변 자리처럼 보이게 한다 */
.ai-suggest-loading {
  display: flex;
  align-items: flex-end;
  gap: 6px;
  margin-top: 4px;
}

.ai-suggest-loading-avatar {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--color-primary-soft);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
}

.ai-suggest-loading-bubble {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 11px 14px;
  border-radius: 14px;
  border-bottom-left-radius: 4px;
  background: var(--color-input-bg);
  border: 1px solid var(--color-border);
}

.ai-typing-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-text-muted);
  animation: ai-typing-bounce 1.2s ease-in-out infinite;
}

.ai-typing-dot:nth-child(2) {
  animation-delay: 0.15s;
}

.ai-typing-dot:nth-child(3) {
  animation-delay: 0.3s;
}

@keyframes ai-typing-bounce {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.5; }
  30% { transform: translateY(-4px); opacity: 1; }
}

.toast {
  position: fixed;
  top: 20px;
  left: 50%;
  transform: translateX(-50%) translateY(-8px);
  background: #1f2333;
  color: #fff;
  padding: 10px 18px;
  border-radius: 10px;
  font-size: 13px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s, transform 0.2s;
  z-index: 100;
}

.toast.show {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

.schedule-reminder-container {
  position: fixed;
  top: 52px; /* .quote-bar(상단 인용구 바) 높이(약 40px) 아래로 - 겹치지 않게 */
  left: 50%;
  transform: translateX(-50%);
  z-index: 200;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: center;
  pointer-events: none;
}

.schedule-reminder {
  pointer-events: auto;
  width: 280px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 4px solid var(--color-primary);
  border-radius: var(--radius-md);
  box-shadow: 0 8px 24px rgba(20, 20, 40, 0.16);
  padding: 10px 32px 10px 14px;
  cursor: pointer;
  position: relative;
  animation: schedule-reminder-in 0.2s ease-out;
}

@keyframes schedule-reminder-in {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* 닫기 버튼/클릭/자동 dismiss 모두 이 클래스를 먼저 붙이고, 애니메이션이 끝나면(animationend) DOM에서
   지운다(dashboard.js showScheduleReminder의 dismiss 참고) - schedule-reminder-in과 정확히 대칭 */
.schedule-reminder.dismissing {
  animation: schedule-reminder-out 0.18s ease-in forwards;
  pointer-events: none;
}

@keyframes schedule-reminder-out {
  from {
    opacity: 1;
    transform: translateY(0);
  }
  to {
    opacity: 0;
    transform: translateY(-10px);
  }
}

.schedule-reminder-badge {
  font-size: 10.5px;
  font-weight: 700;
  color: var(--color-primary-dark);
  margin-bottom: 2px;
}

/* 완료 알림은 시작 알림과 카드 구조는 같지만, 강조색을 완료 상태색(--color-completed)으로 바꿔
   대시보드 다른 곳의 상태 색상 규칙(STATUS_COLOR_VAR, dashboard.js)과 톤을 맞춘다 */
.schedule-reminder.completed {
  border-left-color: var(--color-completed);
}

.schedule-reminder.completed .schedule-reminder-badge {
  color: var(--color-completed);
}

.schedule-reminder-title {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--color-text);
}

.schedule-reminder-meta {
  font-size: 11.5px;
  color: var(--color-text-muted);
  margin-top: 2px;
}

.schedule-reminder-close {
  position: absolute;
  top: 6px;
  right: 8px;
  border: none;
  background: none;
  color: var(--color-text-muted);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  padding: 2px 4px;
}

.schedule-reminder-close:hover {
  color: var(--color-danger);
}

.mandalart-grid {
  display: grid;
  grid-template-columns: repeat(9, minmax(0, 1fr));
  grid-template-rows: repeat(9, minmax(70px, 1fr));
  gap: 2px;
  background: var(--color-border);
  border: 2px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
  max-width: 900px;
}

/* 중앙 블록에서 8개 바깥 블록으로 뻗어나가는 화살표. 칸(.mandalart-cell)과 같은 grid 안에 explicit
   grid-row/grid-column 으로 경계선에 걸쳐 배치되고(mandalart.js 의 CENTER_ARROWS 참고), 칸들보다
   나중에 그려 넣어(z-index 로도 보강) 자연스럽게 위에 겹쳐 보인다. 클릭은 그대로 밑 textarea 로
   전달돼야 하므로 pointer-events: none 필수 */
.mandalart-arrow {
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
  pointer-events: none;
}

/* 얇은 유니코드 화살표 글자 대신, 머리(삼각형)+몸통(사각형)이 있는 두꺼운 화살표를 clip-path 로
   직접 그린다 - 방향은 mandalart.js 가 심어둔 --arrow-rotate 커스텀 프로퍼티로 회전시킨다
   (0deg = 위쪽 기준, 시계방향) */
.mandalart-arrow::before {
  content: "";
  width: 30px;
  height: 30px;
  background: var(--color-primary);
  opacity: 0.75;
  transform: rotate(var(--arrow-rotate, 0deg));
  clip-path: polygon(50% 0%, 100% 55%, 68% 55%, 68% 100%, 32% 100%, 32% 55%, 0% 55%);
}

.mandalart-cell {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  background: var(--color-surface);
  padding: 6px;
  font-size: 12px;
  line-height: 1.35;
  white-space: pre-wrap;
  word-break: break-word;
  cursor: text;
  overflow: hidden;
}

.mandalart-cell.edge-right {
  border-right: 2px solid var(--color-border);
}

.mandalart-cell.edge-bottom {
  border-bottom: 2px solid var(--color-border);
}

/* sub-goal(각 블록의 중심 칸)은 내용이 비어 있어도 구조를 알아볼 수 있도록 글자 굵기만 항상 강조한다.
   배경색은 다른 칸과 동일하게 내용이 채워졌을 때만(.filled) 나타난다 */
.mandalart-cell.sub-goal {
  font-weight: 600;
}

/* 정중앙(메인 목표) 칸만 유일하게 항상 특별 취급하는 대신, 다른 칸과 마찬가지로 내용이 채워졌을 때만
   진하게 칠해진다 — 비어 있으면 구조 힌트로 글자만 굵게 표시한다 */
.mandalart-cell.main-goal {
  font-weight: 700;
}

/* 메인 목표도 다른 칸들처럼 파스텔톤을 쓰되, 유일한 중심 칸이라는 걸 알아볼 수 있도록 8개 블록
   파스텔(15% 혼합)보다 살짝 더 진한 파스텔(25% 혼합)로 구분한다 - 더 이상 원색(solid) 배경은 쓰지 않는다 */
.mandalart-cell.main-goal.filled {
  background: #d3d1f9;
}

/* 채워진 칸의 배경은 다크 모드에서도 바뀌지 않는 고정 파스텔이라(위/아래 색상표 참고), 글자색도
   var(--color-text)(다크 모드에서 밝은 색으로 바뀜)가 아니라 항상 어두운 잉크색으로 고정해야 한다 -
   안 그러면 다크 모드에서 밝은 파스텔 배경 위에 밝은 글자가 올라가 안 보인다 */
.mandalart-cell.filled {
  color: #201f2c;
}

/* 8개 바깥 블록 색상 - dataviz 스킬의 8색 categorical 팔레트(고정 순서)를 옅은 배경 톤으로 변환해 사용.
   중앙 블록의 8칸(각 바깥 블록의 서브골을 그대로 옮겨 적은 칸)도 자신이 대응하는 바깥 블록과 같은
   색을 물려받는다(mandalart.js 의 cellBlockHueClass 참고) — "중심점을 기준으로" 방향별 색이 배정된다.
   내용이 없으면(.filled 아님) 색이 나타나지 않아, 채워나갈수록 보드에 색이 퍼지는 효과를 준다 */
.mandalart-cell.hue-1.filled { background: #dfebf9; } /* blue   - 좌상단 블록 */
.mandalart-cell.hue-2.filled { background: #fce8e1; } /* orange - 상단 블록 */
.mandalart-cell.hue-3.filled { background: #ddf3eb; } /* aqua   - 우상단 블록 */
.mandalart-cell.hue-4.filled { background: #fcf1d9; } /* yellow - 좌측 블록 */
.mandalart-cell.hue-5.filled { background: #fcebf1; } /* magenta - 우측 블록 */
.mandalart-cell.hue-6.filled { background: #d9ecd9; } /* green  - 좌하단 블록 */
.mandalart-cell.hue-7.filled { background: #e4e1f2; } /* violet - 하단 블록 */
.mandalart-cell.hue-8.filled { background: #fbe4e4; } /* red    - 우하단 블록 */

.mandalart-cell textarea {
  width: 100%;
  /* 높이는 JS(autoResizeTextarea)가 내용에 맞춰 매겨준다 - 부모가 flex+align-items:center 라
     내용이 짧으면 칸 안에서 세로 중앙에 위치하고, 칸보다 길어지면 max-height 로 잘려 기존과 동일하게
     넘치는 부분은 보이지 않는다(overflow: hidden) */
  max-height: 100%;
  text-align: center;
  border: none;
  outline: none;
  resize: none;
  overflow: hidden;
  background: transparent;
  color: inherit;
  font-size: 12px;
  font-family: inherit;
  line-height: 1.35;
  padding: 0;
}

@media (max-width: 980px) {
  .board {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 720px) {
  /* 모바일에서는 사이드바가 본문 위에 쌓이는 레이아웃이라, 데스크톱용 뷰포트 고정(shell-page/app-shell
     의 height:100vh + overflow:hidden, main 의 overflow-y:auto)을 그대로 두면 사이드바 하나가 화면
     전체를 차지하거나 스크롤이 이상해진다 - 원래의 자연스러운 문서 스크롤로 되돌린다 */
  .shell-page {
    height: auto;
    overflow: visible;
  }
  .app-shell {
    flex-direction: column;
    flex: none;
    min-height: 0;
    overflow: visible;
  }
  .sidebar {
    width: auto;
    border-right: none;
    border-bottom: 1px solid var(--color-border);
    overflow: visible;
  }
  /* 모바일은 사이드바가 본문 위에 세로로 쌓이는 레이아웃이라 데스크톱처럼 좁은 세로 아이콘
     레일로 접는 게 어색하다 - 접힘 상태에서는 완전히 감춘다(높이 0) */
  .app-shell.sidebar-collapsed .sidebar {
    width: auto;
    max-height: 0;
    padding: 0;
    overflow: hidden;
    border-bottom: none;
  }
  .sidebar-section-scroll {
    overflow-y: visible;
  }
  .main {
    overflow-y: visible;
  }
}

/* ---------- 첫 로그인 온보딩 투어 (js/onboarding.js) ----------
   구조: .tour-backdrop(전체 화면을 덮는 투명한 클릭 차단막) + .tour-spotlight(대상 위에 정확히 겹쳐지는
   투명한 사각형 - box-shadow 를 뷰포트 전체로 퍼뜨려서 "그 칸만 빼고 어둡게" 처리하는 트릭) +
   .tour-tooltip(설명 말풍선). 세 요소 모두 position:fixed 라 target.getBoundingClientRect() 값을
   그대로 top/left 로 써도 스크롤 오프셋 계산이 필요 없다 */

.tour-backdrop {
  position: fixed;
  inset: 0;
  z-index: 500;
  background: transparent;
}

.tour-spotlight {
  position: fixed;
  z-index: 501;
  pointer-events: none;
  border-radius: 12px;
  box-shadow: 0 0 0 9999px rgba(15, 15, 26, 0.6);
  transition: top 0.25s ease, left 0.25s ease, width 0.25s ease, height 0.25s ease;
}

/* 스포트라이트 안쪽 일부만 콕 집어 강조할 때(step.highlightTargets, tour.js positionHighlight) -
   완료 상태와 같은 초록 계열 파스텔 톤을 재사용해 "여기부터 채우세요" 느낌을 준다 */
.tour-highlight-region {
  position: fixed;
  z-index: 501;
  pointer-events: none;
  border-radius: 6px;
  background: rgba(21, 128, 61, 0.16);
  border: 2px solid rgba(21, 128, 61, 0.55);
  transition: top 0.25s ease, left 0.25s ease, width 0.25s ease, height 0.25s ease;
}

.tour-tooltip {
  position: fixed;
  z-index: 502;
  width: 260px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: 14px 16px;
  animation: ai-bubble-in 0.25s ease-out;
}

.tour-tooltip-close {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 20px;
  height: 20px;
  border: none;
  background: transparent;
  color: var(--color-text-muted);
  font-size: 15px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  cursor: pointer;
}

.tour-tooltip-close:hover {
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
}

.tour-tooltip-step {
  font-size: 11px;
  font-weight: 700;
  color: var(--color-primary);
  margin-bottom: 4px;
}

.tour-tooltip-title {
  font-size: 14px;
  font-weight: 700;
  color: var(--color-text);
  margin-bottom: 6px;
  padding-right: 16px;
}

.tour-tooltip-text {
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--color-text-muted);
  margin: 0 0 14px;
}

.tour-tooltip-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

/* .btn-primary는 기본적으로 width:100%라(모달 안에서 꽉 채운 저장 버튼 등을 위한 기본값)
   좁은 말풍선 안에서는 명시적으로 풀어줘야 한다 - .board-title-actions .btn과 같은 패턴 */
.tour-tooltip-actions .btn {
  width: auto;
}

.tour-tooltip-nav {
  display: flex;
  gap: 6px;
}

.tour-tooltip-tail {
  position: absolute;
  width: 12px;
  height: 12px;
  background: var(--color-surface);
  transform: rotate(45deg);
}

.tour-tooltip[data-placement="bottom"] .tour-tooltip-tail {
  top: -7px;
  left: 50%;
  margin-left: -6px;
  border-top: 1px solid var(--color-border);
  border-left: 1px solid var(--color-border);
}

.tour-tooltip[data-placement="top"] .tour-tooltip-tail {
  bottom: -7px;
  left: 50%;
  margin-left: -6px;
  border-bottom: 1px solid var(--color-border);
  border-right: 1px solid var(--color-border);
}

.tour-tooltip[data-placement="right"] .tour-tooltip-tail {
  left: -7px;
  top: 50%;
  margin-top: -6px;
  border-bottom: 1px solid var(--color-border);
  border-left: 1px solid var(--color-border);
}

.tour-tooltip[data-placement="left"] .tour-tooltip-tail {
  right: -7px;
  top: 50%;
  margin-top: -6px;
  border-top: 1px solid var(--color-border);
  border-right: 1px solid var(--color-border);
}

@media (prefers-reduced-motion: reduce) {
  .tour-spotlight,
  .tour-highlight-region {
    transition: none;
  }
}

/* ---------- 기간별 리포트(report.html/report.js) ---------- */

.report-date-nav {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}

.report-range-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--color-text-muted);
  min-width: 180px;
  text-align: center;
}

.report-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  align-items: start;
}

.report-summary-card,
.report-insight-card {
  max-width: none;
}

.report-stat-tiles {
  display: flex;
  gap: 12px;
  margin: 14px 0;
}

.report-stat-tile {
  flex: 1;
  background: var(--color-bg);
  border-radius: var(--radius-lg);
  padding: 14px;
  text-align: center;
}

.report-stat-value {
  font-size: 24px;
  font-weight: 700;
  color: var(--color-primary-dark);
}

.report-stat-label {
  font-size: 12px;
  color: var(--color-text-muted);
  margin-top: 2px;
}

.report-comparison {
  font-size: 12.5px;
  color: var(--color-text-muted);
  padding-bottom: 12px;
  border-bottom: 1px solid var(--color-border);
  margin-bottom: 4px;
}

.report-comparison-empty {
  color: var(--color-text-muted);
}

.report-delta {
  font-weight: 700;
}

.report-delta-up { color: var(--color-completed); }
.report-delta-down { color: var(--color-pending); }
.report-delta-flat { color: var(--color-text-muted); }

.report-status-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.report-status-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 0;
  font-size: 13px;
}

.report-status-name {
  flex: 1;
  color: var(--color-text-muted);
}

.report-status-count {
  font-weight: 600;
}

/* 추이 그래프와 파이 차트 사이 남는 공간에 카테고리 건수 순위를 넣는다 - categoryBreakdown이 이미
   서버에서 건수 내림차순으로 내려오므로(ReportService) report.js는 그 순서를 그대로 쓰기만 하면 된다 */
/* flex-grow를 pie-wrap과 같은 1로 줘야(예전엔 0이라 전혀 안 늘어났음) 남는 폭을 트렌드/파이 쪽과
   공평하게 나눠 갖는다 - trend-wrap이 max-width 없이 flex-grow:2로 혼자 다 먹던 걸 옆(아래)에서
   같이 고쳤다(.report-trend-wrap 참고) */
.report-rank-wrap {
  flex: 1 1 170px;
  min-width: 150px;
  align-self: stretch;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.report-rank-title {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--color-text-muted);
  text-align: center;
  margin-bottom: 10px;
}

/* rank-wrap 자체는 이제 늘어나지만(flex:1) 목록 내용까지 그 폭에 맞춰 늘어나면 배지-이름-건수 사이
   간격만 불필요하게 벌어져 보인다 - 목록은 원래 폭(170px)으로 고정해 두고 margin:auto로 넓어진
   rank-wrap 안에서 가운데 정렬만 되게 한다 */
.report-rank-list {
  list-style: none;
  margin: 0 auto;
  padding: 0;
  width: 170px;
}

/* 순위가 위→아래로 하나씩, 작았다가 커지면서 나타나는 효과 - stagger-index로 순서대로 살짝씩
   늦게 시작해 "한 줄씩 내려가며 커지는" 느낌을 낸다 */
.report-rank-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  font-size: 12.5px;
  animation: reportRankIn 420ms cubic-bezier(0.34, 1.56, 0.64, 1) backwards;
  animation-delay: calc(var(--stagger-index, 0) * 90ms);
}

@keyframes reportRankIn {
  from {
    opacity: 0;
    transform: scale(0.6) translateY(-6px);
  }
  to {
    opacity: 1;
    transform: scale(1) translateY(0);
  }
}

.report-rank-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--color-bg);
  color: var(--color-text-muted);
  font-size: 10.5px;
  font-weight: 700;
  flex-shrink: 0;
}

/* 금/은/동 배지는 테마와 무관하게 고정된 밝은 색이라(다크 모드에서도 안 바뀜), 글자색도 var(--color-text)가
   아니라 고정 잉크색을 써야 한다 - mandalart 채워진 칸과 같은 이유(고정 파스텔 배경 위에 테마별로 바뀌는
   글자색을 얹으면 다크 모드에서 안 보이는 조합이 나올 수 있음) */
.report-rank-badge.rank-1 { background: #f5c451; color: #4a3400; }
.report-rank-badge.rank-2 { background: #c9ccd6; color: #33363f; }
.report-rank-badge.rank-3 { background: #d9a066; color: #3a2308; }

.report-rank-name {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.report-rank-count {
  margin-left: auto;
  color: var(--color-text-muted);
  flex-shrink: 0;
}

.report-pie-wrap {
  position: relative;
  display: flex;
  align-items: center;
  gap: 20px;
  flex: 1 1 260px;
  min-width: 220px;
}

.report-pie-box {
  position: relative;
  width: 176px;
  height: 176px;
  flex-shrink: 0;
}

/* report.js가 매 렌더마다 <defs>(그라데이션)+조각 <g>를 innerHTML로 새로 그리는 고정 SVG 노드 - viewBox는
   report.html에서 PIE_R(70)+POP_PX/여유 패딩을 감안한 -88 -88 176 176으로 고정. overflow visible이라야
   호버 시 조각이 바깥으로 튀어나오는(report-pie-slice-group.hovered) translate가 잘리지 않는다 */
.report-pie-svg {
  width: 176px;
  height: 176px;
  overflow: visible;
  transform-origin: center;
}

.report-pie-slice-group {
  transition: transform 160ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

.report-pie-slice {
  cursor: pointer;
  stroke: var(--color-surface);
  stroke-width: 1.5px;
  transition: opacity 160ms ease;
}

/* 마우스가 파이 위에 있을 때(has-hover)만 호버 중이 아닌 조각을 어둡게 - 조각 사이 간격만으로는
   대비가 약해서(호버 요청 반영) 나머지 조각의 opacity를 낮춰 명암 대비를 준다 */
.report-pie-svg.has-hover .report-pie-slice-group:not(.hovered) .report-pie-slice {
  opacity: 0.45;
}

.report-pie-slice-group.hovered .report-pie-slice {
  filter: drop-shadow(0 3px 6px rgba(20, 23, 45, 0.28));
}

/* 조각 안 퍼센트 라벨 - 어떤 파스텔 색 위에서도 읽히도록 흰 글자 + 어두운 외곽선(paint-order로 외곽선을
   글자보다 먼저 그려 덮이지 않게 함)을 쓴다. 너무 작은 조각(6% 미만)은 report.js가 애초에 라벨을 생성하지
   않는다(글자가 서로 겹치므로) */
.report-pie-slice-label {
  fill: #fff;
  font-size: 11px;
  font-weight: 700;
  text-anchor: middle;
  dominant-baseline: middle;
  paint-order: stroke;
  stroke: rgba(20, 23, 45, 0.35);
  stroke-width: 3px;
  stroke-linejoin: round;
  pointer-events: none;
  user-select: none;
}

.report-legend {
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 1;
  min-width: 0;
}

.report-legend-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 0;
  font-size: 12.5px;
  animation: reportLegendIn 400ms ease-out backwards;
  animation-delay: calc(var(--stagger-index, 0) * 60ms);
}

.report-legend-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex-shrink: 0;
}

/* flex:1(늘어남 허용)이 아니라 flex:0 1 auto(줄어듦만 허용)를 써야 한다 - flex:1이면 이름이 남는
   폭을 다 먹어치워서 뒤에 오는 report-legend-value(건수·퍼센트)가 카드 오른쪽 끝까지 밀려나
   이름/값 사이에 큰 빈 틈이 생긴다. 그래도 진짜 긴 카테고리 이름은 min-width:0 덕분에 여전히
   말줄임(ellipsis) 처리된다 */
.report-legend-name {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.report-legend-value {
  color: var(--color-text-muted);
  flex-shrink: 0;
}

.report-legend-empty {
  color: var(--color-text-muted);
  font-size: 12.5px;
}

.report-insight-body {
  margin-top: 16px;
}

.report-insight-section {
  padding: 12px 0;
  border-top: 1px solid var(--color-border);
}

.report-insight-section:first-child {
  border-top: none;
}

.report-insight-section h3 {
  font-size: 13px;
  margin: 0 0 6px;
}

.report-insight-section ul {
  margin: 0;
  padding-left: 18px;
  font-size: 13px;
  line-height: 1.6;
}

.report-insight-section p {
  margin: 0;
  font-size: 13px;
  line-height: 1.6;
  color: var(--color-text);
}

.report-insight-empty {
  color: var(--color-text-muted);
}

.report-insight-action-row {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  flex-wrap: wrap;
}

/* 다크 모드에서는 사이트 전체에 이미 쓰이는 보라색(--color-primary) 대신, "AI 코멘트 생성" 버튼만
   눈에 띄는 밝은 앰버 톤으로 구분해준다 - 어두운 카드 위에서 다른 보라색 버튼들 사이에 묻히지 않게 */
:root[data-theme="dark"] #report-insight-btn {
  background: #f5b942;
  color: #2a1a05;
}

:root[data-theme="dark"] #report-insight-btn:hover {
  background: #f7c665;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) #report-insight-btn {
    background: #f5b942;
    color: #2a1a05;
  }

  :root:not([data-theme="light"]) #report-insight-btn:hover {
    background: #f7c665;
  }
}

.report-insight-stale-hint {
  margin: 0;
  padding: 8px 12px;
  border-radius: 8px;
  background: var(--color-pending-bg);
  color: var(--color-pending);
  font-size: 12.5px;
  font-weight: 600;
}

.report-trend-card {
  grid-column: 1 / -1;
  max-width: none;
}

/* 카테고리별 추이(선 그래프)와 비율(파이차트)을 한 카드 안에 나란히 배치 - 추이 차트가 max-width(560px)
   때문에 카드 오른쪽에 남던 빈 공간에 파이차트가 들어간다. 좁은 화면에서는 flex-wrap으로 아래로 쌓인다 */
.report-trend-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 28px;
  margin-top: 14px;
}

/* 예전엔 flex-grow:2(순위·파이의 2배)에 max-width도 없어서 실제 차트(최대 560px, .report-trend-chart-box)
   보다 훨씬 넓게 늘어나 그 안에 빈 여백만 생기고, 남는 폭이 전부 여기로만 쏠려 순위·파이 쪽은 좁게
   눌려 있었다 - max-width를 차트 실제 크기에 맞추고 grow도 1로 낮춰 세 블록이 공평하게 남는 폭을
   나눠 갖게(.report-rank-wrap도 grow:1로 맞춤) 했다 */
.report-trend-wrap {
  flex: 1 1 420px;
  min-width: 300px;
  max-width: 560px;
}

.report-trend-chart-box {
  position: relative;
  width: 100%;
  max-width: 560px;
}

.report-trend-svg {
  width: 100%;
  height: auto;
  display: block;
}

.report-trend-gridline {
  stroke: var(--color-border);
  stroke-width: 1;
}

.report-trend-axis-label {
  font-size: 10px;
  fill: var(--color-text-muted);
}

.report-trend-crosshair {
  stroke: var(--color-border);
  stroke-width: 1;
}

/* hover 시 값을 읽는 히트 영역 - 마크가 아니라 순수 인터랙션 레이어라 커서만 바꾸고 시각 요소는 없다 */
#report-trend-hover-area {
  cursor: crosshair;
}

.report-trend-tooltip {
  position: absolute;
  z-index: 5;
  pointer-events: none;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 8px;
  padding: 8px 10px;
  font-size: 12px;
  box-shadow: var(--shadow-card);
  min-width: 120px;
}

.report-trend-tooltip-label {
  font-weight: 700;
  margin-bottom: 4px;
  color: var(--color-text);
}

.report-trend-tooltip-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 2px 0;
  color: var(--color-text-muted);
}

.report-trend-tooltip-row strong {
  margin-left: auto;
  color: var(--color-text);
  font-weight: 700;
}

.report-trend-legend {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
}

/* ---------- 리포트 랜딩 애니메이션 (수치 카운트업은 report.js의 animateNumber, 나머지는 아래 CSS) ---------- */

@keyframes reportPieIn {
  from {
    opacity: 0;
    transform: scale(0.72) rotate(-6deg);
  }
  to {
    opacity: 1;
    transform: scale(1) rotate(0deg);
  }
}

/* pieSvgEl(svg 자체)은 innerHTML을 다시 만들지 않는 고정 노드라(내부의 조각 <g>만 매번 새로 그려짐) 이
   클래스를 report.js의 replayAnimation()이 매번 떼었다 붙이는 방식으로 재생시킨다(레이아웃 재계산 없이는
   CSS가 "이미 있던 클래스"로 보고 재생을 건너뜀) */
.report-pie-animate-in {
  animation: reportPieIn 800ms cubic-bezier(0.16, 1, 0.3, 1);
}

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

/* 격자선/눈금 글자는 데이터(선/점)보다 먼저 살짝 페이드인되어 캔버스부터 자리 잡은 뒤 그 위로 선이
   그려지는 순서로 보이게 한다 */
@keyframes reportTrendAxisIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.report-trend-axis-group {
  animation: reportTrendAxisIn 300ms ease-out;
}

/* report-trend-line(선 자체)의 "그려지는" 효과는 CSS가 아니라 report.js의 playTrendLineDrawIn()이
   Web Animations API(el.animate())로 만든다 - dasharray/dashoffset을 실제 경로 길이(주로 150~400
   정도)에 맞춰야 하는데, 그 값은 DOM에 삽입된 뒤 path.getTotalLength()로 재야만 알 수 있어 CSS만으로는
   불가능하다. 처음엔 CSS transition(두 번 값을 바꿔 트리거)으로 시도했지만, 막 삽입된 요소는 브라우저가
   "이전 상태"를 실제로 한 프레임 페인트했다는 보장이 없어 트랜지션 없이 즉시 완료돼버리는 경우가
   있었다(실측 확인) - animate()는 시작/종료 값을 한 번에 넘겨받아 이 문제 자체가 없다. */

@keyframes reportTrendDotIn {
  from {
    opacity: 0;
    transform: scale(0.3);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

.report-trend-dot {
  transform-box: fill-box;
  transform-origin: center;
  opacity: 0;
  animation: reportTrendDotIn 300ms ease-out forwards;
  animation-delay: var(--dot-delay, 0ms);
}

@media (prefers-reduced-motion: reduce) {
  .report-pie-animate-in,
  .report-legend-item,
  .report-rank-item,
  .report-trend-axis-group,
  .report-trend-dot {
    animation: none;
    opacity: 1;
  }

  .report-pie-slice-group {
    transition: none;
  }
}

@media (max-width: 860px) {
  .report-grid {
    grid-template-columns: 1fr;
  }

  .view-toolbar {
    flex-wrap: wrap;
    gap: 10px;
  }

  .report-date-nav {
    margin-left: 0;
  }
}
