:root{
  /* Main — 터콰이즈 */
  --color-main:#14c9c1;
  --color-main-100:#e7f8f7;
  --color-main-200:#b8ecea;
  --color-main-400:#4ad9d3;
  --color-main-600:#0d827c;
  --color-main-700:#0a7d78;

  /* Sub — 코랄 */
  --color-sub:#ff6a4d;
  --color-sub-100:#ffece7;
  --color-sub-300:#ffc0b0;
  --color-sub-600:#e8512f;
  --color-sub-700:#b83a1c;

  /* Sub3 — 앰버 */
  --color-sub3:#ffb020;
  --color-sub3-100:#ffeac5;
  --color-sub3-600:#e0930a;
  --color-sub3-700:#8d6112;

  /* Ground */
  --color-bg:#f2f7f6;
  --color-surface:#fdfefe;
  --color-text:#0d1f1e;
  --color-track:#d5e8e6;
  --color-divider:rgba(0,0,0,.14);
  /* 명시적 muted 토큰이 없어서, 본문 텍스트색을 옅게 섞어 파생합니다 */
  --color-text-muted: color-mix(in srgb, var(--color-text) 60%, transparent);

  /* Accent (sub3 기반 배지/버튼 배경·글자 쌍) */
  --color-accent: var(--color-sub3);
  --color-accent-bg: color-mix(in srgb, var(--color-accent) 30%, transparent);
  --color-accent-text: color-mix(in srgb, var(--color-sub3) 55%, #000000);

  /* Type */
  /* Caprasimo가 영어 + 숫자까지 커버하게 */
  --font-heading: "Caprasimo","Jua",system-ui,sans-serif;
  --font-body: "Figtree","Gowun Dodum",system-ui,sans-serif;

  /* Radius */
  --radius-sm:8px;
  --radius-md:16px;
  --radius-lg:28px;
  --radius-card:30px;
  --radius-pill:999px;

  /* 상단 바 높이. h1 한 줄(1.5rem × line-height 1.2) + header 세로 패딩(10+10)으로 적어두고
     header가 이 값을 min-height로 씁니다 — 제목만 있는 탭과 버튼이 있는 홈의 상단 바 높이가
     같아서, 탭을 오갈 때 본문이 위아래로 들썩이지 않습니다. (상단 바는 고정하지 않으므로, 화면
     위에 붙는 줄 아래에 무언가를 붙일 때는 이 값이 아니라 stickyBar가 재는 --sticky-bar-h를
     씁니다.) */
  --header-h: calc(1.5rem * 1.2 + 20px);

  /* 화면 아래에 고정된 바들의 실제 높이입니다. 본문 아래 여백(.app-shell, .has-floating-
     footer-action)과 입력 바의 위치(.floating-add-bar)가 전부 이 값에서 나옵니다 — 예전에는
     숫자를 곳곳에 따로 적어서, 탭 바 여백을 바꾸자 본문 끝이 탭 바·입력 바 뒤로 숨었습니다.
     .app-footer나 .floating-add-bar의 여백·크기를 바꾸면 이 두 값을 같이 고쳐주세요.
       탭 바  = 위 여백 10 + 아이콘·글자 48 + 아래 여백 24 + 윗선 1
       입력 바 = 위아래 여백 20 + 입력칸 44 + 윗선 1 */
  --tab-bar-h: 83px;
  --floating-add-bar-h: 65px;
  /* 본문 마지막 카드와 그 아래 바 사이에 남기는 숨 쉴 틈입니다. */
  --bottom-bar-gap: 12px;
  /* "맨 위로" 버튼(components/scrollTop)의 크기와, 아래 바에서 띄우는 간격입니다. 버튼을 쓰는
     화면이 마지막 카드가 버튼에 가리지 않도록 여백을 계산할 때도 이 값을 씁니다. */
  --scroll-top-size: 44px;
  --scroll-top-gap: 16px;

  /* Shadow */
  --shadow-sm:0 1px 2px rgba(13,31,30,.12);
  --shadow-md:0 3px 10px rgba(13,31,30,.14);
  --shadow-lg:0 12px 32px rgba(13,31,30,.20);
}

:root[data-theme="dark"]{
  color-scheme: dark;

  /* 1) 바탕 계열 — 전부 교체 */
  --color-bg:#0d1a19;
  --color-surface:#172827;
  --color-text:#e6f7f5;
  --color-track:#263836;
  --color-divider:rgba(255,255,255,.16);

  /* 2) 베이스(500)·400·600 — 손대지 않음. 브랜드색은 테마와 무관 */

  /* 3) 텍스트용 700 — 밝은 단계로 뒤집기 */
  --color-main-700: var(--color-main-400);   /* #4ad9d3 */
  --color-sub-700:  #ff9880;
  --color-sub3-700: #ffca63;

  /* 4) 틴트용 100·200·300 — 흰색이 아니라 surface에 섞어 재계산 */
  --color-main-100: color-mix(in srgb, var(--color-main) 18%, var(--color-surface));
  --color-main-200: color-mix(in srgb, var(--color-main) 28%, var(--color-surface));
  --color-sub-100:  color-mix(in srgb, var(--color-sub)  18%, var(--color-surface));
  --color-sub-300:  color-mix(in srgb, var(--color-sub)  40%, var(--color-surface));
  --color-sub3-100: color-mix(in srgb, var(--color-sub3) 18%, var(--color-surface));

  /* 5) 파생 토큰 — 기준만 바꿔서 다시 섞기 */
  --color-accent-bg:   color-mix(in srgb, var(--color-accent) 22%, var(--color-surface));
  --color-accent-text: color-mix(in srgb, var(--color-accent) 55%, #ffffff);
  --color-text-muted:  color-mix(in srgb, var(--color-text) 60%, transparent);
}
* {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}

/* [hidden] 속성은 브라우저 기본 스타일(display:none)로 처리되는데, .icon-btn/.wizard-step
   처럼 display를 직접 지정하는 클래스와 같이 쓰이면 그 클래스가 이겨서 hidden이 무시되고
   요소가 계속 보이는 문제가 있었습니다. hidden이 항상 최우선으로 이기도록 고정합니다. */
[hidden] {
  display: none !important;
}

body {
  margin: 0;
  min-height: 100vh;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  transition: background-color 0.2s ease, color 0.2s ease;
  /* 버튼 연타 시 브라우저가 더블탭으로 오해해서 확대하는 걸 막습니다. 핀치 줌은
     base.html의 viewport(user-scalable=no)와 common.js의 gesture 차단이 맡습니다. */
  touch-action: manipulation;
  /* 앱처럼 느껴지려면 웹페이지 티가 나는 것부터 없애야 합니다 — 화면 아무 데나 꾹 눌러도
     아이폰 사파리 특유의 "텍스트 선택 + 복사하기/번역…" 말풍선이 뜨지 않게 합니다.
     -webkit-touch-callout이 그 말풍선 자체를, user-select가 파랗게 선택되는 것 자체를
     막습니다(안드로이드/크롬은 -webkit-touch-callout이 없어도 user-select만으로 같은
     효과). 실제로 값을 입력/편집해야 하는 input, textarea 등에는 아래에서 다시 켜서
     거기서는 여전히 선택하고 복사·붙여넣기할 수 있게 둡니다. */
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

button,
input,
select,
textarea {
  font: inherit;
}

input,
textarea,
[contenteditable] {
  -webkit-touch-callout: default;
  -webkit-user-select: text;
  user-select: text;
}

.app-shell {
  max-width: 960px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 14px;
  /* 하단 탭 바(position: fixed)에 마지막 카드가 가리지 않게 그 높이만큼 비워둡니다. */
  padding-bottom: calc(var(--tab-bar-h) + var(--bottom-bar-gap));
}

.app-inner {
  padding: 0 16px;
  box-sizing: border-box;
}

@media (min-width: 768px) {
  .app-shell {
    gap: 18px;
  }

  .app-inner {
    padding: 0 20px;
  }
}

h1
{
  margin: 0;
  font-family: var(--font-heading);
  font-size: 1.5rem;
  font-weight: 900;
  line-height: 1.2;
}

/* editor-header에 h1(페이지 제목)이 없는 화면(예: 튜토리얼)에서, 본문 안의 큰 제목으로 씁니다. */
h2 {
  font-family: var(--font-heading);
  font-size: 1.7rem;
  font-weight: 900;
  line-height: 1.25;
}

h3 {
  font-family: var(--font-heading);
  font-size: 1.3rem;
  font-weight: 800;
  line-height: 1.25;
}

h4{
  font-size: 1.1rem;
  font-weight: 700;
  line-height: 1.2;
  padding:2px 0;
}

h5 {
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--color-text-muted);
  line-height: 1.3;
  padding:5px 0;
}
h6{
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--color-text-muted);
  line-height: 1.3;
}

.section-label {
  font-size: 0.85rem;
  font-weight: 700;
}

.input {
  height: 44px;
  padding: 0 14px;
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text);
}

/* 타이머 세션 설정 폼 (타이머 세션 등록/수정, 회원가입 마법사에서 공통으로 씁니다). */
.field-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding-bottom: 10px;
}

/* 회원가입 마법사의 한 단계입니다. h3(제목)/h5(설명) 아래로 입력 요소들이 이어집니다. */
.wizard-step {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* 마법사/모달의 "이전·뒤로" 같은 부차적인 링크입니다(세션 타이머 입력 단계, 완수 모달). */
.step-back-link {
  display: inline-block;
  margin-top: 4px;
  font-size: 0.85rem;
  color: var(--color-text-muted);
  text-decoration: none;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.field label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--color-text-muted);
}

/* 예상 소요 시간 스테퍼 자체의 생김새(.stepper-*)는 common/components/minuteStepper.css로
   옮겼습니다 — 그 컴포넌트를 쓰는 페이지에서만 따로 불러옵니다. */

.alert-group {
  display: flex;
  flex-direction: column;
}

.option-row {
  display: flex;
  gap: 8px;
}

.option-btn {
  flex: 1;
  height: 44px;
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text);
  font-size: 0.85rem;
  cursor: pointer;
}

.option-btn.active {
  border-color: var(--color-main-600);
  background: color-mix(in srgb, var(--color-main-600) 14%, var(--color-surface));
  color: var(--color-main-700);
  font-weight: 700;
}

/* 값을 고르는 다른 .option-btn들과 모양은 완전히 같고 색만 다른, "지금 값에 더하기"
   같은 동작용 칩입니다(예: 세션 행의 +5분 — app/views/tasks/components/taskRowEditor.js). */
.option-btn.option-btn--accent {
  border-color: var(--color-accent-bg);
  background: var(--color-accent-bg);
  color: var(--color-accent-text);
}

/* 화이트노이즈처럼 옵션이 늘어나 한 줄에 다 못 들어갈 수 있는 option-row에 붙이는
   수정자입니다(메인 화면 타이머 캐러셀 .timer-carousel과 같은 가로 스크롤 패턴).
   기본 .option-row/.option-btn(균등 폭으로 꽉 채우는 형태)은 옵션 수가 고정된
   종료음 선택 등에서 그대로 쓰이므로 건드리지 않습니다. */
.option-row--scroll {
  flex-wrap: nowrap;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  padding-bottom: 2px;
}

.option-row--scroll .option-btn {
  flex: 0 0 auto;
  scroll-snap-align: start;
  padding: 0 16px;
}

/* 소리/진동을 고르면 그 아래로 슬라이드하며 세부 옵션이 열립니다. */
.suboption-panel {
  display: flex;
  gap: 8px;
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  margin-top: 0;
  transition: max-height 0.25s ease, opacity 0.2s ease, margin-top 0.25s ease;
}

.suboption-panel.open {
  max-height: 60px;
  opacity: 1;
  margin-top: 10px;
}

.suboption-chip {
  flex: 1;
  height: 38px;
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-pill);
  background: var(--color-bg);
  color: var(--color-text);
  font-size: 0.8rem;
  cursor: pointer;
}

.suboption-chip.active {
  border-color: var(--color-main-600);
  background: var(--color-main-600);
  color: var(--color-bg);
}

/* 명상 켜기/끄기 같은 on/off 스위치입니다. */
.toggle-switch {
  position: relative;
  width: 44px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--color-track);
  cursor: pointer;
  flex-shrink: 0;
  transition: background-color .16s ease;
}

.toggle-switch::before {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
  transition: transform .16s ease;
}

.toggle-switch.active {
  background: var(--color-main-600);
}

.toggle-switch.active::before {
  transform: translateX(18px);
}

/* on/off 스위치 하나 + 라벨을 나란히 두는 줄입니다(명상 켜기/끄기, 자동재생 등에서
   공용으로 씁니다). */
.toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  padding: 5px 14px;
}

.toggle-row-label {
  font-size: 0.9rem;
  font-weight: 600;
}

/* 명상 시작음/종료음/배경음 하위 섹션입니다. 명상이 꺼져 있으면 접혀 있다가
   토글을 켜면 슬라이드로 펼쳐집니다(.suboption-panel과 같은 패턴). */
.meditation-options {
  display: flex;
  flex-direction: column;
  gap: 16px;
  overflow: hidden;
  max-height: 2000px;
  opacity: 1;
  margin-top: 16px;
  transition: max-height 0.3s ease, opacity 0.2s ease, margin-top 0.3s ease;
}

.meditation-options.is-collapsed {
  max-height: 0;
  opacity: 0;
  margin-top: 0;
}

/* 명상 배경음처럼 아직 기능이 없는 옵션 줄을 흐리게 비활성 처리합니다. */
.option-row.is-disabled {
  opacity: 0.45;
  pointer-events: none;
}

/* 폭 전체를 채우는 굵은 CTA 버튼입니다 (저장하기/로그인하기/다음 등). */
.btn-block {
  width: 100%;
  justify-content: center;
  height: 50px;
  font-size: 0.95rem;
  margin-top: 8px;
}

/* .btn-block 버튼 여러 개를 한 줄에 나란히 두고 폭을 똑같이 나눠 갖게 합니다
   (예: 튜토리얼의 "이전"/"다음"). .btn-block의 width:100%를 flex:1로 덮어씁니다. */
.btn-row {
  display: flex;
  gap: 8px;
}

.btn-row .btn-block {
  width: auto;
  flex: 1;
  min-width: 0;
}

/* 화면 하단에서 30px 위에 고정되는 액션 버튼 영역입니다. 내용이 길면 위 콘텐츠가
   스크롤되고, 짧아도 이 버튼은 항상 같은 자리에 붙어 있습니다 (로그인/회원가입 등). */
.bottom-action-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 30px;
  z-index: 5;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: 960px;
  margin: 0 auto;
  padding: 0 16px;
  box-sizing: border-box;
}

/* bottom-action-bar를 쓰는 페이지는 마지막 입력이 버튼에 가리지 않도록 여백을 더 둡니다. */
.app-shell.has-bottom-action {
  padding-bottom: 130px;
}

/* bottom-action-bar와 같은 뜻이지만, 하단 탭 바(.app-footer)가 있는 화면에서 씁니다.
   탭 바 위에 그대로 띄우면 서로 겹쳐서 못 누르므로, 탭 바의 실제 높이(--tab-bar-h)만큼
   위로 띄웁니다. */
.floating-add-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: var(--tab-bar-h);
  z-index: 5;
  max-width: 960px;
  margin: 0 auto;
  padding: 10px 16px;
  box-sizing: border-box;
  background: var(--color-surface);
  border-top: 1px solid var(--color-divider);
}

/* 휴대폰 키보드가 떠 있는 동안(common/keyboard.js가 <html>에 붙이는 .is-keyboard-open)에는 적는 데
   필요 없는 하단 바를 치웁니다. 화면이 줄어드는 기기에서는 이 바들이 키보드 위로 따라 올라와 적는 칸을
   가리고, 보이는 부분만 줄어드는 기기(아이폰)에서도 키보드를 띄운 채 스크롤하면 키보드 위로 불쑥
   떠 보이곤 합니다. 띄워둔 입력 바는 그 안의 칸에 적는 중일 때만 남깁니다. 고정 위치라 치워도 본문
   배치는 그대로입니다.

   남긴 입력 바는 **키보드 바로 위**에 붙입니다 — --keyboard-inset은 keyboard.js가 실제로 잰 "레이아웃
   바닥에서 키보드 윗변까지"입니다. 탭 바 자리(83px) 위에 그대로 두면, 아이폰이 화면 바닥을 키보드
   윗변까지 통째로 올렸을 때 입력 바와 키보드 사이에 그만큼 빈 틈이 생겨 목록이 비쳐 보였습니다. */
.is-keyboard-open .app-footer {
  display: none;
}

.is-keyboard-open .floating-add-bar {
  bottom: var(--keyboard-inset, 0px);
}

.is-keyboard-open .floating-add-bar:not(:focus-within) {
  display: none;
}

/* floating-add-bar를 쓰는 화면은 본문 마지막 카드가 그 뒤에 가리지 않도록, 탭 바에 더해
   이 바 자체의 높이만큼 여백을 더 둡니다. .app-shell 없이 써도(징검다리처럼) 적용되도록
   이 클래스 하나로만 겁니다 — 예전엔 .app-shell과 같이 있어야만 걸려서, 징검다리에서
   .app-shell을 빼자 여백이 0이 되어 목록 끝이 입력 바·탭 바 뒤로 숨었습니다. */
.has-floating-footer-action {
  padding-bottom: calc(var(--tab-bar-h) + var(--floating-add-bar-h) + var(--bottom-bar-gap));
}

/* 헤더(고정 높이) + 스크롤되는 본문(.screen-body) + 하단 버튼 영역(.screen-footer, 고정
   높이)으로 이뤄진 전체화면 레이아웃입니다. 화면 전체를 뷰포트 높이에 맞추고 본문만 내부
   스크롤하므로, 본문 내용이 짧든 넘치든 헤더/하단 버튼의 위치가 항상 그대로입니다. 헤더 +
   스크롤 본문 + 하단 고정 버튼 구조인 페이지라면 어디든(예: 튜토리얼 마법사) 재사용합니다. */
.screen-shell {
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.screen-shell > * {
  flex: none;
}

.screen-shell > .screen-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

.screen-footer {
  padding: 16px;
  box-sizing: border-box;
}

/* 주기(사이클) "주제"/"세션" 입력칸 아래에 다는 "추천받기"입니다(app/views/common/bundleTaskForm.js).
   /tutorial, 회원가입 마법사, /tasks 주기(사이클) 편집, 홈 탭 즉흥 시작 폼이 같이 씁니다.
   예전엔 입력칸 옆에 버튼처럼 나란히 붙어 있어서, 직접 타이핑해놓고도 습관적으로 그
   버튼을 눌러 방금 쓴 글자가 추천 문구로 덮어써지는 일이 잦았습니다("확인" 버튼처럼
   보였던 탓). 입력칸 아래로 내리고 작은 글자 텍스트 링크로 바꿔서, 눌러도 그만 안
   눌러도 그만인 곁다리 기능임이 보이게 했습니다. */
.field-suggest-link {
  align-self: flex-start;
  margin-top: -2px;
  padding: 2px 0;
  border: 0;
  background: transparent;
  color: var(--color-sub-600);
  font: inherit;
  font-size: 0.78rem;
  cursor: pointer;
}

.field-suggest-link:hover {
  text-decoration: underline;
}

/* 주제 입력 다음, 그 값을 그대로 보여주는 읽기 전용 칩입니다(.task-timer-status와 같은
   알약 배지 느낌). */
/* 주제를 아직 안 적었으면(글자가 없으면) 숨깁니다 — 빈 알약 모양만 덩그러니 보였습니다. */
.bundle-topic-chip:empty {
  display: none;
}

.bundle-topic-chip {
  display: inline-flex;
  align-items: center;
  width: fit-content;
  padding: 6px 14px;
  border-radius: var(--radius-pill);
  background: var(--color-main-100);
  color: var(--color-main-700);
  font-size: 0.85rem;
  font-weight: 700;
}

/* "담기"로 쌓은 세션 목록(app/views/tasks/components/taskRowEditor.js)입니다. /tasks의
   주기(사이클) 만들기·수정, /tutorial, 회원가입 마법사가 전부 같이 씁니다. 행 안의
   제목칸/분칸/단위/삭제 버튼 모양은 위쪽의 .editable-row-*를 그대로 씁니다. */
.task-row-editor {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 10px 0;
}

.bundle-task-row {
  padding: 8px 10px;
}

/* 행 하나(제목+분+삭제)와 그 아래 예상 시간 빠른 선택 칩 줄을 한 덩어리로 묶습니다. */
.bundle-task-item {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* /tutorial·회원가입 마법사와 /tasks 주기(사이클) 편집기는 "세션 추가"를 누르기 전에 이미
   프리셋(각 페이지 상단의 공용 프리셋 한 벌)으로 시간을 정해두므로, 행마다 또 뜨는 시간
   프리셋 칩은 중복이라 숨깁니다. 시간 자체는 분칸(.editable-row-minutes)에서 여전히
   바로 고칠 수 있습니다. */
#tutorial-task-list .task-row-minute-options,
#bundle-task-list .task-row-minute-options {
  display: none;
}

/* ── 상단 바 ─────────────────────────────────────────────────────────────
   상단 바는 고정하지 않습니다 — 스크롤을 내리면 본문과 함께 그냥 위로 올라가서 안 보입니다.
   휴대폰 화면은 세로가 짧아서, 제목 줄이 늘 한 자리를 차지하고 있으면 정작 보려는 목록이
   그만큼 덜 보입니다. 목록을 내려도 손 닿는 데 있어야 하는 줄(징검다리의 탭·보기 전환 등)은
   화면마다 공통 컴포넌트 stickyBar(common/components/stickyBar)로 감싸서 화면 맨 위에 붙입니다.

   배경은 남겨둡니다 — 페이지 배경과 같은 색이라 평소엔 티가 안 나지만, 위에 뜨는 것들과
   겹쳐 보일 때 글자가 비치지 않게 합니다. 튜토리얼(.screen-shell)의 상단 바는 그 화면 자체가
   뷰포트에 맞춰져 있어서(본문만 안에서 스크롤) 원래도 움직이지 않습니다. */
header,
.editor-header {
  background: var(--color-bg);
}

header {
  padding: 10px 14px;
  min-height: var(--header-h);
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
}

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

/* /user, /profile/invite 같은 뒤로가기+타이틀 형태의 "작업용" 페이지 헤더입니다. */
.editor-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
}

.editor-header h1 {
  flex: 1;
  font-family: var(--font-heading);
  font-size: 1.2rem;
  font-weight: 800;
}

/* 회원가입 같은 여러 단계 화면의 editor-header에서, h1 대신 현재 단계를 보여주는 점입니다. */
.step-dots {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.step-dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--color-divider);
  transition: width .16s ease, background-color .16s ease;
}

.step-dot.active {
  width: 20px;
  background: var(--color-main-600);
}

/* .step-dots는 기본적으로 가운데 정렬(양옆에 다른 헤더 버튼이 있는 회원가입 마법사 등)인데,
   반대쪽에 버튼이 하나뿐이라 dots를 왼쪽에 붙이고 싶을 때(예: 튜토리얼의 "건너뛰기") 쓰는
   수정자입니다. 점 모양 자체는 .step-dot을 그대로 씁니다. */
.step-dots--left {
  flex: none;
  justify-content: flex-start;
}

.session-meta {
  color: var(--color-text-muted);
  line-height: 1.6;
}

/* 가로/세로 스크롤은 그대로 되지만, 스크롤바 자체는 안 보이게 합니다. */
.no-scrollbar {
  scrollbar-width: none; /* Firefox */
  -ms-overflow-style: none; /* 구형 Edge/IE */
}
.no-scrollbar::-webkit-scrollbar {
  display: none; /* Chrome/Safari/Edge(Chromium) */
}

/* 데이터가 없을 때 공통으로 쓰는 빈 상태 컴포넌트입니다. (common/noData.js)
   아이콘+텍스트만 기본이고, __desc/__action은 쓰는 쪽에서 선택적으로 추가합니다. */
.no-data {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 120px 20px;
  text-align: center;
  box-sizing: border-box;
}

.no-data__icon {
  width: 64px;
  height: 64px;
  opacity: 0.7;
}

.no-data__text {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--color-text);
}

.no-data__desc {
  font-size: 0.8rem;
  color: var(--color-text-muted);
}

.no-data__action {
  margin-top: 4px;
  color: var(--color-main-700);
  font-size: 0.85rem;
  font-weight: 700;
  text-decoration: none;
}

.no-data__action:hover {
  text-decoration: underline;
}

/* 특정 섹션 위에 얹는 공용 로딩 오버레이입니다. (common/loadingOverlay.js)
   전체 화면을 덮는 #global-spinner-overlay와 달리, 대상 요소 하나만 덮습니다. */
.loading-overlay-host {
  position: relative;
}

.loading-overlay {
  display: none;
}

.loading-overlay-host.is-loading > .loading-overlay {
  display: flex;
  position: absolute;
  inset: 0;
  align-items: center;
  justify-content: center;
  font-size: 0.95rem;
  color: var(--color-sub3-700);
}

.loading-overlay-host.is-loading > :not(.loading-overlay) {
  visibility: hidden;
}

.app-footer {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 10;
  display: flex;
  justify-content: space-around;
  background: var(--color-surface);
  border-top: 1px solid var(--color-divider);
  padding: 10px 0 24px 0;
}

.app-footer-link {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  /* 탭이 6개이던 시절 320px 화면에서 한 칸이 53px이라, 좌우 여백이 12px이면 글자 자리가
     29px밖에 안 남아 라벨이 죄다 잘렸습니다. 지금은 5개(세션 탭이 홈으로 흡수됨)라
     넉넉하지만, 탭이 다시 늘어도 잘리지 않도록 6px을 그대로 둡니다. */
  padding: 4px 6px;
  font-size: 0.7rem;
  color: var(--color-text-muted);
  text-decoration: none;
  /* 라벨은 한 줄로 고정합니다. 줄이 접히면 하단 바가 높아져서 본문 아래 여백(82px)을
     넘어 마지막 카드를 가리고, 더 길어지면 마지막 탭이 화면 밖으로 밀려 잘립니다.
     넘치면 말줄임으로 끝내서, 낱말을 새로 넣거나 번역을 바꿔도 배치는 무너지지 않게 합니다
     (지금은 어느 라벨도 잘리지 않습니다 — 그래서 하단 탭은 짧은 이름을 씁니다:
      app/i18n/messages.py의 stepping_stones_short). */
  min-width: 0;
  flex: 0 1 auto;
}

.app-footer-link span {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.app-footer-link .icon {
  width: 22px;
  height: 22px;
}

.app-footer-link.active {
  color: var(--color-main-700);
}

.icon {
  width: 1.25em;
  height: 1.25em;
  flex: none;
  display: block;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.75;
  stroke-linecap: round;
  stroke-linejoin: round;
  pointer-events: none;
  vertical-align: middle;
}

.icon-btn {
  inline-size: 44px;
  block-size: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  margin: 0;
  border: 0;
  border-radius: 999px;
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
  line-height: 1;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background-color .16s ease, color .16s ease, transform .12s ease;
}

/* 헤더에서 back-btn과 짝을 맞춰, step-dots 같은 가운데 요소가 진짜 중앙에 오도록
   자리만 차지하는 투명한 자리표시자입니다 (auth/signup.html 참고). */
.icon-btn-spacer {
  inline-size: 44px;
  block-size: 44px;
  flex: none;
}

.icon-btn--sm {
  background: none;
  position: relative;
  isolation: isolate;
}
.icon-btn--sm::before {
  content: "";
  position: absolute;
  inline-size: 32px;
  block-size: 32px;
  border-radius: 999px;
  background: var(--color-surface);
  z-index: -1;
}

/* 친구 목록/알림 목록처럼 "왼쪽 텍스트 + 오른쪽 액션" 형태로 반복되는 행의 공통 틀입니다.
   각 화면은 이 클래스와 함께 자기만의 클래스(.friend-row, .notification-row 등)를 덧붙여
   텍스트 스타일이나 강조 표시 같은 세부 사항만 얹습니다. */
.list-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 16px;
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  margin-bottom: 10px;
}

/* 이름+분(예상 시간)을 한 행에서 바로 고치는 화면(홈의 "등록된 세션" 큐, /tasks의 주기(사이클)
   행 편집기)이 함께 쓰는 입력 필드 조각들입니다. 각 화면은 행 뼈대 자체(.task-queue-row,
   .bundle-task-row)만 자기 사정(진행/완료 표시 필요 여부 등)에 맞게 따로 두고, 안의
   제목칸/분칸/단위/삭제 버튼은 여기 하나로 공유합니다. */
/* 손잡이입니다. 실제 드래그 처리는 공용 dragReorder.js가 Pointer Events로 합니다
   (HTML5 드래그 API는 iOS Safari 등 모바일에서 안 먹혀서 안 씀).
   touch-action은 항상 none입니다 — 처음엔 pan-y로 열어뒀다가 꾹 눌린 뒤에만 JS로
   none으로 바꾸는 방법도 시도해봤지만, 실제 기기에서는 브라우저가 "이 터치가 스크롤해도
   되는가"를 터치가 시작되는 순간의 touch-action 값으로 이미 정해버려서, 그 뒤에 JS가
   값을 바꿔도 소용이 없었습니다(꾹 누르고 있다가 옮기려 해도 드래그 대신 페이지가
   스크롤돼버림). 그래서 touch-action은 처음부터 계속 none으로 두어 애초에 스크롤
   후보에서 빼두고, 손잡이를 스친 손길이 스크롤처럼 보이게 하는 일은 dragReorder.js가
   scrollTop을 직접 옮겨 대신합니다. */
.editable-row-drag-handle {
  flex: none;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 999px;
  background: transparent;
  color: var(--color-text-muted);
  cursor: grab;
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
}

.editable-row-drag-handle:active {
  cursor: grabbing;
}

/* 드래그 중인 행 — 붕 떠서 옮겨지고 있다는 느낌을 주기 위해 살짝 커지고 그림자가 짙어집니다. */
.bundle-task-item--dragging {
  position: relative;
  z-index: 1;
  opacity: 0.9;
}

.bundle-task-item--dragging .bundle-task-row {
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.18);
}

.editable-row-title {
  flex: 1;
  min-width: 0;
  height: 40px;
}

.editable-row-minutes {
  flex: none;
  width: 56px;
  height: 40px;
  padding: 0 6px;
  text-align: center;
}

.editable-row-unit {
  flex: none;
  font-size: 0.85rem;
  color: var(--color-text-muted);
}

.editable-row-delete {
  flex: none;
  width: 32px;
  height: 32px;
  border: none;
  border-radius: 999px;
  background: transparent;
  color: var(--color-text-muted);
  font-size: 0.9rem;
  cursor: pointer;
}

.editable-row-delete:hover {
  background: var(--color-sub-100);
  color: var(--color-sub-700);
}

/* 카드/행 옆에 붙는 작은 원형 "수정" 링크입니다 — /tasks 목록의 주기(사이클) 행, 홈의
   주기(사이클) 불러오기 카드가 같이 씁니다. --sm은 더 좁은 카드 안(홈)에서 쓰는 축소판. */
.icon-pill-edit-link {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 32px;
  height: 32px;
  border-radius: 999px;
  color: var(--color-text-muted);
  font-size: 0.9rem;
  text-decoration: none;
}

.icon-pill-edit-link:hover {
  background: var(--color-main-100);
  color: var(--color-main-700);
}

.icon-pill-edit-link--sm {
  width: 28px;
  height: 28px;
  font-size: 0.85rem;
}

/* 헤더 알림 벨의 안 읽음 배지입니다. [data-notification-badge]는 안 읽은 알림이 없으면
   hidden 속성이 붙는데, 위쪽의 [hidden] { display:none !important } 규칙이 이 display보다
   항상 이겨서 그때는 그대로 숨겨집니다. */
.icon-btn--notification {
  position: relative;
}

.notification-badge {
  position: absolute;
  top: 4px;
  right: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: var(--radius-pill);
  background: var(--color-sub-600);
  color: #fff;
  font-size: 0.65rem;
  font-weight: 700;
  line-height: 1;
  box-shadow: 0 0 0 2px var(--color-surface);
}

.icon-btn:hover {
  background: color-mix(in srgb, var(--color-main) 14%, var(--color-surface));
  color: var(--color-main-700);
}

.icon-btn:active {
  background: color-mix(in srgb, var(--color-main) 24%, var(--color-surface));
  transform: scale(.94);
}

.icon-btn:focus-visible {
  outline: 2px solid var(--color-main);
  outline-offset: 2px;
}

.icon-btn[disabled],
.icon-btn[aria-disabled="true"] {
  opacity: .45;
  cursor: not-allowed;
}

.icon-btn-label {
  display: inline-flex;
  align-items: center;
  gap: .5em;
  min-block-size: 44px;
  padding-inline: 16px;
  border: 0;
  border-radius: 999px;
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
  text-decoration: none;
  cursor: pointer;
}

/* 화면의 주된 동작(예: 입력창 옆 시작 버튼) 옆에 곁다리로 붙는 보조 버튼용 크기입니다
   ("추천받기"처럼 눌러도 되고 안 눌러도 되는 액션). */
.icon-btn-label--sm {
  min-block-size: 34px;
  padding-inline: 12px;
  font-size: 0.85rem;
}

.icon-btn--primary {
  background: var(--color-main-600);
  color: var(--color-bg);
}
.icon-btn--sub{
  color: var(--color-main-700);
  border-color: var(--color-main-600);
  background: color-mix(in srgb, var(--color-main-600) 14%, var(--color-surface));
}
.icon-btn--primary:hover {
  background: var(--color-main-700);
  color: var(--color-bg);
}

/* 지금은 누를 수 없는 버튼들입니다. OS와 동기화 중인 헤더의 해/달 버튼(.icon-btn),
   기기 설정의 한국어/English 버튼(.option-btn), 그리고 아직 다 채우지 않았을 때의
   "비밀번호 바꾸기"(.icon-btn-label)가 같이 씁니다. 눌러도 안 바뀌는 상태라는 게 보여야
   사용자가 고장으로 오해하지 않습니다 — 왜 안 되는지는 그 옆이나 아래 안내 문구에
   적어둡니다. */
.icon-btn:disabled,
.option-btn:disabled,
.icon-btn-label:disabled {
  opacity: 0.45;
  cursor: default;
}

/* 되돌릴 수 없는 동작(계정 삭제)용입니다. 포인트 색(터콰이즈)과 확실히 달라 보이도록
   서브 색(코랄)을 쓰되, 실수로 누르기 쉬운 꽉 찬 버튼 대신 테두리만 있는 모양으로 둡니다. */
.btn-danger {
  justify-content: center;
  border: 1.5px solid var(--color-sub-600);
  background: transparent;
  color: var(--color-sub-700);
}

.btn-danger:hover {
  background: var(--color-sub-100);
}

/* "네이버로 로그인" 버튼입니다. 네이버가 정한 브랜드 색(#03C75A)을 그대로 써야 사용자가
   한눈에 알아봅니다 — 그래서 이 버튼만 앱의 색 토큰을 따르지 않고 값을 직접 박아둡니다.
   같은 이유로 다크모드에서도 색을 바꾸지 않습니다. */
.btn-naver {
  justify-content: center;
  background: #03c75a;
  color: #ffffff;
  font-weight: 700;
}

.btn-naver:hover {
  background: #02b351;
  color: #ffffff;
}

/* 개인정보처리방침처럼 "읽어야 하는 긴 글"을 담는 곳입니다. 이 앱은 화면마다 글씨를 최대한
   줄이는 쪽이라 본문용 스타일이 따로 없었는데, 법적 고지는 짧게 줄일 수 없어서 여기서만
   줄간격과 목록 모양을 읽기 좋게 풀어둡니다. */
.legal-doc {
  line-height: 1.7;
  font-size: 0.95rem;
}

.legal-doc h3 {
  margin-top: 28px;
  margin-bottom: 8px;
  font-size: 1.1rem;
}

.legal-doc h4 {
  margin-top: 18px;
  margin-bottom: 6px;
  color: var(--color-main-700);
}

.legal-doc p {
  margin: 0 0 12px;
}

.legal-doc ul {
  margin: 0 0 12px;
  padding-left: 20px;
}

.legal-doc li {
  margin-bottom: 6px;
}

.legal-updated {
  color: var(--color-text-muted);
  font-size: 0.85rem;
}

/* 본문 흐름에서 한 발 물러난 보충 설명(주의사항 등)입니다. */
.legal-note {
  padding: 12px 14px;
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text-muted);
  font-size: 0.88rem;
}

/* 화면 맨 아래에 작게 놓는 방침 링크입니다. */
.legal-link {
  display: inline-block;
  color: var(--color-text-muted);
  font-size: 0.85rem;
}

/* 비밀번호를 아직 정하지 않은 계정에 띄우는 경고 문구입니다. */
.password-warning {
  color: var(--color-sub-700);
}

.icon-btn[aria-pressed="true"] {
  background: var(--color-main-600);
  color: var(--color-bg);
}
.btn-delete{
  background: var(--color-sub-600);
  color: var(--color-bg);
}

.icon-btn:hover {
  background: color-mix(in srgb, var(--color-main) 14%, var(--color-surface));
  color: var(--color-main-700);
}

.btn-delete:hover {
  background: color-mix(in srgb, var(--color-sub) 14%, var(--color-surface));
  color: var(--color-sub-700);
}
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

@media (prefers-reduced-motion: reduce) {
  .icon-btn { transition: none; }
  .icon-btn:active { transform: none; }
}

@media (forced-colors: active) {
  .icon-btn { border: 1px solid ButtonBorder; background: ButtonFace; color: ButtonText; }
  .icon-btn:focus-visible { outline: 2px solid Highlight; }
  .icon { stroke: currentColor; }
}

.btn-dark-toggle{
  background: var(--color-sub3-100);
  color: var(--color-sub3-600);
}
.btn-dark-toggle:hover{
  background: color-mix(in srgb, var(--color-sub3) 14%, var(--color-surface));
  color: var(--color-sub3-700);
}

/* 전체 화면을 덮는 반투명 배경 */
/* fetch 중일 때만 Spinner.show()가 .is-visible을 붙이는 용도라 기본은 숨김 상태여야
   합니다(항상 떠 있으면 아무 fetch도 없는 화면에서도 계속 덮여 있게 됩니다). display는
   항상 flex로 두고 opacity/visibility만 여닫아서, 예전처럼 뚝 끊겨 나타나고 사라지지
   않고 자연스럽게 페이드되도록 합니다. */
#global-spinner-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    /* 예전에는 rgba(0,0,0,.45) + backdrop-filter: blur(3px)였습니다. blur는 화면 전체를
       매 프레임 다시 합성해야 해서 폰에서 특히 비싸고, 0.45는 잠깐 뜨기에도 너무 어두워
       "화면이 검게 덮였다"는 인상을 줬습니다. 흐림을 빼고 톤을 낮춰, 가려지되 답답하지
       않을 만큼만 덮습니다. */
    background-color: rgba(0, 0, 0, 0.22);
    z-index: 9999;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    /* 뜰 때는 이미 250ms를 기다린 뒤라 빠르게, 사라질 때도 질질 끌지 않게 짧게 잡습니다. */
    transition: opacity 0.12s ease, visibility 0.12s ease;
}

#global-spinner-overlay.is-visible {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
}

/* 스피너 디자인 — 링 전체가 일정한 속도로 돌면서(spinner-rotate), 안쪽 호의 길이가
   늘었다 줄었다 합니다(spinner-dash). 예전의 두 색 링이 그냥 linear로 도는 것보다
   훨씬 생동감 있게 움직입니다(구글 머티리얼 스피너와 같은 방식). */
.spinner-circle {
    width: 50px;
    height: 50px;
    animation: spinner-rotate 1.6s linear infinite;
}

.spinner-track {
    stroke: rgba(255, 255, 255, 0.25);
}

.spinner-arc {
    stroke: var(--color-main-400);
    stroke-linecap: round;
    transform-origin: center;
    animation: spinner-dash 1.4s ease-in-out infinite;
}

@keyframes spinner-rotate {
    100% { transform: rotate(360deg); }
}

@keyframes spinner-dash {
    0% { stroke-dasharray: 1, 200; stroke-dashoffset: 0; }
    50% { stroke-dasharray: 90, 200; stroke-dashoffset: -35px; }
    100% { stroke-dasharray: 90, 200; stroke-dashoffset: -124px; }
}

/* 화면 가운데 뜨는 작은 팝업(모달) 공통 뼈대입니다 — 특정 페이지 전용 팝업(예: 세션
   추가 팝업, app/views/home/index.html)이 새로 생길 때마다 이 두 클래스를 재사용하면
   됩니다. z-index는 아래 완수 모달과 같은 자리(전체 화면 스피너보다 아래, 콘텐츠/푸터보다 위)를 씁니다. */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(0, 0, 0, 0.5);
  box-sizing: border-box;
}

.modal-card {
  width: 100%;
  max-width: 360px;
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  padding: 24px 20px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* 세션 기준 타이머의 "완수하셨나요?" 모달입니다. 타이머 세션이 어느 화면에서든
   이어지는 것과 마찬가지로 이 모달도 어느 페이지에서든 뜰 수 있어야 해서(taskOutcomeModal.html이
   base.html에서 항상 로드됨), 특정 페이지 CSS가 아니라 여기(common.css)에 둡니다.
   전체 화면 스피너(#global-spinner-overlay, z-index 9999)보다는 아래, 나머지 콘텐츠/푸터보다는
   위에 오도록 z-index를 잡습니다. */
.task-outcome-backdrop {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(0, 0, 0, 0.5);
  box-sizing: border-box;
}

.task-outcome-modal {
  width: 100%;
  max-width: 360px;
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  padding: 24px 20px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.task-outcome-task {
  font-size: 0.85rem;
  color: var(--color-text-muted);
  text-align: center;
}

.task-outcome-choices {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.task-outcome-choices .btn-block {
  margin-top: 0;
}

.task-outcome-textarea {
  height: auto;
  min-height: 90px;
  padding: 12px 14px;
  resize: vertical;
}

.task-outcome-memo-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

.task-outcome-close {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  margin-top: -4px;
  border: none;
  border-radius: 999px;
  background: transparent;
  color: var(--color-text-muted);
  font-size: 0.9rem;
  cursor: pointer;
}

.task-outcome-close:hover {
  background: var(--color-sub-100);
  color: var(--color-sub-700);
}

/* 기록 탭에서 주기(사이클)(한 번에 이어서 등록한 세션들) 하나를 박스 하나로 묶어 보여줍니다.
   상단엔 세션 제목 대신 그 묶음을 시작한 날짜/시간을 구분용 제목으로 쓰고, 옆에 묶음
   전체의 실제 소요 시간 합계를 같이 보여줍니다. */
.task-group-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.task-group-card {
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-lg);
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.task-group-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.task-group-datetime {
  font-weight: 700;
  font-size: 0.9rem;
}

.task-group-total {
  flex: none;
  font-size: 0.75rem;
  font-weight: 700;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--color-main-100);
  color: var(--color-main-700);
}

.task-group-meditation {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

/* 명상은 5분 타이머지만 중간에 건너뛸 수 있어서, 실제로 얼마나 했는지(1분 미만이면 "0분",
   그 이상이면 분:초)를 배지 오른쪽에 같이 보여줍니다
   (app/controllers/task_controller.py의 _format_meditation_duration 참고). */
.task-group-meditation-value {
  font-size: 0.8rem;
  color: var(--color-text-muted);
}

/* 기록 탭에서 보여주는 세션 타이머 로그 목록입니다. */
.task-log-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.task-log-item {
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.task-log-item-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.task-log-title {
  font-weight: 700;
}

.task-log-outcome {
  flex: none;
  font-size: 0.75rem;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--color-main-100);
  color: var(--color-main-700);
}

.task-log-outcome--not_completed {
  background: var(--color-sub-100);
  color: var(--color-sub-700);
}

.task-log-meta {
  font-size: 0.8rem;
  color: var(--color-text-muted);
}

.task-log-memo {
  font-size: 0.85rem;
  color: var(--color-text);
  background: var(--color-bg);
  border-radius: var(--radius-md);
  padding: 8px 10px;
}

/* ───────────────────────────────────────────────────────────────────────────
   기기 로컬 상태 — 첫 페인트부터 제 모습으로

   화면 모드는 이 기기의 localStorage에만 있어서 서버가 알 수 없습니다. 예전에는 화면이
   뜬 뒤 JS가 스위치와 아이콘을 하나씩 고쳐 그렸는데, 그러면 화면을 열 때마다 "꺼진 스위치가
   한 번 보였다가 켜지는" 게 눈에 띄었습니다. 언어를 바꾸면 새로고침하므로, 언어 토글을
   누를 때마다 화면 모드 스위치가 같이 깜빡이는 것처럼 보였고요.

   그래서 화면에 보이는 모습은 전부 <html>의 두 속성에서 나오게 했습니다. base.html의
   첫 페인트 스크립트가 CSS보다 먼저 이 값을 적어두므로, 아래 규칙들은 처음 그릴 때부터
   맞는 모습으로 그려집니다 — 고쳐 그리는 순간이 아예 없습니다.

     data-theme         light | dark    지금 실제로 보여줄 모습
     data-theme-source  system | manual 그 모습을 OS에서 가져왔는지, 직접 골랐는지

   theme.js의 applyTheme()이 이 두 속성을 갱신하므로, 사람이 스위치를 누르면 아래 규칙들이
   한꺼번에 따라옵니다. JS가 따로 챙기는 것은 눈에 보이지 않는 것(aria-*, disabled)뿐입니다.

   ⚠️ 서버가 아는 상태(예: 언어 설정)는 이 방식을 쓰지 않습니다 — 서버가 처음부터 .active를
      붙여 내려주면 되고, 그게 더 단순합니다. 이 절은 "서버가 알 수 없는 것"만 다룹니다.
   ─────────────────────────────────────────────────────────────────────────── */

/* 헤더의 해/달 버튼입니다. 아이콘 두 개를 다 마크업에 두고 지금 모드에 맞는 쪽만 보입니다
   (예전에는 달만 그려두고 JS가 해로 바꿔치기해서, 다크모드에서 달이 한 번 스쳤습니다). */
.icon[data-theme-icon] {
  display: none;
}

:root[data-theme="light"] .icon[data-theme-icon="moon"],
:root[data-theme="dark"] .icon[data-theme-icon="sun"] {
  display: block;
}

/* OS와 동기화 중이면 이 버튼으로는 바꿀 수 없습니다. 실제로 막는 건 JS가 붙이는 disabled
   이지만(theme.js), 그건 화면이 뜬 뒤라 여기서 보이는 모습만 먼저 맞춰둡니다.
   예전엔 opacity: .45로 흐리게 했는데, 헤더에 늘 떠 있는 버튼이라 비활성 표시라기보다 헤더가
   뿌옇게 번진 것처럼 보여서 뺐습니다 — 모습은 그대로 두고 눌러도 반응만 하지 않습니다. */
:root[data-theme-source="system"] [data-theme-toggle],
[data-theme-toggle]:disabled {
  opacity: 1;
  cursor: default;
}

/* 기기 설정(/device-settings)의 "OS와 동기화" 스위치입니다. 켜진 모습은 .toggle-switch.active와
   같지만, 클래스가 아니라 <html>에서 읽어 그립니다 — JS가 클래스를 붙이는 방식이면 그 순간이
   눈에 보이기 때문입니다. */
:root[data-theme-source="system"] .toggle-switch[data-theme-system-switch] {
  background: var(--color-main-600);
}

:root[data-theme-source="system"] .toggle-switch[data-theme-system-switch]::before {
  transform: translateX(18px);
}

/* 스위치 아래 안내 문구입니다. 두 문장을 다 써두고 맞는 쪽만 보입니다 — 예전에는 빈 줄로
   그려두고 JS가 글자를 채워서, 문구가 뒤늦게 튀어나오며 아래 내용이 밀렸습니다. */
[data-theme-source-hint] {
  display: none;
}

:root[data-theme-source="system"] [data-theme-source-hint="system"],
:root[data-theme-source="manual"] [data-theme-source-hint="manual"] {
  display: block;
}

/* 그 문구 안에서 "다크"/"라이트"만 갈아끼우는 자리입니다. */
[data-theme-name] {
  display: none;
}

:root[data-theme="dark"] [data-theme-name="dark"],
:root[data-theme="light"] [data-theme-name="light"] {
  display: inline;
}

/* ── 탭 화면 좌우 밀기(common/tabSwipe.js) ─────────────────────────────────
   하단 탭 바의 화면들을 좌우로 밀어 넘길 때 <main>의 내용(화면 아래 고정 바는 빼고)이 움직입니다.
   미는 동안 손가락을 따라오는 것과, 놓았는데 넘어가지 않아 제자리로 돌아오는 것은 tabSwipe.js가 그
   순간에만 직접 적습니다 — 여기 전환을 항상 걸어두면 화면마다 자기 움직임이 있는 요소에 끼어듭니다.
   넘어가면 민 쪽으로 살짝 빠지며 옅어진 뒤 옆 화면이 열리고, 새 화면은 들어온 쪽에서 미끄러져 들어옵니다. */
/* ── 페이지 사이 화면 전환(View Transitions) ─────────────────────────────
   화면을 옮길 때(탭 바를 누르거나 밀어서 넘기거나 링크를 누를 때) 브라우저가 새 화면이 다 준비될
   때까지 지금 화면을 그대로 보여주다가 짧게 바꿔 줍니다. 이게 없으면 새 화면을 받는 동안 하얀 빈
   화면이나 반쯤 그려진 화면이 잠깐씩 보였습니다(크롬 126+, 사파리 18.2+ — 모르는 브라우저는 예전대로).
     - 보통은 0.15초 동안 겹쳐 바뀝니다.
     - 탭 화면을 밀어서 넘기면(common/tabSwipe.js → base.html이 종류를 붙임) 민 방향으로 미끄러집니다.
     - 하단 탭 바는 따로 떼어(view-transition-name) 제자리에 둡니다 — 탭 바까지 미끄러지면 어지럽습니다. */
@view-transition {
  navigation: auto;
}

.app-footer {
  view-transition-name: app-tab-bar;
}

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.15s;
}

/* 왼쪽으로 밀어 오른쪽 화면으로: 지금 화면은 왼쪽으로 빠지고 새 화면이 오른쪽에서 들어옵니다. 나가는
   화면을 더 빨리(0.14초) 빼서 두 화면의 글자가 겹쳐 보이는 순간을 줄입니다. */
:root:active-view-transition-type(tab-from-right)::view-transition-old(root) {
  animation: vt-tab-out-to-left 0.14s cubic-bezier(0.4, 0, 1, 1) both;
}

:root:active-view-transition-type(tab-from-right)::view-transition-new(root) {
  animation: vt-tab-in-from-right 0.22s cubic-bezier(0.2, 0.8, 0.2, 1) both;
}

:root:active-view-transition-type(tab-from-left)::view-transition-old(root) {
  animation: vt-tab-out-to-right 0.14s cubic-bezier(0.4, 0, 1, 1) both;
}

:root:active-view-transition-type(tab-from-left)::view-transition-new(root) {
  animation: vt-tab-in-from-left 0.22s cubic-bezier(0.2, 0.8, 0.2, 1) both;
}

@keyframes vt-tab-out-to-left {
  to {
    transform: translateX(-28%);
    opacity: 0;
  }
}

@keyframes vt-tab-in-from-right {
  from {
    transform: translateX(28%);
    opacity: 0;
  }
}

@keyframes vt-tab-out-to-right {
  to {
    transform: translateX(28%);
    opacity: 0;
  }
}

@keyframes vt-tab-in-from-left {
  from {
    transform: translateX(-28%);
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

/* hidden이 아니라 clip인 이유: hidden은 <main>을 스크롤 상자로 만들어, 안에서 위에 붙어 있던 줄
   (position: sticky — 징검다리의 탭 줄 등)이 그 순간 풀립니다. clip을 모르는 오래된 브라우저는 그냥
   잘라내지 않습니다. */
@supports (overflow: clip) {
  main.is-tab-moving {
    overflow-x: clip;
  }
}

/* 화면 전환(View Transitions)을 모르는 브라우저에서만 씁니다. 흐리게 하지 않습니다 — 흐려진 채로 새 화면을
   기다리는 동안이 하얗게 멈춘 것처럼 보였습니다. */
main > .is-tab-leaving-to-left,
main > .is-tab-leaving-to-right {
  transition: transform 0.09s ease-in;
}

main > .is-tab-leaving-to-left {
  transform: translateX(-40px);
}

main > .is-tab-leaving-to-right {
  transform: translateX(40px);
}

main > .is-tab-entering-from-right {
  animation: tab-enter-from-right 0.22s ease-out;
}

main > .is-tab-entering-from-left {
  animation: tab-enter-from-left 0.22s ease-out;
}

@keyframes tab-enter-from-right {
  from {
    transform: translateX(32px);
    opacity: 0.4;
  }
}

@keyframes tab-enter-from-left {
  from {
    transform: translateX(-32px);
    opacity: 0.4;
  }
}

@media (prefers-reduced-motion: reduce) {
  main > .is-tab-entering-from-right,
  main > .is-tab-entering-from-left {
    animation: none;
  }
}
