/*
 * 대시보드 전용 보충 CSS (SPEC §12.6 / D17) — 캐스케이드 **맨 뒤**에 붙는다.
 *
 * 랜딩은 캡처 번들만으로 버텼지만 대시보드는 못 버틴다. 없는 것들이 하필 대시보드에
 * 꼭 필요한 것들이다 — 임의값 grid 템플릿(사이드바+본문), calc 높이(스크롤 영역),
 * file: 유틸(증빙 업로드 버튼). 이걸 전부 인라인 style로 푸는 코드는 지저분해진다.
 *
 * ⛔ 번들에 **이미 있는** 유틸리티를 재정의하지 않는다. 같은 이름을 다시 정의하면
 * 캐스케이드 막차라 **랜딩 렌더가 조용히 바뀐다.** 그래서 전부 `psl-dash-` 접두다.
 *
 * ⚠️ 실측으로 확인한 "번들에 없는 것"만 담는다(2026-08-07 `_workspace/css-tokens.json`
 * 8,256개 토큰 대조): grid-cols-[280px_1fr] · h-[calc(100vh-64px)] · file:mr-4 ·
 * rounded-lg/md 별칭. 그 외 rounded-2/3 · w-[280px] · file:bg-transparent · gap-8 ·
 * max-w-[350px] · py-20 · divide-y · disabled:opacity-50은 **번들에 있으므로**
 * 여기서 다시 정의하지 않는다.
 *
 * 색은 번들의 CSS 변수를 그대로 참조한다 — 값을 직접 적으면 랜딩과 어긋난다.
 */

/* ── 셸: 사이드바 + 본문 ────────────────────────────────────────────────── */

/* grid-cols-[280px_1fr]이 번들에 없다. 사이드바 폭은 Lovable 실측(280px). */
.psl-dash-shell {
  display: grid;
  grid-template-columns: 280px 1fr;
  min-height: 100dvh;
}

.psl-dash-sidebar {
  display: flex;
  flex-direction: column;
  min-height: 0;
  height: 100dvh;
  position: sticky;
  top: 0;
  border-right: var(--border-default, 0.5px) solid
    var(--border-primary, rgba(0, 0, 0, 0.08));
}

/* 좁은 화면 전용 상단 바 — 넓은 화면에는 없다(사이드바가 상시로 서 있다). */
.psl-dash-topbar {
  display: none;
}

/*
 * ⚠️ 이 블록은 반드시 `.psl-dash-sidebar` 기본 규칙 **뒤에** 와야 한다. 미디어
 * 쿼리는 특이도를 더해주지 않아서, 같은 선택자를 앞에 두면 뒤의 `display: flex`가
 * 이긴다 — 실제로 그래서 좁은 화면에서 사이드바가 접히지 않고 폭을 통째로
 * 차지했다(500px 뷰포트 실측). 순서가 곧 동작이다.
 */
@media (max-width: 1023px) {
  /*
   * 좁은 화면에서 사이드바는 **사라지지 않고 드로어로 접힌다**(운영자 2026-08-09).
   *
   * ⛔ 예전에는 그냥 `display: none`이었다. 그런데 계정 메뉴가 사이드바 맨 아래로
   * 내려가면서, 그 한 줄이 모바일에서 **로그아웃·언어 전환·탈퇴로 가는 유일한 문**을
   * 통째로 닫아버렸다. 별도 진입점을 새로 만드는 대신 사이드바 자체를 열 수 있게
   * 한다 — ChatGPT·Gemini 모바일이 똑같이 한다. 스레드 목록·새 질문도 같이 살아난다.
   */
  .psl-dash-shell {
    grid-template-columns: 1fr;
  }

  .psl-dash-topbar {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    border-bottom: var(--border-default, 0.5px) solid
      var(--border-primary, rgba(0, 0, 0, 0.08));
  }

  /*
   * 드로어 본체. `position: fixed`라 그리드 칸을 차지하지 않는다 — 그래서 본문은
   * 드로어가 있든 없든 전체 폭을 그대로 쓴다.
   *
   * 닫힌 상태를 `display: none`이 아니라 **화면 밖 이동**으로 두는 이유는,
   * `display`는 전환(transition)이 걸리지 않아서다. 없앴다 만들면 슬라이드가
   * 아니라 깜빡임이 된다.
   */
  .psl-dash-sidebar {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 70;
    width: 280px;
    max-width: 86vw;
    background: var(--bg-secondary, #fff);
    transform: translateX(-100%);
    transition: transform 150ms ease-out;
    box-shadow: 0 0 40px -8px rgba(0, 0, 0, 0.24);
  }

  .psl-dash-sidebar[data-open="true"] {
    transform: none;
  }

  /*
   * ⚠️ 닫힌 드로어는 **초점도 받지 않아야** 한다. 화면 밖으로 밀기만 하면 링크와
   * 버튼이 그대로 살아 있어서, Tab을 누르면 보이지 않는 곳으로 초점이 걸어 들어간다
   * (스크린리더에도 그대로 읽힌다). 눈에서 치우는 것과 조작에서 치우는 것은 다르다.
   */
  .psl-dash-sidebar[data-open="false"] {
    visibility: hidden;
  }
  .psl-dash-sidebar[data-open="true"] {
    visibility: visible;
  }
}

/* 드로어 뒤를 덮는 면 — 이건 어둡게 한다(드롭다운과 달리 화면을 가리는 물건이다). */
.psl-dash-drawer-scrim {
  position: fixed;
  inset: 0;
  z-index: 65;
  background: rgba(0, 0, 0, 0.32);
  animation: psl-dash-fade 150ms ease-out;
}

@keyframes psl-dash-fade {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/* h-[calc(100vh-64px)]이 번들에 없다. 헤더 높이는 body의 --header-height(64px). */
.psl-dash-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.psl-dash-main {
  display: flex;
  flex-direction: column;
  min-height: 0;
  height: 100dvh;
}

/* ── 인터랙션 공통 ────────────────────────────────────────────────────── */

/*
 * 누를 수 있는 것은 눌러보기 전에 눌러진다는 걸 알려야 한다(운영자 2026-08-09:
 * "전반적으로 훨씬 화려하고 인터랙티브하게"). 상태 전환은 **한 벌**로 고정한다 —
 * 자리마다 다른 시간·다른 곡선을 쓰면 화면이 들썩이는 것처럼 읽힌다.
 *
 * 150ms / ease-out은 ChatGPT·Gemini 실측값이다. 그보다 길면 굼뜨고, 짧으면
 * 전환이 아니라 깜빡임으로 보인다.
 */
.psl-dash-hover {
  transition:
    background-color 150ms ease-out,
    color 150ms ease-out;
}
.psl-dash-hover:hover {
  background: rgba(0, 0, 0, 0.05);
}

/*
 * 아이콘만 있는 버튼(새 질문·스레드 ⋯). **배경을 채우지 않는다**(운영자
 * 2026-08-09: "배경 없애고 아이콘만"). 채운 사각 버튼은 사이드바에서 혼자
 * 강조되어, 정작 아래 목록보다 먼저 읽힌다 — ChatGPT·Gemini 둘 다 여기를
 * 배경 없는 아이콘으로 둔다.
 */
.psl-dash-icon-btn {
  border-radius: 10px;
  transition:
    background-color 150ms ease-out,
    transform 150ms ease-out;
}
.psl-dash-icon-btn:hover {
  background: rgba(0, 0, 0, 0.05);
}
/* 눌린 순간 살짝 줄어든다 — 클릭이 먹었다는 유일한 즉시 신호다. */
.psl-dash-icon-btn:active {
  transform: scale(0.94);
}

/* ── 빈 화면 제안 질문 ─────────────────────────────────────────────────── */

/*
 * 배경 없는 한 줄, hover에만 배경(ChatGPT 제안 행 실측). 배경을 상시로 깔면
 * 카드 세 장이 되어 인사말·입력창과 무게를 다투는데, 이 자리는 **거들 뿐**이다.
 */
.psl-dash-suggestion {
  color: var(--fg-secondary, rgba(0, 0, 0, 0.72));
  transition:
    background-color 150ms ease-out,
    transform 150ms ease-out;
}
.psl-dash-suggestion:hover {
  background: rgba(0, 0, 0, 0.04);
}
.psl-dash-suggestion:active {
  transform: scale(0.99);
}

/* ── 스레드 목록 항목 ──────────────────────────────────────────────────── */

.psl-dash-thread {
  transition: background-color 150ms ease-out;
}
/* 열려 있는 스레드는 인라인 배경을 갖는다 — hover는 그보다 옅게만 얹는다. */
.psl-dash-thread:hover {
  background: rgba(0, 0, 0, 0.04);
}

/*
 * hover에서만 [⋯]가 뜬다(§11.2.1). group-hover 유틸이 번들에 있지만, 메뉴가 열린
 * 동안에도 버튼이 보여야 해서(data-open) 여기서 한 규칙으로 묶는다 —
 * data-[state=open] 유틸이 번들에 없다.
 */
.psl-dash-thread-menu {
  opacity: 0;
  transition: opacity 150ms ease-out;
}
.psl-dash-thread:hover .psl-dash-thread-menu,
.psl-dash-thread:focus-within .psl-dash-thread-menu,
.psl-dash-thread-menu[data-open="true"] {
  opacity: 1;
}

/* ── 증빙 업로드 버튼 ─────────────────────────────────────────────────── */

/*
 * file: 유틸 중 `file:mr-4`만 번들에 없다. 네이티브 파일 버튼을 통째로 우리 버튼처럼
 * 보이게 하려면 규칙 하나로 묶는 편이 읽기 쉬워서 여기서 다 적는다.
 */
.psl-dash-file::file-selector-button {
  margin-right: 12px;
  padding: 6px 10px;
  border: 0.5px solid var(--border-primary, rgba(0, 0, 0, 0.12));
  border-radius: 8px;
  background: transparent;
  font-size: 14px;
  font-weight: 480;
  color: inherit;
  cursor: pointer;
}
.psl-dash-file::file-selector-button:hover {
  background: rgba(0, 0, 0, 0.04);
}

/* ── 드롭다운·모달 ────────────────────────────────────────────────────── */

.psl-dash-menu {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  min-width: 240px;
  border: 0.5px solid var(--border-primary, rgba(0, 0, 0, 0.12));
  border-radius: 12px;
  background: var(--bg-base, #fcfbf8);
  box-shadow:
    0 8px 24px -8px rgba(0, 0, 0, 0.16),
    0 2px 6px -2px rgba(0, 0, 0, 0.08);
  padding: 6px;
  z-index: 50;
}

/*
 * 위로 열리는 변형 — 계정 메뉴가 사이드바 **맨 아래**로 내려갔기 때문이다.
 * 아래로 열면 뷰포트 밖으로 나가 항목 절반이 잘린다. ChatGPT·Gemini·Claude가
 * 전부 좌하단 + 위로 열림이다.
 */
.psl-dash-menu-up {
  top: auto;
  bottom: calc(100% + 6px);
  left: 0;
  right: auto;
}

/*
 * 드롭다운·계정 메뉴가 뜰 때의 등장. 즉시 튀어나오면 "떴다"가 아니라 "화면이
 * 바뀌었다"로 읽힌다 — 짧은 슬라이드가 어디서 나왔는지를 알려준다.
 */
.psl-dash-menu {
  animation: psl-dash-pop 140ms ease-out;
}

@keyframes psl-dash-pop {
  from {
    opacity: 0;
    transform: translateY(4px) scale(0.98);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* 메뉴 안쪽 행들도 hover를 갖는다 — 안 그러면 눌리는 물건인지 알 수 없다. */
.psl-dash-menu-item {
  border-radius: 8px;
  transition: background-color 150ms ease-out;
}
.psl-dash-menu-item:hover {
  background: rgba(0, 0, 0, 0.05);
}

/*
 * 드롭다운 바깥 클릭을 받는 투명 면. **어둡게 하지 않는다** — 드롭다운은 화면을
 * 가리는 물건이 아니라 옆에 뜨는 물건이고, 여기서 어둡게 하면 사이드바 메뉴 하나
 * 열 때마다 화면 전체가 깜빡인다. 어둡게 하는 것은 모달(psl-dash-backdrop)뿐이다.
 */
.psl-dash-outside {
  position: fixed;
  inset: 0;
  z-index: 40;
}

.psl-dash-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.32);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  z-index: 60;
}

/* 모달 본체 — 위험 확인(탈퇴·스레드 삭제)과 이름 바꾸기가 같이 쓴다. */
.psl-dash-modal {
  width: 100%;
  max-width: 420px;
  border-radius: 16px;
  background: var(--bg-secondary, #fff);
  padding: 24px;
  box-shadow: 0 24px 64px -16px rgba(0, 0, 0, 0.32);
}

/* ── 빈 상태(새 질문 화면) ────────────────────────────────────────────── */

/*
 * 인사말 + 컴포저가 **화면 세로 중앙**에 선다(운영자 2026-08-09 · ChatGPT·Gemini
 * 실측). 좌상단 정적 제목은 "여기가 뭐 하는 화면인지" 알려주지만 시선을 입력창으로
 * 데려가지 못한다 — 중앙에 두면 인사말과 입력창이 한 덩어리로 읽히고, 그 덩어리가
 * 곧 "여기에 물어보세요"다.
 *
 * 스크롤 영역 전체를 세로 중앙 정렬로 쓰되 살짝 위(-4dvh)로 올린다 — 기하학적
 * 정중앙은 눈에는 아래로 처져 보인다(광학 중심 보정, ChatGPT도 같다).
 */
.psl-dash-hero {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 28px;
  padding: 24px 16px calc(24px + 4dvh);
}

/*
 * 개인화 인사말 — 크고 **얇다**(Gemini 실측 36px/weight 320). 굵게 쓰면 안내문이
 * 아니라 배너 카피로 읽힌다. 30px은 우리 본문 폭(max-w-3xl)에서 한 줄에 들어가는
 * 상한이다 — 넘치면 이름 길이에 따라 두 줄이 됐다 말았다 한다.
 */
.psl-dash-greeting {
  font-size: 30px;
  line-height: 1.3;
  font-weight: 400;
  letter-spacing: -0.01em;
  text-align: center;
  margin: 0;
  word-break: keep-all;
  animation: psl-dash-rise 420ms ease-out;
}

/* 중앙 컴포저는 인사말보다 아주 살짝 늦게 올라온다 — 둘이 동시에 뜨면 한 장의
   그림이 통째로 나타난 것처럼 보이고, 시선이 어디에 앉을지 정해지지 않는다. */
.psl-dash-hero-composer {
  width: 100%;
  animation: psl-dash-rise 420ms ease-out 60ms backwards;
}

@keyframes psl-dash-rise {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ── 컴포저 ───────────────────────────────────────────────────────────── */

/*
 * 캡슐이 포커스를 받으면 링이 또렷해진다. 랜딩 `ChatCapsule`과 같은 형태를 쓰되
 * (radius 28 · 흰 테두리 · shadow) 여기는 실제로 타이핑하는 자리라 "지금 여기에
 * 쓰고 있다"는 표시가 필요하다.
 */
.psl-dash-composer {
  transition:
    box-shadow 180ms ease-out,
    border-color 180ms ease-out;
}
.psl-dash-composer:focus-within {
  box-shadow:
    0 12px 32px -12px rgba(0, 0, 0, 0.22),
    0 0 0 3px rgba(69, 180, 139, 0.16);
}

/*
 * 보내기·중지 — **채워진 원형**(운영자 2026-08-09 · ChatGPT 실측 36px 원형).
 * 글자 pill("보내기")은 캡슐 안에서 두 번째 문장처럼 읽히는데, 이 자리에 필요한
 * 것은 읽을 것이 아니라 누를 것이다.
 */
/*
 * ⚠️ 크기는 여기서 정하지 않는다 — 랜딩 캡슐과 같은 `size-8`(32px)을 클래스로
 * 받는다(R5). 예전에는 여기서 36px을 박아 랜딩 버튼(32px)보다 혼자 컸다.
 */
.psl-dash-send {
  border-radius: 9999px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  transition:
    background-color 150ms ease-out,
    opacity 150ms ease-out,
    transform 150ms ease-out;
}
.psl-dash-send:not(:disabled):hover {
  transform: scale(1.06);
}
.psl-dash-send:not(:disabled):active {
  transform: scale(0.94);
}
/*
 * 아직 아무것도 안 쳤을 때. 반투명(opacity)이 아니라 **회색 배경**으로 죽인다 —
 * 반투명은 강조색을 옅게 만들 뿐이라 "누를 수 있는데 흐릿한 것"으로 보인다.
 */
.psl-dash-send:disabled {
  background: rgba(0, 0, 0, 0.12) !important;
  color: rgba(0, 0, 0, 0.35);
  cursor: default;
}

/* ── 답변 등장 ────────────────────────────────────────────────────────── */

/*
 * 답변은 **한 번에** 온다(§8.2 — 출력 게이트를 통과한 뒤라 토큰이 흐르지 않는다).
 * 그래서 아무 전환도 없으면 20초 정적 화면 뒤에 글 한 덩어리가 순간이동한 것처럼
 * 꽂힌다. 짧은 fade + slide가 "지금 도착했다"를 만든다 — 스트리밍 흉내가 아니라
 * 도착 자체의 표시다.
 */
.psl-dash-answer-in {
  animation: psl-dash-rise 360ms ease-out;
}

/* ── 답변 본문 ────────────────────────────────────────────────────────── */

/*
 * 본문 16px / line-height 1.65(ChatGPT 실측 16px·26px = 1.625, 운영자 지시 1.65).
 * 예전에는 14px이었는데, 답변은 이 화면에서 **유일하게 오래 읽는 글**이라 14px은
 * UI 라벨과 같은 눈금으로 서서 "읽을 것"이 아니라 "표시된 것"으로 보였다.
 *
 * 번들의 `leading-relaxed`(1.625)로 대신하지 않는 이유는 이 값이 표 셀까지
 * 상속되기 때문이다 — 표는 13px 조밀한 눈금을 유지해야 한 화면에 들어온다.
 */
.psl-dash-answer {
  font-size: 16px;
  line-height: 1.65;
}
/* 표·코드는 본문 눈금을 따라가지 않는다 — 셀이 커지면 열이 접힌다. */
.psl-dash-answer .psl-dash-table {
  font-size: 13px;
  line-height: 1.5;
}

/* ── 답변 표 ──────────────────────────────────────────────────────────── */

/*
 * `table`·`border-collapse`는 번들에 있지만 셀 패딩·구분선은 없다. 표는 답변의
 * 주요 형식이라(§7.4) 규칙으로 고정한다 — 셀마다 유틸을 붙이면 반드시 어긋난다.
 */
.psl-dash-table th,
.psl-dash-table td {
  padding: 8px 12px;
  text-align: left;
  border-bottom: 0.5px solid var(--border-primary, rgba(0, 0, 0, 0.08));
  font-size: 13px;
}
.psl-dash-table th {
  font-weight: 480;
  color: var(--fg-tertiary, rgba(0, 0, 0, 0.55));
}
.psl-dash-table td {
  font-variant-numeric: tabular-nums;
}
.psl-dash-table tr:last-child td {
  border-bottom: none;
}

/* ── 답변 차트 ────────────────────────────────────────────────────────── */

/*
 * vega-embed가 그리는 상자. `AnswerChart`는 `width: "container"`로 스펙을 넘겨
 * 차트가 답변 폭을 따라가게 하는데, 그러려면 **재는 대상인 컨테이너에 폭이
 * 있어야** 한다.
 *
 * ⛔ 그런데 vega-embed는 자기 래퍼를 `display: inline-block`으로 만든다. 그러면
 * 폭이 **내용에서** 나오고, 내용인 SVG의 폭은 `container`라 **컨테이너에서**
 * 나온다 — 서로를 보고 있어 0으로 접힌다. 실제로 그렇게 됐다: `figure`는 736px인데
 * 안쪽 `.vega-embed`와 `svg`가 `width="0"`이라 **높이 182px짜리 빈 상자**만 남았다
 * (운영자 2026-08-11: "중간에 뭔 빈공간이 있는데 이건 뭐여?").
 *
 * `figure`의 `overflow-x: auto`가 이걸 막아 주지 못한다 — 스크롤 컨테이너 안에서는
 * 자식이 부모 폭을 물려받는 대신 자기 내용대로 커지기 때문이다. 그래서 래퍼를
 * `block`으로 세워 폭의 출처를 한 방향(부모 → 자식)으로 만든다.
 *
 * ⚠️ **덮는 힘은 특이도에서 나온다.** vega-embed는 자기 기본 스타일을 `<style
 * id="vega-embed-style">` 태그로 **문서에 주입**하는데(`embed.js`의 `defaultStyle`
 * 분기), 그 선택자는 `.vega-embed` 하나(0,1,0)이고 우리 것은 `.psl-dash-chart
 * .vega-embed`(0,2,0)라 순서와 무관하게 우리가 이긴다. 래퍼에 붙는 인라인 스타일은
 * `cursor: default`뿐이라(실측) `!important`가 필요 없다.
 */
.psl-dash-chart .vega-embed {
  display: block;
  width: 100%;
}
/* 그려진 SVG도 상자를 넘지 않게 — 모델이 폭을 직접 적은 스펙은 `container`를
   무시하므로(그쪽이 이긴다) 넘칠 수 있다. 그때는 figure가 가로 스크롤한다. */
.psl-dash-chart .vega-embed svg {
  max-width: 100%;
}

/* ── 리즈닝 패널 ───────────────────────────────────────────────────────── */

/*
 * 진행 중 표시. 번들에는 `animate-pulse` 계열이 없어서 여기 둔다 (있는 이름을
 * 다시 정의하면 캐스케이드 막차라 랜딩이 조용히 바뀐다).
 *
 * 질의 1건이 수 분이므로 **정지한 화면은 멈춘 화면과 구분되지 않는다** —
 * 숨쉬는 아이콘과 뛰는 점이 "아직 일하는 중"의 유일한 신호다.
 *
 * ⛔ 예전에는 `psl-dash-spin`(360° 회전)이었다. 그런데 이 자리의 아이콘은 ✨
 * **반짝임**(AutoAwesome)이라 도는 물건이 아니다 — 로딩 스피너로 안 읽히고 그냥
 * 이상해 보였다(운영자 2026-08-11 R3: "이상하게 아이콘 돌아가고 <- 이거
 * 뭐하시는겨"). 반짝임에 맞는 움직임은 회전이 아니라 **명멸**이다.
 */
.psl-dash-twinkle {
  animation: psl-dash-twinkle 1.6s ease-in-out infinite;
}

@keyframes psl-dash-twinkle {
  0%,
  100% {
    opacity: 1;
    transform: scale(1);
  }
  50% {
    opacity: 0.5;
    transform: scale(0.86);
  }
}

/*
 * 도구 카드가 **교체되는** 순간(R2 — 진행 중에는 최근 하나만 선다).
 *
 * 목록에 줄이 추가되는 것과 카드가 통째로 갈리는 것은 다른 사건이다. 전부 SQL이라
 * 명령 텍스트로는 바뀐 줄 모르므로, 이 짧은 교체 동작과 회차 숫자가 "새 호출이
 * 들어왔다"를 알리는 전부다 — 이게 없으면 화면이 멈춘 것으로 보인다.
 */
.psl-dash-step-swap {
  animation: psl-dash-step-swap 260ms ease-out;
}

@keyframes psl-dash-step-swap {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* 진행 중인 단계의 점. 완료 단계는 체크 아이콘이라 이 표시를 쓰지 않는다. */
.psl-dash-step-pulse {
  width: 8px;
  height: 8px;
  background: #0e8a67;
  animation: psl-dash-step-pulse 1.4s ease-in-out infinite;
}

@keyframes psl-dash-step-pulse {
  0%,
  100% {
    opacity: 1;
    transform: scale(1);
  }
  50% {
    opacity: 0.45;
    transform: scale(0.72);
  }
}

/*
 * "생각 중" 라벨의 shimmer — 글자 위를 광택이 훑고 지나간다(ChatGPT·Gemini
 * 공통 시그니처, 운영자 2026-08-09 지시).
 *
 * ⚠️ 이 자리가 **기다림의 유일한 신호**다. 첫 진행 프레임은 수 초 뒤에 오는데
 * 그전까지 아무 움직임도 없으면 20초 침묵이 "멈춘 화면"과 구분되지 않는다.
 * 도는 아이콘은 이미 있지만 아이콘은 구석의 작은 점이고, 훑고 지나가는 글자는
 * 시선이 이미 가 있는 자리에서 움직인다.
 *
 * 구현: 글자를 그라디언트로 칠하고(`background-clip: text`) 그 그라디언트를
 * 좌→우로 민다. `-webkit-` 접두가 함께 필요하다 — Safari가 표준 이름을 아직
 * 안 받는다. 폭 200%는 sweep이 끝에서 끊기지 않게 하는 최소치다.
 */
.psl-dash-shimmer {
  background: linear-gradient(
    100deg,
    var(--fg-tertiary, rgba(0, 0, 0, 0.5)) 26%,
    var(--fg-primary, rgba(0, 0, 0, 0.9)) 44%,
    #0e8a67 50%,
    var(--fg-primary, rgba(0, 0, 0, 0.9)) 56%,
    var(--fg-tertiary, rgba(0, 0, 0, 0.5)) 74%
  );
  background-size: 200% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: psl-dash-shimmer 2s linear infinite;
}

@keyframes psl-dash-shimmer {
  from {
    background-position: 200% 0;
  }
  to {
    background-position: -200% 0;
  }
}

/*
 * 첫 이벤트 전의 자리지기 — 앱 `Shimmer`(0.3↔1로 호흡하는 회색 바)의 웹판.
 *
 * ⚠️ 여기서는 **호흡 대신 sweep**을 쓴다. 글자 shimmer가 바로 아래·위에서 훑고
 * 지나가는데 이 바만 통째로 깜빡이면 두 리듬이 싸워 화면이 산만해진다 — 같은
 * 방향으로 같이 흐르는 편이 한 덩어리로 읽힌다.
 *
 * 글자와 달리 `background-clip`을 쓰지 않는다(칠할 글자가 없다) — 그래서 reduce
 * 블록에서 애니메이션만 꺼도 회색 바가 그대로 남는다.
 */
.psl-dash-skeleton {
  background: linear-gradient(
    100deg,
    rgba(0, 0, 0, 0.06) 26%,
    rgba(14, 138, 103, 0.18) 50%,
    rgba(0, 0, 0, 0.06) 74%
  );
  background-size: 200% 100%;
  animation: psl-dash-shimmer 2s linear infinite;
}

/* 두 줄이 같은 박자로 뛰면 로딩이 아니라 고장 난 UI로 보인다 — 반 박자 늦춘다. */
.psl-dash-skeleton-late {
  animation-delay: 300ms;
}

/*
 * 단계가 하나씩 나타난다. 목록에 줄이 순간이동으로 추가되면 "몇 개가 됐는지"만
 * 보이고 "방금 하나 더 됐다"가 안 보인다 — 진행감은 그 하나에서 나온다.
 */
.psl-dash-step-in {
  animation: psl-dash-step-in 300ms ease-out;
}

@keyframes psl-dash-step-in {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* `<details>` 기본 삼각형 제거 — 우리 아이콘이 그 역할을 한다. */
.psl-dash-shell summary::-webkit-details-marker {
  display: none;
}

/* 접힌 요약도 눌리는 물건이다 — hover 없이는 펼칠 수 있는지 알 수 없다. */
.psl-dash-reasoning {
  transition: background-color 150ms ease-out;
}
.psl-dash-reasoning:hover {
  background: rgba(0, 0, 0, 0.045);
}

/*
 * 움직임을 줄이라고 설정한 사용자에게는 돌리지 않는다. 상태는 색과 아이콘이
 * 이미 구분하므로 애니메이션이 없어도 읽힌다.
 *
 * ⚠️ shimmer는 애니메이션만 끄면 **글자가 사라진다**(`color: transparent` +
 * 그라디언트 클립이 남으므로 정지한 그라디언트 한 지점만 칠해진다). 여기서는
 * 클립 자체를 풀어 평범한 회색 글자로 되돌린다.
 */
@media (prefers-reduced-motion: reduce) {
  .psl-dash-twinkle,
  .psl-dash-step-pulse,
  .psl-dash-step-in,
  .psl-dash-step-swap,
  .psl-dash-greeting,
  .psl-dash-hero-composer,
  .psl-dash-answer-in,
  .psl-dash-drawer-scrim,
  .psl-dash-menu {
    animation: none;
  }

  /*
   * 드로어는 **여닫히는 것 자체는 남기고** 미끄러지는 동작만 없앤다. 여기서
   * transform까지 지우면 닫힌 드로어가 제자리(화면 안)에 서서 본문을 덮는다 —
   * 모션을 줄이려다 화면을 못 쓰게 만드는 셈이다.
   */
  .psl-dash-sidebar {
    transition: none;
  }

  /*
   * hover 배경은 남기고 눌림 축소만 없앤다 — 배경은 "누를 수 있다"는 정보이고
   * 축소는 장식이다. 정보까지 지우면 제안 줄이 그냥 글자로 보인다.
   */
  .psl-dash-suggestion,
  .psl-dash-icon-btn {
    transition: none;
  }
  .psl-dash-suggestion:active,
  .psl-dash-icon-btn:active {
    transform: none;
  }

  .psl-dash-shimmer {
    animation: none;
    background: none;
    -webkit-background-clip: border-box;
    background-clip: border-box;
    color: var(--fg-tertiary, rgba(0, 0, 0, 0.5));
  }

  /*
   * ⚠️ 스켈레톤은 애니메이션만 끈다 — 글자 shimmer와 달리 배경을 지우면 자리를
   * 잡으라고 세운 바가 통째로 투명해져, 첫 이벤트 전 침묵이 다시 빈 화면이 된다.
   * 흐르지 않는 회색 띠로 남는 것이 맞다.
   */
  .psl-dash-skeleton {
    animation: none;
    background: rgba(0, 0, 0, 0.06);
  }

  .psl-dash-icon-btn:active,
  .psl-dash-send:not(:disabled):hover,
  .psl-dash-send:not(:disabled):active {
    transform: none;
  }
}
