/* ============================================================================
 * Tech-GPT · /view/chat 디자인 CSS
 * ----------------------------------------------------------------------------
 * 원본: techgpt_ver2/static/css/index.css (1111줄, 전 페이지 공용)
 * 이 파일은 그중 chat 페이지가 실제로 사용하는 규칙만 추려 옮긴 것이다.
 * 레이아웃/여백/색 등 대부분의 디자인은 마크업의 Tailwind 유틸리티가 담당하고,
 * 여기에는 Tailwind로 표현할 수 없는 것(폰트, 그라데이션 테두리, 스크롤바,
 * 마크다운 타이포, 애니메이션)만 둔다.
 *
 * 제외한 원본 규칙: gwangyang 폰트, .btn-11, .btn-grad*, .background(moveBg),
 *   .imgBox, .two-lines, .bubble, .blink-text, .main-response, .custom-table,
 *   .custom-tbody, .newsScroll*, .tools_group(우측 패널로 대체됨),
 *   .contract_pane, .contract-card-body  — chat 페이지에서 참조하지 않음.
 * ========================================================================== */

/* ── 폰트 ─────────────────────────────────────────────────────────────── */
/* @font-face 는 css/glassmorphism.css 로 옮겼다 — 모든 페이지가 그 파일을
 * 로드하므로 거기 있어야 index.html 에도 폰트가 적용된다. */

html,
body,
input,
select,
textarea,
a,
button {
    font-family: "Pretendard Variable", Pretendard, sans-serif !important;
    color: #232323;
}

/* ── 페이지 스크롤 잠금 ───────────────────────────────────────────────────
 * 이 화면은 고정 앱 셸이다. body 는 h-[100svh] · overflow-hidden 이고 자식이
 * 전부 absolute/fixed 라, 세로로 흐르는 내용이 애초에 하나도 없다. 스크롤은
 * 안쪽 패널(#chat_scroll · #aside_scroll · .ctr-sheet …)이 각자 갖는다.
 *
 * 그런데도 기본 화면이 세로로 스크롤됐다. 범인은 여기 있던 overflow-x 한 줄이다.
 *
 *   ① 뷰포트의 스크롤 동작은 html 의 overflow 값을 따른다. 단, html 이
 *      visible 이면 예외적으로 body 의 값을 대신 가져간다(전파 규칙).
 *   ② 한 축이 visible 이 아니면 나머지 축의 visible 은 auto 로 계산된다.
 *      → overflow-x: hidden 만 줬는데 html 은 (hidden, auto) 가 된다.
 *   ③ html 이 더 이상 visible 이 아니므로 ①의 예외가 깨진다. 전파 주체가
 *      body 에서 html 로 바뀌고, 뷰포트는 overflow-y: auto — 즉 스크롤 가능한
 *      상태가 된다. body 의 overflow-hidden 은 body 자기 박스만 자를 뿐
 *      페이지 스크롤은 막지 못한다.
 *
 * 스크롤 컨테이너가 열려 있으니, 100svh 와 ICB 의 1px 반올림 차이나 모바일
 * 주소창이 접히는 순간의 높이 변화 같은 미세한 초과가 그대로 스크롤로 드러난다.
 * → 두 축을 함께 잠가 애매한 계산 자체를 없앤다.
 *
 * overscroll-behavior 는 다른 문제다. 넘치는 내용이 없어도 모바일은 고무줄
 * 오버스크롤로 페이지가 튕기는데, none 이면 그 반동이 사라진다.
 * ------------------------------------------------------------------------ */
html,
body {
    overflow: hidden;
    overscroll-behavior: none;
}

/* ── 페이지 배경 ─────────────────────────────────────────────────────────
 * 페이지 전체가 이미지 한 장(images/img_bg_mesh_a.png)으로 통일된다.
 * 빈 화면이든 대화 중이든 같은 배경 — 상태에 따라 배경이 바뀌지 않는다.
 *
 * 후보가 세 장 있다 (white·cyan·sky·blue 메시 그라데이션).
 *   · img_bg_mesh_a.png — 오로라. 우상단 흰빛 → 좌하단 시안 → 하단 블루 (현재)
 *   · img_bg_mesh_b.png — 소프트. 가운데가 희고 네 귀퉁이에만 색
 *   · img_bg_mesh_c.png — 딥. 시안·블루 대비가 가장 강함
 * 바꾸려면 아래 url() 과 html/body 의 background-color 를 같이 갈아끼운다.
 * (스크림 .50 기준 평균 톤: a #D7EDFC / b #E4F2FC / c #D3E7F9)
 * bg_preview.html 을 열면 세 장을 스크림·유리카드까지 얹어 비교할 수 있다.
 *
 * ① 이미지는 cover 로 깔고 가운데 정렬. 원본이 4:3.2 라 세로로 긴 화면에서는
 *    좌우가, 와이드 화면에서는 위아래가 잘린다. 어느 쪽으로 잘려도 색의 중심이
 *    화면 안에 남도록 center 로 둔다.
 * ② 원본은 채도가 높아서 그 위에 글자(답변 본문은 카드가 아니라 배경 위에
 *    바로 놓인다)를 얹으면 읽기 힘들다. 흰 스크림을 한 겹 덮어 톤을 낮춘다.
 *    --bg-scrim 하나만 조절하면 "이미지가 선명한 정도"가 바뀐다.
 *    · .40 이하 → 이미지는 살지만 진한 파랑 위 본문 대비가 4:1 밑으로 떨어진다
 *    · .65 이상 → 거의 흰 배경이 되어 유리(glass)가 비칠 색을 잃는다
 * ------------------------------------------------------------------------ */
:root {
    --bg-scrim: .50;
}

#app_bg {
    background-image: url("../images/img_bg_mesh_a.png");
    background-position: center center;
    background-repeat: no-repeat;
    background-size: cover;
}

#app_bg::after {
    content: '';
    position: absolute;
    inset: 0;
    background-color: rgb(255 255 255 / var(--bg-scrim));
}

/* ── 안전영역(노치·상태바) ────────────────────────────────────────────────
 * 배경 이미지는 body 안의 absolute div 라, body 바깥 영역은 아무도 안 칠한다.
 * 그 자리가 캔버스 기본색(흰색)으로 드러나는 게 화면 최상단의 흰 띠였다.
 *
 * ① 루트에 베이스 색을 깔아 어떤 경로로도 흰색이 새지 않게 한다.
 *    (안전영역, 오버스크롤 바운스, 주소창 접힘 순간 모두 이 색으로 채워진다)
 *    값은 스크림을 씌운 배경 이미지의 평균 톤 — 다른 색을 쓰면 가장자리에
 *    색 단차가 생긴다. --bg-scrim 을 바꿨다면 이 값도 같이 손봐야 한다.
 * ② viewport-fit=cover(chat.html)로 페이지가 안전영역까지 덮게 되므로,
 *    헤더 내용이 상태바/노치 밑으로 들어가지 않도록 그만큼 위를 밀어준다.
 *    높이도 같이 키워야 아래 대화 영역이 위로 딸려 올라가지 않는다.
 *    (h-14 = 3.5rem. 유틸리티보다 ID 선택자가 이기므로 !important 불필요)
 * ------------------------------------------------------------------------ */
html,
body {
    background-color: #D7EDFC;
}

@supports (padding: env(safe-area-inset-top)) {
    #header {
        height: calc(3.5rem + env(safe-area-inset-top));
        padding-top: env(safe-area-inset-top);
        /* 가로모드에서 노치가 옆으로 오면 내용이 그 밑으로 들어간다 (px-4 = 1rem) */
        padding-left: calc(1rem + env(safe-area-inset-left));
        padding-right: calc(1rem + env(safe-area-inset-right));
    }
}

img {
    display: inline-block;
    border: 0 none;
    vertical-align: top;
}

/* ── 리셋 ─────────────────────────────────────────────────────────────── */
*, *:focus {
    appearance: none;   /* 모든 요소의 기본 스타일 제거 */
    outline: none;      /* 포커스 시 아웃라인 제거 */
}

input, button, select, textarea {
    appearance: none;   /* 폼 요소의 브라우저 기본 스타일 제거 */
}

*:focus {
    outline: none;
}

/* 다크모드 무시 — 페이지는 항상 라이트.
   순백(#fff)이 아니라 배경 이미지 베이스 톤으로 맞춰야 가장자리가 어긋나지 않는다. */
@media (prefers-color-scheme: dark) {
    html,
    body {
        background: #DCE9FA !important;
        background-color: #DCE9FA !important;
    }
}

/* ── 사이드바 그림자가 aside 밖으로 나가게 ────────────────────────────────
 * 증상: 묶음들의 그림자가 사이드바 경계에서 칼로 자른 듯 끊긴다.
 *
 * 원인은 두 가지가 겹친 것이다.
 *
 * ① 클리핑 — #aside 에 overflow-hidden 이 걸려 있다. overflow 는 자식의
 *    "그리기"를 자르므로 box-shadow 도 함께 잘린다. 사이드바 폭 안쪽까지만
 *    그려지고 그 밖은 사라진다.
 * ② 페인트 순서 — 잘림을 풀어도 그것만으로는 부족하다. xl+ 에서 aside 는
 *    static 이고 옆의 <main> 은 relative(=위치 지정 요소)다. 같은 스택
 *    컨텍스트에서 위치 지정 요소가 항상 나중에 그려지므로, 밖으로 나온
 *    그림자가 main 밑에 깔려 다시 안 보인다. aside 를 relative + z-index 로
 *    올려야 본문 위에 얹힌다.
 *
 * ※ 여기서 풀 수 없는 것 — #aside_scroll(overflow-y-auto)과 세션 묶음
 *   (.glass-group 의 overflow-hidden) 안쪽 요소들. 스크롤 상자는 잘라야
 *   스크롤이 성립하므로, 그 안의 행은 어떤 방법으로도 밖으로 못 나간다.
 *   지금은 그 행들에 그림자가 없어서 문제되지 않는다.
 *
 * ※ 접기 애니메이션(288px→64px)에서 아주 짧게 내용이 비어져 나올 수 있다.
 *   펼침 전용 요소가 group-[.is-collapsed]:xl:hidden 으로 즉시 사라지므로
 *   실제로는 거의 눈에 띄지 않는다. 신경 쓰이면 이 블록을 지우면 된다.
 *
 * ID 선택자(1,0,0)라 Tailwind 의 overflow-hidden(클래스)을 이긴다.
 * ------------------------------------------------------------------------ */
@media (min-width: 1280px) {
    #aside {
        overflow: visible;
        position: relative;   /* static 이면 z-index 가 아예 안 먹는다 */
        z-index: 1;
    }
}

/* ── 스크롤바 ─────────────────────────────────────────────────────────── */
/* 전역 스크롤바는 css/glassmorphism.css 가 담당한다(투명 트랙 + 반투명 글래스 썸).
   여기서는 ID 특정도(1,0,0) 때문에 전역 규칙이 밀리는 사이드바만 따로 맞춘다. */

/* 사이드바 세션 목록 — 더 얇은 스크롤바 */
#aside_scroll::-webkit-scrollbar {
    width: 5px;
    height: 5px;
}

#aside_scroll::-webkit-scrollbar-thumb {
    background-color: rgba(120, 150, 220, .40);
    border-radius: 5px;
    background-clip: padding-box;
    cursor: pointer;
    border: 0;
}

#aside_scroll::-webkit-scrollbar-thumb:hover {
    background-color: rgba(28, 111, 214, .60);
}

#aside_scroll::-webkit-scrollbar-track {
    background-color: transparent;
    border-radius: 5px;
}

@media (max-width: 768px) {
    /* 모바일 디바이스에서 스크롤바 숨기기 */
    html, body {
        -ms-overflow-style: none;   /* IE and Edge */
        scrollbar-width: none;      /* Firefox */
    }

    .hide-scrollbar::-webkit-scrollbar {
        display: none;              /* Chrome, Safari, Opera */
    }
}

.no-scrollbar::-webkit-scrollbar {
    display: none;
}

/* ── 유틸 ─────────────────────────────────────────────────────────────── */
.aspect-square { aspect-ratio: 1 / 1; }

.card-shadow {
    box-shadow: 0 6px 18px -10px rgba(70, 95, 170, .20);
}

/* 표 셀의 줄바꿈 규칙 — 칸 폭은 내용이 정한다.
   word-break:break-word 는 "아무 글자 사이에서나 끊어도 된다"는 뜻이라 셀의 최소 폭이
   글자 한 개로 내려간다. 표 auto 레이아웃은 그 최소 폭까지 칸을 눌러도 된다고 보고
   실제로 누르기 때문에, 좁은 칸에서 "10" 같은 두 자리 숫자가 "1 / 0" 으로 쪼개졌다
   ("11" 은 1이 좁아 우연히 살아남는다 — 그래서 몇 줄만 깨진 것처럼 보인다).
   - word-break: normal      한글은 지금처럼 글자 단위로 접히고, 숫자·코드는 안 쪼개진다.
   - overflow-wrap: break-word  칸보다 긴 단어(URL 등) 하나가 표를 밀어낼 때만 그 단어를 끊는다.
     anywhere 와 달리 최소 폭 계산에는 끼어들지 않아 위 문제를 되살리지 않는다.
   칸을 다 벌려도 표가 넘치면 표를 담은 상자가 가로로 스크롤한다
   (.ptable-wrap 의 overflow:auto, .md table 의 overflow-x:auto). */
table td, table th {
    word-break: normal;
    overflow-wrap: break-word;
    white-space: normal;
}

/* ── 진행 상황 패널 ───────────────────────────────────────────────────────
 * .glass-panel(흰 58% + blur)로 두면 대화 영역과 구분이 안 된다. 유리는
 * "뒤가 비쳐야" 성립하는데, 이 패널 뒤에 있는 건 배경이 아니라 읽고 있던
 * 대화라서 글자 위에 글자가 겹쳐 보인다. 게다가 xl+ 인라인 컬럼일 때는
 * xl:shadow-none 이라 그림자마저 없어 경계가 완전히 사라졌다.
 *
 * → 이 패널만 유리에서 빼고 불투명한 흰 판으로 만든다. 페이지에서 유일하게
 *   불투명한 면이라 그 자체로 "다른 층위"로 읽힌다.
 * ------------------------------------------------------------------------ */
.progress-panel {
    /* 흰 판 대신 위에서 아래로 옅어지는 흰 그라데이션 — 컴포저와 같은 방식.
       불투명한 #FFFFFF 는 페이지에서 유일하게 안 비치는 면이라, 옆의 유리들과
       나란히 놓이면 혼자 무거운 판으로 보였다. */
    background-color: transparent;
    background-image: linear-gradient(180deg,
        rgba(255, 255, 255, .30) 0%,
        rgba(255, 255, 255, .12) 100%);
    border-left-color: rgba(255, 255, 255, .55);
    /* 오른쪽에 붙어 있으므로 그림자는 왼쪽으로만 나가야 한다.
       가까운 선(경계) + 중간(들림) + 넓은 확산(깊이) 3단. */
    box-shadow:
        -1px 0 0 rgba(70, 95, 170, .07),
        -10px 0 26px -14px rgba(70, 95, 170, .32),
        -30px 0 64px -32px rgba(70, 95, 170, .26);
    /* saturate 는 1 — 증폭도 탈색도 하지 않는다. 여기서 보이는 파랑은 유리가
       만든 색이 아니라 뒤 배경 그 자체다. blur 는 컴포저(6px)보다 조금 높다 —
       이 패널은 표·코드·차트가 들어차서 뒤 무늬가 그대로 비치면 읽기 힘들다. */
    -webkit-backdrop-filter: blur(18px) saturate(100%);
            backdrop-filter: blur(18px) saturate(100%);
}

/* ── 진행 상황 패널의 내용물 ───────────────────────────────────────────────
 * 패널이 불투명한 흰 판이 되면서, 안쪽 요소들이 쓰던 유리 클래스가 전부
 * 무력화됐다 — bg-white/70 은 흰 배경 위에서 흰색이고, border-white/70 은
 * 아예 안 보인다. 그래서 카드도 표도 윤곽이 없었다.
 *
 * 여기서는 유리를 흉내내지 않는다. 담는 내용이 다르면 표면도 달라야 한다:
 *   .pcard      도구 하나 = 컨테이너.   옅은 블루틴트 면 + 상태색 좌측 띠
 *   .pfield     입력/결과 = 코드.       흰 면 + 안쪽으로 눌린 느낌
 *   .ptable     표 = 데이터 격자.       격자선 대신 헤더 틴트 + 행 구분선
 *   .pchart     차트 = 캔버스.          흰 면 + 얇은 테두리
 *
 * 색은 회색이 아니라 전부 블루틴트다(#E4EAF6 등) — 페이지의 페일 블루 톤에서
 * 순회색 선은 혼자 탁하게 뜬다.
 * ------------------------------------------------------------------------ */

/* ① 도구 카드 — 상태(data-status)가 색을 결정한다.
      상태는 머리 띠(summary)가 깐 옅은 면 하나로 말한다. 카드에 면·모서리·그림자는
      두지 않는다 — 이 카드는 이미 흰 판 위에 있고, 여기에 또 테두리와 라운드를 얹으면
      안쪽 결과 블록(.pblock)과 상자가 두 겹으로 겹친다.
      카드가 여러 장 쌓여도 띠만 세로로 훑으면 상태가 읽힌다. */
.pcard {
    position: relative;
    /* clip 이지 hidden 이 아니다 — hidden 은 그 자체로 스크롤 컨테이너를 만들어,
       안쪽 .plabel 의 sticky 가 #progress_list 가 아니라 이 카드에 붙어 버린다.
       clip 은 라운드 클립만 하고 스크롤 컨테이너가 되지 않는다.
       두 줄인 이유: clip 을 모르는 브라우저는 그 선언을 통째로 버린다 →
       hidden 조차 없으면 라운드 밖으로 표가 삐져나온다(sticky 만 포기하고 클립은 남긴다). */
    overflow: hidden;
    overflow: clip;
    background-color: #FFFFFF;
    /* 여백은 카드가 아니라 안쪽(summary·presult)이 준다 — 머리 띠가 폭을 꽉 채워야 한다 */
    padding: 0;
}

/* 머리 띠의 글자 — 면과 같은 계열로, 도구 이름은 진하게 / 소요시간·셰브론은 한 단 옅게.
   (.pname 은 유틸리티 클래스로 색이 박혀 있어 특이도를 한 단 올려 덮는다) */
.pcard[data-status="running"] > summary .pname { color: #1B5FBE; }
.pcard[data-status="done"]    > summary .pname { color: #047857; }
.pcard[data-status="error"]   > summary .pname { color: #B91C1C; }
.pstate { font-size: 11px; flex-shrink: 0; }
.pcard[data-status="running"] .pstate { color: #7CA9E6; }
.pcard[data-status="done"]    .pstate { color: #67BDA5; }
.pcard[data-status="error"]   .pstate { color: #E39191; }
.pcard[data-status="running"] > summary .pcard__chev { color: #9CBCE9; }
.pcard[data-status="done"]    > summary .pcard__chev { color: #93CDB8; }
.pcard[data-status="error"]   > summary .pcard__chev { color: #E8AAAA; }

/* 완료 카드(지난 답변)의 헤더 구분선 */
.pcard-head { border-bottom: 1px solid #E4EAF6; padding-bottom: .5rem; }

/* 상태 배지 */
.pbadge {
    font-size: 11px;
    font-weight: 600;
    padding: .125rem .5rem;
    border-radius: 9999px;
    white-space: nowrap;
}
.pcard[data-status="done"]  .pbadge { color: #047857; background-color: #D1FAE5; }
.pcard[data-status="error"] .pbadge { color: #B91C1C; background-color: #FEE2E2; }

/* ② 입력/결과 — 코드다. 카드 면보다 밝은 흰 면에 안쪽으로 눌린 느낌을 준다.
      (카드가 F8FAFD 라 흰색이 오히려 "파인 곳"으로 읽힌다) */
.pfield {
    margin-top: .5rem;
    padding: .5rem .625rem;
    max-height: 20rem;
    overflow: auto;
    border: 1px solid #E4EAF6;
    border-radius: .625rem;
    background-color: #FFFFFF;
    box-shadow: inset 0 1px 2px rgba(70, 95, 170, .05);
    font-size: 11px;
    line-height: 1.65;
    color: var(--body-700);
    white-space: pre-wrap;
    word-break: break-word;
}

/* ②-2 결과 블록(.pblock) — 도구 카드를 걷어낸 뒤 패널에 남은 유일한 카드.
   제목 줄은 옅은 틴트 면, 본문은 흰 면. 두 면의 밝기 차만으로 "머리와 몸"이
   갈리므로 그 사이에 선을 하나만 긋는다(펼쳤을 때만). */
.pblock {
    border: 1px solid #E2E8F0;
    border-radius: .875rem;
    background-color: #FFFFFF;
    box-shadow: 0 1px 2px rgba(70, 95, 170, .04);
    overflow: hidden;   /* 헤더 틴트가 라운드 밖으로 새지 않게 */
    overflow: clip;     /* clip 인 이유와 두 줄인 이유는 .pcard 와 같다 */
    transition: border-color .2s var(--ease-glass), box-shadow .2s var(--ease-glass);
    /* 결과는 한 덩어리씩 도착한다 — 떠오르며 나타나야 '새로 왔음'이 보인다 */
    animation: pblock_in .34s var(--ease-glass) both;
}
.pblock:hover {
    box-shadow: 0 1px 2px rgba(70, 95, 170, .05), 0 10px 24px -14px rgba(70, 95, 170, .35);
}

/* 결과 블록이 놓이는 자리 — 붙어 있으면 라운드 모서리가 맞물려 한 장처럼 보이고,
   hover 때 떠오르는 그림자가 옆 블록을 덮는다.
   간격은 블록이 아니라 담는 쪽이 준다 — .pblock 에 margin 을 주면 이 블록을 함께 쓰는
   계약서 결과 패널의 gap 과 겹쳐 간격이 두 배가 된다. */
/* 아코디언 카드 — 진행 상황 모달에서는 .pcard 가 <details> 다.
   접힘/펼침을 카드 단위로 두면 도구가 여러 개인 턴도 목록으로 먼저 훑고
   필요한 것만 열어볼 수 있다. 표식은 .pblock 과 같은 규칙(오른쪽 끝 셰브론).
   머리는 카드 폭을 꽉 채우는 상태색 띠다 — 점·이름·소요시간이 한 색 계열로 묶여
   띠 하나만 보고도 '무슨 도구가 어떤 상태인지'가 읽힌다. */
details.pcard > summary {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .4375rem .75rem;
    cursor: pointer;
    user-select: none;
    list-style: none;
    background-color: #F6F9FF;
    border-bottom: 1px solid #E8EEFA;
    transition: background-color .2s var(--ease-glass);
}
.pcard[data-status="running"] > summary { background-color: #F5F9FF; border-bottom-color: #DDE9FE; }
.pcard[data-status="done"]    > summary { background-color: #F4FEF9; border-bottom-color: #DEF6EA; }
.pcard[data-status="error"]   > summary { background-color: #FEF6F6; border-bottom-color: #FBE3E3; }
/* 접혀 있으면 띠 하나가 카드 전부다 — 밑줄이 남으면 아래에 몸이 있는 것처럼 보인다 */
details.pcard:not([open]) > summary { border-bottom-color: transparent; }
details.pcard > summary::-webkit-details-marker { display: none; }
details.pcard > summary::marker { content: ''; }
.pcard__chev {
    flex-shrink: 0;
    display: inline-flex;
    color: #94A3B8;
    transition: transform .25s var(--ease-glass);
}
details.pcard[open] > summary .pcard__chev { transform: rotate(180deg); }

/* 턴 아코디언 — 진행 상황 패널이 세션 전체를 담을 때의 바깥 껍데기.
   머리 한 줄이 대화 하나다: 번호 · 질문 · 도구 수. 질문은 한 줄로 잘라 둔다 —
   여기서 읽을 것은 '어떤 질문의 진행 사항인가'이지 질문 전문이 아니다.
   카드(.pcard)보다 한 급 위라, 면을 주지 않고 선과 번호만으로 층을 만든다. */
.pturn { border: 1px solid #E4EAF6; border-radius: .75rem; background-color: #FFFFFF; }
.pturn__sum {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .5rem .625rem;
    cursor: pointer;
    user-select: none;
    list-style: none;
}
.pturn__sum::-webkit-details-marker { display: none; }
.pturn__sum::marker { content: ''; }
.pturn__no {
    flex-shrink: 0;
    min-width: 1.25rem;
    height: 1.25rem;
    padding: 0 .25rem;
    border-radius: .375rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 10px;
    font-weight: 700;
    color: #FFFFFF;
    background-image: var(--grad-official);
}
.pturn__q {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 13px;
    font-weight: 600;
    color: var(--ink-900);
}
.pturn__chev { flex-shrink: 0; display: inline-flex; color: #94A3B8; transition: transform .25s var(--ease-glass); }
.pturn[open] > .pturn__sum .pturn__chev { transform: rotate(180deg); }
.pturn[open] > .pturn__sum { border-bottom: 1px solid #EDF1F9; }
/* 턴 안의 도구 카드 — 턴 껍데기가 이미 테두리를 두르므로 안쪽 여백만 준다 */
.pturn__body { display: flex; flex-direction: column; gap: .5rem; padding: .5rem; }

.presult {
    display: flex;
    flex-direction: column;
    gap: .625rem;
}
/* 카드 몸 — 머리 띠가 카드 폭을 꽉 채우도록 .pcard 의 padding 을 0 으로 두었으니
   안쪽 여백은 몸이 직접 준다. 비어 있을 때(실행 중)는 여백도 주지 않는다 —
   띠 한 줄만 남아야 '아직 결과가 없다'가 한눈에 읽힌다. */
.presult:not(:empty) { padding: .75rem; }

@keyframes pblock_in {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}

/* 제목 줄 — 이제 패널에 남은 유일한 제목이다. 10px 대문자 이름표로는 표를
   가리키는 힘이 모자라, 카드 헤더가 쓰던 급(13px semibold)으로 올렸다. */
.plabel {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .625rem .75rem;
    cursor: pointer;
    user-select: none;
    font-size: 13px;
    font-weight: 600;
    color: var(--ink-900);
    background-image: linear-gradient(180deg, #FBFCFF 0%, #F4F8FE 100%);
    border-bottom: 1px solid transparent;   /* 펼침 상태에서만 색이 든다 */
    transition: background-color .18s;
}
.plabel::-webkit-details-marker { display: none; }   /* Safari 기본 삼각형 */
.plabel::marker { content: ''; }
.pblock[open] .plabel { border-bottom-color: #EDF1F9; }
.plabel:hover { background-image: linear-gradient(180deg, #F7FAFF 0%, #EDF3FD 100%); }

/* 펼친 블록의 제목 줄은 패널 위쪽에 붙여 둔다 — 표가 수백 행이면 접으려고
   맨 위까지 되짚어 올라가야 했다. 중간에서 그대로 눌러 접을 수 있어야 한다.
   (sticky 범위는 담는 .pblock 안이라, 블록이 지나가면 알아서 같이 떠난다.)
   면은 불투명해야 한다 — 밑을 지나가는 표가 비치면 제목과 숫자가 겹쳐 읽힌다.
   top 이 0 이면 스크롤 컨테이너의 padding 만큼(12px) 위에 표가 지나가는 틈이 생긴다 —
   그만큼 위로 당겨 패널 머리에 바로 붙인다(--pstick-top 은 컨테이너가 자기 padding 으로 준다). */
.progress-cards { --pstick-top: 0px; }
#progress_list { --pstick-top: -.75rem; }        /* p-3 */
#progress_modal_list { --pstick-top: -1rem; }    /* pt-4 */
.progress-cards .pblock[open] > .plabel {
    position: sticky;
    top: var(--pstick-top, 0px);
    z-index: 2;
    background-color: rgba(255, 255, 255, .92);
    -webkit-backdrop-filter: blur(12px) saturate(180%);
            backdrop-filter: blur(12px) saturate(180%);
    box-shadow: 0 6px 12px -10px rgba(70, 95, 170, .6);
}

/* 종류 표식 — 표/차트를 아이콘 하나로 구분한다. 색은 정식 도구의
   하늘→파랑(--grad-official) 그대로. 패널에서 유일한 채도 있는 면이라
   제목 줄 전체가 이 24px 사각형에 걸려 읽힌다. */
.plabel__icon {
    flex-shrink: 0;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: .5rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 10px;
    color: #FFFFFF;
    background-image: var(--grad-official);
    box-shadow: 0 2px 6px -2px rgba(70, 110, 215, .5);
}
.plabel__text {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.plabel__chev {
    flex-shrink: 0;
    display: inline-flex;
    color: #94A3B8;
    transition: transform .25s var(--ease-glass);
}
.pblock[open] .plabel__chev { transform: rotate(180deg); }

.pblock__body { padding: .75rem; }
/* 표·글은 블록 폭을 그대로 쓴다 — 담기는 것이 '면'이라(격자 한 장, 글 한 덩어리)
   바깥 여백을 한 겹 더 두면 블록 머리와 내용이 다른 상자처럼 갈라져 보인다.
   카드·차트는 각자 테두리를 두른 조각들이라 여백 안에 앉는 쪽이 맞다. */
.pblock[data-view-type="table"] > .pblock__body,
.pblock[data-view-type="text"] > .pblock__body { padding: 0; }
/* 카드 안에서는 표/차트가 이미 카드 테두리를 두르고 있다 — 이중 테두리를 푼다 */
.pblock__body > .ptable-wrap,
.pblock__body > .pchart { margin-top: 0; }
.pblock__body > .ptable-wrap { border-top: 1px solid #E5E7EB; }

/* 결과를 기다리는 동안의 빈 패널 — 아무 말도 없으면 고장으로 읽힌다 */
#progress_list:empty::before {
    content: '도구가 실행되면 결과가 여기에 쌓입니다';
    display: block;
    padding: 2.5rem 1rem;
    text-align: center;
    font-size: 12px;
    color: #94A3B8;
}

/* ③ 표 — 격자·줄무늬를 걷어낸 '흰 카드 위의 목록'.
      담기는 값이 문장(제목·저자·출처)이라 칸마다 색을 깔면 글이 안 읽힌다.
      대신 층을 셋으로 나눈다: 첫 칸(무엇) · 가운데(누가·어디서) · 끝(바로 가기).
      세로선은 긋지 않는다 — 가로 헤어라인 하나로 행이 갈린다. */
/* 컨테이너 선언 — 안쪽 셀 상한(cqw)과 아래 @container 가 이 폭을 기준으로 잰다.
   @media 로는 안 된다: 패널 폭은 뷰포트가 아니라 리사이저 드래그로도 바뀌어서,
   넓은 화면에서 패널만 좁게 끈 경우를 뷰포트 쿼리는 못 잡는다.
   overflow:auto 는 그대로 둔다 — 열이 더는 못 줄어드는 극단에서의 마지막 안전망이다. */
.ptable-wrap {
    container-type: inline-size;
    margin-top: .5rem;
    overflow: auto;
    border: 1px solid #E6EAF0;
    background-color: #FFF;
}
.ptable {
    width: 100%;
    border-collapse: collapse;
    font-size: 11px;
    /* 자릿수 고정폭 — 번호·연도·금액 칸에서 숫자가 세로로 줄맞춤되고,
       "10" 과 "11" 의 폭이 달라 한쪽만 접히는 일도 생기지 않는다. */
    font-variant-numeric: tabular-nums;
}
/* 머리줄 — 색을 빼고 크기로만 낮춘다. 값 쪽이 이미 색을 쓰는 표라(연도 배지·링크)
   헤더까지 물들이면 어디를 먼저 볼지가 사라진다. */
.ptable th {
    position: sticky;          /* 세로로 긴 표에서 헤더 고정 */
    top: 0;
    z-index: 1;
    /* 스티키라 스크롤하면 본문 위를 지나간다 → 여기만 알파를 주지 않는다.
       반투명하면 밑을 지나가는 글자가 헤더 글자와 겹쳐 읽힌다. */
    background-color: #F8FAFC;
    color: #5C697B;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .02em;
    text-align: left;
    /* 칸 이름은 한 줄로만 선다. 접히면 "국제특허분" / "류" 처럼 낱말이 잘려
       머리줄이 두 줄이 되고, 이름이 무엇인지도 한눈에 안 읽힌다.
       대가로 헤더 글자가 그 열의 최소 폭 바닥이 된다 — 열 이름 다섯이 패널보다
       넓어지는 극단에서는 .ptable-wrap 의 overflow:auto 가 가로로 밀어 받는다. */
    white-space: nowrap;
    padding: .625rem .75rem;
    border-bottom: 1px solid #E6EAF0;
}
.ptable td {
    padding: .75rem;
    color: var(--body-700);
    /* 행 높이는 제목 2줄이 정한다 — 나머지 값은 첫 줄에 맞춰 위로 붙인다.
       가운데 정렬하면 짧은 값이 제목 두 줄 사이에 떠서 어느 행인지 흐려진다. */
    vertical-align: top;
    border-top: 1px solid #F0F3F7;
}
.ptable tbody tr:first-child td { border-top: 0; }
/* 가로로 긴 표에서 눈이 행을 놓치지 않게 */
.ptable tbody tr { transition: background-color .12s; }
.ptable tbody tr:hover td { background-color: #F6FAFD; }
/* 값이 URL인 행 — 셀 안 링크 아이콘을 겨냥하지 않아도 행 아무 데나 눌러 열린다 */
.ptable tbody tr[data-url] { cursor: pointer; }

/* 셀 내용 그릇 — 줄 수와 폭을 여기서 잡는다.
   ① td 에 직접 -webkit-box 를 주면 셀이 블록이 되어 열 정렬이 무너진다.
   ② 표는 table-layout:auto 라 td 의 max-width 를 무시하지만 안쪽 인라인 박스의
      상한은 지킨다 — 그래서 폭 제한도 여기 건다. 제목이 길다고 열이 벌어지지 않는다.
   ③ overflow-wrap 이 break-word 가 아니라 anywhere 인 이유: 두 값은 '넘칠 때 쪼갠다'는
      같지만, 열의 최소 폭(min-content)을 잴 때 break-word 는 반영되지 않는다 — 즉 열은
      '가장 긴 단어'보다 좁아지지 못한다. anywhere 는 최소 폭 계산에도 들어가서 열이
      실제로 줄어든다(보이는 줄바꿈은 둘이 같다).
   ④ 상한을 rem 고정이 아니라 cqw 와 함께 두는 이유: 패널 폭은 리사이저 드래그로도
      바뀐다. rem 만 쓰면 패널이 좁아져도 상한이 그대로라 열 상한의 합이 패널을 넘겨
      가로 스크롤이 상시로 생긴다. 컨테이너 폭에 비례해 같이 줄게 둔다. */
.ptable .ptc {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    max-width: min(11rem, 26cqw);
    line-height: 1.45;
    overflow-wrap: anywhere;
}
/* 첫 칸은 그 행이 무엇인지 가리키는 이름표 — 유일하게 잉크를 진하게 쓴다.
   제목만은 자르지 않는다: 두 줄로 잘라 '…' 을 붙이면 어느 문서인지 고르는 기준이
   사라져, 결국 하나씩 눌러 확인해야 한다. 폭 상한도 줄 수 제한도 풀어 몇 줄이 되든
   전부 보인다(행 높이는 어차피 가장 긴 셀이 정한다). */
.ptable td:first-child .ptc {
    display: block;
    -webkit-line-clamp: none;
    line-clamp: none;
    overflow: visible;
    max-width: none;
    color: var(--ink-900);
    font-size: 12px;
    font-weight: 600;
}
/* 값 칸은 첫 칸과 같은 잉크색·굵기로 읽는다.
   예전에는 나머지 칸을 브랜드 파랑으로 뒀는데('다른 문서를 가리킨다'는 뜻이었다),
   표 대부분이 파랗게 물들어 정작 눌러서 열리는 것(링크 아이콘·이름 버튼)과 구분이 안 됐다.
   색은 '누를 수 있다'는 뜻으로만 남긴다. */
.ptable td:not(:first-child) .ptc { color: var(--ink-900); font-weight: 600; }

/* 좁아지면 셀 여백부터 줄인다 — 다섯 열이면 좌우 패딩만 120px 를 먹는다.
   글자를 줄이기 전에 여백을 먼저 내주는 편이 읽기에 덜 상한다. */
@container (max-width: 30rem) {
    .ptable th, .ptable td { padding-left: .4375rem; padding-right: .4375rem; }
}
@container (max-width: 20rem) {
    .ptable th, .ptable td { padding-left: .3125rem; padding-right: .3125rem; }
    .ptable .ptc { max-width: none; }   /* 이 폭에서는 상한이 오히려 열을 굳힌다 */
}

/* ── 좁아지면 표를 아코디언 목록으로 ────────────────────────────────────────
   다섯 열을 그대로 밀어 넣으면 어느 칸도 못 읽는 폭이 온다. 그 아래에서는 표의
   골격을 풀고, 행 하나가 '제목 + 두 번째 값' 한 줄이 되는 목록으로 바꾼다. 나머지
   칸은 접어 두고 행을 누르면 그 아래로 펼친다(session.js 의 위임 클릭).

   ※ 행을 새로 만들지 않고 있는 칸을 감췄다 편다. view_link.js 가 .ptable tbody tr 의
     순번을 그대로 좌표로 쓰기 때문에, 상세용 <tr> 을 끼워 넣으면 그 아래 모든 행의
     번호가 밀려 본문 링크가 다른 행으로 간다.
   ※ 칸 이름은 마크업의 data-label 을 ::before 로 꺼내 쓴다 — 의사요소라 textContent
     에 섞이지 않아 본문 링크의 원문 매칭도 그대로다.
   ※ 몇 번째 칸이냐로 가르는 건 여기서는 맞다 — 표의 열은 본디 순서가 의미다. */
@container (max-width: 26rem) {
    .ptable, .ptable thead, .ptable tbody { display: block; }
    .ptable thead tr,
    .ptable tbody tr {
        display: flex;
        flex-wrap: wrap;
        align-items: baseline;
        column-gap: .5rem;
        position: relative;
        padding: .625rem 1.5rem .625rem .625rem;   /* 오른쪽은 셰브론 자리 */
    }
    .ptable th, .ptable td {
        display: block;
        padding: 0;
        border: 0;
    }
    /* 머리줄의 면·고정은 th 가 아니라 tr 이 진다 — th 가 블록이라 제 글자 폭만큼만
       칠해져, 두 조각이 뜬 채 행 여백은 비고 스티키도 조각마다 따로 붙는다. */
    .ptable thead tr {
        position: sticky;
        top: 0;
        z-index: 1;
        padding-bottom: .5rem;
        background-color: #F8FAFC;
        border-bottom: 1px solid #E6EAF0;
    }
    .ptable th {
        position: static;
        background-color: transparent;
    }
    /* 제목은 남는 폭을 다 쓰고, 두 번째 칸은 오른쪽 끝에 붙는다 */
    .ptable th:first-child, .ptable td:first-child { flex: 1 1 60%; min-width: 0; }
    .ptable th:nth-child(2), .ptable td:nth-child(2) { flex: 0 0 auto; margin-left: auto; }
    /* 펼친 행 — 몸은 흰 면으로 두고, 머리(첫 두 칸) 한 줄만 면을 든다. 행 전체를 물들이면
       '펼쳐진 상세'까지 색이 깔려 어디가 눌린 줄인지 되레 흐려진다. hover 색도 여기서
       끈다 — 편 직후엔 커서가 그 행 위에 있어서, 안 끄면 펼치자마자 다시 물든다. */
    .ptable tbody tr.is-open,
    .ptable tbody tr.is-open:hover {
        background-color: #FFFFFF;
        column-gap: 0;
        align-items: stretch;
    }
    .ptable tbody tr.is-open > td:first-child,
    .ptable tbody tr.is-open > td:nth-child(2) {
        margin-top: -.625rem;
        padding-top: .625rem;
        padding-bottom: .625rem;
        background-color: #F1F6FC;
    }
    .ptable tbody tr.is-open > td:first-child {
        margin-left: -.625rem;
        padding-left: .625rem;
    }
    .ptable tbody tr.is-open > td:nth-child(2) {
        margin-right: -1.5rem;
        padding-right: 1.5rem;
        padding-left: .5rem;
    }
    /* 3번부터는 접힌다. 펼치면 한 줄을 통째로 쓴다 */
    .ptable th:nth-child(n+3), .ptable td:nth-child(n+3) { display: none; }
    .ptable tbody tr.is-open td:nth-child(n+3) {
        display: block;
        flex: 1 1 100%;
        margin-top: .5rem;
    }
    /* 펼친 첫 칸 — 머리 줄과 가르는 선. 줄 양끝까지 닿아야 "여기서부터 상세"가 읽힌다. */
    .ptable tbody tr.is-open td:nth-child(3) {
        margin-top: 0;
        margin-left: -.625rem;
        margin-right: -1.5rem;
        padding: .625rem 1.5rem 0 .625rem;
        border-top: 1px solid #F0F3F7;
    }
    /* 펼친 칸의 이름표 — 마크업의 data-label 을 그대로 쓴다. 접히는 칸(3번부터)에만
       붙인다: 머리줄에 이미 서 있는 첫 두 칸까지 이름을 달면 같은 말이 두 번 나온다. */
    .ptable td:nth-child(n+3)[data-label]::before {
        content: attr(data-label);
        display: block;
        margin-bottom: .1875rem;
        font-size: 10px;
        font-weight: 500;
        color: #94A3B8;
    }
    /* 목록이 된 이상 셀 안의 줄 수 제한과 폭 상한은 의미가 없다 */
    .ptable .ptc { max-width: none; -webkit-line-clamp: none; line-clamp: none; overflow: visible; }
    .ptable tbody tr {
        cursor: pointer;
        border-top: 1px solid #F0F3F7;
        transition: background-color .12s;
    }
    .ptable tbody tr:first-child { border-top: 0; }
    /* hover 도 칸이 아니라 줄이 진다 — td 에 칠하면 칸 사이와 행 여백이 비어
       색이 조각조각 뜬다(표일 때는 칸이 줄을 꽉 채워서 문제가 없었다). */
    .ptable tbody tr:hover { background-color: #F6FAFD; }
    .ptable tbody tr:hover td { background-color: transparent; }
    /* 펼침 표식 — 오른쪽 끝. tr 이 블록이 되었으므로 의사요소를 붙일 수 있다. */
    .ptable tbody tr::after {
        position: absolute;
        top: .75rem;
        right: .5rem;
        font-family: "Font Awesome 6 Free";
        font-weight: 900;
        font-size: 10px;
        color: #94A3B8;
        transition: transform .2s var(--ease-glass);
        content: "\f078";                      /* chevron-down */
    }
    .ptable tbody tr.is-open::after { transform: rotate(180deg); }
}

/* 배지 칸(format:"pill") — 연도처럼 짧고 갈래가 정해진 값.
   글자만 두면 긴 제목 옆에서 부스러기로 보인다. 면을 하나 줘 '분류'로 읽히게 한다. */
.ptable .ptc--pill {
    display: inline-block;
    max-width: none;
    padding: .1rem .3rem;
    border-radius: 3px;
    background-color: rgba(208, 236, 248, .40);
    color: var(--brand-sky);
    font-weight: 600;
    white-space: nowrap;
}
/* 링크 칸(값이 URL) — 주소를 그대로 깔면 한 열을 다 먹고 글자로도 안 읽힌다.
   동그란 표적 하나로 줄이고 주소는 title 로 넘긴다. */
.ptable .ptable__link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.375rem;
    height: 1.375rem;
    border-radius: 999px;
    background-color: #E3F6EE;
    color: #0E9F6E;
    font-size: 10px;
    text-decoration: none;
    transition: background-color .12s, color .12s;
}
.ptable .ptable__link:hover { background-color: #0E9F6E; color: #FFF; }
.ptable .ptable__link:focus-visible { outline: 2px solid var(--brand-sky); outline-offset: 2px; }

/* 수치 컬럼만 우측 정렬 — 자릿수가 맞아야 크기가 눈으로 비교된다.
   연도·사번은 이름과 값이 둘 다 연도일 때 걸러지므로 왼쪽에 남는다(is_measure). */
.ptable th.is-num, .ptable td.is-num { text-align: right; }
.ptable td.is-num .ptc { max-width: none; }
/* highlight 컬럼 — 색을 더 쓰지 않고 면 하나만 얹는다.
   셀 강조에 background-color 를 쓰면 행 hover(.ptable tbody tr:hover td)가 특이도에서
   이겨 마우스가 얹힌 행만 강조가 빠진다. 강조는 image 로 한 겹 올려 특이도 싸움을 피하고,
   알파를 남겨 밑의 hover 색이 그대로 비치게 둔다. 헤더는 스티키라 불투명이어야 해서 color 로 둔다. */
.ptable th.is-hl { background-color: #F1F6FC; }
.ptable td.is-hl { background-image: linear-gradient(rgba(219, 235, 250, .55), rgba(219, 235, 250, .55)); }
.ptable td.is-hl .ptc { font-weight: 600; color: var(--ink-900); }

/* 단위(우상단)·각주(하단) — 표와 카드가 같이 쓴다. 본문보다 한 단 작고 흐리게. */
.pmeta-unit {
    margin-bottom: .25rem;
    padding-right: .25rem;
    text-align: right;
    font-size: 10px;
    line-height: 1;
    color: #64748B;
}
.pmeta-note { margin-top: .375rem; font-size: 10px; color: #64748B; }
/* 표 블록은 바깥 여백이 없다(위 .pblock__body) → 각주가 제 여백을 스스로 만든다 */
.pblock[data-view-type="table"] .pmeta-note { margin-top: 0; padding: .375rem .6rem; }
.pblock[data-view-type="table"] .pmeta-unit { padding-right: .6rem; }

/* ③-2 카드 — 같은 rows를 세로로 편 것. 표가 '비교'라면 카드는 '한 건 읽기'다.
      그래서 격자 대신 제목/부제를 세우고 항목을 라벨·값 쌍으로 내린다.
      (.pcard 는 도구 카드라 이름이 겹치지 않게 .pvcard) */
.pvcards { display: flex; flex-direction: column; gap: .5rem; }
/* 짜임은 한 벌뿐이다 — 제목 한 덩이 + (라벨 열 5rem | 값 열) 줄들, 줄 사이에만 헤어라인.
   특허 상세든 검색 결과든 카드가 담는 건 같은 fields·rows 라, 항목 종류에 따라 다른 모양을
   주면 같은 값이 화면마다 달라 보인다. 그래서 항목별 modifier 를 두지 않는다.
   값이 오다 말다 하는 자리라 '몇 번째 줄이냐'로 정하는 규칙도 두지 않는다 — DOM 순서가 곧
   화면 순서고, 안 온 항목은 줄째로 없으며, 선은 인접 형제(+)로만 그어 첫 줄에 남지 않는다. */
.pvcard {
    display: flex;
    flex-direction: column;
    padding: .875rem;
    border-top: 1px solid #E9E9E9;
    background-color: rgba(255, 255, 255, .78);
}
/* 표의 행 hover 와 같은 색·같은 시간 — 같은 데이터의 같은 동작이다 */
.pvcard[data-url] { cursor: pointer; transition: background-color .12s; }
.pvcard[data-url]:hover { background-color: #F6FAFD; }
/* 몸통 껍데기를 지워 값 줄들이 카드의 직계 자식이 된다(제목과 한 흐름에 놓여야 순서가 산다) */
.pvcard__body { display: contents; }

/* 제목 — 값 줄들과 달리 옅은 회색 면에 얹는다. 카드에서 '무엇에 대한 값들인가'를 말하는
   한 덩어리라, 면과 테두리로 한 칸 물러나 있어야 아래 줄들과 층이 갈린다.
   라운드는 살짝만 — 카드 자체에는 모서리가 없어서, 여기만 크게 굴리면 혼자 뜬다. */
.pvcard__head {
    margin-bottom: .25rem;
    padding: .625rem .75rem;
    border: 1px solid #E2E8F0;
    border-radius: .4rem;
    background-color: #F8FAFC;
}
.pvcard__title { font-size: 15px; font-weight: 700; line-height: 1.4; color: var(--ink-900); overflow-wrap: break-word; }
.pvcard__sub { font-size: 12px; font-weight: 500; color: #6B7DE8; overflow-wrap: break-word; }

/* 줄 — 라벨 열 / 값 열, 둘 다 왼쪽 정렬. 값을 줄 끝에 붙이면 값마다 길이가 달라 시작점이
   들쭉날쭉해진다. 라벨 칸을 고정폭으로 잡아 카드의 모든 값이 같은 x 에서 시작하게 한다. */
.pvcard__row {
    display: flex;
    align-items: flex-start;
    gap: .5rem;
    padding: .5rem;
}
.pvcard__row + .pvcard__row { border-top: 1px solid #E7ECF5; }
.pvcard__k {
    flex-shrink: 0;
    width: 5rem;
    font-size: 12px;
    font-weight: 500;
    color: #8A97AC;
}
.pvcard__v {
    min-width: 0;
    font-size: 12px;
    font-weight: 400;
    line-height: 1.65;
    color: var(--ink-900);
    overflow-wrap: break-word;
}
.pvcard__v.is-hl { font-weight: 600; color: var(--brand-sky); }

/* ③-2b 링크 · 삽입 화면(link · iframe) — 표와 카드가 같이 쓴다.
   셀 값이 URL 일 때의 동그란 표적(.ptable__link)과는 다른 것이다: 저쪽은 주소만 있어
   글자가 없고, 이쪽은 규격이 준 이름(text)이 있어 그 이름을 그대로 눌러 연다.
   그래서 색으로만 '누를 수 있다'고 말한다 — 표 안의 다른 값은 색을 쓰지 않는다. */
.pcell__a {
    color: var(--brand-sky);
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 2px;
    overflow-wrap: anywhere;
}
.pcell__a:hover { color: var(--ink-900); }
/* 카드 제목·부제는 이미 제 색과 굵기를 갖는다 — 링크가 되어도 그 자리의 글자로 남는다. */
.pvcard__title .pcell__a, .pvcard__sub .pcell__a { color: inherit; font-weight: inherit; }

/* 삽입 화면을 여는 문 — 겉은 link 컬럼의 링크와 같게 둔다.
   '누르면 더 볼 수 있다'는 뜻은 하나여서, 링크는 밑줄 글자인데 이것만 상자 버튼이면
   같은 자리에 두 가지 표적이 생긴다. 면·테두리를 걷고 글자만 남긴다 —
   다른 것은 동작뿐이다: href 로 이동하지 않고 클릭 이벤트로 모달을 연다.
   여럿이면 세로로 이어 붙는다(그래서 inline 이 아니라 block). */
.pframe-btn {
    display: block;
    max-width: 100%;
    padding: 0;
    border: 0;
    background: none;
    color: var(--brand-sky);
    font: inherit;
    font-weight: 600;
    text-align: left;
    text-decoration: underline;
    text-underline-offset: 2px;
    overflow-wrap: anywhere;
    cursor: pointer;
}
.pframe-btn + .pframe-btn { margin-top: .125rem; }
.pframe-btn:hover { color: var(--ink-900); }
.pframe-btn:focus-visible { outline: 2px solid var(--brand-sky); outline-offset: 2px; }

/* 삽입 화면 모달 — 크기는 논문 전문가 팝업(popup_paper_pro.html)과 같게 둔다.
   여닫기는 handlePopup 이 하므로(opacity-100 을 붙였다 뗀다) 그 자국에 맞춰 칠한다 —
   tailwind 유틸이 없는 화면(표출 규격 미리보기)에서도 같게 동작해야 해서 여기서 정의한다.
   z-index 는 참조정보 모달(9999) 위 — 그 안의 블록에서도 열린다. */
.pframe-modal {
    position: fixed;
    inset: 0;
    z-index: 10000;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    pointer-events: none;
    transition: opacity .3s ease-out;
}
.pframe-modal.opacity-100 { opacity: 1; pointer-events: auto; }
.pframe-modal__veil { position: absolute; inset: 0; background-color: rgba(0, 0, 0, .5);
                      backdrop-filter: blur(4px); cursor: pointer; }
.pframe-modal__box {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: .5rem;
    width: 90vw;
    height: 80vh;
    padding: 1rem;
    border: 1px solid #0270F4;
    border-radius: .5rem;
    background-color: #FFF;
}
.pframe-modal__head { display: flex; align-items: center; gap: .75rem; }
.pframe-modal__title {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    color: var(--ink-900);
    font-size: 1.125rem;
    font-weight: 700;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* 삽입을 거부하는 사이트는 빈 화면으로 뜨는데 브라우저가 그 실패를 알려주지 않는다 —
   화면을 링크로 바꿔 그릴 수 없으니, 나가는 문을 늘 머리에 열어 둔다. */
.pframe-modal__out { flex-shrink: 0; color: var(--brand-sky); font-size: 12px; font-weight: 600; }
.pframe-modal__out:hover { text-decoration: underline; }
.pframe-modal__close {
    display: flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border-radius: .5rem;
    background-color: #DBEAFE;
    color: #0270F4;
    cursor: pointer;
    transition: background-color .075s;
}
.pframe-modal__close:hover { background-color: #BFDBFE; }
.pframe-modal__frame { flex: 1; width: 100%; border: 1px solid #E4EAF6; border-radius: .5rem;
                       background-color: #FFF; }

/* ③-3 텍스트 — 마크다운 한 덩어리. 서식(제목·목록·인용)은 아래 .md 가 이미 잡고 있어
      여기서는 면과 글자 크기만 준다.
      크기를 .ptext 가 아니라 .ptext.md 에 두는 이유: 이 파일 뒤쪽의 `.md { font-size: inherit }`
      가 특이도가 같고(0,1,0) 더 뒤에 있어 .ptext 의 font-size 를 덮는다. 그러면 이 블록만
      패널 기본 크기로 커져 표·카드·차트(11~12px) 사이에서 혼자 튄다. */
.ptext {
    padding: .625rem .75rem;
    border-top: 1px solid #E9E9E9;
    background-color: rgba(255, 255, 255, .62);
    font-size: 12px;
    color: var(--body-700);
    word-break: break-word;
}
/* 긴 한국어 본문(초록 1천자 · 청구항 5천자 · 상세설명 1만8천자)은 셋 다 <p> 한 덩어리로 온다.
   문단이 없으니 줄바꿈 규칙과 <br> 간격이 가독성의 전부다.
     ① word-break: 한국어의 기본 줄바꿈은 '글자 단위'라 어절 한가운데가 잘린다
        ("재증착된 물 / 질을"). keep-all 로 어절을 붙여 두고, 대신 끊을 자리가 없는 긴
        영문·화학식은 overflow-wrap: anywhere 로 푼다 — keep-all 만 두면 그런 토큰이
        상자를 뚫고 나간다. 값을 .ptext 가 아니라 .ptext.md 에 두는 이유는 font-size 와 같다:
        뒤쪽 `.md { word-break: break-word }` 가 특이도가 같고 더 뒤에 있어 .ptext 를 덮는다.
     ② <br>: 청구항·상세설명은 문장 사이가 전부 <br> 다. 인라인이라 간격이 0 이어서
        16개 청구항이 한 덩어리로 읽힌다 → 블록으로 바꿔 아래로 한 숨 띄운다.
     ③ 자간을 아주 살짝 좁힌다 — 13px 한글은 기본 자간이 헐거워 어절 경계가 흐려진다. */
.ptext.md {
    font-size: 13px;
    line-height: 1.8;
    word-break: keep-all;
    overflow-wrap: anywhere;
    letter-spacing: -.01em;
}
.ptext.md br {
    display: block;
    margin-top: .55em;
    content: "";
}
.ptext--raw { white-space: pre-wrap; }   /* marked 미로딩 폴백 — 줄바꿈만 살린다 */

/* ④ 차트 — 캔버스는 흰 면 위에서 색이 가장 정확하다 */
.pchart {
    margin-top: .5rem;
    padding: .5rem;
    border: 1px solid #E4EAF6;
    border-radius: .625rem;
    background-color: #FFFFFF;
}

/* ⑤ 실행 중 스켈레톤 — 스피너 하나만 도는 것보다, 곧 들어올 표의 실루엣이
      보이는 편이 기다림이 짧게 느껴진다. 빛줄기는 좌→우로 한 방향만. */
.pskeleton {
    display: flex;
    flex-direction: column;
    gap: .375rem;
    margin-top: .625rem;
}
.pskeleton span {
    height: .5rem;
    border-radius: 9999px;
    background-image: linear-gradient(90deg, #E8EFFC 0%, #F8FBFF 50%, #E8EFFC 100%);
    background-size: 200% 100%;
    animation: pshimmer 1.4s linear infinite;
}
.pskeleton span:nth-child(1) { width: 100%; }
.pskeleton span:nth-child(2) { width: 82%; }
.pskeleton span:nth-child(3) { width: 56%; }

@keyframes pshimmer {
    from { background-position: 200% 0; }
    to   { background-position: -200% 0; }
}

/* 모션을 줄여달라고 한 사용자에게는 등장/빛줄기를 걷어낸다.
   스피너(animate-spin)는 '진행 중'이라는 정보라 그대로 둔다. */
@media (prefers-reduced-motion: reduce) {
    .pblock { animation: none; }
    .pskeleton span { animation: none; }
}

/* ── 진행 상황 패널 헤더 ───────────────────────────────────────────────────
 * 회색 실선 한 줄로 끊으면 패널이 '창'이 아니라 잘린 면으로 보인다.
 * 경계는 브랜드 그라데이션 헤어라인으로 받고, 좌측 아이콘 하나로 제목을 세운다.
 * ------------------------------------------------------------------------ */
/* 머리글은 면을 갖지 않는다. 패널이 유리가 된 뒤로는 여기에 흰 그라데이션을
   깔면 그 부분만 판으로 도드라진다 — 패널의 그라데이션이 그대로 지나가게 둔다. */
.progress-head {
    position: relative;
}

/* (삭제) .progress-head::after — 머리글 아래 그라데이션 헤어라인.
   제목과 본문 사이는 여백만으로 갈린다. */
.progress-head__icon {
    flex-shrink: 0;
    width: 1.625rem;
    height: 1.625rem;
    border-radius: .625rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    color: var(--on-brand);
    background-image: var(--grad-bubble);
    box-shadow: 0 3px 8px -3px rgba(70, 110, 215, .55);
}

/* 닫기 — 아이콘만 덩그러니 두면 눌리는 곳이 어디까지인지 알 수 없다.
   28px 정사각형 히트영역을 주고, 호버에서만 면이 나타난다. */
.progress-close {
    flex-shrink: 0;
    width: 1.75rem;
    height: 1.75rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: .5rem;
    color: #64748B;
    transition: background-color .15s, color .15s;
}
.progress-close:hover {
    background-color: #EFF4FE;
    color: var(--ink-900);
}

/* 모두접기 · 모두펴기 — 닫기 버튼 왼쪽. 글자 버튼으로 둔 이유는 '무엇을 접는지'를
   아이콘으로 가리키기 어려워서다(겹화살표는 스크롤로도 읽힌다). 닫기(X)와 나란히
   서므로 면은 주지 않고 hover 에서만 든다 — 면을 늘 깔면 헤더에서 버튼 셋이 경쟁하고,
   되돌릴 수 있는 동작에 비해 과하다. */
.progress-collapse {
    flex-shrink: 0;
    padding: .25rem .5rem;
    border-radius: .5rem;
    font-size: 12px;
    font-weight: 500;
    color: #64748B;
    white-space: nowrap;
    transition: background-color .15s, color .15s;
}
.progress-collapse:hover {
    background-color: #EFF4FE;
    color: var(--ink-900);
}
.progress-collapse:focus-visible {
    outline: 2px solid var(--brand-sky);
    outline-offset: 1px;
}

/* 맨 위로 — 목록 위에 떠 있는 동그란 버튼. 아이콘 대신 글자(TOP)로 둔다:
   화살표 하나는 "위로"인지 "접기"인지 갈리는데, 이 패널엔 접힘 상자가 널려 있다.
   패널은 xl 에서 relative, 그 아래에서 fixed 라 어느 쪽이든 이 버튼의 기준이 된다.

   면은 프로젝트의 공식 파랑(--grad-official)을 알파로 낮춘 유리다. 불투명하게 칠하면
   그냥 파란 알약이 되고, 뒤로 카드가 비쳐 지나가야 '떠 있는 것'으로 읽힌다.

   보이는 조건은 두 가지다(session.js): 목록에 스크롤이 있고, 맨 위가 아닐 때. */
.progress-top {
    position: absolute;
    right: 1rem;
    bottom: 1rem;
    z-index: 20;
    width: 2.25rem;
    height: 2.25rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid rgba(255, 255, 255, .55);
    border-radius: 9999px;
    background-image: linear-gradient(135deg,
        rgba(62, 142, 240, .80) 0%,
        rgba(28, 111, 214, .74) 100%);
    -webkit-backdrop-filter: blur(12px) saturate(170%);
            backdrop-filter: blur(12px) saturate(170%);
    box-shadow:
        0 4px 14px -4px rgba(28, 79, 168, .48),
        inset 1px 1px 0 rgba(255, 255, 255, .50),
        inset -1px -1px 0 rgba(255, 255, 255, .14);
    color: #FFFFFF;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .04em;
    /* 안 보일 때도 레이아웃에서 빼지 않는다(display:none) — 다시 나타날 때 애니메이션이
       걸리지 않고 툭 튀어나온다. 투명도와 위치로만 감춘다. */
    opacity: 0;
    transform: translateY(.375rem);
    pointer-events: none;
    transition: opacity .18s var(--ease-glass), transform .18s var(--ease-glass),
                background-image .15s, box-shadow .15s;
}
.progress-top.is-on {
    opacity: 1;
    transform: none;
    pointer-events: auto;
}
/* hover 는 유리를 한 단 진하게 — 어둡게 누르는 게 아니라 색이 또렷해지며 떠오른다 */
.progress-top:hover {
    background-image: linear-gradient(135deg,
        rgba(62, 142, 240, .95) 0%,
        rgba(28, 111, 214, .92) 100%);
    box-shadow:
        0 6px 18px -4px rgba(28, 79, 168, .55),
        inset 1px 1px 0 rgba(255, 255, 255, .55),
        inset -1px -1px 0 rgba(255, 255, 255, .16);
}
.progress-top:active { transform: translateY(1px); }
.progress-top:focus-visible { outline: 2px solid var(--brand-sky); outline-offset: 2px; }

/* 진행 상황 패널 너비 조절 핸들 ─────────────────────────────────────────────
 * 단색 #1C6FD6 한 줄은 브라우저 기본 포커스 링처럼 보여서, 조작 중이라는
 * 신호가 아니라 "뭔가 잘못 선택됐나" 로 읽힌다.
 * → 페이지의 컬러 축(하늘 → 파랑 → 분홍)을 세로로 흘려 이 페이지의 요소임을
 *   분명히 한다. 세션 활성 바와 같은 180deg 그라데이션이다.
 *
 * ※ background-image 는 transition 이 안 걸린다. 그래서 그라데이션은 항상
 *   깔아두고 opacity 로 나타냈다 사라지게 한다(마크업의 transition-colors
 *   만으로는 아무 것도 부드러워지지 않는다). */
.progress_resizer > span {
    background-image: linear-gradient(180deg, #0E7BBE 0%, #1C6FD6 50%, #0F4BD8 100%);
    border-radius: 9999px;
    opacity: 0;
    transition: opacity .15s ease, width .15s ease;
}
.progress_resizer:hover > span,
.progress_resizer.is-resizing > span {
    width: 2px;
    opacity: 1;
}
/* 드래그 중에는 한 단계 더 — 손이 붙어 있다는 피드백 */
.progress_resizer.is-resizing > span {
    width: 3px;
    box-shadow: 0 0 10px rgba(28, 111, 214, .45);
}

/* 드래그 중에는 커서가 어디에 있든 col-resize 유지 */
body.is-col-resizing,
body.is-col-resizing * {
    cursor: col-resize !important;
}

/* 안내 팝업 배경 격자 */
.bg-grid-pattern {
    background-image:
            linear-gradient(to right, rgba(120,150,220,.16) 1px, transparent 1px),
            linear-gradient(to bottom, rgba(120,150,220,.16) 1px, transparent 1px);
    background-size: 35px 35px;
}

/* ── 배경 베일 (제거됨) ───────────────────────────────────────────────────
 * 예전에는 대화 중에만 흰 베일(#aurora_veil)을 덮어 배경 채도를 눌렀다.
 * 그래서 첫 질문을 보내는 순간 배경 톤이 바뀌었다.
 * 지금은 배경이 어느 화면에서나 같아야 하므로 상태별 베일을 없애고,
 * 그 역할을 #app_bg::after 의 고정 스크림(--bg-scrim)이 대신한다.
 * 본문 가독성 조절이 필요하면 --bg-scrim 만 손대면 된다.
 * ------------------------------------------------------------------------ */

/* ── 컴포저 뒤 스크림 ─────────────────────────────────────────────────────
 * 떠 있는 컴포저 뒤로 대화가 지나가면 글자와 입력창이 겹쳐 읽힌다.
 * 예전에는 배경색(#EFF3FA)에서 투명으로 가는 그라데이션이었지만, 배경이
 * 이미지가 된 지금은 어떤 단색을 골라도 그 아래 이미지와 어긋나 띠로 보인다.
 * → 색으로 덮지 않고 "뒤를 흐린다". 아래는 진하게, 위로 갈수록 사라지도록
 *   마스크를 씌워 경계가 생기지 않게 한다. 흰 그라데이션은 흐림만으로는
 *   부족한 대비를 아주 옅게 보태는 용도다.
 * (backdrop-filter 미지원 브라우저에서는 흰 그라데이션만 남는다 — 대화가
 *  살짝 비칠 뿐 컴포저는 그대로 읽힌다.)
 * ------------------------------------------------------------------------ */
/* ── 하단 페이드 — 아래로 갈수록 대화가 흐려져 묻힌다 ─────────────────────
 * 색을 한 방울도 칠하지 않는다. background 없이 backdrop-filter 로 뒤를
 * 흐리기만 하고, 그 효과의 세기를 mask 로 위쪽에서 0 까지 떨어뜨린다.
 * (예전 버전에는 흰 그라데이션이 함께 깔려 있었다. 배경이 이미지라 어떤
 *  흰색을 골라도 그 아래 이미지와 어긋나 가로 띠로 보였다.)
 *
 * ※ 왜 #chat_scroll 에 mask 를 걸어 내용을 투명하게 지우지 않는가 —
 *   mask 가 걸린 요소는 backdrop root 가 된다. 그러면 그 안의 .bubble-ai 가
 *   backdrop-filter 로 페이지 배경을 샘플링하지 못해 유리가 납작한 반투명
 *   판으로 죽는다. 그래서 지우는 대신, 형제 레이어를 위에 겹쳐 흐린다.
 *   말풍선 유리를 포기해도 좋다면 그때는 mask 쪽이 더 깔끔하다.
 *
 * ※ 마스크 중간에 .6 을 한 점 찍은 건 falloff 를 앞당기기 위해서다. 선형
 *   두 점(#000 → transparent)이면 흐림이 위쪽까지 어정쩡하게 남아, 흐려지는
 *   게 아니라 전체가 조금 뿌연 것처럼 보인다.
 * ------------------------------------------------------------------------ */
#chat_bottom_fade {
    -webkit-backdrop-filter: blur(14px);
            backdrop-filter: blur(14px);
    -webkit-mask-image: linear-gradient(to top, #000 0%, rgba(0, 0, 0, .6) 45%, transparent 100%);
            mask-image: linear-gradient(to top, #000 0%, rgba(0, 0, 0, .6) 45%, transparent 100%);
}

/* ── 입력창(컴포저) ───────────────────────────────────────────────────────
 * 기존 .inputBox(그라데이션 테두리 알약)는 glass-card 컴포저로 대체되어 폐기했다.
 *
 * ※ 배경·테두리·투명도·backdrop-filter 를 여기서 재정의하지 않는다.
 *   ID 선택자(1,0,0)로 값을 갈아끼우면 같은 컴포저인데 페이지마다 유리가
 *   달라진다. 유리 단계를 바꿔야 할 때는 마크업의 클래스를 바꾸고, 값 자체를
 *   손봐야 할 때는 glassmorphism.css 의 변형 클래스를 고친다.
 *   → 지금은 .glass-card--crystal (흰 18% / blur 44px / saturate 250% /
 *     1px 그라데이션 링). --sheer(46%)보다 한 단계 더 비치는 판유리다.
 * ------------------------------------------------------------------------ */

/* 입력 영역은 완전 투명 — 유리 배경이 그대로 보이도록.
   (chat.css 상단 리셋이 textarea 에 배경을 넣으므로 이 한 줄은 필요하다) */
#chat_composer_box #chat_input {
    background-color: transparent;
}

/* 스크롤바도 무채색으로. 전역 썸은 rgba(120,150,220,.40) 옅은 파랑인데,
   컴포저는 유리에서 색을 전부 뺀 자리라 여러 줄을 입력해 스크롤바가 나타나는
   순간 그 파랑만 혼자 남는다. 이 안에서만 회색으로 덮는다. */
#chat_composer_box #chat_input {
    scrollbar-color: rgba(100, 104, 116, .38) transparent;
}
#chat_composer_box #chat_input::-webkit-scrollbar-thumb {
    background-color: rgba(100, 104, 116, .38);
}
#chat_composer_box #chat_input::-webkit-scrollbar-thumb:hover {
    background-color: rgba(100, 104, 116, .60);
}

/* ── '생각 중…' 표시 ──────────────────────────────────────────────────────
 * 회색 정지 텍스트는 그 자체로 "멈춰 있는 것"으로 보인다.
 * 답변을 기다리는 가장 불안한 구간이라 살아 있다는 신호가 필요하다.
 *
 * 새 요소를 만들지 않고, 이 페이지의 시그니처 두 개를 span 하나에 얹는다 —
 *   ① Orb        : 앞에 붙는 작은 그라데이션 점이 숨쉬듯 커졌다 작아진다.
 *   ② .text-grad : 글자를 그라데이션으로 칠하되 그 그라데이션을 옆으로 흘려,
 *                  빛이 글자 위를 훑고 지나가게 한다.
 *
 * 마크업이 그대로라 첫 답변 조각이 같은 자리를 덮어써도 레이아웃 점프가 없다.
 * (무한 애니메이션이지만 glassmorphism.css 의 prefers-reduced-motion 규칙이
 *  전역으로 정지시키므로 별도 처리는 하지 않는다.)
 * ------------------------------------------------------------------------ */
.chat_thinking {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    font-weight: 500;
    color: var(--body-600);
}

/* ① 앞의 작은 구체 — 페이지의 Orb 모티프를 한 점으로 축약 */
.chat_thinking::before {
    content: '';
    flex-shrink: 0;
    width: .5rem;
    height: .5rem;
    border-radius: 9999px;
    background-image: var(--grad-soft);
    box-shadow: 0 0 8px rgba(120, 160, 240, .55);
    animation: thinking-pulse 1.5s var(--ease-glass) infinite;
}

/* ② 글자 위를 훑고 지나가는 빛.
   미지원 브라우저에서는 위의 --body-600 단색으로 남는다(문구는 그대로 읽힌다). */
@supports ((background-clip: text) or (-webkit-background-clip: text)) {
    .chat_thinking {
        background-image: linear-gradient(100deg,
            #64748B 0%,
            #64748B 40%,
            #1C6FD6 50%,     /* --grad-text 의 중간 파랑 */
            #64748B 60%,
            #64748B 100%);
        background-size: 220% 100%;
        -webkit-background-clip: text;
        background-clip: text;
        color: transparent;
        -webkit-text-fill-color: transparent;
        animation: thinking-sweep 2.4s linear infinite;
    }
}

/* ③ 문구 뒤에 하나씩 늘어나는 점(글자 수는 session.js 가 바꿈).
   다섯 칸을 미리 잡아 둔다 — 점이 늘 때마다 버블 폭이 들쌭거리면
   진행 중이라는 신호가 아니라 산만함으로 읽힐다. */
.chat_thinking__dots {
    display: inline-block;
    width: 1.4em;
    text-align: left;
}

@keyframes thinking-pulse {
    0%, 100% { transform: scale(.7); opacity: .5; }
    50%      { transform: scale(1);  opacity: 1; }
}

@keyframes thinking-sweep {
    from { background-position: 100% 0; }
    to   { background-position: 0 0; }
}

/* ── 로딩 ─────────────────────────────────────────────────────────────── */
/* 사이드바 세션 로딩 · 검색 모달 로딩 */
.loader {
    width: 24px;
    height: 24px;
    border: 4px dotted #1C6FD6;
    border-radius: 50%;
    display: inline-block;
    position: relative;
    box-sizing: border-box;
    animation: rotation 2s linear infinite;
}

@keyframes rotation {
    0%   { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}

/* 전역 로딩 모달 */
.loader2 {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    display: block;
    margin: 15px auto;
    position: relative;
    color: #FFF;
    box-sizing: border-box;
    animation: animloader 1s linear infinite alternate;
}

@keyframes animloader {
    0%   { box-shadow: -38px -6px, -14px  6px,  14px -6px; }
    33%  { box-shadow: -38px  6px, -14px -6px,  14px  6px; }
    66%  { box-shadow: -38px -6px, -14px  6px,  14px -6px; }
    100% { box-shadow: -38px  6px, -14px -6px,  14px  6px; }
}

/* ── 안내 팝업 애니메이션 ─────────────────────────────────────────────── */
/* 타이핑 커서 */
.cursor {
    display: inline-block;
    width: 2px;
    height: 1em;
    background-color: #4b5563;
    margin-left: 4px;
    animation: blink 1s steps(2, start) infinite;
}

@keyframes blink {
    to { visibility: hidden; }
}

.tracking-in-expand {
    -webkit-animation: tracking-in-expand 0.7s cubic-bezier(0.215, 0.610, 0.355, 1.000) both;
            animation: tracking-in-expand 0.7s cubic-bezier(0.215, 0.610, 0.355, 1.000) both;
}

@-webkit-keyframes tracking-in-expand {
    0%   { letter-spacing: -0.5em; opacity: 0; }
    40%  { opacity: 0.6; }
    100% { opacity: 1; }
}
@keyframes tracking-in-expand {
    0%   { letter-spacing: -0.5em; opacity: 0; }
    40%  { opacity: 0.6; }
    100% { opacity: 1; }
}

.text-focus-in {
    -webkit-animation: text-focus-in 0.5s cubic-bezier(0.550, 0.085, 0.680, 0.530) both;
            animation: text-focus-in 0.5s cubic-bezier(0.550, 0.085, 0.680, 0.530) both;
}

@-webkit-keyframes text-focus-in {
    0%   { -webkit-filter: blur(12px); filter: blur(12px); opacity: 0; }
    100% { -webkit-filter: blur(0px);  filter: blur(0px);  opacity: 1; }
}
@keyframes text-focus-in {
    0%   { -webkit-filter: blur(12px); filter: blur(12px); opacity: 0; }
    100% { -webkit-filter: blur(0px);  filter: blur(0px);  opacity: 1; }
}

/* ── 채팅 마크다운(어시스턴트 답변) ─────────────────────────────────────
 * 장식을 걷어낸 판이다. 이전에는 표·인용·코드에 유리 표면(반투명 면 +
 * backdrop-filter + 확산 그림자 + 1rem 라운드)을 입히고 제목 밑에 오로라
 * 그라데이션 밑줄을 그었는데, 답변 한 통에 이런 덩어리가 서너 개 들어가면
 * 읽는 글이 아니라 컴포넌트 진열장이 된다.
 *
 * 남기는 것은 '읽는 구조'뿐이다 — 제목 크기 위계, 목록 마커, 표 괘선,
 * 코드의 고정폭. 빼는 것은 '꾸밈'이다 — 그라데이션 밑줄/구분선, 유리 카드,
 * 표 헤더 틴트·줄무늬·hover, 인라인 코드 알약, 이미지 라운드·그림자.
 *
 * 선과 면은 계속 블루틴트를 쓴다. 페일 블루 배경 위에서 순회색 선은 혼자
 * 탁하게 뜬다 — 장식이 아니라 이 배경에서의 '중립색'이 무엇이냐의 문제다.
 * 색 규칙은 docs/design-concept.md §1.2 그대로: 제목·강조는 ink,
 * 본문은 slate-600 이상(글래스 위 AA 하한). */
/* 대화 본문(답변) 크기의 기준점. .md 가 아래에서 inherit 하고,
   heading·code 는 em 단위라 이 값 하나로 답변 전체가 비율대로 움직인다.
   ※ 질문 말풍선만 예외로 15px — js/chat.js 의 append_bubble 참고. */
#chat_box { font-size: 14px; }

/* ── 대화 영역 — #chat_box 자체는 아무 표면도 갖지 않는다 ─────────────────
 * 한때 이 자리에 대화 전체를 받치는 유리 시트가 있었다(배경·테두리·그림자·
 * backdrop-filter). 지금은 유리를 답변 블록(.bubble-ai) 쪽으로 옮겼다.
 *
 * 둘을 겹쳐 쓰지 않는 이유: 유리는 뒤가 비쳐야 유리인데, 유리 위에 유리를
 * 얹으면 두 겹의 흰 판이 더해져 둘 다 그냥 흰 판이 된다. 받치는 면과 담기는
 * 면 중 하나만 유리여야 한다. 답변마다 판이 하나씩 뜨는 쪽이 대화의 덩어리가
 * 눈에 보여서 이쪽을 택했다.
 *
 * 아래 margin 은 표면이 아니라 레이아웃이다 — 대화 내용을 화면 가장자리에서
 * 떼어놓는 역할이라 시트가 사라져도 그대로 둔다.
 * ------------------------------------------------------------------------ */
#chat_area.is-chatting #chat_box {
    margin-top: 1rem;
    margin-left: 1.25rem;
    margin-right: 1.25rem;
}

@media (min-width: 768px) {
    #chat_area.is-chatting #chat_box {
        margin-top: 1.5rem;
        margin-left: 2rem;
        margin-right: 2rem;
    }
}

.md { font-size: inherit; line-height: 1.75; word-break: break-word; text-wrap: pretty; }
.md > :first-child { margin-top: 0; }
.md > :last-child { margin-bottom: 0; }

.md h1, .md h2, .md h3, .md h4, .md h5, .md h6 {
    font-weight: 700; line-height: 1.3; margin: 1.15em 0 .5em;
    color: var(--ink-900); letter-spacing: -.01em; text-wrap: balance;
}
/* 밑줄 장식은 뺐다. 제목이 제목으로 읽히는 건 크기와 굵기가 하는 일이고,
   위아래 여백(margin: 1.15em 0 .5em)이 이미 구획을 나눈다.
   색도 마찬가지다 — h3~h6 을 파랑 계열(--ink-800)로 두면 누를 수 있는 글자처럼
   보이는데 제목에는 아무 이벤트가 없다. 굵기와 크기만으로 제목 노릇을 시킨다. */
.md h1 { font-size: 1.4em; color: var(--ink-950); }
.md h2 { font-size: 1.22em; }
.md h3 { font-size: 1.08em; }
.md h4, .md h5, .md h6 { font-size: 1em; }

.md p { margin: .6em 0; }
/* 굵게는 색을 쓰지 않는다 — 굵기만으로 이미 눈에 띈다.
   파랑 계열(--ink-800)로 두면 본문 안에서 링크처럼 읽혀 눌러 보게 된다. */
.md strong { font-weight: 700; color: var(--ink-950); }
.md em { font-style: italic; }
.md del { color: #475569; }

.md a {
    color: var(--ink-600); text-decoration: underline;
    text-underline-offset: 3px; text-decoration-thickness: 1px;
    text-decoration-color: rgba(29, 78, 216,.4);
    transition: color .2s, text-decoration-color .2s;
}
.md a:hover { color: #1D4ED8; text-decoration-color: currentColor; }

/* 밖으로 나가는 링크에만 표식을 단다. 본문에는 누를 것이 둘인데(.md a 는 새 탭,
   .vlink 는 우측 패널의 그 행) 색은 둘이 같다 — 색은 '눌러서 더 볼 수 있다'만
   말하기로 했다(.vlink 규칙의 주석). 그래서 갈라 말할 것은 하나뿐이다:
   이 페이지를 떠나느냐. 표식은 드문 쪽인 새 탭에만 단다 — 앵커는 한 턴에 수십 개라
   거기에 달면 문단이 화살표로 얼룩진다. .vlink 는 span 이라 선택자가 저절로 갈린다. */
.md a[href]::after {
    content: "↗";
    font-size: .78em;
    margin-left: 1px;
    opacity: .6;
}

.md ul, .md ol { margin: .6em 0; }
.md ul { list-style: disc; padding-left: 1.35em; }
/* 번호 목록은 한 단 더 준다 — 1.35em 이면 "10." 이 컨테이너 밖으로 삐져나온다. */
.md ol { list-style: decimal; padding-left: 2.2em; }
.md li { margin: .3em 0; }
.md li > ul, .md li > ol { margin: .25em 0; }

/* 인용 — 왼쪽 선 한 줄. 유리 카드(반투명 면 + blur + 그림자 + 라운드)와
   브랜드 그라데이션 막대를 뺐다. 인용은 '다른 사람 말'이라는 표시지
   본문에서 떠 있어야 하는 덩어리가 아니다. */
.md blockquote {
    margin: .85em 0; padding: .1em 0 .1em 1em;
    border-left: 2px solid rgba(99,130,220,.30);
    color: #475569;
}
.md blockquote p { margin: .28em 0; }

/* 구분선 — 그라데이션을 빼고 헤어라인 한 줄 */
.md hr { border: 0; height: 1px; margin: 1.15em 0; background-color: rgba(99,130,220,.18); }

/* 인라인 코드 — 알약(테두리 + 안쪽 흰 림 + 흰 면)을 빼고 옅은 틴트만 남긴다.
   고정폭 글꼴만으로도 코드인 건 읽히지만, 문장 한가운데 섞이면 어디서
   시작하고 끝나는지가 안 보여 최소한의 면은 남겼다. */
.md code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .875em; color: var(--ink-800);
    background-color: rgba(99,130,220,.09);
    padding: .1em .3em; border-radius: 3px;
    overflow-wrap: break-word;   /* 표 칸 안에 들어가도 칸의 최소 폭을 글자 하나로 끌어내리지 않게 */
}

/* 코드 블록 — 어두운 유리 카드(대각선 그라데이션 + 확산 그림자 + 1rem 라운드)
   대신 평평한 옅은 면. 여러 줄 코드는 면이 있어야 어디까지가 코드인지
   잡히므로 면 자체는 남기고, 뜨는 장치만 걷어낸다. */
.md pre {
    margin: .85em 0; padding: .85em 1em; border-radius: 6px;
    background-color: #F4F6FA;
    border: 1px solid rgba(99,130,220,.18);
    color: var(--ink-900); overflow-x: auto;
    font-size: .875em; line-height: 1.6;
}
.md pre code {
    background: transparent; padding: 0;
    color: inherit; font-size: 1em; white-space: pre;
}

/* 표 — 괘선만 남긴 표. 유리 카드(면·blur·그림자·라운드), 헤더 틴트,
   짝수 행 줄무늬, hover 를 전부 뺐다. 데이터를 읽는 데 필요한 건
   칸을 나누는 선과 머리행이 머리행임을 알리는 굵기뿐이다.
   ※ display:block + overflow-x 는 장식이 아니라 넘침 방지다 — 폭이 넓은
     표가 말풍선을 밀어내지 않고 표 안에서만 가로 스크롤한다. */
.md table {
    display: block; width: max-content; max-width: 100%;
    overflow-x: auto; margin: .9em 0; font-size: .92em;
    border-collapse: collapse;
    font-variant-numeric: tabular-nums;   /* 숫자 칸 줄맞춤(.ptable 과 같은 이유) */
}
.md thead th {
    color: var(--ink-900); font-weight: 700; white-space: nowrap;
    border-bottom: 1px solid rgba(99,130,220,.35);   /* 머리행 아래만 한 단 진하게 */
}
.md th, .md td {
    border: 0; border-bottom: 1px solid rgba(99,130,220,.14);
    padding: 7px 14px 7px 0; text-align: left; vertical-align: top;
}
/* 마지막 칸의 오른쪽 여백은 필요 없다(칸 사이 간격만 있으면 된다) */
.md th:not(:last-child), .md td:not(:last-child) { padding-right: 1.5rem; }
.md tbody tr:last-child td { border-bottom: 0; }

.md img { max-width: 100%; height: auto; margin: .6em 0; }

/* ── '사용한 툴 N개 보기' 알약 ───────────────────────────────────────────
 * glassmorphism.css 의 .chip 축소판. 답변 아래 메타 줄이라 44px 터치
 * 타깃까지는 잡지 않되, 유리 표면·확산광·호버 리프트 규칙은 칩과 같다. */
/* 알약은 유리, 아이콘은 브랜드 그라데이션 — 역할을 나눠 맡긴다.
   예전에는 알약 전체를 오로라 블루(#E2E9FD)로 채웠는데, 주변이 전부 유리가
   된 지금은 그 한 조각만 불투명한 색면으로 남아 튄다. 대신 알약을 배경과
   같은 재질(유리)로 되돌리고, 눈에 띄는 일은 안쪽 아이콘 하나에 몰아준다.
   작고 진한 그라데이션 점 하나가 넓고 옅은 색면보다 시선을 잘 잡는다. */
.tools_pill {
    display: inline-flex; align-items: center; gap: .5rem;
    padding: .4375rem .75rem .4375rem .5rem;
    border-radius: 9999px;
    font-size: 13px; font-weight: 600; line-height: 1.2;
    color: var(--brand-sky);
    background-color: transparent;
    background-image: linear-gradient(180deg,
        rgba(255, 255, 255, .55) 0%,
        rgba(255, 255, 255, .20) 100%);
    border: 1px solid rgba(255, 255, 255, .75);
    box-shadow:
        0 1px 2px rgba(70, 95, 170, .08),
        0 8px 18px -10px rgba(70, 95, 170, .30),
        /* 유리 두께 — 위 밝은 림 / 아래 되비치는 반사 */
        inset 0 1px 0 rgba(255, 255, 255, .95),
        inset 0 -1px 0 rgba(255, 255, 255, .40);
    -webkit-backdrop-filter: blur(14px) saturate(100%);
            backdrop-filter: blur(14px) saturate(100%);
    transition: transform .3s var(--ease-glass), background-image .2s,
                border-color .2s, box-shadow .3s var(--ease-glass);
}

/* 아이콘 타일 — 전송·New Chat 버튼과 같은 그라데이션의 축소판.
   같은 색을 쓰면 "이것도 누르는 것"이라는 신호가 설명 없이 전달된다. */
.tools_pill__icon {
    display: grid; place-items: center;
    flex-shrink: 0;
    width: 1.375rem; height: 1.375rem;
    border-radius: 9999px;
    background-image: var(--grad-bubble);
    color: #FFFFFF;
    font-size: 10px;
    box-shadow:
        0 1px 2px rgba(40, 70, 160, .22),
        inset 0 1px 0 rgba(255, 255, 255, .50),
        inset 0 -1px 0 rgba(20, 40, 120, .18);
    transition: transform .3s var(--ease-glass), box-shadow .3s var(--ease-glass);
}
.tools_pill__chev {
    font-size: 10px;
    opacity: .55;
    transition: transform .3s var(--ease-glass), opacity .2s;
}

/* 호버 — 유리가 밝아지며 떠오르고, 아이콘이 한 번 기울며 빛난다 */
.tools_pill:hover {
    transform: translateY(-1px);
    background-image: linear-gradient(180deg,
        rgba(255, 255, 255, .78) 0%,
        rgba(255, 255, 255, .38) 100%);
    border-color: rgba(255, 255, 255, .95);
    box-shadow:
        0 1px 2px rgba(70, 95, 170, .10),
        0 12px 24px -10px rgba(70, 95, 170, .36),
        inset 0 1px 0 rgba(255, 255, 255, 1),
        inset 0 -1px 0 rgba(255, 255, 255, .45);
}
.tools_pill:hover .tools_pill__icon {
    transform: rotate(-12deg);
    box-shadow:
        0 2px 6px -1px rgba(40, 70, 160, .32),
        inset 0 1px 0 rgba(255, 255, 255, .60),
        inset 0 -1px 0 rgba(20, 40, 120, .18);
}
.tools_pill:hover .tools_pill__chev { transform: translateX(2px); opacity: 1; }
.tools_pill:active { transform: translateY(0); }

/* ── 답변 피드백 ──────────────────────────────────────────────────────
 * 같은 액션 줄(.turn_actions)에 얹히지만 톤은 반대다. '논문 분석'은 하는 일이
 * 있어 유리 필로 부르고, 피드백은 늘 있어야 하되 답변을 읽는 데 방해가 되면
 * 안 되므로 테두리 없는 고스트 아이콘으로 둔다 — 평소 흐리고, 다가가면 또렷해진다.
 * ------------------------------------------------------------------ */
/* 액션 줄을 답변 버블과 같은 폭(.bubble-ai 의 max-w-[85%])으로 묶는다. 그래야 피드백을
   오른쪽 끝으로 밀었을 때 화면 끝이 아니라 '답변의 오른쪽 끝'에 선다. */
.turn_actions { max-width: 85%; }

/* 액션 줄의 다른 버튼(논문 분석 등)은 왼쪽에 남고 피드백만 오른쪽 끝으로 민다 —
   답변을 다 읽고 시선이 도착하는 자리다. */
.fb_group { display: inline-flex; align-items: center; gap: .125rem; margin-left: auto; }

.fb_btn {
    display: grid; place-items: center;
    width: 1.75rem; height: 1.75rem;
    border-radius: 9999px;
    font-size: 13px;
    color: #64748B;
    opacity: .45;
    transition: opacity .2s, color .2s, background-color .2s,
                transform .3s var(--ease-glass);
}
.fb_btn:hover { opacity: 1; background-color: rgba(255, 255, 255, .70); }
.fb_btn:active { transform: scale(.92); }
.fb_btn.is-on {
    opacity: 1;
    color: var(--brand-sky);
    /* 누른 순간 한 번 튄다 — 저장됐다는 걸 문구 없이 알리는 유일한 신호다 */
    animation: fb_pop .32s var(--ease-glass);
}
@keyframes fb_pop {
    0%   { transform: scale(1); }
    45%  { transform: scale(1.28); }
    100% { transform: scale(1); }
}

/* 사유 카드 — 답변 아래에서 펼쳐진다. 모달로 띄우지 않는 이유는 답변을 보면서
   고르게 하려는 것이다(무엇이 문제였는지는 답변을 다시 봐야 나온다). */
/* 피드백 모달 배너 — 부제 없이 제목 한 줄뿐이라 기본 여백이 헐렁하게 남는다.
   여백을 줄이는 대신 아이콘·제목을 키워 배너를 채운다. 다른 모달은 부제가 있어
   지금 비율이 맞으므로 이 모달로만 한정한다. */
#feedback_modal .modal-hero { padding: 1rem 1.375rem; }
#feedback_modal .modal-hero__icon { width: 2.75rem; height: 2.75rem; font-size: 1.0625rem; }
#feedback_modal .modal-hero__title { font-size: 1.1875rem; }

.fb_text {
    display: block;
    width: 100%;
    padding: .75rem .875rem;
    border-radius: .875rem;
    font-size: 14px; line-height: 1.6;
    color: var(--ink-900);
    background-color: rgba(255, 255, 255, .78);
    border: 1px solid rgba(148, 163, 184, .35);
    resize: none;
    transition: background-color .2s, border-color .2s, box-shadow .2s;
}
.fb_text::placeholder { color: #94A3B8; }
.fb_text:focus {
    outline: none;
    background-color: rgba(255, 255, 255, .96);
    border-color: rgba(3, 105, 161, .55);
    box-shadow: 0 0 0 3px rgba(3, 105, 161, .12);
}

@media (max-width: 640px) {
    .turn_actions { max-width: 100%; }
}

/* 애니메이션을 줄여 달라고 한 사용자에게는 튐을 주지 않는다 */
@media (prefers-reduced-motion: reduce) {
    .fb_btn.is-on { animation: none; }
}

/* ── 프로토타입 전용: 디자인 프리뷰 툴바 ──────────────────────────────
 * 실제 서비스에는 없는 UI다. 각 모달/팝업을 띄워보기 위한 개발용 도구이며,
 * 운영에 옮길 때는 이 블록과 chat.html 하단의 #preview_bar 를 지우면 된다.
 * ------------------------------------------------------------------ */
#preview_bar {
    position: fixed;
    right: 12px;
    bottom: 12px;
    z-index: 10000;
    font-size: 12px;
}

#preview_bar summary {
    list-style: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 12px;
    border-radius: 9999px;
    background: #0f172a;
    color: #fff;
    box-shadow: 0 6px 20px rgba(15, 23, 42, .28);
    user-select: none;
}

#preview_bar summary::-webkit-details-marker { display: none; }

#preview_bar[open] summary { border-radius: 0.8rem 0.8rem 0 0; box-shadow: none; margin: 0 8px; }

#preview_bar .preview_panel {
    width: 220px;
    max-height: 60vh;
    overflow-y: auto;
    padding: 8px;
    border-radius: 8px;
    background: rgba(255,255,255,.75);
    border: 1px solid rgba(120,150,220,.25);
    box-shadow: 0 12px 32px rgba(15, 23, 42, .18);
}

#preview_bar .preview_group {
    padding: 6px 4px 3px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .04em;
    color: #94a3b8;
    text-transform: uppercase;
}

#preview_bar button {
    display: block;
    width: 100%;
    text-align: left;
    padding: 6px 8px;
    border-radius: 8px;
    color: #334155;
    background: transparent;
    cursor: pointer;
}

#preview_bar button:hover { background: rgba(255,255,255,.9); color: #1D4ED8; }

/* ==========================================================================
 * 계약서 분석 패널 — 원본 앱에서 쓰는 커스텀 유틸(Tailwind 기본에 없는 클래스)
 * ========================================================================== */
.text-md { font-size: 1rem; line-height: 1.5rem; }
.text-sm-md { font-size: .9375rem; line-height: 1.5rem; }
.min-w-1\/2 { min-width: 50%; }
.w-42 { width: 10.5rem; }
.duration-3 { transition-duration: 300ms; }
.break-keep { word-break: keep-all; }
.whitespace-wrap { white-space: normal; }

/* 드래그 중인 업로드 영역 */
/* 드롭존 스타일은 css/glassmorphism.css 의 .dropzone / .dropzone.is-dragover 가 담당한다.
   (기존 #contract_dropzone.is-dragover 규칙은 중복이라 제거) */

/* ==========================================================================
 * 계약서 분석 결과 모달 — 결과 패널 (.ctr-*)
 * --------------------------------------------------------------------------
 * 이 패널은 .glass-modal(불투명도 .96) 위에 얹힌다. 즉 바닥이 사실상 흰 판이라
 * 유리 어휘(bg-white/70 · border-white/70)를 그대로 쓰면 면도 선도 사라진다 —
 * 진행 상황 패널에서 겪은 문제와 같다. 여기서도 유리를 흉내내지 않고, 담는
 * 내용에 따라 표면을 나눈다.
 *
 *   .ctr-file    파일   = 출처.      브랜드 그라데이션 타일 + 교체 가능 신호
 *   .ctr-tab     탭     = 내비게이션. 평소 틴트 알약 / 선택 시 그라데이션
 *   .ctr-sheet   본문   = 읽는 면.    흰 면, 스크롤은 여기서만
 *   .ctr-field   추출값 = 데이터.     이름/값 두 칸의 괘선 표 한 행
 *   .ctr-risk    리스크 = 경고.       등급색 좌측 띠 + 틴트 배지
 *   .ctr-check   체크   = 판정.       상태색 아이콘 타일
 *
 * 내용물은 카드가 아니라 '읽는 글'로 다룬다. 이 화면이 담는 것은 계약서를
 * 분석한 결과지 대시보드가 아니다. 그래서 상자·그림자·큰 라운드를 걷어내고
 * 제목 괘선 + 이름/값 표로 옮겼다. 면과 상자가 남은 곳은 실제로 본문에서
 * 떠 있어야 하는 것(개선 제안, 판정 집계)뿐이다.
 *
 * 선과 면은 회색이 아니라 전부 블루틴트다(#E4EAF6 등). 페일 블루 톤 위에서
 * 순회색 선은 혼자 탁하게 뜬다 — .pcard / .ptable 과 같은 규칙이다.
 * ========================================================================== */

/* 패널 본문 — 바깥 틀은 고정하고 스크롤은 .ctr-sheet 안에서만 일어난다.
   (본문 전체가 스크롤하면 탭 레일까지 같이 밀려 올라가 위치를 잃는다)
   모달이던 시절보다 폭이 좁으므로 좌우 여백을 한 단 줄인다. */
#contract_result_body {
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
    padding: .875rem 1rem 1rem;
}

/* --- 채팅에 남는 분석 안내 카드 -------------------------------------------
   '계약서 분석'을 누를 때마다 대화에 한 장씩 쌓인다. 결과 자체가 아니라
   결과로 가는 입구라, 답변 말풍선(.md)과 같은 폭·같은 위치에 서되 면을 갖는다.
   유리는 쓰지 않는다 — 결과 패널과 같은 이유(글이 많고 정확히 읽혀야 한다). */
.ctr-intro {
    display: flex;
    flex-direction: column;
    gap: .625rem;
    max-width: 32rem;
    padding: 1rem 1.125rem 1.125rem;
    border: 1px solid #E2E8F0;
    border-radius: 1.25rem;
    background-color: #FFFFFF;
    box-shadow: 0 1px 2px rgba(70, 95, 170, .05), 0 12px 28px -22px rgba(70, 95, 170, .40);
    transition: border-color .2s, box-shadow .3s var(--ease-glass);
}
/* 지금 패널에 열려 있는 건 — 카드가 여럿 쌓였을 때 어느 것을 보고 있는지 */
.ctr-intro.is-active {
    border-color: rgba(29, 78, 216, .38);
    box-shadow: 0 1px 2px rgba(70, 95, 170, .05), 0 12px 28px -20px rgba(70, 95, 170, .45);
}
/* 카드 전체가 결과 패널로 가는 입구다(별도 버튼 없음).
   신호는 커서와 테두리 색으로만 준다 — box-shadow 를 덮으면 위 두 규칙의 층진 그림자가
   통째로 사라져 카드가 평평해진다. */
.ctr-intro { cursor: pointer; }
.ctr-intro:hover { border-color: rgba(29, 78, 216, .38); }

.ctr-intro__head { display: flex; align-items: center; gap: .625rem; min-width: 0; }
.ctr-intro__icon {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: .875rem;
    background-image: var(--grad-bubble);
    color: var(--on-brand);
    font-size: .875rem;
    box-shadow: 0 6px 14px -8px rgba(70, 110, 215, .75);
}
/* 업로드 요청 카드 — 아직 파일이 없는 상태. 아이콘 타일을 채우지 않고
   점선으로 두면 드롭존(.dropzone)과 같은 어휘가 되어 '여기에 파일을 넣는
   차례'로 읽힌다. 결과 카드(채운 그라데이션 타일)와 한눈에 구분된다. */
.ctr-intro--ask .ctr-intro__icon {
    border: 1px dashed rgba(29, 78, 216, .38);
    background-image: none;
    background-color: #F2F6FE;
    color: var(--brand-blue);
    box-shadow: none;
}

.ctr-intro__meta { display: flex; flex-direction: column; min-width: 0; }
.ctr-intro__title { font-size: .875rem; font-weight: 600; color: #15181D; }
.ctr-intro__file {
    font-size: .75rem;
    color: #64748B;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.ctr-intro__desc { font-size: .8125rem; line-height: 1.7; color: #475569; }

/* 무엇을 볼 수 있는지 — 탭 4개를 그대로 옮긴 목록.
   아이콘 · 이름 · 설명을 3열 격자로 세워 이름이 같은 x 좌표에서 시작한다. */
.ctr-intro__list { display: flex; flex-direction: column; gap: .4375rem; }
.ctr-intro__list li {
    display: grid;
    grid-template-columns: 1rem max-content 1fr;
    align-items: baseline;
    gap: .5rem;
    font-size: .8125rem;
    line-height: 1.6;
    word-break: keep-all;
}
.ctr-intro__list i { justify-self: center; font-size: .6875rem; color: #94A3B8; }
.ctr-intro__list b { font-weight: 600; color: #334155; }
.ctr-intro__list span { color: #64748B; }
.ctr-intro .btn-primary { align-self: flex-start; margin-top: .125rem; }

@media (max-width: 640px) {
    .ctr-intro__list li { grid-template-columns: 1rem 1fr; }
    .ctr-intro__list span { grid-column: 2; }
}

/* --- 우측 패널의 업로드 화면 ------------------------------------------------
   결과 화면과 같은 패널 자리에 열린다. 위에서부터 '무엇을 올리나 → 올리는 자리
   → 무엇을 받나 → 시작' 한 줄기로 세우고, 세로가 모자라면 이 안에서 스크롤한다
   (패널 머리글은 고정 — 결과 화면의 .ctr-sheet 와 같은 규칙). */
.ctr-upload {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    height: 100%;
    overflow-y: auto;
    padding-bottom: .25rem;
}
/* 머리글 — 아이콘 타일 + 두 줄 글. .modal-hero__inner 와 같은 구성이다
   (타일 · gap 14px · 세로 가운데). 글만 두 줄 있던 자리라 화면을 여는 지점이
   어디인지 표시가 없었다 — 이 앱에서 그 표시는 늘 아이콘 타일이 한다. */
.ctr-upload__head { display: flex; flex-direction: row; align-items: center; gap: .875rem; }
.ctr-upload__headtext { display: flex; flex-direction: column; min-width: 0; }
/* 타일은 .modal-hero__icon 규격(40px / 라운드 14px / 흰 유리 + 브랜드 블루).
   패널이 유리라 흰 타일이 그대로 뜬다 — 채색 그라데이션을 쓰면 이 화면에서
   유일한 색면이 되어 정작 주인공인 드롭존보다 먼저 눈에 들어온다. */
.ctr-upload__icon {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: .875rem;
    border: 1px solid rgba(255, 255, 255, .95);
    background-color: rgba(255, 255, 255, .82);
    box-shadow: var(--shadow-glass-sm);
    color: var(--brand-blue);
    font-size: .9375rem;
}
.ctr-upload__title { font-size: .9375rem; font-weight: 600; color: #15181D; }
.ctr-upload__desc { font-size: .8125rem; line-height: 1.7; color: #64748B; word-break: keep-all; }
.ctr-upload__zone { flex-shrink: 0; }
/* 목록은 요청 카드에서 쓰던 것을 그대로 가져온다 — 같은 안내는 같은 모양 */
.ctr-upload .ctr-intro__list { padding: 0 .125rem; }
.ctr-upload__confirm { align-self: flex-start; margin-top: auto; }

.ctr {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;

    /* 이 패널은 유리를 쓰지 않는다.
       반투명 면은 뒤 색이 글자 뒤로 올라와 실효 대비를 떨어뜨리는데, 여기는
       금액·날짜·조항처럼 정확히 읽혀야 하는 값이 빽빽한 화면이다. 분위기보다
       판독성이 먼저다. (문서 §1.3 이 말한 "유리가 성립하려면 뒤에 색이 있어야
       한다"는 조건 자체가, 글자가 많은 면에서는 대비와 정면으로 충돌한다.)

       대신 고전적이고 확실한 방식 — 옅은 회청 캔버스 위에 순백 페이지.
       면끼리 명도차가 뚜렷해 경계가 선 없이도 잡히고, 글자는 항상 순백 위에
       놓여 대비가 최대가 된다.

       글자색은 앱 전역의 ink 팔레트(남보라 계열)를 쓰지 않고 여기서만 따로
       잡는다. ink-950(#0A1E3D)·ink-900(#0F2A4F)은 보라가 섞여 있어 글이
       많아지면 화면 전체가 파랗게 물든 것처럼 보인다. 읽는 화면은 잉크가
       검고 회색이 중립이어야 하므로 거의 블랙 + 슬레이트 그레이로 간다.
       색은 강조점(조항 칩·등급 배지·개선 제안)에만 남긴다. */
    --ctr-surface: #FFFFFF;
    --ctr-stroke: #E2E8F0;   /* 판면·표 바깥선 */
    --ctr-line: #EDF1F6;     /* 표 안쪽 행 구분선 */
    --ctr-rule: #C4CEDC;     /* 표 머리 괘선 · 절 제목 밑줄(제일 진한 선) */
    --ctr-lift: 0 1px 2px rgba(70, 95, 170, .04);

    /* 잉크 4단 — 흰 면 대비: 16.9 / 10.4 / 7.6 / 4.8 */
    --ctr-ink: #15181D;      /* 문서 제목, 표의 값 */
    --ctr-head: #334155;     /* 절 제목, 소제목 */
    --ctr-body: #475569;     /* 본문 */
    --ctr-muted: #64748B;    /* 라벨·캡션(순백 위에서만 쓴다) */
}

/* 레일(파일 + 탭) 위 / 본문 아래 — 항상 세로로 쌓는다.
   출처(어떤 파일인가) → 내비(무엇을 볼 것인가) → 본문(내용) 순으로 읽힌다.
   가로 2단이던 시절의 @media (min-width:1024px) 규칙은 제거했다. */
/* 파일 카드(고정 정보) / 탭+본문 카드 두 덩어리 */
.ctr__layout {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: .75rem;
    min-height: 0;
}
.ctr__layout > .ctr-file { flex-shrink: 0; }

/* 탭 줄과 본문을 담는 한 장의 카드.
   · overflow: hidden  — 안쪽 시트가 스크롤할 때 내용이 둥근 모서리를 넘지 않게
   · min-height: 0     — flex 자식이 내용 크기 아래로 줄어들 수 있어야 시트에
                         스크롤이 생긴다. 빠지면 카드가 늘어나 모달이 넘친다. */
.ctr__panel {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
    border: 1px solid var(--ctr-stroke);
    border-radius: 1.25rem;
    background-color: white;
    box-shadow: var(--ctr-lift);
}

/* --- 업로드한 계약서 ------------------------------------------------------
   눌러서 다른 파일로 교체할 수 있다는 신호가 필요하다. 점선 드롭존을 다시
   그리면 "아직 안 올렸다"로 읽히므로, 결과가 나온 뒤에는 채워진 카드 + 우측
   교체 아이콘으로 바꾼다(아이콘은 hover 에서 또렷해진다). */
.ctr-file {
    display: flex;
    align-items: center;
    gap: .625rem;
    width: 100%;
    /* 우측만 더 넓게 — 왼쪽은 36px 아이콘 타일이라 12px 여백으로도 덩어리가
       잡히지만, 오른쪽은 11px 글리프 하나뿐이라 같은 값이면 모서리에 붙어
       보인다. hover 에서 90도 회전하는 것도 감안해 여유를 둔다. */
    padding: .6875rem 1.125rem .6875rem .75rem;
    border: 1px solid #E4EAF6;
    border-radius: 1rem;
    background-color: #F7FAFF;
    text-align: left;
    cursor: pointer;
    transition: background-color .2s, border-color .2s, box-shadow .3s var(--ease-glass);
}
.ctr-file:hover {
    border-color: rgba(29, 78, 216, .35);
    background-color: #FFFFFF;
    box-shadow: var(--shadow-glass-sm);
}
.ctr-file__icon {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: .75rem;
    background-image: var(--grad-bubble);
    color: var(--on-brand);
    font-size: .875rem;
    box-shadow: 0 6px 14px -8px rgba(70, 110, 215, .75);
}
.ctr-file__meta { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.ctr-file__label {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .06em;
    color: var(--ctr-muted);
}
.ctr-file__name {
    font-size: .8125rem;
    font-weight: 600;
    color: var(--ctr-ink);
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.ctr-file__swap {
    flex-shrink: 0;
    font-size: 15px;
    color: var(--ctr-muted);
    opacity: .5;
    transition: opacity .2s, transform .3s var(--ease-glass);
}
.ctr-file:hover .ctr-file__swap { opacity: 1; transform: rotate(90deg); }

/* --- 탭 ------------------------------------------------------------------
 * 세로 레일일 때는 탭마다 면을 깔아 "줄"로 세웠지만, 가로 한 줄이 된 지금은
 * 그 방식이 알약 4개가 떠 있는 것으로 보인다. 하나의 오목한 트랙에 넣어
 * 단일 선택 컨트롤로 묶고, 트랙이 본문 시트 바로 위에 놓여 "이 탭이 아래
 * 시트를 바꾼다"는 관계가 위치로 읽히게 한다.
 * (마켓플레이스 필터 .tool-filters 와 같은 어휘 — 앱 전체에서 일관된다)
 * ------------------------------------------------------------------------ */
/* 트랙(면 + 라운드)을 없앤 밑줄 탭. 시트 바로 위에 붙어(.ctr__layout gap:0)
   활성 탭의 그라데이션 밑줄이 시트 윗선에 그대로 얹힌다 — 어느 탭이 아래
   면을 열고 있는지가 위치로 읽힌다.
   기준선은 따로 긋지 않는다. 시트의 top border 가 이미 그 역할을 하므로
   여기서 border-bottom 을 주면 1px 짜리 선이 두 줄로 겹쳐 보인다. */
.ctr__nav {
    display: flex;
    gap: .125rem;
    flex-shrink: 0;
    /* 첫 탭 라벨이 시트 안쪽 여백(1.25rem)과 같은 선에서 시작하도록
       nav .25rem + 탭 1rem = 1.25rem */
    padding: 0 0 0 .25rem;
    /* 카드 안 머리줄 — 흰 면으로 본문(캔버스)과 층을 나누고, 아래 선이 경계 */
    background-color: var(--ctr-surface);
    border-bottom: 1px solid var(--ctr-stroke);
    overflow-x: auto;
    scrollbar-width: none;
    -ms-overflow-style: none;
}
.ctr__nav::-webkit-scrollbar { display: none; }

/* 폭을 늘려 꽉 채우지 않는다 — 탭마다 좌우 1rem 패딩이 간격을 만들고,
   라벨 길이만큼만 차지한다(넓은 화면에서 4등분하면 짧은 라벨의 밑줄이
   불필요하게 길어져 무엇이 선택됐는지가 오히려 흐려진다). */
.ctr-tab {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .375rem;
    flex: 0 0 auto;
    min-width: 0;
    padding: .7rem 1rem .7rem;
    border: 0;
    border-radius: 0;
    background-color: transparent;
    color: var(--ctr-body);      /* 흰 면 위 7.6:1 */
    font-size: .8125rem;
    font-weight: 600;
    line-height: 1.35;
    white-space: nowrap;
    text-align: left;
    cursor: pointer;
    transition: color .2s;
}

/* 활성 밑줄 — 단색. border-bottom 은 두께·라운드를 따로 못 잡아 바를 깐다.
   bottom: -1px 로 시트의 윗선 위에 살짝 겹쳐 두 요소가 이어져 보이게 한다. */
.ctr-tab::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 3px;
    border-radius: 9999px 9999px 0 0;
    background-color: var(--brand-blue);
    opacity: 0;
    transition: opacity .2s;
}
.ctr-tab.is-active::after { opacity: 1; }
/* 아이콘은 타일을 벗고 맨 글리프로. 24px 흰 사각형이 탭마다 붙어 있던 것이
   높이와 무게를 다 잡아먹고 있었다. 평소엔 조용히 라벨 앞에 서고, 활성일 때만
   브랜드색으로 켜진다. */
.ctr-tab__icon {
    display: inline-grid;
    place-items: center;
    flex-shrink: 0;
    color: #94A3B8;
    /* 라벨과 같은 13px. 12px 글리프에 그라데이션을 얹으면 색이 뭉개진다
       (docs 의 .text-grad 주의사항과 같은 이유) */
    font-size: .8125rem;
    transition: color .2s;
}

/* 밑줄 탭에서는 면을 칠하지 않는다 — 글자색만 진해진다 */
.ctr-tab:hover:not(.is-active) { color: var(--ctr-ink); }
.ctr-tab:hover:not(.is-active) .ctr-tab__icon { color: var(--ctr-body); }

/* 활성 = 그라데이션 밑줄 + 진한 글자. 면·테두리·그림자는 없다. */
.ctr-tab.is-active {
    color: var(--brand-blue);
}
/* 활성 아이콘도 밑줄과 같은 단색 — 내비 한 줄에 색은 하나면 충분하다 */
.ctr-tab.is-active .ctr-tab__icon { color: var(--brand-blue); }

/* --- 본문 시트 ------------------------------------------------------------
 * 면·테두리·라운드는 부모 .ctr__panel 이 갖는다. 여기 남는 건 여백과
 * 스크롤뿐이다. 이 화면에서 스크롤이 생기는 유일한 곳.
 *
 * 안에 담기는 것들은 '카드 격자'가 아니라 '문서를 읽는 면'의 어휘로 짠다.
 * 종이를 흉내내는 게 아니라(표지·서지·면책 같은 건 넣지 않는다) 계약서를
 * 읽을 때 눈이 기대하는 형태 — 제목 아래 괘선, 이름/값 두 칸 표, 한 단으로
 * 쭉 이어지는 조항 — 를 그대로 쓴다. 모달 폭이 좁아지면 표가 한 칸으로
 * 접히므로 웹·모바일 어디서든 같은 구조가 유지된다.
 * ------------------------------------------------------------------------ */
.ctr-sheet {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-width: 0;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 1.375rem 1.5rem 1.5rem;
}
@media (max-width: 640px) {
    .ctr-sheet { padding: 1.125rem 1rem 1.25rem; }
}

/* 절 사이 간격. 제목마다 괘선이 붙었으므로 이 간격은 선과 선 사이의 숨 쉴
   자리다 — 좁히면 표가 연달아 붙어 한 덩어리로 보인다.

   ※ flex-shrink 는 0 이어야 한다(예전 값: flex: 1 1 auto + min-height: 0).
     줄어들 수 있으면 내용이 긴 탭 — 필드 추출처럼 블록이 여럿 쌓이는 탭 —
     에서 이 상자가 시트 높이만큼 눌린다. 안의 블록들은 min-height: auto 라
     같이 줄지 않으므로 내용이 상자 '밖으로' 흘러나가는데, 자손의 넘침은
     스크롤 컨테이너의 끝 패딩을 데려가지 않는다. 그래서 맨 아래까지 내리면
     .ctr-sheet 의 padding-bottom(1.5rem)이 사라지고 글이 모달 바닥에 붙었다.
     늘어나기(grow)는 남겨 둔다 — 빈 상태(.ctr-empty)가 margin:auto 로
     가운데 서려면 이 상자가 시트 높이를 채워야 한다. */
.ctr-pane {
    display: flex;
    flex-direction: column;
    flex: 1 0 auto;
    gap: 2rem;
}

/* --- 섹션 ---------------------------------------------------------------
   제목 아래 괘선 한 줄. 카드 격자였을 때는 이 선이 화면을 가로로 썰어
   답답했지만(그래서 한 번 걷어냈었다), 카드가 사라지고 표가 들어온 지금은
   반대로 이 선이 없으면 섹션이 시작됐다는 신호가 없다. 아래 표의 머리
   괘선과 짝을 이뤄 '제목 — 표' 한 벌로 읽힌다. */
.ctr-block { display: flex; flex-direction: column; gap: .9375rem; min-width: 0; }

/* 회색 괘선만 반복되면 섹션이 전부 같은 무게로 깔린다. 밑줄 왼쪽 끝에만
   브랜드 블루를 겹쳐 시작점을 표시한다. 그라데이션(하늘→파랑→분홍)은 쓰지
   않는다 — 36px 폭에서는 축이 보이지 않고 분홍만 잘려 나와, 계약 검토
   화면에 뜬금없는 색이 하나 얹힌 것처럼 보인다.

   두 줄 다 배경으로 그린다. ::after + position:relative 로 그리면 이 상자가
   컨테이닝 블록이 되어, PC 에서 탭 줄로 올려 보내는 .ctr-block__actions 가
   여기에 갇힌다(아래 min-width:1024px 규칙). */
.ctr-block__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .75rem;
    flex-wrap: wrap;
    padding-bottom: .5rem;
    background-image:
        linear-gradient(var(--brand-blue), var(--brand-blue)),
        linear-gradient(var(--ctr-rule), var(--ctr-rule));
    background-repeat: no-repeat;
    background-size: 2.25rem 2px, 100% 1px;
    background-position: left bottom, left bottom;
}
.ctr-block__title {
    display: flex;
    align-items: baseline;
    gap: .5rem;
    font-size: 1.0625rem;
    font-weight: 600;
    letter-spacing: -.015em;
    color: var(--ctr-ink);
    word-break: keep-all;
}

/* 본문 글자 크기의 천장은 탭 라벨(13px)이다. 섹션 제목만 그 위로 올라가고,
   나머지는 전부 13px 이하 · 굵기 400~600 으로 눕힌다. 굵은 글자가 많으면
   문서가 아니라 UI 라벨 모음처럼 보인다 — 강조는 굵기가 아니라 위치(이름
   칸)와 색(등급·조항)이 맡는다. */
/* 액션은 시트 안, 섹션 제목 줄의 오른쪽 끝에 붙는다.

   한동안 PC 에서만 탭 줄 오른쪽으로 올려 두었는데(결과가 모달이던 시절의
   폭 기준), 결과가 우측 패널로 옮겨 오면서 탭 4개와 자리를 다투게 됐다.
   탭 줄은 overflow-x: auto 라 탭이 액션 밑으로 흘러 들어가 서로 가린다.
   → 올리지 않고 시트 안에 둔다. 제목은 줄어들 수 있게(min-width: 0),
   액션은 줄어들지 않게(flex-shrink: 0) 해서 폭이 좁아지면 제목이 먼저
   양보하고, 그래도 모자라면 액션이 통째로 다음 줄 오른쪽 끝으로 내려간다. */
.ctr-block__title { flex: 1 1 auto; min-width: 0; }
.ctr-block__actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: .5rem;
    flex: 0 0 auto;
    flex-wrap: wrap;
    margin-left: auto;
}

/* 리드 문단 — 섹션을 열었을 때 눈이 처음 닿는 자리.
   글자를 키워 세우면 본문보다 큰 덩어리가 되어 오히려 읽는 부담이 된다.
   크기는 본문과 같게 두고, 옅은 면과 왼쪽 그라데이션 띠로 '요지'라는 것만
   표시한다. 요약 탭과 리스크 탭의 도입부가 이 클래스를 공유한다. */
.ctr-text {
    position: relative;
    overflow: hidden;
    padding: .9375rem 1.125rem;
    border-radius: 1rem;
    background-color: #F5F8FE;
    box-shadow: none;
    font-size: .8125rem;
    line-height: 1.85;
    color: var(--ctr-head);
    word-break: keep-all;
}

/* 문장 속 등급어 — 카드의 등급 배지와 같은 색을 쓴다.
   면을 깔지 않고 글자색만 올린다(문장 안에 배지가 박히면 글이 끊긴다). */
.ctr-lv { font-weight: 600; }
.ctr-lv[data-level="CRITICAL"] { color: #BE123C; }
.ctr-lv[data-level="HIGH"] { color: #C2410C; }
.ctr-lv[data-level="INTERMEDIATE"] { color: #B45309; }
.ctr-lv[data-level="LOW"] { color: #047857; }
/* 왼쪽 띠 — 상자 왼쪽 모서리에 붙인다. overflow:hidden 이라 띠의 위아래가
   상자의 라운드를 그대로 따라 잘려, 왼쪽 테두리만 굵어진 모양이 된다
   (.ctr-note--brand 와 같은 처리).
   색은 그라데이션이 아니라 단색이다. 3px 폭에 135deg 그라데이션을 얹으면
   축(하늘→파랑→분홍)이 보이는 게 아니라 그중 한 색만 잘려 나온다. */
.ctr-text::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 3px;
    background-color: var(--brand-blue);
}

/* 조항 칩 — 어느 조에서 나온 값인지가 이 화면의 핵심 메타다.
   모양은 앱 전체의 칩(알약)과 같이 간다. 이 패널만 각진 꼬리표를 쓰면
   같은 화면 안에서 다른 컴포넌트처럼 보인다. */
.ctr-chip {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    padding: .0625rem .5rem;
    border: 1px solid #DCE6FD;
    border-radius: 9999px;
    background-color: #EEF3FE;
    font-size: 11px;
    font-weight: 500;
    line-height: 1.55;
    color: var(--brand-blue);
    white-space: nowrap;
}
.ctr-chip--muted {
    border-color: var(--ctr-stroke);
    background-color: #F4F6F9;
    color: var(--ctr-body);
}

/* --- 요약 ---------------------------------------------------------------
   원문은 <br>· 로 이어붙인 한 덩어리다. 그대로 흘리면 몇 가지 이야기인지
   세어지지 않는다. 항목으로 끊고 앞머리를 굵게 세워 훑을 수 있게 한다. */
/* 항목마다 상자를 두르면 문장 6개가 카드 6장이 되어 읽는 글이 아니라 목록
   컴포넌트로 보인다. 상자 대신 얇은 구분선으로 행을 나누고, 앞에 번호를 세워
   "핵심 N건"이 실제로 세어지게 한다. 번호는 counter 라 마크업은 그대로다. */
/* 표는 표대로 두되(이름 칸 + 행 구분선) 바깥은 둥근 상자로 닫는다.
   위아래 괘선만 긋는 인쇄물식 표는 이 앱 안에서 혼자 각져 보인다 —
   주변이 전부 16~24px 라운드다. 유리는 쓰지 않는다(글자가 빽빽한 면에서
   반투명은 대비를 떨어뜨린다). 불투명 흰 면 + 얇은 테두리로 같은 부드러움을
   낸다. overflow:hidden 이라 첫 행·끝 행의 이름 칸 틴트도 모서리를 따라
   깔끔하게 잘린다. */
.ctr-sum {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    padding: 0;
    border: 1px solid var(--ctr-stroke);
    border-radius: 1rem;
    background-color: var(--ctr-surface);
    box-shadow: var(--ctr-lift);
}

/* 이름 / 내용 2열 표. 이 데이터는 "구독료 = 월 99,000원 (제6조)" 처럼
   키·값·근거 세 조각이라 문장 목록이 아니라 스펙 표가 맞다. 이름을 같은
   x 좌표에 세우면 훑을 때 왼쪽 열만 따라 내려가면 된다.
   불릿·번호는 두지 않는다 — 이름 열이 이미 그 자리를 잡는다. */
/* 이름 칸에 면을 깔아 두 칸을 나눈다(.ctr-field 와 같은 어법) */
.ctr-sum__item {
    display: grid;
    grid-template-columns: minmax(6rem, 9rem) 1fr;
    align-items: stretch;
    gap: 0;
    padding: 0;
    border-top: 1px solid var(--ctr-line);
}
.ctr-sum__item:first-child { border-top: 0; }

/* 앞머리 라벨이 없는 문장은 표가 아니라 그냥 한 줄이므로 전체 폭을 쓴다 */
.ctr-sum__item--full { grid-template-columns: 1fr; }

.ctr-sum__key {
    display: flex;
    align-items: center;
    padding: .8125rem .875rem;
    background-color: #F4F7FB;
    font-size: .8125rem;
    font-weight: 500;
    line-height: 1.6;
    color: var(--ctr-head);
    word-break: keep-all;
}

/* 값 + 근거 조항은 한 덩어리 — 값이 길어 줄바꿈되면 칩이 아래로 흐른다 */
.ctr-sum__body {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: .375rem;
    min-width: 0;
    padding: .8125rem 1rem;
}
.ctr-sum__val {
    font-size: .8125rem;
    font-weight: 400;
    line-height: 1.75;
    color: var(--ctr-ink);
    min-width: 0;
}

/* 좁아지면 이름이 값 위로 올라간다(9rem 열을 유지하면 값이 눌린다) */
@media (max-width: 640px) {
    .ctr-sum__item { grid-template-columns: 1fr; }
    .ctr-sum__key { padding: .375rem .75rem; }
    .ctr-sum__body { padding: .5rem .75rem .625rem; }
}


/* --- 필드 추출 -----------------------------------------------------------
   추출값은 짧은 이름/값 쌍이라 한 단으로 늘어놓으면 오른쪽이 통째로 빈다.
   넓은 화면에서만 두 단으로 나눠 표 두 장이 나란히 서게 한다(모바일은 한 단).
   반대로 리스크·체크는 긴 글이라 항상 한 단이다 — 폭이 아니라 내용이 단을
   정한다. */
/* 항상 한 단이다. 결과가 모달(최대 1600px)에서 우측 패널(~900px)로 옮겨 오면서
   2단으로 쪼갤 폭이 남지 않는다 — 이름 칸 9rem 을 뺀 값 열이 너무 좁아진다. */
.ctr-cols { display: grid; grid-template-columns: 1fr; gap: 2rem; }
.ctr-col { display: flex; flex-direction: column; gap: 2rem; min-width: 0; }

/* 추출값도 요약 탭과 같은 이름/값 표로 맞춘다. 라벨을 값 위에 얹고 격자로
   흩어 놓으면 이름과 값이 짝지어 보이지 않아 글자 무더기가 된다.
   같은 x 좌표에 이름을 세우면 왼쪽 열만 따라 내려가며 훑을 수 있다. */
/* 추출값 = 타일 격자였다. 타일은 값 하나를 카드 하나로 만들어 훑기엔 좋지만,
   '계약 체결일 2025-02-18' 같은 이름/값 쌍이 9개 흩어지면 문서의 표가 아니라
   상태 위젯 벽이 된다. 요약 표(.ctr-sum)와 같은 어법으로 통일한다 —
   왼쪽에 이름, 오른쪽에 값, 행마다 얇은 괘선. */
.ctr-fields {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    border: 1px solid var(--ctr-stroke);
    border-radius: 1rem;
    background-color: var(--ctr-surface);
    box-shadow: var(--ctr-lift);
}
/* 홀짝 줄무늬는 쓰지 않는다 — 값이 짧은 표에서는 무늬만 남아 촌스럽다.
   대신 이름 칸에만 옅은 면을 깔아 두 칸을 나눈다. 세로로 이어진 이름 열이
   그 자체로 표의 머리 칸이 되어, 선을 더 긋지 않아도 구조가 잡힌다. */
.ctr-field {
    display: grid;
    grid-template-columns: minmax(6rem, 9rem) 1fr;
    align-items: stretch;
    gap: 0;
    min-width: 0;
    padding: 0;
    border: 0;
    border-top: 1px solid var(--ctr-line);
    border-radius: 0;
    background-color: transparent;
    box-shadow: none;
}
.ctr-field:first-child { border-top: 0; }

.ctr-field__label {
    display: flex;
    align-items: center;
    padding: .6875rem .875rem;
    background-color: #F4F7FB;
    font-size: .8125rem;
    font-weight: 500;
    line-height: 1.6;
    color: var(--ctr-head);
    word-break: keep-all;
}
/* 날짜·금액이 대부분이라 등폭 숫자를 쓴다. 자릿수가 세로로 맞아떨어져야
   ₩9,000,000 / 2026-02-17 같은 값이 데이터로 읽힌다. */
.ctr-field__value {
    display: flex;
    align-items: center;
    padding: .6875rem 1rem;
    font-size: .8125rem;
    font-weight: 400;
    line-height: 1.65;
    letter-spacing: -.01em;
    font-variant-numeric: tabular-nums;   /* 날짜·금액 자릿수 정렬 */
    color: var(--ctr-ink);
    word-break: break-word;
    min-width: 0;
}

/* 좁아지면 이름이 값 위로 — 표 두 칸을 유지하면 값이 눌린다(.ctr-sum 과 동일).
   이름이 한 줄 위로 올라오는 만큼 행 높이가 늘어나므로 줄간·패딩을 함께 줄인다 */
@media (max-width: 640px) {
    .ctr-field { grid-template-columns: 1fr; }
    .ctr-field__label { padding: .375rem .75rem; line-height: 1.5; }
    .ctr-field__value { padding: .5rem .75rem .625rem; line-height: 1.6; }
}

/* 계약 당사자 — 어느 쪽이 '내 입장'인지가 리스크 해석의 전제라 강조한다 */
/* 요약·기본정보 표와 같은 어법(둥근 상자 + 행 구분선) */
.ctr-parties {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    padding: 0;
    border: 1px solid var(--ctr-stroke);
    border-radius: 1rem;
    background-color: var(--ctr-surface);
    box-shadow: var(--ctr-lift);
}
/* 당사자 두 줄이 똑같은 카드면 "어느 쪽이 내 입장인가"가 안 보인다.
   기본은 면 없이 두고, .is-me 에만 브랜드 틴트를 줘서 하나만 켜지게 한다. */
.ctr-party {
    display: flex;
    align-items: center;
    gap: .625rem;
    padding: .6875rem .75rem;
    border: 0;
    border-top: 1px solid var(--ctr-line);
    border-radius: 0;
    background-color: transparent;
}
.ctr-party:first-child { border-top: 0; }
/* 회사명 첫 글자 아바타 — 각을 살린 사각 인장에 가깝게 */
.ctr-party__icon {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 1.875rem;
    height: 1.875rem;
    border-radius: 9999px;
    background-color: #EEF1F6;
    border: 0;
    color: var(--ctr-body);
    font-size: .8125rem;
    font-weight: 600;
}
.ctr-party__body { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.ctr-party__name { font-size: .8125rem; font-weight: 500; color: var(--ctr-ink); }
.ctr-party__role { font-size: 11px; color: var(--ctr-muted); }
/* 내 입장인 쪽은 아바타만 브랜드로 켠다 — 리스크 해석의 전제라 한눈에
   보여야 하지만, 행 전체를 칠하면 표 안에 색면이 생겨 정돈이 깨진다. */
.ctr-party.is-me .ctr-party__name { color: var(--ctr-ink); }
.ctr-party.is-me .ctr-party__icon {
    background-color: var(--brand-blue);
    color: #FFFFFF;
}

/* 추가 항목 = 소제목 + 문단이 반복되는 부분. 카드로 두르면 다섯 장이 쌓여
   또 상자 밭이 되므로, 항목마다 얇은 괘선으로 끊고 제목 옆에 근거 조항을
   붙인다. 문서의 '별표(別表)' 같은 자리다. */
.ctr-extras {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    border: 1px solid var(--ctr-stroke);
    border-radius: 1rem;
    background-color: var(--ctr-surface);
    box-shadow: var(--ctr-lift);
}
.ctr-extra {
    display: flex;
    flex-direction: column;
    gap: .375rem;
    padding: .9375rem 1rem;
    border: 0;
    border-top: 1px solid var(--ctr-line);
    border-radius: 0;
    background-color: transparent;
    box-shadow: none;
}
.ctr-extra:first-child { border-top: 0; }
.ctr-extra__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .5rem;
    flex-wrap: wrap;
}
.ctr-extra__title {
    font-size: .8125rem;
    font-weight: 600;
    letter-spacing: -.01em;
    color: var(--ctr-ink);
}
.ctr-extra__text { font-size: .8125rem; line-height: 1.8; color: var(--ctr-body); word-break: keep-all; }

/* --- 리스크 --------------------------------------------------------------
   2단 격자를 쓰지 않는다. 리스크 하나는 설명·개선 제안·상대방 대응까지
   묶인 긴 글이라, 반으로 접으면 줄이 짧아져 읽기 리듬이 끊기고 좌우로
   번갈아 보게 된다. 한 건이 가로 폭을 다 쓰고 위에서 아래로 쌓인다. */
.ctr-risks { display: flex; flex-direction: column; gap: .75rem; }

/* 한 건 = 카드 한 장. 등급은 세 번 말하지 않는다 —
   테두리는 중립 회색으로 두고(카드가 색 상자가 되면 네 장이 알록달록해진다),
   등급색은 ① 우상단에서 번지는 빛과 ② 배지, 두 곳에서만 말한다.
   빛은 모서리에서 시작해 흰 면으로 사라지는 원형 그라데이션이라, 면을 칠한
   게 아니라 카드가 그 색을 머금은 것처럼 보인다(글자 뒤로 갈수록 투명해져
   본문 대비를 건드리지 않는다 — 유리를 쓰지 않는 이유와 같다). */
.ctr-risk {
    position: relative;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    gap: 0;
    padding: 0;
    border: 1px solid var(--ctr-stroke);
    border-radius: 1.125rem;
    background-color: var(--ctr-surface);
    background-repeat: no-repeat;
    box-shadow: var(--shadow-glass-sm);
    transition: box-shadow .3s var(--ease-glass), border-color .2s;
}
/* 마우스를 올리면 살짝 떠오른다 — 4건을 훑을 때 지금 보는 건이 어느 것인지
   드러나고, 카드가 '한 덩어리'라는 것도 이때 확인된다. */
.ctr-risk:hover {
    border-color: #D7DFEA;
    box-shadow: var(--shadow-glass);
}
/* 머리줄 — 등급 배지 · 번호 · 조항이 한 줄에 앉는 곳.
   면도 밑줄도 두지 않는다. 우상단 빛이 이 줄을 지나가는데 그 위에 면이나
   선을 얹으면 번지는 흐름이 잘려 보인다. 구분은 여백이 한다. */
.ctr-risk__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    flex-wrap: wrap;
    padding: 1.0625rem 1.125rem .5rem;
}
.ctr-risk__id { display: flex; align-items: center; gap: .5rem; }
.ctr-risk__no {
    font-size: .8125rem;
    font-weight: 600;
    letter-spacing: -.01em;
    color: var(--ctr-ink);
}
.ctr-risk__body {
    display: flex;
    flex-direction: column;
    gap: .875rem;
    padding: .375rem 1.125rem 1.125rem;
}

.ctr-level {
    padding: .125rem .5rem;
    border-radius: 9999px;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .06em;
    color: var(--ctr-body);
    background-color: #EEF1F6;
}
/* 등급색 — 면은 파스텔, 글자는 선명하게.
   ① 솔리드 면 + 흰 글자는 이 화면(파스텔 오로라 배경 위의 유리 모달)에서
      혼자 튄다. ② 그렇다고 예전처럼 면을 파스텔로 두고 글자를 -800/-900
      갈색으로 내리면 네 등급이 전부 탁한 흙색으로 뭉친다.
   → 면은 맑은 파스텔(-100 대), 글자는 채도가 살아 있는 -600/-700 로 잡는다.
   띠는 배지보다 한 단 진한 파스텔이라 세로로 훑을 때 먼저 눈에 걸린다. */
/* 우상단에서 번지는 등급색 — 모서리를 원의 중심으로 삼아 카드 안쪽으로
   퍼지다 흰 면에 녹는다. 반지름(18rem×12rem)은 카드 폭의 1/4 쯤 — 이보다
   작으면 구석에 색 덩어리가 뭉쳐 얼룩처럼 보이고, 크면 본문 뒤까지 색이
   깔려 글자 대비를 갉아먹는다. 알파는 배지 색이 무엇인지 알아볼 만큼만. */
.ctr-risk[data-level="CRITICAL"] {
    background-image: radial-gradient(18rem 12rem at 100% 0%, rgba(244, 63, 94, .38) 0%, rgba(244, 63, 94, 0) 70%);
}
.ctr-risk[data-level="HIGH"] {
    background-image: radial-gradient(18rem 12rem at 100% 0%, rgba(251, 146, 60, .42) 0%, rgba(251, 146, 60, 0) 70%);
}
.ctr-risk[data-level="INTERMEDIATE"] {
    background-image: radial-gradient(18rem 12rem at 100% 0%, rgba(251, 191, 36, .42) 0%, rgba(251, 191, 36, 0) 70%);
}
.ctr-risk[data-level="LOW"] {
    background-image: radial-gradient(18rem 12rem at 100% 0%, rgba(16, 185, 129, .34) 0%, rgba(16, 185, 129, 0) 70%);
}
.ctr-risk[data-level="CRITICAL"] .ctr-level { color: #E11D48; background-color: #FFE4EA; }
.ctr-risk[data-level="HIGH"] .ctr-level { color: #C2410C; background-color: #FFEBDC; }
.ctr-risk[data-level="INTERMEDIATE"] .ctr-level { color: #B45309; background-color: #FEF2CE; }
.ctr-risk[data-level="LOW"] .ctr-level { color: #047857; background-color: #D6FBE9; }
/* 머리줄에 깔던 등급 틴트는 걷었다 — 카드 우상단 그라데이션이 같은 일을
   더 부드럽게 하고, 둘을 겹치면 색 면이 두 겹으로 쌓여 탁해진다. */

/* 라벨 — 내용이 아니라 이름표라 작고 조용하게(.plabel 과 같은 급) */
.ctr-label {
    display: flex;
    align-items: center;
    gap: .375rem;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .02em;
    color: var(--ctr-muted);
}
.ctr-label i { font-size: 10px; color: #94A3B8; }

.ctr-sub { display: flex; flex-direction: column; gap: .3125rem; }
.ctr-sub__text { font-size: .8125rem; line-height: 1.8; color: var(--ctr-body); word-break: keep-all; }

/* 주석 — 본문에서 한 칸 들여쓴 곳. 상자 두 개가 연달아 붙으면 리스크 한 건이
   또 상자 밭이 되므로, 기본 주석은 면 없이 왼쪽 괘선만 세운 인용 블록이다.
   면을 갖는 건 아래 --brand(개선 제안) 하나뿐이다. */
.ctr-note {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: .375rem;
    padding: .125rem 0 .125rem .875rem;
    border: 0;
    border-radius: 0;
    background-color: transparent;
}
/* 인용 막대도 알약으로 — 직선 border-left 는 둥근 이웃들 사이에서 혼자
   각진 선으로 남는다 */
.ctr-note::before {
    content: '';
    position: absolute;
    left: 0;
    top: .1875rem;
    bottom: .1875rem;
    width: 2px;
    border-radius: 9999px;
    background-color: #CBD5E1;
}
/* 개선 제안 — 사용자가 가져갈 결론이라 주석 중 이것만 색을 갖는다.
   막대는 상자 안에 띄우지 않고 왼쪽 모서리에 붙인다. overflow:hidden 이라
   막대의 위아래가 상자의 라운드를 그대로 따라 잘려, 테두리 왼쪽만 굵어진
   것처럼 보인다(모서리 밖으로 삐져나오지 않는다). */
.ctr-note--brand {
    overflow: hidden;
    padding: .8125rem 1rem .8125rem 1.0625rem;
    border-radius: .875rem;
    background-color: #F4F8FF;
}
.ctr-note--brand::before {
    left: 0;
    top: 0;
    bottom: 0;
    width: 3px;
    border-radius: 0;
    background-color: var(--brand-blue);
}
.ctr-note--brand .ctr-label i { color: var(--brand-blue); }
.ctr-note__text { font-size: .8125rem; line-height: 1.8; color: var(--ctr-body); word-break: keep-all; }

.ctr-reply { display: flex; flex-direction: column; gap: .125rem; }
.ctr-reply__label {
    display: flex;
    align-items: center;
    gap: .25rem;
    font-size: 11px;
    font-weight: 500;
    color: var(--ctr-head);
}
.ctr-reply__label i { font-size: 9px; color: #94A3B8; }
.ctr-reply__text {
    padding-left: .8125rem;
    font-size: .8125rem;
    line-height: 1.8;
    color: var(--ctr-body);
    word-break: keep-all;
}

/* --- 체크리스트 ----------------------------------------------------------- */
/* 이 화면에서 제일 먼저 보여야 할 것 = 통과/주의/미흡 개수.
   숫자를 22px → 34px 로 키우고 테두리를 지워, 색면 위에 숫자만 남긴다. */
/* 파스텔 면 3장을 나란히 깔면 색이 넓게 퍼져 탁해 보인다(같은 색이라도
   면적이 커질수록 채도가 낮아 보인다). 면은 흰색으로 되돌리고, 색은 위쪽
   3px 띠와 숫자에만 남긴다 — 좁은 면적에서 색은 오히려 선명해진다.
   숫자는 왼쪽 정렬. 가운데 정렬은 칸이 넓어질수록 숫자와 라벨이 허공에
   떠 투박해 보인다. */
.ctr-stats {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: .625rem;
}
/* 상태 타일 — 아이콘 · 라벨 · 건수 · 비중 막대.
   숫자만 큼직하게 세우는 대신(본문 13px 과 너무 벌어진다) 아이콘 타일과
   아래 비중 막대로 무게를 만든다. 막대는 전체 항목 대비 비율이라, 숫자를
   읽기 전에 '어느 쪽이 많은가'가 길이로 먼저 들어온다. */
.ctr-stat {
    position: relative;
    overflow: hidden;
    display: flex;
    align-items: center;
    gap: .625rem;
    padding: .8125rem 1rem 1rem;
    border: 1px solid var(--ctr-stroke);
    border-radius: 1rem;
    background-color: var(--ctr-surface);
    box-shadow: var(--ctr-lift);
}
.ctr-stat__icon {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 1.875rem;
    height: 1.875rem;
    border-radius: 9999px;
    font-size: .875rem;
    color: var(--ctr-muted);
    background-color: #F1F5F9;
}
.ctr-stat__body { display: flex; flex-direction: column; gap: .0625rem; min-width: 0; }
.ctr-stat__num {
    display: flex;
    align-items: baseline;
    gap: .125rem;
    font-size: 1.0625rem;          /* 값 표시라 본문 13px 상한에서 예외 */
    font-weight: 600;
    line-height: 1.3;
    letter-spacing: -.02em;
    font-variant-numeric: tabular-nums;
    color: var(--ctr-ink);
}
/* 단위는 숫자에 딸린 꼬리라 한 단 작고 조용하게 */
.ctr-stat__unit {
    font-size: 11px;
    font-weight: 500;
    font-style: normal;
    color: var(--ctr-muted);
}
/* 라벨까지 상태색으로 칠하면 칸마다 색이 두 번 반복돼 다시 탁해진다.
   색은 아이콘·숫자·막대에만, 라벨은 중립 회색으로 고정한다. */
.ctr-stat__label {
    font-size: 11px;
    font-weight: 500;
    letter-spacing: .02em;
    color: var(--ctr-muted);
}
/* 비중 막대 — 타일 바닥에 깔린 3px 트랙. 0건이면 트랙만 남는다 */
.ctr-stat__bar {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 3px;
    background-color: #EEF2F7;
}
.ctr-stat__bar i {
    display: block;
    height: 100%;
    border-radius: 0 3px 3px 0;
    background-color: #CBD5E1;
    transition: width .4s var(--ease-glass);
}
/* 상태색 — 아이콘 타일(파스텔 면 + 선명한 글리프) · 숫자 · 막대 세 곳에
   같은 색을 쓴다. 타일 배경까지 칠하면 색면 세 장이 다시 넓어져 탁해진다. */
.ctr-stat[data-status="pass"] .ctr-stat__icon { color: #059669; background-color: #D6FBE9; }
.ctr-stat[data-status="warn"] .ctr-stat__icon { color: #D97706; background-color: #FEF2CE; }
.ctr-stat[data-status="fail"] .ctr-stat__icon { color: #E11D48; background-color: #FFE4EA; }
.ctr-stat[data-status="pass"] .ctr-stat__num { color: #059669; }
.ctr-stat[data-status="warn"] .ctr-stat__num { color: #D97706; }
.ctr-stat[data-status="fail"] .ctr-stat__num { color: #E11D48; }
.ctr-stat[data-status="pass"] .ctr-stat__bar i { background-color: #6EE7B7; }
.ctr-stat[data-status="warn"] .ctr-stat__bar i { background-color: #FCD34D; }
.ctr-stat[data-status="fail"] .ctr-stat__bar i { background-color: #FB7185; }

/* 판정 목록 — 한 건이 카드 한 장. 괘선으로만 나눴더니 이어진 표처럼 보여
   "이 항목은 통과, 저 항목은 주의" 라는 개별 판정이 흐려졌다. 판정마다 면을
   닫고 왼쪽 모서리에 상태색 띠를 세워 카드째로 상태가 읽히게 한다.
   (한 단은 유지 — 설명이 두 줄을 넘는 항목이 많아 반으로 접으면 눌린다) */
.ctr-checks {
    display: flex;
    flex-direction: column;
    gap: .625rem;
}
.ctr-check {
    position: relative;
    overflow: hidden;
    display: flex;
    gap: .75rem;
    padding: .9375rem 1.125rem;
    border: 1px solid var(--ctr-stroke);
    border-radius: 1.125rem;
    background-color: var(--ctr-surface);
    box-shadow: var(--ctr-lift);
}
/* 상태 표시는 왼쪽 띠가 아니라 아이콘 + 테두리색이 맡는다(.ctr-risk 와 같은 이유) */
.ctr-check__icon {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 1.625rem;
    height: 1.625rem;
    margin-top: .0625rem;
    border-radius: 9999px;
    font-size: .6875rem;
    color: var(--ctr-body);
    background-color: #EEF1F6;
}
/* 판정 아이콘 — 배지와 같은 규칙(파스텔 면 + 선명한 글리프),
   카드 왼쪽 띠는 그보다 한 단 진한 파스텔로 세운다 */
.ctr-check[data-status="pass"] .ctr-check__icon { color: #059669; background-color: #D6FBE9; }
.ctr-check[data-status="warn"] .ctr-check__icon { color: #D97706; background-color: #FEF2CE; }
.ctr-check[data-status="fail"] .ctr-check__icon { color: #E11D48; background-color: #FFE4EA; }
.ctr-check[data-status="pass"] { border-color: #C4EFDC; }
.ctr-check[data-status="warn"] { border-color: #F7E4B4; }
.ctr-check[data-status="fail"] { border-color: #FBD0D7; }
.ctr-check__body { display: flex; flex-direction: column; gap: .25rem; min-width: 0; flex: 1 1 auto; }
.ctr-check__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .5rem;
    flex-wrap: wrap;
}
.ctr-check__title {
    font-size: .8125rem;
    font-weight: 600;
    letter-spacing: -.01em;
    color: var(--ctr-ink);
}
.ctr-check__text { font-size: .8125rem; line-height: 1.8; color: var(--ctr-body); word-break: keep-all; }

/* --- 빈 상태 (체크리스트 입력 전) ---------------------------------------- */
.ctr-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .5rem;
    margin: auto;          /* .ctr-pane 가운데 */
    padding: 2rem 1rem;
    text-align: center;
}
.ctr-empty__icon {
    display: grid;
    place-items: center;
    width: 3.25rem;
    height: 3.25rem;
    margin-bottom: .25rem;
    border: 1px solid var(--ctr-stroke);
    border-radius: 9999px;
    background-color: #F4F6F9;
    color: #94A3B8;
    font-size: 1.125rem;
}
.ctr-empty__title { font-size: .8125rem; font-weight: 600; color: var(--ctr-ink); }
.ctr-empty__desc { font-size: .8125rem; line-height: 1.7; color: var(--ctr-body); }
.ctr-empty .btn-primary { margin-top: .5rem; }

/* --- 절 제목 옆의 보조 액션 ------------------------------------------------
   '계약 참여자 변경' · '체크 리스트 변경'. 자체 클래스(.ctr-btn — 옅은 블루
   틴트 + 브랜드 블루 글자)를 버리고 앱 공용 CTA 인 .btn-primary
   .btn-primary--block 로 갈아탔다. 문서 위에 얹힌 유일한 조작 요소라 색을
   채워야 눌리는 것으로 읽히는데, 그 '채운 색'을 이 화면에서만 따로 만들 이유가
   없다. --block 을 함께 쓰는 건 평면 변형이기 때문이다 — 기본 .btn-primary 의
   드롭섀도와 hover 리프트는 이 화면의 다른 요소(전부 납작한 문서 면)와
   어긋난다.

   다만 --block 은 이름 그대로 사이드바 '새 대화' 같은 전폭 CTA 용이라
   width:100% · 42px 이다. 여기는 절 제목 옆(모바일)이자 탭 줄 오른쪽(PC,
   높이 41px)이므로 그 두 값만 되돌린다. 색·그라데이션·라운드·상태 변화는
   원본을 그대로 쓴다.

   높이는 26px 로 잡는다. 탭 줄에 얹히는 만큼 이웃한 .ctr-tab(41px)보다
   확실히 작아야 '탭이 하나 더 있는' 것처럼 보이지 않는다. */
.ctr-block__actions .btn-primary--block {
    width: auto;
    flex-shrink: 0;
    min-height: 1.625rem;
    padding: .1875rem .75rem;
    gap: .3125rem;
    /* --block 의 500 은 사이드바(세로로 촘촘한 자리)에 맞춘 값이다.
       12px 흰 글자가 그라데이션 위에 놓이면 그 굵기로는 배경에 녹는다. */
    font-size: 12px;
    font-weight: 600;
    line-height: 1.4;
}
/* 아이콘은 라벨보다 확실히 작게. 알약은 좌우 여백이 실제보다 좁아 보여서
   글리프가 같이 크면 알약이 아니라 아이콘 버튼처럼 뭉쳐 보인다. */
.ctr-block__actions .btn-primary--block i { font-size: 9.5px; }

/* ==========================================================================
 * 체크리스트 입력 모달 (.chk-*)
 * --------------------------------------------------------------------------
 * 이 모달이 하는 일은 하나다 — "계약서에서 확인할 문장"을 여러 줄 받아내는 것.
 * 그런데 흰 패널(.glass-modal, 불투명도 .96) 안에 흰 통을 두고 그 안에 또 흰
 * 인풋을 넣으면 세 면이 같은 밝기라 어디를 눌러 쓰는지가 안 보인다.
 *
 * 그래서 밝기를 뒤집는다.
 *   .chk__list  담는 통 = 옅은 블루틴트 + 안쪽 그림자.  파인 자리
 *   .chk-item   한 줄   = 순백 + 유리 그림자.           그 위에 놓인 입력란
 *   .chk__add   추가    = 점선.                          아직 비어 있는 다음 칸
 *
 * 행 자체가 입력란이라는 점이 핵심이다. 실제 <input> 은 면도 선도 갖지 않고
 * 글자만 담고, 테두리·포커스 글로우는 행이 :focus-within 으로 진다. 번호와
 * 삭제 버튼이 인풋 '옆'이 아니라 '안'에 놓여야 셋이 한 칸으로 읽힌다.
 * ========================================================================== */
.chk {
    display: flex;
    flex-direction: column;
    gap: .625rem;
}

.chk__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
}
.chk__title {
    font-size: .8125rem;
    font-weight: 600;
    letter-spacing: -.01em;
    color: var(--ink-900);
}
/* 항목 수 — 라벨이 아니라 계기판이다. 숫자 폭이 흔들리지 않게 tabular-nums */
.chk__count {
    flex-shrink: 0;
    padding: .1875rem .5rem;
    border-radius: 9999px;
    background-color: rgba(226, 233, 253, .9);
    color: var(--brand-blue);
    font-size: 11px;
    font-weight: 600;
    line-height: 1.4;
    font-variant-numeric: tabular-nums;
}

/* 담는 통 — 항목이 늘어도 여기서만 스크롤한다(푸터 버튼은 늘 제자리) */
.chk__list {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    counter-reset: chk;
    max-height: 40vh;
    overflow-y: auto;
    padding: .625rem;
    border: 1px solid rgba(99, 130, 220, .16);
    border-radius: 1rem;
    background-color: rgba(240, 245, 255, .75);
    /* 안쪽으로 떨어지는 그림자 — 얹힌 판이 아니라 '파인 통'으로 읽힌다 */
    box-shadow: inset 0 1px 3px rgba(70, 95, 170, .06);
}
/* 전부 지운 상태 — 통이 납작하게 접히지 않게 안내문 한 줄을 남긴다.
   (마크업의 data-empty-text 를 그대로 읽으므로 문구는 HTML 에서 고친다) */
.chk__list:empty::before {
    content: attr(data-empty-text);
    display: block;
    padding: 1.375rem .5rem;
    text-align: center;
    font-size: .75rem;
    color: var(--body-600);
}

/* --- 항목 한 줄 = 입력란 한 칸 -------------------------------------------- */
.chk-item {
    counter-increment: chk;
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .3125rem .375rem .3125rem .5rem;
    border: 1px solid rgba(255, 255, 255, .9);
    border-radius: .875rem;
    background-color: #FFFFFF;
    box-shadow: var(--shadow-glass-sm), inset 1px 1px 0 rgba(255, 255, 255, .9);
    transition: border-color .2s, box-shadow .3s var(--ease-glass);
}
.chk-item:hover { border-color: rgba(99, 130, 220, .32); }
/* 포커스 글로우는 .field:focus-visible 과 같은 3겹 falloff 를 그대로 쓴다 —
   같은 화면 안에서 '지금 여기 쓰는 중' 신호가 두 종류이면 안 된다. */
.chk-item:focus-within {
    border-color: #FFFFFF;
    box-shadow:
        inset 1px 1px 0 rgba(255, 255, 255, .9),
        0 0 6px  0   var(--focus-near),
        0 0 14px 2px var(--focus-mid),
        0 0 24px 5px var(--focus-far),
        0 6px 18px -10px rgba(70, 110, 215, .30);
}

/* 번호 — CSS 카운터라 가운데 항목을 지워도 JS 가 다시 매길 필요가 없다 */
.chk-item__no {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 1.375rem;
    height: 1.375rem;
    border-radius: .5rem;
    background-color: rgba(226, 233, 253, .9);
    color: var(--brand-blue);
    font-size: 11px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.chk-item__no::before { content: counter(chk); }

/* 실제 입력 — 면과 선은 행이 지므로 여기는 비운다.
   ※ 선택자에 element 를 붙이는 이유: @tailwindcss/forms 가 [type='text'] 로
     테두리·링을 넣는데 CDN 이 이 파일보다 나중에 주입된다. 클래스만으로는
     특정도가 같아 밀리므로 input.chk-item__input (0,1,1) 로 올린다. */
input.chk-item__input {
    flex: 1 1 auto;
    min-width: 0;
    padding: .4375rem 0;
    border: 0;
    background-color: transparent;
    font-size: .8125rem;
    line-height: 1.5;
    color: var(--ink-950);
}
input.chk-item__input::placeholder { color: #94A3B8; }
/* 포커스 표시는 행(:focus-within)이 이미 하고 있다 — 칸 안에 링이 하나 더
   그려지면 테두리가 이중으로 보인다. 여기서만 끈다. */
input.chk-item__input:focus,
input.chk-item__input:focus-visible {
    outline: none;
    box-shadow: none;
}

/* 삭제 — 늘 보이되 묵음(회색)이고, 손이 닿았을 때만 위험색으로 바뀐다 */
/* ※ 인풋과 같은 이유로 element 를 붙인다 — Tailwind 의 [type='button'] 은
     배경을 transparent 로 되돌리는데 클래스만으로는 특정도가 같아 밀린다. */
button.chk-item__del {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: .625rem;
    color: #94A3B8;
    font-size: 11px;
    cursor: pointer;
    transition: background-color .2s, color .2s, transform .15s;
}
button.chk-item__del:hover { background-color: #FFE4EA; color: #E11D48; }
button.chk-item__del:active { transform: scale(.92); }

/* --- 추가 버튼 -------------------------------------------------------------
   목록 바로 아래 붙는 '다음 칸'이다. 항목과 같은 폭·같은 라운드에 점선만
   달라 "여기가 아직 비어 있는 자리"로 읽힌다. 알약 버튼(.btn-secondary)으로
   오른쪽에 두었을 때는 목록과 무관한 액션처럼 떠 있었다. */
button.chk__add {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .375rem;
    width: 100%;
    padding: .625rem;
    border: 1px dashed rgba(99, 130, 220, .45);
    border-radius: .875rem;
    background-color: rgba(255, 255, 255, .6);
    color: var(--brand-blue);
    font-size: .8125rem;
    font-weight: 600;
    cursor: pointer;
    transition: background-color .2s, border-color .2s, box-shadow .3s var(--ease-glass),
                transform .15s;
}
button.chk__add i { font-size: 10px; }
button.chk__add:hover {
    background-color: #FFFFFF;
    border-color: var(--brand-blue);
    box-shadow: var(--shadow-glass-sm);
}
button.chk__add:active { transform: translateY(1px); }

.chk__hint {
    display: flex;
    align-items: flex-start;
    gap: .375rem;
    font-size: .75rem;
    line-height: 1.6;
    color: var(--body-600);
    word-break: keep-all;
}
.chk__hint i {
    flex-shrink: 0;
    margin-top: .1875rem;
    color: var(--brand-blue);
}

/* 모바일 — 주소창이 오르내리면 40vh 가 흔들린다. 조금 더 열어 두고,
   행 안쪽 여백만 줄여 한 화면에 들어오는 항목 수를 지킨다. */
@media (max-width: 639px) {
    .chk__list { max-height: 46vh; }
    .chk-item { gap: .375rem; }
    .chk__hint { font-size: 11px; }
}

/* ============================================================================
 * 우측 패널 — 목록형 모달과 같은 유리 표면
 * ----------------------------------------------------------------------------
 * 대상: 진행 상황(#progress) · 계약서 업로드/분석 결과(#contract_result).
 * 셋 다 .progress-panel 을 껍데기로 쓴다.
 *
 * 한동안 이 패널들은 iOS 제어 센터를 따라 "옅은 블랙 스크림 + 강한 blur" 였다.
 * 어두운 바닥 덕에 흰 타일이 스스로 떠오르는 구조였는데, 문제는 이 앱에서
 * 이 패널만 유일하게 어두운 면이었다는 것이다. 옆의 마켓플레이스 모달·사이드바
 * 유리는 전부 흰 유리라, 같은 화면에 유리가 두 종류로 보였고 스크림이 색을
 * 눌러 내용물이 회색으로 칙칙해졌다.
 *
 * → 껍데기를 목록형 모달(#market_modal > .glass-modal)과 같은 값으로 맞춘다.
 *   흰 34% + 125deg 스페큘러 + 흰 테두리 + blur(44) saturate(210).
 *   그 모달이 쓰는 원리를 그대로 가져오는 것이다 — 판을 sheer 로 내리고
 *   그만큼 안의 항목을 밝게 올려서, 두 면의 밝기 차로 층위를 만든다.
 *
 * ※ 바닥이 어두울 때 기준으로 잡았던 타일 알파(.62/.42/.26)는 그대로 두면
 *   안 된다. 밝은 판 위에서는 그 값들이 판과 거의 같은 밝기라 타일이 사라진다.
 *   → 마켓 모달의 .tool-card(.68)에 맞춰 한 단씩 올렸다.
 * ※ 그림자도 같이 바꿔야 한다. 어두운 바닥에서는 밝기 차만으로 떴지만,
 *   밝은 판 위에서는 파란기 도는 외곽 그림자 + 흰 림(inset)이 있어야 두께가
 *   생긴다. --pg-lift 안에 inset 을 넣은 이유다.
 * ※ 이 섹션은 위쪽 원래 규칙을 "덮어쓴다". 원래 규칙과 그 근거는 그대로
 *   남겨 두었다 — 되돌릴 때 그 값이 필요하다.
 * ==========================================================================*/
.progress-panel {
    /* 조절 지점 — 껍데기와 유리 톤은 전부 이 값들에서 나온다 */
    /* 값은 마켓플레이스 모달에서 그대로 가져왔다. 판 .34 / 카드 .68 — 두 배로
       벌어진 이 간격이 층위를 만든다. 카드를 더 올리면(.8+) 불투명한 흰 판이
       되어 유리가 아니게 되고, 내리면 판에 묻힌다. 짝으로 움직일 것. */
    --pg-pane: rgba(255, 255, 255, .34);     /* ① 껍데기   = .glass-modal */
    --pg-surface: rgba(255, 255, 255, .68);  /* ② 유리 타일 = .tool-card  */
    --pg-surface-2: rgba(255, 255, 255, .50);/*   머리줄·보조 면 */
    --pg-surface-3: rgba(255, 255, 255, .34);/*   눌린 면(입력·줄무늬) */
    --pg-stroke: rgba(255, 255, 255, .90);   /*   = .tool-card 테두리 */
    --pg-line: rgba(255, 255, 255, .45);     /*   유리 안쪽 구분선 */
    /* 마켓 모달 카드(.tool-card)와 같은 구성 — 파란기 도는 외곽 2단 + 흰 림 2단 */
    --pg-lift:
        0 1px 3px rgba(40, 60, 130, .07),
        0 16px 34px -18px rgba(40, 60, 130, .42),
        inset 1px 1px 0 rgba(255, 255, 255, 1),
        inset -1px -1px 0 rgba(255, 255, 255, .5);
    /* 항목이 판보다 blur 를 세게 먹어야 뒤 색이 항목 "안에서만" 또렷해진다 —
       그게 굴절처럼 읽히는 지점이다(.tool-card 와 같은 값). */
    --pg-optic: blur(22px) saturate(200%);

    /* 3층으로 쌓는다. background-image 는 먼저 쓴 것이 위에 온다.
         ③ 스페큘러      (맨 위)
         ② 흰 유리       --pg-pane
         ① 투명 블랙     --pg-under  ← background-color, 맨 아래

       ①이 필요한 이유 — 이 패널은 xl 미만에서 대화 위에 그대로 덮인다.
       흰 유리만으로는 밝은 대화 화면 위에서 경계가 녹아 어디까지가 패널인지
       읽히지 않는다. 뒤를 살짝 가라앉히면 같은 흰 유리라도 훨씬 또렷해진다.
       ②가 34% 라 ①은 66% 만 올라온다 — 실효 농도는 .10 이 아니라 .066 이다.
       진하게 하려면 --pg-under 를 올릴 것. 올릴수록 판이 회색으로 간다. */
    --pg-under: rgba(10, 16, 34, .10);
    background-color: var(--pg-under);
    background-image:
        /* ③ 대각선 스페큘러 — 마켓 모달·사이드바 유리와 같은 각도(125deg) */
        linear-gradient(125deg,
            rgba(255, 255, 255, .45) 0%,
            rgba(255, 255, 255, 0)   34%,
            rgba(255, 255, 255, 0)   62%,
            rgba(255, 255, 255, .28) 100%),
        /* ② 흰 유리 — background-color 자리를 이 레이어가 대신한다 */
        linear-gradient(0deg, var(--pg-pane) 0%, var(--pg-pane) 100%);
    border-left-color: rgba(255, 255, 255, .75);
    /* 오른쪽 화면 끝에 붙는 패널이라 모달과 달리 사방 테두리·그림자가 아니다.
       왼쪽 한 변만 모달의 흰 림 + 파란 확산으로 처리한다.
       (모달의 inset -1px -1px 은 뺐다 — 오른쪽·아래는 뷰포트 가장자리다) */
    box-shadow:
        -1px 0 0 rgba(255, 255, 255, .6),
        -14px 0 34px -18px rgba(20, 35, 90, .30),
        -34px 0 70px -34px rgba(20, 35, 90, .24),
        inset 1px 1px 0 rgba(255, 255, 255, 1),
        inset 0 0 60px rgba(255, 255, 255, .16);
    -webkit-backdrop-filter: blur(44px) saturate(210%) brightness(1.04);
            backdrop-filter: blur(44px) saturate(210%) brightness(1.04);
}

/* --- 진행 상황 패널 --------------------------------------------------------- */
.progress-head {
    background-image: linear-gradient(180deg,
        rgba(255, 255, 255, .34) 0%,
        rgba(255, 255, 255, .12) 100%);
}

/* 상태 틴트는 카드 전체가 아니라 머리 띠 한 줄에만 얹는다 — 유리 위에 색면을 넓게
   깔면 타일이 아니라 스티커가 된다. 띠는 좁아서 반투명이어도 색이 선다. */
details.pcard > summary {
    background-color: var(--pg-surface-2);
    border-bottom-color: var(--pg-line);
}
.pcard[data-status="running"] > summary {
    background-color: rgba(238, 245, 255, .74);
    border-bottom-color: rgba(190, 214, 252, .60);
}
.pcard[data-status="done"] > summary {
    background-color: rgba(235, 252, 244, .74);
    border-bottom-color: rgba(186, 232, 211, .60);
}
.pcard[data-status="error"] > summary {
    background-color: rgba(255, 240, 240, .74);
    border-bottom-color: rgba(250, 205, 205, .60);
}
.pcard-head { border-bottom-color: var(--pg-line); }

/* 입력/결과 = 유리에 파인 자리. 면을 더 묽게 하고 안쪽 그림자로 눌러 넣는다 */
.pfield {
    border-color: var(--pg-line);
    background-color: var(--pg-surface-3);
    box-shadow: inset 0 1px 3px rgba(10, 16, 34, .10);
}

.pblock {
    border-color: #E2E8F0;
    background-color: var(--pg-surface);
    box-shadow: var(--pg-lift);
    -webkit-backdrop-filter: var(--pg-optic);
            backdrop-filter: var(--pg-optic);
}
/* hover 는 .tool-card:hover 와 같은 방식 — 면을 .68 → .88 로 올리고 그림자를
   한 단 깊게. 어둡게 눌러 반응하는 게 아니라 유리가 더 하얘지며 떠오른다. */
.pblock:hover {
    background-color: rgba(255, 255, 255, .88);
    box-shadow:
        0 1px 3px rgba(40, 60, 130, .08),
        0 24px 46px -18px rgba(40, 60, 130, .50),
        inset 1px 1px 0 rgba(255, 255, 255, 1),
        inset -1px -1px 0 rgba(255, 255, 255, .6);
}
.plabel {
    background-image: linear-gradient(180deg,
        rgba(255, 255, 255, .40) 0%,
        rgba(255, 255, 255, .10) 100%);
}
.plabel:hover {
    background-image: linear-gradient(180deg,
        rgba(255, 255, 255, .58) 0%,
        rgba(255, 255, 255, .22) 100%);
}
.pblock[open] .plabel { border-bottom-color: var(--pg-line); }

/* 표는 유리를 태우지 않는다 — 글자가 촘촘한 목록이라 뒤가 비치면 행을 놓친다.
   판 위에 불투명한 흰 카드로 한 겹 띄우고, 테두리·그림자만 판 톤에 맞춘다.
   (안쪽 규칙 th/td/hover 는 위쪽 원본을 그대로 쓴다 — 흰 판 위라 덮을 이유가 없다) */
.ptable-wrap {
    border-color: rgba(255, 255, 255, .85);
    background-color: #FFF;
    box-shadow: 0 1px 2px rgba(40, 60, 130, .06);
}

/* 차트는 캔버스라 색이 정확해야 한다 — 여기만 한 단 진한 유리 */
.pchart {
    border-color: var(--pg-line);
    background-color: rgba(255, 255, 255, .70);
}

.pskeleton span {
    background-image: linear-gradient(90deg,
        rgba(255, 255, 255, .22) 0%,
        rgba(255, 255, 255, .62) 50%,
        rgba(255, 255, 255, .22) 100%);
}

/* 기본값 #94A3B8(slate-400)은 유리 위에서 3:1 도 안 나온다. 받침 없이 판 위에
   바로 놓이는 안내문이라 색으로만 버텨야 한다 → slate-600(4.5:1 이상). */
#progress_list:empty::before { color: #475569; }

/* --- 계약서 패널(업로드 · 분석 결과) ---------------------------------------
 * 대부분의 면이 --ctr-* 토큰을 참조하므로, 토큰만 유리로 바꾸면 판·표·카드가
 * 한꺼번에 따라온다. 아래는 토큰을 안 쓰고 색을 직접 박아 둔 몇 곳만 맞춘 것.
 * -------------------------------------------------------------------------- */
/* 결과 화면은 껍데기를 한 단 더 불투명하게 — 표·수치가 빽빽한 화면이라
   뒤가 많이 비치면 셀 사이로 배경 무늬가 읽힌다. 진행 상황(.34)보다 위.
   (스크림이 어둡던 시절에는 반대로 "한 단 얕게"가 밝게 만드는 방향이었다) */
#contract_result { --pg-pane: rgba(255, 255, 255, .44); }

/* 계약서 화면의 카드·표도 마켓 모달의 .tool-card 와 같은 값으로 맞춘다.
   예전 값(.74/.78/.42)은 어두운 스크림 위에서 정해진 것이다 — 바닥이 밝아진
   지금은 판과 카드의 밝기가 붙어 층위가 사라지고, 형태가 안 보이니 화면이
   통째로 뿌옇게 읽힌다.
   ※ .84 까지 올려도 안 된다. 그러면 불투명한 흰 판이 되어 유리가 아니게 된다.
     핵심은 절대 밝기가 아니라 판(.34~.44)과 카드(.68) 사이의 간격이다. */
.ctr {
    --ctr-surface: rgba(255, 255, 255, .68);  /* = .tool-card */
    --ctr-stroke: rgba(255, 255, 255, .90);
    --ctr-line: rgba(255, 255, 255, .45);
    --ctr-rule: rgba(120, 145, 190, .40);
    /* 그림자도 .tool-card 와 같은 구성 — 밝은 판 위에서는 흰 림(inset)이
       있어야 카드에 두께가 생긴다. 검정 확산만으로는 그냥 얼룩으로 보인다. */
    --ctr-lift:
        0 1px 3px rgba(40, 60, 130, .07),
        0 16px 34px -18px rgba(40, 60, 130, .42),
        inset 1px 1px 0 rgba(255, 255, 255, 1),
        inset -1px -1px 0 rgba(255, 255, 255, .5);

    /* 항목이 판보다 blur 를 세게 먹어야 뒤 색이 항목 안에서만 또렷해진다.
       = .tool-card 의 blur(22px) saturate(200%). brightness 는 뺐다 —
       어두운 스크림을 걷어내려고 넣었던 보정이라 이제는 흰색만 들뜬다. */
    --ctr-optic: blur(22px) saturate(200%);
}
.ctr__panel {
    background-color: var(--ctr-surface);
    -webkit-backdrop-filter: var(--ctr-optic);
            backdrop-filter: var(--ctr-optic);
}
.ctr-file {
    border-color: var(--ctr-stroke);
    background-color: var(--ctr-surface);
    box-shadow: var(--ctr-lift);
    -webkit-backdrop-filter: var(--ctr-optic);
            backdrop-filter: var(--ctr-optic);
}
/* .tool-card:hover 와 같은 방식 — .68 → .88 로 하얘지며 그림자가 깊어진다 */
.ctr-file:hover {
    border-color: rgba(255, 255, 255, 1);
    background-color: rgba(255, 255, 255, .88);
    box-shadow:
        0 1px 3px rgba(40, 60, 130, .08),
        0 24px 46px -18px rgba(40, 60, 130, .50),
        inset 1px 1px 0 rgba(255, 255, 255, 1),
        inset -1px -1px 0 rgba(255, 255, 255, .6);
}
/* 판 위에 얹히는 카드들(요약·필드·리스크·체크)도 같은 광학으로 —
   토큰만 바꾸면 색은 따라오지만 backdrop 은 각자 켜야 한다. */
.ctr-sum,
.ctr-fields,
.ctr-parties,
.ctr-stat,
.ctr-check,
.ctr-risk {
    -webkit-backdrop-filter: var(--ctr-optic);
            backdrop-filter: var(--ctr-optic);
}
/* 인용문·값 상자 — 유리에 파인 자리. 카드(68%)보다 묽어야 "파인" 것으로 읽힌다 */
.ctr-text { background-color: rgba(255, 255, 255, .42); }
.ctr-sum__key,
.ctr-field__label { background-color: rgba(255, 255, 255, .40); }
/* 아이콘 칩·미터 트랙 — 작은 무채색 면들 */
.ctr-party__icon,
.ctr-check__icon,
.ctr-stat__bar { background-color: rgba(255, 255, 255, .52); }
.ctr-note::before { background-color: rgba(255, 255, 255, .80); }
.ctr-empty { background-color: rgba(255, 255, 255, .42); }
/* 조항 칩만 색을 유지한다 — 이 화면에서 파랑은 "조항 번호"라는 뜻이다 */
.ctr-chip {
    border-color: rgba(190, 214, 255, .75);
    background-color: rgba(238, 243, 254, .70);
}
.ctr-chip--muted { background-color: rgba(255, 255, 255, .42); }

/* --- 업로드 화면(.ctr-upload) ----------------------------------------------
 * 결과 화면은 카드·표가 스스로 면을 갖지만, 업로드 화면은 제목 · 드롭존 ·
 * 안내 목록 · 버튼뿐이라 판 위에 글자만 떠 있는 상태였다.
 * → 세 덩어리에 각각 유리를 준다. 값은 전부 --pg-* 토큰을 참조하므로
 *   마켓 모달과 같은 층위(판 .34 / 카드 .68)를 그대로 따라간다.
 *     ① 머리글   --pg-surface-2(.50) — 읽고 지나가는 안내
 *     ② 드롭존   --pg-surface  (.68) — 이 화면의 동작 지점(점선은 유지)
 *     ③ 안내 목록 --pg-surface (.68) — 네 줄이 한 덩어리(행 사이만 헤어라인)
 * -------------------------------------------------------------------------- */
.ctr-upload__head {
    padding: .875rem 1rem 1rem;
    border-radius: 1.125rem;
    border: 1px solid var(--pg-stroke);
    background-color: var(--pg-surface-2);
    box-shadow: var(--pg-lift);
    -webkit-backdrop-filter: var(--pg-optic);
            backdrop-filter: var(--pg-optic);
}

/* 드롭존 — 이 화면의 동작 지점이라 머리글(--pg-surface-2)보다 진하고 카드와
   같은 급이다. 점선 테두리는 유지한다(놓는 자리라는 신호). */
.ctr-upload__zone.dropzone {
    border-color: rgba(255, 255, 255, .85);
    background-color: var(--pg-surface);
    background-image: linear-gradient(150deg,
        rgba(255, 255, 255, .5) 0%,
        rgba(255, 255, 255, 0)  55%);
    box-shadow: var(--pg-lift);
    -webkit-backdrop-filter: var(--pg-optic);
            backdrop-filter: var(--pg-optic);
}
/* 기본이 .68 로 올라갔으므로 반응 상태도 그 위로 올린다 — 예전 값(.66/.78)을
   두면 hover 가 기본보다 어두워져 반응이 거꾸로 읽힌다. */
.ctr-upload__zone.dropzone:hover,
.ctr-upload__zone.dropzone.has-file {
    background-color: rgba(255, 255, 255, .82);
}
.ctr-upload__zone.dropzone.is-dragover {
    border-color: #FFFFFF;
    background-color: rgba(255, 255, 255, .92);
}

/* 안내 목록 — 낱줄이 아니라 한 장의 유리. 행 사이만 헤어라인으로 끊는다.
   (같은 .ctr-intro__list 를 쓰는 대화 속 안내 카드는 흰 카드 위라 그대로 둔다)

   면은 카드 규격(--pg-surface .68)이 아니라 그보다 묽다. 이 목록은 "무엇을
   받나"를 읽고 지나가는 안내라, 동작 지점인 드롭존(.68)과 같은 두께면 둘이
   같은 무게로 경쟁한다. 판(.34)과 드롭존(.68) 사이에 놓아 한 단 물러나게 한다.

   묽어진 만큼 형태는 선이 맡는다 — 행 사이 흰 실선을 --pg-line(.45)에서
   .72 로 올렸다. 면이 두꺼울 때는 헤어라인이 그 위에 얹혀 보이지만, 면이
   얇아지면 뒤 배경이 비쳐 올라와 같은 알파로는 선이 끊겨 보인다. */
.ctr-upload .ctr-intro__list {
    gap: 0;
    padding: .25rem .375rem;
    border-radius: 1.125rem;
    border: 1px solid var(--pg-stroke);
    background-color: rgba(255, 255, 255, .40);
    box-shadow: var(--pg-lift);
    -webkit-backdrop-filter: var(--pg-optic);
            backdrop-filter: var(--pg-optic);
}
.ctr-upload .ctr-intro__list li {
    padding: .625rem .5rem;
    border-top: 1px solid rgba(255, 255, 255, .72);
}
.ctr-upload .ctr-intro__list li:first-child { border-top: 0; }
/* 유리 위에서 #94A3B8 점 아이콘은 사라진다 — 목록의 유일한 색으로 세운다 */
.ctr-upload .ctr-intro__list i { color: var(--brand-blue); }

/* 파일을 고르기 전의 '분석 시작' — 꺼진 그라데이션 알약이 아니라
   아직 눌리지 않은 유리 알약으로 둔다 */
.ctr-upload__confirm:disabled,
.ctr-upload__confirm[disabled] {
    border: 1px solid var(--pg-stroke);
    background-color: rgba(255, 255, 255, .34);
    color: rgba(30, 27, 75, .5);
    -webkit-backdrop-filter: blur(16px) saturate(160%);
            backdrop-filter: blur(16px) saturate(160%);
}

/* ============================================================================
 * 목록형 모달(마켓플레이스 · 내 도구 · 채팅 검색 · 계정 설정 · 체크리스트)
 * — 유리 패널
 * ----------------------------------------------------------------------------
 * .glass-modal 기본값은 흰 96% 다. 임의의 페이지 내용 위에 뜨는 모달이라
 * "내용 격리"를 우선한 값인데, 이들은 성격이 다르다 — 목록을 훑어보는
 * 화면이라 뒤를 완전히 끊을 이유가 없고, 안에 이미 유리 항목(.tool-card /
 * .mn-row / .search-result / .glass-item)이 깔려 있어서 96% 흰 바닥 위에서는
 * 그것들이 아무 데도 떠 있지 않았다.
 *
 * → 패널을 sheer(34%)로 내리고, 그만큼 항목을 밝게(68%) 올린다. 두 면의
 *   밝기 차가 벌어지면서 목록이 유리판 위에 떠 있는 층위가 생긴다.
 *   뒷막은 chat.html 에서 어둡기를 낮추고 blur 를 키웠다(같이 봐야 한다).
 *
 * ※ 계약서 흐름의 두 입력 모달(체크리스트 · 참여자)도 여기 들어온다. 짧은 글
 *   하나로 결정을 받는 확인 모달과 달리 여러 칸을 채우는 화면이고, 무엇보다
 *   이 둘은 계약서 결과 패널(#contract_result) "위에" 뜬다 — 그 패널이 유리인데
 *   그 위 모달만 흰 96% 판이면 층이 거꾸로 보인다.
 *   아래 .chk-* / .party-* 보정도 함께 봐야 한다.
 * ※ 나머지 확인 모달(로그아웃 · 삭제 · 회원정보 …)은 그대로 둔다.
 *   짧은 글 하나로 결정을 받는 화면이라 뒤가 비치면 안 된다.
 * ==========================================================================*/
#market_modal > .glass-modal,
#tool_modal > .glass-modal,
#search_modal > .glass-modal,
#setting_modal > .glass-modal,
#checkListModal > .glass-modal,
#analyzeModal > .glass-modal {
    background-color: rgba(255, 255, 255, .34);
    /* 대각선 스페큘러 — 사이드바 유리(.glass-group)와 같은 각도(125deg) */
    background-image: linear-gradient(125deg,
        rgba(255, 255, 255, .45) 0%,
        rgba(255, 255, 255, 0)   34%,
        rgba(255, 255, 255, 0)   62%,
        rgba(255, 255, 255, .28) 100%);
    border: 1px solid rgba(255, 255, 255, .75);
    box-shadow:
        0 2px 6px rgba(40, 60, 130, .10),
        0 48px 92px -34px rgba(20, 35, 90, .55),
        inset 1px 1px 0 rgba(255, 255, 255, 1),
        inset -1px -1px 0 rgba(255, 255, 255, .5),
        inset 0 0 60px rgba(255, 255, 255, .16);
    -webkit-backdrop-filter: blur(44px) saturate(210%) brightness(1.04);
            backdrop-filter: blur(44px) saturate(210%) brightness(1.04);
}

/* 히어로 배너 — 원래 값(흰 85→35→88%)은 흰 패널을 전제로 한 것이라, 유리
   위에서는 배너만 불투명한 띠로 남는다. 같은 색 구성을 유지한 채 알파만 내려
   배너 뒤로도 뒤 화면이 비치게 한다. */
#market_modal .modal-hero,
#tool_modal .modal-hero,
#search_modal .modal-hero,
#setting_modal .modal-hero,
#checkListModal .modal-hero,
#analyzeModal .modal-hero {
    background-image:
        /* ① 상단 스페큘러 — 원본과 같은 각도, 알파만 한 단 아래로 */
        linear-gradient(168deg, rgba(255, 255, 255, .55) 0%, rgba(255, 255, 255, 0) 46%),
        /* ② 좌상단 하늘 · ③ 우상단 파랑 — 위치·크기는 원본 그대로 */
        radial-gradient(78% 165% at -2% -25%, rgba(163, 214, 247, .42) 0%, rgba(163, 214, 247, 0) 66%),
        radial-gradient(66% 150% at 101% -18%, rgba(170, 192, 250, .36) 0%, rgba(170, 192, 250, 0) 62%),
        /* ④ 베이스 — 여기가 핵심. 85/35/88% 는 흰 패널 위에서만 성립한다 */
        linear-gradient(180deg, rgba(238, 246, 255, .40) 0%, rgba(255, 255, 255, .10) 50%, rgba(255, 255, 255, .28) 100%);
}

/* --- 체크리스트 · 참여자 입력 모달의 안쪽 면 -------------------------------
 * .chk-* 는 원래 "흰 96% 패널 위에서 밝기를 뒤집는" 설계였다(통은 파이고
 * 행은 순백). 판이 유리 34% 가 된 지금 그 값을 그대로 두면 반대가 된다 —
 * 통(.75 블루틴트)과 행(#FFF)이 둘 다 판보다 진해서 유리 위에 불투명한
 * 판 두 장이 얹힌 꼴이다.
 *
 * → 관계는 유지하고 기준만 내린다. 통은 판보다 "묽게"(파인 자리), 행은
 *   판보다 "진하게"(얹힌 입력란). 행이 .88 로 높은 건 글자를 직접 받는
 *   입력란이라서다 — 목록 카드(.68)보다 한 단 위여야 읽힌다.
 * -------------------------------------------------------------------------- */
#checkListModal .chk__list {
    border-color: rgba(255, 255, 255, .5);
    /* 판(.34)보다 묽은 파란 틴트 — 안쪽 그림자와 함께 "파인 통"으로 읽힌다 */
    background-color: rgba(226, 236, 255, .28);
    box-shadow: inset 0 1px 4px rgba(40, 60, 130, .10);
}
#checkListModal .chk-item {
    background-color: rgba(255, 255, 255, .88);
    box-shadow:
        0 1px 3px rgba(40, 60, 130, .07),
        0 16px 34px -18px rgba(40, 60, 130, .42),
        inset 1px 1px 0 rgba(255, 255, 255, 1);
    -webkit-backdrop-filter: blur(22px) saturate(200%);
            backdrop-filter: blur(22px) saturate(200%);
}
/* 번호 칩·개수 배지 — 불투명 라벤더(.9)는 유리 위에서 스티커처럼 뜬다 */
#checkListModal .chk-item__no,
#checkListModal .chk__count {
    background-color: rgba(226, 233, 253, .62);
}

/* 참여자 입력(#analyzeModal)은 .field 인풋 몇 개가 전부다. .field 는 이미
   유리 규격이라 따로 손댈 면이 없다 — 판만 바뀌면 그대로 따라온다. */

/* 목록 항목(마켓 = 카드 / 내 도구 = 행) — 패널보다 두 배 밝게.
   여기가 두 모달의 주인공이라 blur 도 항목이 더 세게 먹어야 뒤 색이 항목
   안에서만 또렷해진다(굴절처럼 읽힌다). */
#market_modal .tool-card,
#tool_modal .mn-row {
    background-color: rgba(255, 255, 255, .68);
    border-color: rgba(255, 255, 255, .9);
    box-shadow:
        0 1px 3px rgba(40, 60, 130, .07),
        0 16px 34px -18px rgba(40, 60, 130, .42),
        inset 1px 1px 0 rgba(255, 255, 255, 1),
        inset -1px -1px 0 rgba(255, 255, 255, .5);
    -webkit-backdrop-filter: blur(22px) saturate(200%);
            backdrop-filter: blur(22px) saturate(200%);
}
#market_modal .tool-card:hover,
#tool_modal .mn-row:hover {
    background-color: rgba(255, 255, 255, .88);
    box-shadow:
        0 1px 3px rgba(40, 60, 130, .08),
        0 24px 46px -18px rgba(40, 60, 130, .50),
        inset 1px 1px 0 rgba(255, 255, 255, 1),
        inset -1px -1px 0 rgba(255, 255, 255, .6);
}
/* 꺼진 도구 — "켜진 것보다 묽다"는 관계만 유지한다. 값은 유리 패널 기준으로
   다시 잡았다(원래 34% 는 흰 96% 바닥 위에서 잡은 값이라 여기선 사라진다). */
#tool_modal .mn-row.is-off {
    background-color: rgba(255, 255, 255, .40);
    border-color: rgba(255, 255, 255, .70);
    box-shadow: none;
}
#tool_modal .mn-row.is-off:hover {
    background-color: rgba(255, 255, 255, .72);
    box-shadow:
        0 1px 3px rgba(40, 60, 130, .07),
        0 14px 30px -18px rgba(40, 60, 130, .40);
}

/* 필터 트랙 — 옅은 파랑 틴트(rgba(99,130,220,.07))는 유리 위에서 얼룩으로만
   남는다. 흰 반투명 홈으로 바꿔 안쪽으로 눌린 자리로 만든다. */
#market_modal .tool-filters,
#tool_modal .tool-filters {
    border-color: rgba(255, 255, 255, .55);
    background-color: rgba(255, 255, 255, .30);
    box-shadow: inset 0 1px 3px rgba(40, 60, 130, .10);
}

/* --- 채팅 검색 -------------------------------------------------------------
 * 결과 행은 원래 "평소 투명 / hover 에서만 흰 판"이었다. 흰 96% 패널 위에서는
 * 그래도 글줄이 또렷했지만, 34% 유리 위에서는 배경이 그대로 비쳐 글자만 떠
 * 있게 된다. → 행마다 얇은 유리를 깔되, 도구 카드(68%)보다는 묽게(48%) 둔다.
 * 검색 결과는 "훑는 목록"이라 행 하나하나가 카드처럼 무거우면 안 된다.
 * -------------------------------------------------------------------------- */
#search_modal .search-result {
    border-color: rgba(255, 255, 255, .72);
    background-color: rgba(255, 255, 255, .48);
    -webkit-backdrop-filter: blur(18px) saturate(180%);
            backdrop-filter: blur(18px) saturate(180%);
}
#search_modal .search-result:hover {
    background-color: rgba(255, 255, 255, .82);
    border-color: rgba(255, 255, 255, .95);
    box-shadow:
        0 1px 3px rgba(40, 60, 130, .07),
        0 14px 30px -18px rgba(40, 60, 130, .42);
}
/* 히어로 안의 검색창 — 배너가 묽어진 만큼 입력창이 스스로 면을 가져야 한다 */
#search_modal .modal-hero input.field {
    background-color: rgba(255, 255, 255, .62);
    border-color: rgba(255, 255, 255, .85);
}
#search_modal .modal-hero input.field:hover,
#search_modal .modal-hero input.field:focus {
    background-color: rgba(255, 255, 255, .82);
}

/* --- 계정 설정 -------------------------------------------------------------
 * 항목 셋(.glass-item)이 이 모달의 전부다. 목록형 모달의 항목과 같은 급으로
 * 올려 유리판 위에 뜨게 한다(.tool-card / .mn-row 와 같은 값).
 * -------------------------------------------------------------------------- */
#setting_modal .glass-item {
    border-color: rgba(255, 255, 255, .9);
    background-color: rgba(255, 255, 255, .68);
    box-shadow:
        0 1px 3px rgba(40, 60, 130, .07),
        0 16px 34px -18px rgba(40, 60, 130, .42),
        inset 1px 1px 0 rgba(255, 255, 255, 1),
        inset -1px -1px 0 rgba(255, 255, 255, .5);
    -webkit-backdrop-filter: blur(22px) saturate(200%);
            backdrop-filter: blur(22px) saturate(200%);
}
#setting_modal .glass-item:hover {
    background-color: rgba(255, 255, 255, .88);
    box-shadow:
        0 1px 3px rgba(40, 60, 130, .08),
        0 24px 46px -18px rgba(40, 60, 130, .50),
        inset 1px 1px 0 rgba(255, 255, 255, 1),
        inset -1px -1px 0 rgba(255, 255, 255, .6);
}

/* 투명 효과를 줄여 달라고 한 사용자 — glassmorphism.css 의 폴백은
   (0,2,0) 이라 위 ID 규칙(1,1,0)에 밀린다. 같은 조건으로 한 번 더 막는다. */
@media (prefers-reduced-transparency: reduce) {
    #market_modal > .glass-modal,
    #tool_modal > .glass-modal,
    #search_modal > .glass-modal,
    #setting_modal > .glass-modal,
    #checkListModal > .glass-modal,
    #analyzeModal > .glass-modal {
        background-color: #FFFFFF;
        background-image: none;
        -webkit-backdrop-filter: none;
                backdrop-filter: none;
    }
    #market_modal .tool-card,
    #tool_modal .mn-row,
    #search_modal .search-result,
    #setting_modal .glass-item,
    #checkListModal .chk__list,
    #checkListModal .chk-item {
        background-color: #FFFFFF;
        -webkit-backdrop-filter: none;
                backdrop-filter: none;
    }
    /* 우측 패널도 같이 — 여기도 ID/클래스 조합이라 폴백보다 세다 */
    .progress-panel {
        --pg-pane: #FFFFFF;
        --pg-under: transparent;
        --pg-surface: #FFFFFF;
        --pg-surface-2: #F5F8FF;
        --pg-surface-3: #EDF2FC;
        --pg-optic: none;
        background-image: none;
        -webkit-backdrop-filter: none;
                backdrop-filter: none;
    }
    .ctr {
        --ctr-surface: #FFFFFF;
        --ctr-optic: none;
    }
}

/* ==========================================================================
   ver2 전용 — 레퍼런스 목업에는 없지만 우리 코드가 여전히 참조하는 규칙
   ========================================================================== */

/* contract.js: 고정 높이 카드 + '더보기' 토글 */
.contract-card-body { height: 11rem; overflow: hidden; }
.contract-card-body.is-open { height: auto; overflow: visible; }

/* page/sso_error.html 진입 애니메이션 */
.fade-in-fwd { animation: fade-in-fwd .6s cubic-bezier(.39, .575, .565, 1) both; }
@keyframes fade-in-fwd {
  0%   { transform: translateZ(-80px); opacity: 0; }
  100% { transform: translateZ(0);     opacity: 1; }
}

/* ==========================================================================
   본문 이름 링크 · 워크스페이스 토글
   ========================================================================== */

/* ── 답변 본문의 이름 링크 ──────────────────────────────────────────
   .vlink 는 우측 패널의 그 행으로 데려간다(view_link.js). 색은 '여기는 눌러서 더
   볼 수 있다' 하나만 말하게 두고, 어디로 가는지는 눌러서 열리는 것이 말한다.
   그래서 .md a 와 한 규칙을 쓴다: 글자는 같은 --ink-600, 밑줄은 그 40% 톤.
   (밑줄이 옅은 이유 — 이름 링크는 문장 안에 여럿 박히므로 실선이 진하면 줄이 끊겨 보인다) */
.vlink {
    cursor: pointer;
    font-weight: 600;
    color: var(--ink-600);
    border-bottom: 1px solid rgba(29, 78, 216, .4);
    border-radius: 3px;
    padding: 0 1px;
    transition: background-color .15s, border-color .15s;
}
.vlink:hover,
.vlink:focus-visible {
    outline: none;
    background-color: rgba(29, 78, 216, .10);
    border-bottom-color: currentColor;
}

/* 답변 표에서 링크가 걸린 행 — 표시를 셀이 아니라 행이 맡는다.
   문장 안에서는 이름 하나가 링크라 밑줄이 맞지만, 표에서는 한 행이 곧 한 건이고 누를 자리도
   행 전체다. 셀 글자에만 밑줄을 그으면 '저 글자만 누르는 것'으로 읽힌다.

   표시는 문단 링크와 같은 언어를 쓴다 — 밑줄, 그리고 마우스를 올리면 면. 다만 칸마다 이미
   아래 선이 있으니 새로 긋지 않고 그 선의 색만 올린다. tr 은 pseudo 를 앉히기에 안정적인
   상자가 아니라 ::after 를 쓰지 않는다.
   선을 td 의 border 가 아니라 inset box-shadow 로 긋는 이유: border 를 쓰면 마지막 행의 선을
   지우는 규칙과 특이도 싸움이 붙고, 그걸 이기려고 올린 규칙이 호버 규칙까지 같이 이긴다.
   (설계는 ver2-before-link 의 .md tr.cite_line 을 따랐다. 색만 지금의 --link-line 으로 맞춘다.) */
.md tr.vlink-row { cursor: pointer; }
.md tr.vlink-row > td {
    box-shadow: inset 0 -1.5px 0 var(--link-line);
    transition: box-shadow .15s, background-color .15s;
}
.md tr.vlink-row:hover > td,
.md tr.vlink-row:focus-visible > td {
    background-color: rgba(29, 78, 216, .08);
    box-shadow: inset 0 -1.5px 0 rgba(29, 78, 216, .85);
}
.md tr.vlink-row:focus-visible { outline: none; }
/* 셀 안의 이름은 표시를 내려놓는다 — 행이 이미 말하고 있다. */
.md tr.vlink-row .vlink,
.md tr.vlink-row .vlink:hover,
.md tr.vlink-row .vlink:focus-visible {
    border-bottom: none;
    background-color: transparent;
    padding: 0;
    color: inherit;
    font-weight: inherit;
}

/* 짚은 행 — 표의 행 hover 와 겹쳐도 지지 않게 배경 대신 그림자 테두리를 쓴다
   (.ptable tbody tr:hover td 가 배경을 잡고 있다). */
.progress-cards .vlink-hit > td,
.progress-cards .vlink-hit.pvcard {
    background-color: #FFF7E6 !important;
    box-shadow: inset 0 0 0 9999px rgba(255, 214, 133, .18);
}
.progress-cards tr.vlink-hit > td:first-child { box-shadow: inset 3px 0 0 #F0A93B, inset 0 0 0 9999px rgba(255, 214, 133, .18); }
.progress-cards .vlink-hit.pvcard { border-color: #F0A93B; }

/* 순회 바 — 패널 머리와 목록 사이에 한 줄. 같은 이름이 여러 행에 있을 때만 뜬다. */
.vlink_bar {
    display: flex;
    align-items: center;
    gap: .375rem;
    margin: 0 .75rem .25rem;
    padding: .375rem .5rem;
    border: 1px solid rgba(240, 169, 59, .45);
    border-radius: .5rem;
    background-color: #FFF7E6;
    font-size: 11px;
    color: var(--body-700);
}
.vlink_bar.hidden { display: none; }
.vlink_bar__icon { flex-shrink: 0; color: #C9821E; font-size: 10px; }
.vlink_bar__name {
    min-width: 0;
    flex: 1;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-weight: 600;
    color: var(--ink-900);
}
.vlink_bar__at { flex-shrink: 0; color: #8A6524; font-variant-numeric: tabular-nums; }
.vlink_bar__step,
.vlink_bar__x {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.25rem;
    height: 1.25rem;
    border-radius: .25rem;
    color: #8A6524;
    font-size: 10px;
    transition: background-color .12s, color .12s;
}
.vlink_bar__step:hover:not(:disabled),
.vlink_bar__x:hover { background-color: rgba(240, 169, 59, .22); color: #7A5312; }
.vlink_bar__step:disabled { opacity: .35; cursor: default; }
.vlink_bar__step:focus-visible,
.vlink_bar__x:focus-visible { outline: 2px solid var(--brand-sky); outline-offset: 1px; }

/* 워크스페이스 토글 — 대화 오른쪽 가장자리에 붙은 손잡이(서랍 탭).
   아이콘 버튼이 글 위에 떠 있으면 무엇을 여는 버튼인지 알 수 없다. 열릴 자리에 붙여
   두면 위치가 곧 설명이 되고, 화살표가 방향을 말한다 — 닫혀 있으면 왼쪽(당겨 연다),
   열려 있으면 오른쪽(밀어 닫는다). 좁은 면이라 색으로 존재를 알린다. */
#progress_toggle_btn {
    width: 1.125rem;
    height: 3.25rem;
    min-height: 0;
    padding: 0;
    align-items: center;
    justify-content: center;
    border: 1px solid rgba(255, 255, 255, .5);
    border-right: 0;
    border-radius: .5rem 0 0 .5rem;
    background-color: rgba(26, 32, 48, .55);
    -webkit-backdrop-filter: blur(8px) saturate(140%);
            backdrop-filter: blur(8px) saturate(140%);
    box-shadow: -2px 0 10px rgba(40, 60, 130, .16);
    color: #FFFFFF;
    font-size: 10px;
    transition: width .15s, background-color .15s;
}
#progress_toggle_btn:hover { width: 1.375rem; background-color: rgba(26, 32, 48, .78); }
#progress_toggle_btn:focus-visible { outline: 2px solid #1C6FD6; outline-offset: 2px; }
/* 열려 있으면 화살표를 뒤집는다 — 같은 자리에서 '민다'가 된다 */
#progress_toggle_btn > i { transition: transform .2s ease; }
#progress_toggle_btn.is-on > i { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) {
    #progress_toggle_btn, #progress_toggle_btn > i { transition: none; }
}

/* ==========================================================================
 * 논문 전문가 · 논문 기관 리포트로 가는 문
 * --------------------------------------------------------------------------
 * 팝업 자체의 모양은 v1 마크업(Tailwind)이 그대로 들고 있다. 여기 남은 것은
 * 우측 워크스페이스에서 그 팝업을 여는 자리 하나뿐이다.
 * ========================================================================== */

/* --- 워크스페이스의 이름 셀 ----------------------------------------------
   답변 본문이 아니라 우측 패널의 표에서 연다. 논문 응답은 전문가·기관을 표로
   주고 그 표가 이미 패널에 그려져 있으니, 이름을 한 번 더 본문에서 찾을 이유가
   없다(특허 쪽은 본문 이름을 링크한다 — 그쪽은 표가 아니라 문장에 이름이 있다).
   전문가·기관 두 팝업이 키 컬러가 달라 예전에는 이름 색도 갈라 뒀는데(전문가 파랑 ·
   기관 보라), 같은 표 안에서 이름만 두 색으로 서니 색이 '무엇이 열리나'가 아니라
   '무슨 종류인가'로 읽혔다. 여는 자리는 한 색으로 두고, 종류는 팝업이 열려서 말한다. */
.progress-cards .ent_cell {
    display: inline;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    /* 브라우저 기본값이 button 을 가운데 정렬한다 — 이름이 길어 줄바꿈되는 셀만 글자가
       칸 가운데로 몰려, 같은 열인데 짧은 이름과 정렬이 달라 보였다. 칸을 따라가게 되돌린다. */
    text-align: inherit;
    color: #4a69bd;
    text-decoration: underline dotted currentColor;
    text-underline-offset: 2px;
    cursor: pointer;
}
.progress-cards .ent_cell:hover { text-decoration-style: solid; }
.progress-cards .ent_cell:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; border-radius: 3px; }


/* --- 블록 안 검색 · 엑셀 내려받기 -------------------------------------------
 * 검색은 블록 하나 안에서만 돌고, 안 맞는 것을 감추기만 한다(.vs-hide) — 표는 행,
 * 카드는 카드 한 장이 단위다. tr 에도 걸리므로 display:none 으로 통일한다.
 * -------------------------------------------------------------------------- */
.vs-hide { display: none !important; }

/* 검색·엑셀은 워크스페이스·참조정보에서만 뜬다. 같은 블록 마크업을 쓰는 계약서
   분석 결과에는 내려받을 좌표(chat_id·seq)가 없다 — 거기서는 아예 안 보여야 한다. */
.bsearch,
.pblock__xlsx { display: none; }

/* 엑셀 버튼 — 평소엔 옅은 회색 칩. 배경이 없으면 아이콘 하나가 제목 줄에 떠 있는
   것처럼 보여 누를 수 있는 것으로 안 읽힌다. 호버에서만 엑셀 초록이 든다. */
.progress-cards .pblock__xlsx {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 1.625rem;
    height: 1.625rem;
    border-radius: 8px;
    border: 1px solid rgba(15, 23, 42, .06);
    background-color: rgba(15, 23, 42, .045);
    color: #1D7044;                /* 엑셀 초록 — 무엇으로 나가는지 아이콘 색이 먼저 말한다 */
    font-size: .8125rem;
    cursor: pointer;
    transition: background-color .16s, border-color .16s, box-shadow .16s, transform .16s;
}
.progress-cards .pblock__xlsx:hover {
    border-color: rgba(29, 112, 68, .28);
    background-color: rgba(29, 112, 68, .12);
    box-shadow: 0 4px 10px -6px rgba(29, 112, 68, .55);
    transform: translateY(-1px);
}
.progress-cards .pblock__xlsx:active { transform: none; box-shadow: none; }
.progress-cards .pblock__xlsx:focus-visible { outline: 2px solid #1D7044; outline-offset: 1px; }

/* 블록 안 검색 — 평소엔 돋보기 하나만 둔다. 420px 패널에서 입력창을 늘 띄우면
   제목이 그만큼 눌린다 → 누를 때만 짧은 입력칩이 열린다(view_search.js). */
.progress-cards .bsearch {
    display: inline-flex;
    align-items: center;
    gap: .375rem;
    flex: 0 0 auto;
    min-width: 0;
}
/* 검색을 열어도 제목은 남긴다 — 어느 블록을 뒤지는 중인지가 제목이다.
   자리가 모자라면 제목이 먼저 줄고(말줄임), 전체 이름은 title 로 뜬다. */
.plabel--search .bsearch { flex: 0 1 auto; }

.bsearch__toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 1.625rem;
    height: 1.625rem;
    border-radius: 8px;
    border: 1px solid rgba(15, 23, 42, .06);   /* 엑셀 버튼과 같은 칩 — 나란히 서는 둘이다 */
    background-color: rgba(15, 23, 42, .045);
    color: #64748B;
    font-size: .75rem;
    cursor: pointer;
    transition: background-color .16s, border-color .16s, box-shadow .16s, color .16s, transform .16s;
}
.bsearch__toggle:hover {
    border-color: rgba(15, 23, 42, .12);
    background-color: rgba(15, 23, 42, .09);
    box-shadow: 0 4px 10px -6px rgba(20, 35, 90, .5);
    color: var(--ink-900);
    transform: translateY(-1px);
}
.bsearch__toggle:active { transform: none; box-shadow: none; }
.bsearch__toggle:focus-visible { outline: 2px solid #6E8BFF; outline-offset: 1px; }
.plabel--search .bsearch__toggle { display: none; }

.bsearch__field { display: none; }
.plabel--search .bsearch__field {
    display: flex;
    align-items: center;
    gap: .25rem;
    flex: 0 1 7.5rem;
    width: 7.5rem;
    min-width: 0;
    height: 1.625rem;
    padding: 0 .1875rem 0 .5rem;
    border: 1px solid #D8E2F5;
    border-radius: 999px;
    background-color: #FFFFFF;
    box-shadow: inset 0 1px 2px rgba(10, 16, 34, .06);
    cursor: text;
    transition: border-color .16s, box-shadow .16s;
}
.bsearch__ficon {
    flex-shrink: 0;
    font-size: .6875rem;
    line-height: 1;
    color: #94A3B8;
}
.bsearch__input {
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    outline: 0;
    background: transparent;
    font-size: 12px;
    font-weight: 400;
    color: var(--ink-900);
}
.bsearch__input::placeholder { color: #94A3B8; }
/* 전역 포커스 링(glassmorphism.css 의 :where(...):focus-visible)을 여기서 끈다 —
   특정도가 같아 나중에 읽히는 그쪽이 이긴다. 칩 안의 입력칸이라 링이 칩을 뚫고 나온다.
   (투명 outline 은 남긴다 — 고대비 모드는 그 자리만 시스템 색으로 칠한다) */
.bsearch__input:focus,
.bsearch__input:focus-visible {
    outline: 2px solid transparent;
    outline-offset: 0;
    box-shadow: none;
}
.bsearch__clear {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 1.25rem;
    height: 1.25rem;
    border-radius: 999px;
    color: #94A3B8;
    font-size: .6875rem;
    line-height: 1;
    cursor: pointer;
}
.bsearch__clear > i { display: block; line-height: 1; }
.bsearch__clear:hover { background-color: rgba(15, 23, 42, .06); color: var(--ink-900); }
/* 빈 칸에는 지울 것이 없다. 자리는 남겨 둔다 — 글자를 넣을 때마다 칸이 흔들린다. */
.bsearch__input:placeholder-shown ~ .bsearch__clear { visibility: hidden; }
/* 결과 수는 표 왼쪽 위에 조용히 적는다 — 제목 줄에 붙이면 글자를 칠 때마다
   칩이 옆으로 자란다. */
.vs-count {
    margin-bottom: .375rem;
    font-size: .6875rem;
    color: #475569;
}
/* 표·글 블록은 본문에 여백이 없어(.pblock__body 의 padding:0) 이 줄이 제목 줄과 표 사이에
   끼어 양쪽에 달라붙는다. 카드 블록은 본문 여백이 이미 있어 이 규칙이 필요 없다.
   가로는 표의 칸과 같은 .75rem — 첫 열과 왼쪽 선이 맞는다. */
.pblock[data-view-type="table"] > .pblock__body > .vs-count,
.pblock[data-view-type="text"] > .pblock__body > .vs-count { padding: .5rem .75rem 0; }
.vs-count__word { font-weight: 600; color: var(--ink-900); }

/* 걸린 글자 — 남긴 줄 안에서 어디가 맞은 것인지. 브라우저 기본 mark(형광 노랑)는
   유리 위에서 혼자 튀어 표를 찢어 놓는다 → 옅은 호박색 판에 글자색은 그대로.
   (검색어 칩의 .vs-count__word 와 같은 편, 색만 한 단 옅다) */
mark.vs-mark {
    background-color: #FEF0C7;
    color: inherit;
    border-radius: 3px;
    padding: 0 1px;
}
/* 검색어에 맞는 것이 하나도 없을 때 — 빈 표만 남으면 원래 비어 있는 것으로 읽힌다. */
.vs-empty {
    padding: .875rem .5rem;
    text-align: center;
    font-size: 12px;
    color: #64748B;
}

/* 검색과 엑셀은 같은 '이 블록에 대한 도구'다 — 제목과 벌어진 만큼 서로 떨어질 이유가 없다. */
.bsearch + .pblock__xlsx { margin-left: -.3125rem; }
