﻿/* =============================================================================
   PARK'S HUB — premium business portal UI. Light/dark, responsive, a11y-aware.
   No external CSS. Charts are pure SVG/CSS.
   ============================================================================= */
:root {
  /* **테마는 다크 하나다.** 브랜드는 '어둠에서 빛으로' 이고 탭 배경 영상이 어둡다.
     고르는 기능은 없앴다 — 아래 주석 참조. */

  --bg: #0f1217;
  --bg-elev: #171b22;
  --bg-soft: #1d232c;
  --border: #262d38;
  --border-strong: #333c49;
  --text: #e7eaf0;
  --text-soft: #b3bccb;
  --text-mute: #7c8797;
  --accent: #6c9bff;
  --accent-soft: #1a2740;
  --accent-ink: #b9ceff;
  --ok: #4cc79b;
  --ok-soft: #10281f;
  --warn: #e0b15a;
  --warn-soft: #2a2313;
  --danger: #f0796f;
  --danger-soft: #2c1614;
  --shadow: 0 1px 2px rgba(0,0,0,.4), 0 6px 20px rgba(0,0,0,.35);
  --shadow-lg: 0 12px 44px rgba(0,0,0,.55);

  --radius: 14px;
  --radius-sm: 9px;
  --radius-xs: 5px;
  /* 모션 (tokens.json motion). 값이 아니라 **의도**로 고른다 */
  --dur-fast: 120ms;   /* hover·색 변화 */
  --dur-base: 150ms;   /* 기본 전환 */
  --dur-slow: 300ms;   /* 레이아웃·진행바 */
  --dur-veil: 550ms;   /* 인트로 오버레이 전용 */
  --dur-ambient: 1400ms; /* 끝나지 않는 표시(스켈레톤·스피너) */
  --ease: ease;
  --ease-out: cubic-bezier(.2,.8,.2,1);  /* 들어오는 것: 빠르게 나타나 부드럽게 멈춘다 */
  /* 발광 — **선과 점에만** 쓴다(가이드 §0). 넓은 면에 쓰면 그 위 글자가 안 읽힌다. */
  --glow-line: rgba(108, 155, 255, .28);   /* 유리 표면의 경계선 */
  --glow-soft: 0 0 12px rgba(108, 155, 255, .35);
  --glow-hard: 0 0 18px rgba(108, 155, 255, .55);
  --nav-h: 62px;
  --maxw: 1180px;
  /* --- 타이포 스케일 -----------------------------------------------------------
     2026-09-15: px 이 78곳에 흩어져 있어 '한 단계 키우기' 가 78번의 수정이었다.
     색과 마찬가지로 **한 곳**에 모았다. 여기 한 줄이 전부를 움직인다.

     같은 날 한 단씩 올렸다(13→14 등). 어두운 바탕에 흐린 영상까지 뒤에 깔린
     화면에서 13px 본문은 작았다 — 화면이 비어 보이던 이유의 절반은 밀도가 아니라
     **글자가 작아서** 생긴 여백이었다. 커진 글자가 같은 상자를 정직하게 채운다. */
  --fs-xs: 12px;        /* 메타·캡션·배지 */
  --fs-sm: 13px;        /* 보조 설명·표 내부 */
  --fs-base: 14px;      /* 본문 기본 */
  --fs-md: 16px;        /* 카드 제목 */
  --fs-lg: 20px;        /* 섹션 제목 */
  --fs-xl: 26px;        /* 페이지 제목·KPI 수치 */
  --fs-display: 32px;   /* 탭 첫 화면의 제목. 그 외 금지 */
  --font: "Segoe UI", "Malgun Gothic", "Apple SD Gothic Neo", system-ui, -apple-system, sans-serif;
  /* 기계가 만든 값 전용. 웹폰트를 쓰지 않는 원칙은 여기도 같다. */
  --on-fill: #ffffff;   /* 채워진 면(액센트·상태색) 위의 텍스트 */
  --logo-grad-to: #7a5cff;  /* 로고 그라데이션 끝색. 브랜드 심볼 전용 */
  --paper: #ffffff;         /* 리포트 지면. 테마를 따르지 않는다 — 문서는 종이다 */   /* 채워진 면(액센트·상태색) 위의 텍스트 */
  --font-mono: ui-monospace, "Cascadia Mono", Consolas, "D2Coding", monospace;
}
/* 라이트 팔레트는 없앴다 (2026-09-15).
   두 벌을 함께 관리하면 한쪽만 손보다가 다른 쪽이 조용히 깨진다 —
   라이트에서 탭 배경 영상을 통째로 꺼야 했던 것이 그 증거였다.
   시스템 설정도 따라가지 않는다. 색이 하나뿐이라 스크립트를 기다릴 일도,
   흰 화면이 번쩍일 일도 없다. */
/* --- 바탕 -------------------------------------------------------------------
   2026-09-15 에 발견: **body 규칙이 아예 없었다.**
   --font 토큰은 정의돼 있고 검사기가 tokens.json 과 대조까지 하고 있었지만,
   그 값을 쓰는 곳이 한 군데도 없었다 — 화면은 내내 브라우저 기본 글꼴로 그려졌다.
   크기도 마찬가지여서, 규칙이 붙은 요소는 13px, 안 붙은 요소는 브라우저 기본 16px 로
   갈라져 있었다. 화면이 들쭉날쭉해 보이던 진짜 원인이 이것이다.

   기준선을 여기서 한 번 선언한다. 이 아래의 모든 규칙은 이 기준에서의 **차이**다. */
/* 2026-09-15 에 발견: box-sizing 규칙이 **아예 없었다.**
   그래서 `min-height: 100vh` 에 `padding: 20px` 을 준 로그인 화면이 항상
   화면보다 정확히 40px 더 컸다 — 없어야 할 스크롤바가 늘 있던 이유다.
   여백을 크기 안쪽으로 세는 것이 이 코드가 내내 가정하던 방식이기도 하다. */
*, *::before, *::after { box-sizing: border-box; }
html { color-scheme: dark; }
body {
  margin: 0;
  font-family: var(--font);
  font-size: var(--fs-base);
  line-height: 1.6;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4 { line-height: 1.3; text-wrap: balance; }
a { color: var(--accent-ink); text-decoration: none; }
a:hover, a:focus-visible { text-decoration: underline; }
button { font-family: inherit; cursor: pointer; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-xs); }
img { max-width: 100%; }

/* --- buttons --- */
.btn {
  display: inline-flex; align-items: center; gap: 8px; justify-content: center;
  border: 1px solid var(--border-strong); background: var(--bg-elev); color: var(--text);
  padding: 8px 16px; border-radius: var(--radius-sm); font-size: var(--fs-base); font-weight: 600;
  transition: var(--dur-base) var(--ease); white-space: nowrap;
}
.btn:hover { border-color: var(--accent); color: var(--accent-ink); }
/* 화면에서 할 일이 하나라면 그게 무엇인지 한눈에 보여야 한다 */
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-fill); font-weight: 700; }
.btn.primary:hover { filter: brightness(1.06); color: var(--on-fill); }
.btn.ghost { background: transparent; border-color: transparent; }
.btn.ghost:hover { background: var(--bg-soft); }
.btn.danger { color: var(--danger); border-color: var(--danger); }
.btn.danger:hover { background: var(--danger-soft); }
.btn.sm { padding: 4px 12px; font-size: var(--fs-sm); }
.btn:disabled { opacity: .5; cursor: not-allowed; }

.icon-btn {
  width: 38px; height: 38px; display: inline-grid; place-items: center;
  border-radius: var(--radius-sm); border: 1px solid transparent; background: transparent; color: var(--text-soft);
}
.icon-btn { transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease); }
.icon-btn:hover { background: var(--bg-soft); color: var(--text); }

/* --- top nav --- */
.nav {
  position: sticky; top: 0; z-index: 40; height: var(--nav-h);
  /* 고정 표면이라 유리가 허용된다(가이드 §유리). 배경 영상이 비쳐 화면이 이어져 보인다.
     불투명도는 측정으로 정했다 — 뒤에 순백 프레임이 올 때 비활성 링크(--text-soft)가
     .72 에서 3.74:1 로 미달했다. .82 에서 5.4:1 이 나온다. */
  background: rgba(18, 23, 31, .82);
  backdrop-filter: saturate(150%) blur(18px);
  -webkit-backdrop-filter: saturate(150%) blur(18px);
  border-bottom: 1px solid var(--glow-line);
  display: flex; align-items: center; gap: 16px; padding: 0 20px;
}
.nav .brand { display: flex; align-items: center; gap: 12px; font-weight: 800; letter-spacing: .2px; }
.nav .brand .logo {
  width: 30px; height: 30px; border-radius: var(--radius-sm);
  background: linear-gradient(135deg, var(--accent), var(--logo-grad-to));
  display: grid; place-items: center; color: var(--on-fill); font-weight: 900; font-size: var(--fs-md);
}
img.logo { object-fit: cover; background: none; display: block; }
/* 탭은 **화면 한가운데** 둔다.
   셋뿐이고, 그 셋이 제품의 전부다(수집 → 연결 → 답). 구석에 붙여 놓으면
   도구모음의 일부처럼 보이고, 가운데 있으면 그 자체가 목차가 된다.
   흐름에서 빼내 절대 위치로 중앙을 잡는 이유: 왼쪽 로고와 오른쪽 검색·아이콘의
   폭이 서로 달라, 흐름 안에서는 `flex:1` 을 아무리 넣어도 진짜 중앙이 안 나온다. */
.nav .links {
  position: absolute; left: 50%; transform: translateX(-50%);
  display: flex; gap: 2px; margin-left: 0;
}
.nav .links a {
  padding: 10px 20px; border-radius: var(--radius-sm); color: var(--text-soft);
  font-weight: 600; font-size: var(--fs-base); letter-spacing: .01em;
}
.nav .links a:hover { background: var(--bg-soft); color: var(--text); text-decoration: none; }
.nav .links a.active { background: var(--accent-soft); color: var(--accent-ink); }
.nav .spacer { flex: 1; }
/* 상단 바의 통일성은 **양 끝이 같은 크기**일 때 생긴다.
   왼쪽 브랜드 마크 30px ↔ 오른쪽 아바타 30px. 눌리는 면(38px)은 둘 다 같고,
   아바타는 동그라미이므로 그 버튼의 눌림 표시도 동그라미로 맞춘다 —
   네모 안의 동그라미가 겹쳐 보이던 것이 이 어긋남이었다. */
.nav .menu .icon-btn { border-radius: 50%; padding: 0; }
.nav .avatar { width: 30px; height: 30px; border: 0; }
/* 검색이 빠지면서 오른쪽 덩어리가 아이콘 둘(≈110px)로 줄었다. 왼쪽 브랜드도 비슷한 폭이라
   이제 600px 위 어디서나 중앙 정렬이 성립한다 — 900px 예외를 둘 이유가 없어졌다.
   브랜드 이름이 길어지면 겹치는 대신 **잘린다**. */
.nav .brand { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 검색은 내비에서 아카이브 도구줄로 내려갔다. 모든 화면에 떠 있을 이유가 없었고,
   내려오면서 상단 바가 **브랜드 · 탭 · 계정** 세 덩어리로 정리됐다. */
.search {
  display: flex; align-items: center; gap: 8px; background: var(--bg-soft);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm); padding: 8px 12px;
  width: min(420px, 100%);
}
.search:focus-within { border-color: var(--accent); }
/* 아래쪽의 일반 input[type=text] 규칙과 명시도가 같아 그대로 두면 **뒤에 오는 쪽이 이겨**
   테두리가 두 겹이 된다. 타입까지 적어 명시도를 한 칸 올린다. */
.search input[type=text] { border: 0; background: transparent; color: var(--text);
  width: 100%; font-size: var(--fs-base); outline: none; padding: 0; }
.avatar {
  width: 34px; height: 34px; border-radius: 50%; object-fit: cover;
  background: var(--accent-soft); color: var(--accent-ink);
  display: inline-grid; place-items: center; font-weight: 700; font-size: var(--fs-base); border: 1px solid var(--border);
}
.menu { position: relative; }
.menu .dropdown {
  position: absolute; right: 0; top: 46px; border: 1px solid var(--glow-line);
  background: rgba(23, 27, 34, .92);
  backdrop-filter: blur(20px) saturate(140%); -webkit-backdrop-filter: blur(20px) saturate(140%);
  border-radius: var(--radius); box-shadow: var(--shadow-lg); min-width: 220px; padding: 8px; display: none;
}
.menu.open .dropdown { display: block; }
.menu .dropdown .who { padding: 8px 12px; border-bottom: 1px solid var(--border); margin-bottom: 8px; }
.menu .dropdown .who b { display: block; }
.menu .dropdown button {
  width: 100%; text-align: left; border: 0; background: transparent; color: var(--text);
  padding: 8px 12px; border-radius: var(--radius-sm); font-size: var(--fs-base); display: flex; gap: 8px; align-items: center;
}
.menu .dropdown button:hover { background: var(--bg-soft); }

/* --- layout --- */
main { max-width: var(--maxw); margin: 0 auto; padding: 24px 20px 80px; }
.page-head { display: flex; align-items: center; gap: 16px; margin: 8px 0 20px; }
/* 크기는 스케일 상한(22px)에 묶여 있으므로 **굵기와 자간**으로 위계를 만든다.
   가이드가 허용하는 굵기는 400/500/700/800 네 단계다. */
.page-head h1 { font-size: var(--fs-xl); margin: 0; font-weight: 800; letter-spacing: -.4px; }
/* 페이지 부제는 읽는 글이다 — 메타 색(3.13:1)이 아니라 보조 텍스트 색(7.56:1) */
.page-head .sub { color: var(--text-soft); font-size: var(--fs-base); }

/* --- filter bar --- */
.toolbar { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-bottom: 16px; }
.toolbar .grow { flex: 1; }
select, input[type=text], input[type=email], textarea {
  font-family: inherit; font-size: var(--fs-base); color: var(--text);
  background: var(--bg-elev); border: 1px solid var(--border-strong); border-radius: var(--radius-sm); padding: 8px 12px;
}
select:focus, input:focus, textarea:focus { border-color: var(--accent); outline: none; }
.seg { display: inline-flex; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); overflow: hidden; }
.seg button { border: 0; background: var(--bg-elev); color: var(--text-soft); padding: 8px 12px; font-size: var(--fs-base); }
.seg button.active { background: var(--accent-soft); color: var(--accent-ink); }

/* --- post grid / list --- */
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 16px; }
.card {
  background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 16px; position: relative; overflow: hidden;
  transition: var(--dur-base) var(--ease); cursor: pointer;
}
.card:hover { transform: translateY(-2px); border-color: var(--border-strong); box-shadow: var(--shadow-lg); }
.card::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px; background: var(--dom, var(--accent)); }
.card .title { font-weight: 700; font-size: var(--fs-md); margin: 2px 0 8px; }
.card .summary { color: var(--text-soft); font-size: var(--fs-base); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.card .meta { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; align-items: center; }
.card .foot { display: flex; gap: 12px; margin-top: 12px; color: var(--text-mute); font-size: var(--fs-sm); }

.list { display: flex; flex-direction: column; gap: 8px; }
.row {
  display: grid; grid-template-columns: 6px 1.6fr 90px 90px auto; gap: 12px; align-items: center;
  background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 12px 16px;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
/* 누를 수 있다고 **말하는 것**은 실제로 누를 수 있는 행에만. 같은 .row 를 표 머리글로도
   쓰는데, 거기에 손가락 커서와 hover 를 주면 눌러도 아무 일이 없는 거짓말이 된다. */
.row[role="button"] { cursor: pointer; }
.row[role="button"]:hover, .row[role="button"]:focus-visible {
  border-color: var(--border-strong); background: var(--bg-elev); }
.row .dom-bar { width: 6px; height: 34px; border-radius: var(--radius-xs); background: var(--dom, var(--accent)); }
.row .t b { font-size: var(--fs-base); }
.row .t small { color: var(--text-mute); display: block; }
.row .foot { color: var(--text-mute); font-size: var(--fs-sm); text-align: right; }

/* --- pills / tags --- */
.pill { display: inline-flex; align-items: center; gap: 4px; font-size: var(--fs-xs); font-weight: 700;
  padding: 3px 8px; border-radius: 999px; background: var(--bg-soft); color: var(--text-soft); }
.pill.dom { background: color-mix(in srgb, var(--dom) 16%, transparent); color: var(--dom-ink, var(--dom)); }
.pill.status-in_progress { background: var(--warn-soft); color: var(--warn); }
.pill.status-published { background: var(--ok-soft); color: var(--ok); }
.pill.status-draft { background: var(--bg-soft); color: var(--text-mute); }
.pill.status-review { background: var(--accent-soft); color: var(--accent-ink); }
.pill.status-archived { background: var(--bg-soft); color: var(--text-mute); }
.pill.vis-private { background: var(--danger-soft); color: var(--danger); }
.pill.vis-shared { background: var(--accent-soft); color: var(--accent-ink); }
.pill.vis-public { background: var(--ok-soft); color: var(--ok); }
.tag { font-size: var(--fs-xs); color: var(--text-soft); background: var(--bg-soft); padding: 2px 8px; border-radius: var(--radius-xs); }

/* --- detail --- */
.detail { display: grid; grid-template-columns: 1fr 300px; gap: 20px; align-items: start; }
/* 평면 컨테이너는 **경계선**으로 구분한다. 그림자는 진짜로 떠 있는 것(모달·드롭다운·
   토스트·팝오버)만 갖는다. 전부 떠 있으면 아무것도 떠 있지 않은 것과 같다. */
.panel { background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius); padding: 24px; }
.panel h3 { margin: 0 0 12px; font-size: var(--fs-md); }
.detail .body :is(h1,h2,h3){ line-height:1.3; }
.detail .body img { border-radius: var(--radius-sm); }
.kv { display: flex; justify-content: space-between; gap: 12px; padding: 8px 0; border-bottom: 1px dashed var(--border); font-size: var(--fs-base); }
.kv:last-child { border-bottom: 0; }
.kv .k { color: var(--text-soft); }
/* 리포트 본문은 밝은 지면 위에 쓰인 문서다. 다크 테마를 따라가면 저자가 의도한
   대비가 무너진다 — 그래서 이 면만 테마에서 떼어 놓는다. */
.viewer-frame { width: 100%; height: 70vh; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--paper); }
.att-item, .ver-item { display: flex; align-items: center; gap: 12px; padding: 8px 0; border-bottom: 1px solid var(--border); font-size: var(--fs-base); }
.att-item:last-child, .ver-item:last-child { border-bottom: 0; }
.att-item .ext { width: 40px; height: 30px; border-radius: var(--radius-xs); background: var(--bg-soft); display: grid; place-items: center; font-size: var(--fs-xs); font-weight: 800; color: var(--text-soft); }

/* --- comments --- */
.comment { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 12px; margin-bottom: 12px; background: var(--bg-elev); }
.comment.resolved { opacity: .62; }
.comment .head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.comment .body { white-space: pre-wrap; font-size: var(--fs-base); }
.comment .replies { margin-top: 12px; padding-left: 16px; border-left: 2px solid var(--border); }
.mention { color: var(--accent-ink); font-weight: 700; background: var(--accent-soft); padding: 0 4px; border-radius: var(--radius-xs); }

/* --- modal --- */
/* 뒤에 영상이 흐르므로 오버레이가 더 깊어야 모달이 떠 보인다 */
.overlay { position: fixed; inset: 0; background: rgba(6, 10, 16, .66); backdrop-filter: blur(6px); z-index: 60; display: grid; place-items: center; padding: 20px; }
/* 고정 표면 — 유리 허용. 다만 본문을 읽는 곳이라 불투명도를 높게 잡는다. */
.modal { background: rgba(23, 27, 34, .92); border: 1px solid var(--glow-line);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
  backdrop-filter: blur(24px) saturate(140%); -webkit-backdrop-filter: blur(24px) saturate(140%);
  width: min(760px, 100%); max-height: 90vh; overflow: auto; }
/* 모달의 세 면은 같은 가로 여백을 써야 내용이 한 줄로 정렬된다 */
.modal .m-head { display: flex; align-items: center; justify-content: space-between; padding: 16px 24px; border-bottom: 1px solid var(--border); position: sticky; top: 0; background: var(--bg-elev); }
.modal .m-head h2 { margin: 0; font-size: var(--fs-lg); }
.modal .m-body { padding: 24px; }
.modal .m-foot { padding: 16px 24px; border-top: 1px solid var(--border); display: flex; justify-content: flex-end; gap: 12px; position: sticky; bottom: 0; background: var(--bg-elev); }
.field { margin-bottom: 16px; }
.field label { display: block; font-size: var(--fs-sm); font-weight: 700; color: var(--text-soft); margin-bottom: 8px; }
.field input, .field select, .field textarea { width: 100%; }
.field .hint { color: var(--text-mute); font-size: var(--fs-xs); margin-top: 4px; }
.two { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }

/* --- dashboard --- */
/* 숫자 타일을 담는 격자. .stat-grid 와 .kpi-row 가 각각 다른 값으로 같은 일을 하고 있었다 —
   타일을 한 벌로 합쳤으면 격자도 한 벌이어야 한다. auto-fit 이라 개수가 달라도 빈칸이 남지 않는다. */
.stat-grid, .kpi-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 20px; }
.stat-grid { margin-bottom: 24px; }
/* .stat 과 .kpi 는 **같은 컴포넌트**다 — 숫자 하나와 그 이름.
   두 벌로 두면 한쪽만 고쳐져 서로 다르게 보이기 시작한다. 한 규칙으로 묶는다. */
.stat, .kpi { background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 16px; }
.stat .n, .kpi-value { font-size: var(--fs-xl); font-weight: 800; letter-spacing: -.5px; }
.stat .l, .kpi-label { color: var(--text-mute); font-size: var(--fs-sm); }
.stat .l { margin-top: 4px; }

/* --- chat / ask --- */
.chat { display: flex; flex-direction: column; gap: 16px; max-width: 820px; margin: 0 auto; }
.msg { display: flex; gap: 12px; }
.msg .bubble { background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 16px; white-space: pre-wrap; }
.msg.me { justify-content: flex-end; }
.msg.me .bubble { background: var(--accent); color: var(--on-fill); border-color: var(--accent); }
.sources { margin-top: 12px; display: flex; flex-wrap: wrap; gap: 8px; }
/* 본문 위에 실제로 겹쳐 떠 있으므로 그림자를 유지한다 */
.chat-input { position: sticky; bottom: 12px; display: flex; gap: 8px; background: var(--bg-elev); border: 1px solid var(--border-strong); border-radius: var(--radius); padding: 8px; box-shadow: var(--shadow); }
.chat-input textarea { flex: 1; border: 0; background: transparent; resize: none; min-height: 24px; max-height: 140px; }

/* --- misc --- */
/* 빈 상태는 '무엇을 하면 채워지는지' 를 말하는 안내문이다. 반드시 읽혀야 한다. */
.empty { text-align: center; color: var(--text-soft); padding: 60px 20px; }
.spinner { width: 18px; height: 18px; border: 2px solid var(--border-strong); border-top-color: var(--accent); border-radius: 50%; animation: spin .7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.toast-wrap { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); z-index: 90; display: flex; flex-direction: column; gap: 8px; }
.toast { background: var(--text); color: var(--bg); padding: 12px 16px; border-radius: var(--radius-sm); font-size: var(--fs-base); box-shadow: var(--shadow-lg); animation: rise var(--dur-base) var(--ease-out); }
.toast.err { background: var(--danger); color: var(--on-fill); }
.toast.ok { background: var(--ok); color: var(--on-fill); }
@keyframes rise { from { opacity: 0; transform: translateY(8px); } }

/* --- login --- */
/* 로그인은 **한 화면에 끝난다.** 스크롤이 생기면 그것만으로 덜 완성돼 보인다.
   100vh 는 모바일에서 주소창 높이를 빼지 않아 항상 조금 넘친다 — dvh 가 실제로
   보이는 높이다. dvh 를 모르는 브라우저를 위해 vh 를 먼저 적어 둔다. */
.login-wrap { min-height: 100vh; min-height: 100dvh;
  display: grid; place-items: center; padding: 20px;
  background: radial-gradient(1200px 500px at 50% -10%, var(--accent-soft), transparent), var(--bg); }
/* 로그인 배경 영상 — 인트로가 끝나면 이 화면이 드러난다. 카드 뒤에서 조용히 돈다.
   영상은 장식이므로 보조기술에서 숨기고 탭 순서에서도 뺀다(마크업의 aria-hidden/tabindex). */
/* 바닥에 정지 프레임을 먼저 깐다. 영상이 자동재생을 거절당하든, 디코딩에 실패하든,
   데이터 절약 모드든 — **배경이 검게 비는 일이 없어야 한다.** 영상은 그 위에 얹힐 뿐이다. */
.login-wrap { position: relative; overflow: hidden;
  background-image: url("/static/background-poster.jpg");
  background-size: cover; background-position: center; }
/* 영상을 숨겼다가 드러내지 않는다. 숨김을 CSS 전환에 맡기면, 탭이 백그라운드일 때
   전환이 진행되지 않아 **영원히 안 보이는** 상태가 생긴다(실제로 그렇게 만들어 봤다).
   video 의 poster 와 이 배경 이미지가 이미 같은 장면이라, 영상이 준비될 때까지
   보이는 것은 정지 프레임이고 검게 비는 순간이 없다. */
.login-bg { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; z-index: 0;
  background: url("/static/background-poster.jpg") center / cover no-repeat; }
.login-bg-still { background: url("/static/background-poster.jpg") center / cover no-repeat; }
/* 스크림은 **가장자리만** 눌러 카드를 앉힌다.
   영상 평균 휘도가 32/255 로 이미 어둡다 — 전면을 덮으면 배경이 통째로 사라진다.
   (한 번 그렇게 만들어 검은 화면이 됐다. 밝은 소재로 바꾸면 여기 값을 다시 잰다.) */
.login-scrim { position: absolute; inset: 0; z-index: 1;
  background: radial-gradient(115% 85% at 50% 45%, transparent 35%, rgba(4,10,16,.55) 100%); }
/* 로그인 카드는 **Expressive 표면**이다 — 브랜드 영상 위에 떠 있고, 작업 화면이 아니다.
   가이드가 인트로 오버레이를 Expressive 예외로 둔 것과 같은 자리라 유리 처리를 쓴다.
   Product 작업 화면(패널·카드·통계)에는 쓰지 않는다.

   유리는 대비를 갉아먹으므로 바탕을 **충분히 어둡게** 깔고 그 위에 블러를 얹는다.
   backdrop-filter 를 못 쓰는 브라우저에서는 아래 @supports 가 불투명 배경으로 되돌린다. */
.login-card { position: relative; z-index: 2;
  width: min(420px, 100%); padding: 32px; text-align: center;
  /* 불투명도는 취향이 아니라 **측정으로** 정한다.
     .62 에서는 밝은 입자가 뒤에 올 때 작은 글자 대비가 1.63:1 까지 떨어졌다(기준 4.5:1).
     측정값 — 최악 프레임 기준: .62→3.68  .78→5.90  .88→7.89
     .78 을 쓴다. 기준을 여유 있게 넘기면서 유리의 투명감이 남는 지점이다.
     배경 영상을 밝은 소재로 바꾸면 이 값을 **다시 재야 한다**. */
  background: rgba(18, 29, 40, .78);
  border: 1px solid rgba(255, 255, 255, .14);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
  backdrop-filter: blur(20px) saturate(140%);
  -webkit-backdrop-filter: blur(20px) saturate(140%); }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  /* 유리를 못 쓰면 불투명 배경으로 되돌린다 — 반투명만 남으면 글자가 뒤와 겹친다. */
  .login-card, .modal, .menu .dropdown { background: var(--bg-elev); border-color: var(--border); }
  .nav { background: var(--bg-elev); }
}
/* 유리 위에서는 토큰 색이 아니라 **밝은 고정색**을 쓴다 — 카드 뒤는 토큰이 모르는
   영상이기 때문이다. 대비는 아래 주석의 실측값을 지킨다. */
.login-card h1 { color: var(--on-fill); }
.login-card .logo { width: 54px; height: 54px; border-radius: var(--radius); margin: 0 auto 16px;
  background: linear-gradient(135deg, var(--accent), var(--logo-grad-to)); display: grid; place-items: center; color: var(--on-fill); font-weight: 900; font-size: var(--fs-xl); }
.login-card h1 { font-size: var(--fs-xl); margin: 0 0 4px; }
/* 유리 위에서는 토큰 색을 쓰지 않는다 — 카드 뒤가 어두운 영상이라 테마와 무관하다.
   반투명 글자를 반투명 배경에 올리면 손실이 곱해지므로 불투명한 값을 쓴다.
   측정: 최악 프레임에서 5.90:1 (기준 4.5:1). */
.login-card p { color: #c2cdda; font-size: var(--fs-base); margin: 0 0 24px; }
.login-card .btn { width: 100%; padding: 12px; }
.login-note { margin-top: 16px; font-size: var(--fs-sm); color: #c2cdda; }

/* --- dashboard --- */
.notif-bar { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
.notif { font-size: var(--fs-sm); font-weight: 600; padding: 8px 12px; border-radius: var(--radius-sm);
  background: var(--bg-soft); color: var(--text-soft); border-left: 3px solid var(--border-strong); }
.notif.lv-danger { background: var(--danger-soft); color: var(--danger); border-color: var(--danger); }
.notif.lv-warn { background: var(--warn-soft); color: var(--warn); border-color: var(--warn); }
.notif.lv-accent { background: var(--accent-soft); color: var(--accent-ink); border-color: var(--accent); }

.cal-head { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }

.cal { user-select: none; }
.cal-row, .cal-body { display: grid; grid-template-columns: repeat(7, 1fr); }
.cal-dow { margin-bottom: 8px; }
.dow { text-align: center; font-size: var(--fs-xs); font-weight: 700; color: var(--text-mute); padding: 4px 0; }
.dow.sun, .cal-day.sun { color: var(--danger); }
.dow.sat, .cal-day.sat { color: var(--accent); }
.cal-body { gap: 4px; }
.cal-cell { min-height: 78px; border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 4px 8px;
  background: var(--bg-elev); cursor: pointer; overflow: hidden; transition: var(--dur-fast) var(--ease); }
/* hover 는 마우스에만 있다. 같은 단서를 키보드에도 준다. */
.cal-cell { transition: border-color var(--dur-fast) var(--ease); }
.cal-cell:hover, .cal-cell:focus-visible { border-color: var(--accent); }
.cal-cell.muted { opacity: .42; }
.cal-cell.today { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.cal-day { font-size: var(--fs-sm); font-weight: 700; margin-bottom: 3px; }
.cal-evs { display: flex; flex-direction: column; gap: 2px; }
.cal-ev { font-size: var(--fs-xs); padding: 1px 4px; border-radius: var(--radius-xs); white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; background: color-mix(in srgb, var(--dom) 20%, transparent); color: var(--text);
  border-left: 3px solid var(--dom); }
.cal-more { font-size: var(--fs-xs); color: var(--text-mute); padding-left: 3px; }

.todo { display: flex; align-items: flex-start; gap: 8px; padding: 8px 0; border-bottom: 1px solid var(--border); }
.todo:last-child { border-bottom: 0; }
.todo-check { width: 20px; height: 20px; flex: 0 0 auto; margin-top: 1px; border: 2px solid var(--border-strong);
  border-radius: var(--radius-xs); background: transparent; display: grid; place-items: center; }
.todo-check { transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease); }
.todo-check:hover, .todo-check:focus-visible { border-color: var(--accent); }
.todo-check::after { content: "✓"; font-size: var(--fs-sm); color: transparent; }
.todo-main { flex: 1; min-width: 0; }
.todo-text { font-size: var(--fs-base); }
.todo-meta { display: flex; align-items: center; gap: 8px; margin-top: 3px; font-size: var(--fs-xs); color: var(--text-mute); }
.todo-meta .dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.due-over { color: var(--danger); font-weight: 700; }
.due-today { color: var(--warn); font-weight: 700; }

.up-item { display: flex; gap: 12px; align-items: center; padding: 8px 0; border-bottom: 1px solid var(--border); }
.up-item:last-child { border-bottom: 0; }
.up-bar { width: 4px; align-self: stretch; min-height: 28px; border-radius: var(--radius-xs); background: var(--dom, var(--accent)); }

.proj-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px; }
.proj { border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; background: var(--bg-elev);
  border-top: 3px solid var(--dom, var(--accent)); }
.proj-top { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.proj-desc { font-size: var(--fs-sm); color: var(--text-soft); margin-bottom: 12px; }
.proj-bar { height: 10px; background: var(--bg-soft); border-radius: 999px; overflow: hidden; margin-top: 8px; }
.proj-fill { height: 100%; border-radius: 999px; transition: width var(--dur-slow) var(--ease-out); }
.proj-pct { font-size: var(--fs-sm); color: var(--text-mute); margin-top: 4px; }
.proj-ms { margin-top: 12px; display: flex; flex-direction: column; gap: 4px; }
.ms { display: flex; align-items: center; gap: 8px; font-size: var(--fs-sm); color: var(--text-soft); }
/* 완료 항목은 흐려지되 읽을 수는 있어야 한다 — 취소선이 이미 상태를 말한다 */
.ms.done { color: var(--text-soft); text-decoration: line-through; }
.ms-box { width: 16px; height: 16px; border: 1.5px solid var(--border-strong); border-radius: var(--radius-xs);
  display: grid; place-items: center; font-size: var(--fs-xs); color: var(--ok); flex: 0 0 auto; }
.ms.done .ms-box { border-color: var(--ok); background: var(--ok-soft); }

/* --- self-care --- */
.care-grid { display: grid; grid-template-columns: 1.25fr 1fr; gap: 16px; align-items: start; }
.care-entry { border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; margin-bottom: 12px; background: var(--bg-elev); }
.care-entry:last-child { margin-bottom: 0; }
.ce-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.ce-body { white-space: pre-wrap; font-size: var(--fs-base); color: var(--text); line-height: 1.6; }
.care-photos { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.care-photos img { width: 88px; height: 88px; object-fit: cover; border-radius: var(--radius-sm); border: 1px solid var(--border); }
.metric-card { border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 16px; margin-bottom: 12px; background: var(--bg-elev); }
.metric-card:last-child { margin-bottom: 0; }
.mc-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.mc-head b { font-size: var(--fs-md); }
.mc-points { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px; }
/* 측정값 삭제 버튼. 버튼이므로 기본 테두리를 지우고 칩처럼 보이게 한다. */
.mc-pt { font: inherit; font-size: var(--fs-xs); color: var(--text-soft); background: var(--bg-soft);
  border: 0; border-radius: var(--radius-xs); padding: 2px 8px; cursor: pointer;
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease); }
.mc-pt b { color: var(--text-mute); margin-left: 4px; }
.mc-pt:hover, .mc-pt:focus-visible { color: var(--danger); background: var(--danger-soft); }
.judge { font-size: var(--fs-xs); font-weight: 700; padding: 1px 8px; border-radius: 999px; }
.judge.j-ok { background: var(--ok-soft); color: var(--ok); }
.judge.j-warn { background: var(--warn-soft); color: var(--warn); }
.judge.j-danger { background: var(--danger-soft); color: var(--danger); }
.mc-proj { font-size: var(--fs-xs); color: var(--text-soft); margin-top: 8px; }
.rem-item { display: flex; align-items: center; gap: 12px; padding: 8px 0; border-bottom: 1px solid var(--border); }
.rem-item:last-child { border-bottom: 0; }
.art-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; }
.art-card { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 12px 16px; background: var(--bg-elev); cursor: pointer; transition: var(--dur-fast) var(--ease); }
.art-card { transition: border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease-out); }
.art-card:hover, .art-card:focus-visible { border-color: var(--accent); transform: translateY(-1px); }
.art-card b { display: block; margin-bottom: 8px; }
.art-body { max-height: 60vh; overflow: auto; font-size: var(--fs-base); line-height: 1.6; }
.art-body h2 { font-size: var(--fs-lg); margin: .5em 0 .3em; }
.art-body h3 { font-size: var(--fs-md); margin: .5em 0 .3em; }
.art-body h4 { font-size: var(--fs-base); margin: .5em 0 .3em; }
.art-body ul { margin: .3em 0 .3em 1.2em; }
.art-body code { background: var(--bg-soft); padding: 1px 4px; border-radius: var(--radius-xs); font-size: var(--fs-sm); }

/* --- career --- */
.res-item { padding: 12px 0; border-bottom: 1px solid var(--border); }
.res-item:last-child { border-bottom: 0; }
.res-line { display: flex; align-items: center; gap: 8px; }
.res-desc { white-space: pre-wrap; font-size: var(--fs-base); color: var(--text-soft); line-height: 1.55; margin-top: 4px; }
.job-item { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 12px 16px; margin-bottom: 12px; background: var(--bg-elev); }
.job-item:last-child { margin-bottom: 0; }
.job-top { display: flex; align-items: center; gap: 8px; }
.job-top a { color: var(--text); text-decoration: none; }
.job-top a:hover b { color: var(--accent); }
.job-meta { font-size: var(--fs-sm); color: var(--text-mute); margin-top: 4px; }
.job-status { font-size: var(--fs-sm); padding: 3px 8px; }

/* --- assets --- */
.kpi-value { margin-top: 4px; }
.kpi-sub { font-size: var(--fs-xs); color: var(--text-mute); margin-top: 3px; }
.legend-row { display: flex; align-items: center; gap: 8px; font-size: var(--fs-base); padding: 4px 0; }
.legend-dot { width: 10px; height: 10px; border-radius: var(--radius-xs); flex: 0 0 auto; }
.asset-group { font-size: var(--fs-sm); font-weight: 700; color: var(--text-mute); margin: 12px 0 4px; }
.asset-group:first-child { margin-top: 0; }
.asset-row { display: flex; align-items: center; gap: 12px; padding: 8px 0; border-bottom: 1px solid var(--border); }
.asset-row:last-child { border-bottom: 0; }
.kind-pill { font-size: var(--fs-xs); font-weight: 700; padding: 2px 8px; border-radius: 999px; flex: 0 0 auto; }

/* --- intro (plays over the login screen) --- */
.intro { position: fixed; inset: 0; z-index: 200; background: #0b131b;
  display: grid; place-items: center; transition: opacity var(--dur-veil) var(--ease); }
.intro.hide { opacity: 0; pointer-events: none; }
.intro video { width: 100%; height: 100%; object-fit: contain; display: block; }
/* portrait cut already carries its own filled background — let it cover the
   screen so taller phones (19.5:9 etc.) show no bars either */
.intro video.fill { object-fit: cover; }
.intro-ctl { position: absolute; right: 20px; bottom: 20px; display: flex; gap: 8px; }
.intro-ctl .btn { background: rgba(255, 255, 255, .12); color: var(--on-fill);
  border-color: rgba(255, 255, 255, .28); backdrop-filter: blur(6px); }
.intro-ctl .btn:hover, .intro-ctl .btn:focus-visible { background: rgba(255, 255, 255, .22); }
@media (max-width: 600px) { .intro-ctl { right: 12px; bottom: 12px; } }

/* --- lab / transmission confirm --- */
.tx-row { display: flex; align-items: center; gap: 8px; padding: 8px 0; border-bottom: 1px solid var(--border); font-size: var(--fs-base); }
.tx-row:last-child { border-bottom: 0; }

/* --- responsive --- */
@media (max-width: 900px) {
  .detail { grid-template-columns: 1fr; }
  .care-grid { grid-template-columns: 1fr; }
}
@media (max-width: 600px) {
  /* 내비를 숨기지 않는다. 숨기면 폰에서 **탭 이동 자체가 불가능**해진다(실제로 그랬다).
     한 줄 가로 스크롤로 바꿔 모든 탭에 닿을 수 있게 한다 — 브랜드 페이지 목차와 같은 방식. */
  .nav { gap: 8px; padding: 0 12px; }
  .nav .links { position: static; transform: none; margin-left: 0;
    overflow-x: auto; flex: 1 1 auto; min-width: 0;
    scrollbar-width: none; -webkit-overflow-scrolling: touch; }
  .nav .links::-webkit-scrollbar { display: none; }
  .nav .links a { white-space: nowrap; }
  .nav .brand span, .nav .brand { font-size: var(--fs-sm); }
  .two { grid-template-columns: 1fr; }
  .row { grid-template-columns: 6px 1fr auto; }
  .row .r-hide { display: none; }
  main { padding: 16px 16px 72px; }
}
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0; }

/* --- 동작 줄이기 (OS 설정 존중) ---
   브랜드 가이드 §9 접근성 약속. 전환·애니메이션을 사실상 즉시 끝낸다. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  html { scroll-behavior: auto !important; }
}

/* --- 수치 정렬 (브랜드 가이드 §2) ---------------------------------------------
   금액·비율·측정치는 자릿수가 흔들리면 세로 비교가 불가능해진다.
   본문 전체가 아니라 '숫자를 읽는 자리'에만 건다. */
.kpi-value, .kpi-sub, .stat .n, .stat .l, .proj-pct,
.asset-row, .lab-table td, .money, .num,
table td, table th { font-variant-numeric: tabular-nums; }

/* --- 노트 그래프 (연구소) ----------------------------------------------------
   정지 화면으로 그린다. 계속 움직이는 그래프는 읽을 수 없다. */
.gnode { cursor: pointer; }
.gnode circle { transition: r var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease); }
.gnode:hover circle, .gnode:focus-visible circle { opacity: .85; }
.gnode:hover text, .gnode:focus-visible text { fill: var(--text); font-weight: 700; }
.gnode:focus-visible { outline: none; }
.gnode:focus-visible circle { stroke: var(--accent); stroke-width: 3; }

/* --- 입력 수단에 따른 조작 대상 (브랜드 가이드 §7 접근성) --------------------
   마우스는 정밀하고 손가락은 그렇지 않다. 포인터가 거칠 때만 타깃을 넓힌다.
   데스크톱 밀도를 희생하지 않으면서 폰에서 오조작을 없앤다. */
@media (pointer: coarse) {
  .btn { min-height: 44px; padding: 12px 16px; }
  .btn.sm { min-height: 36px; padding: 8px 12px; }
  .icon-btn { width: 44px; height: 44px; }
  .seg button { min-height: 44px; }
  select, input[type=text], input[type=email], textarea { min-height: 44px; }
  .menu .dropdown button { min-height: 44px; }
  .nav .links a { padding: 12px 12px; }
}

/* --- hover 로만 드러나던 것을 키보드에도 드러낸다 ----------------------------
   터치에는 hover 가 없고, 키보드 사용자는 hover 를 만들 수 없다.
   hover 가 상태를 보여주는 곳에는 focus-visible 을 **같은 크기로** 붙인다. */
.btn:focus-visible { border-color: var(--accent); color: var(--accent-ink); }
.btn.primary:focus-visible { filter: brightness(1.06); color: var(--on-fill); }
.btn.ghost:focus-visible { background: var(--bg-soft); }
.btn.danger:focus-visible { background: var(--danger-soft); }
.icon-btn:focus-visible { background: var(--bg-soft); color: var(--text); }
.nav .links a:focus-visible { background: var(--bg-soft); color: var(--text); }
.menu .dropdown button:focus-visible { background: var(--bg-soft); }
.card:focus-visible { transform: translateY(-2px); border-color: var(--border-strong); }

/* --- 로딩: 스켈레톤 ----------------------------------------------------------
   빙글도는 원은 "기다려라" 만 말한다. 스켈레톤은 **무엇이 올지**를 미리 보여줘서
   화면이 이미 도착한 것처럼 느껴지게 하고, 도착했을 때 레이아웃이 튀지 않는다.
   동작 줄이기 설정에서는 흐르는 빛을 끄고 회색 면만 남긴다. */
.skel { background: var(--bg-soft); border-radius: var(--radius-xs);
  position: relative; overflow: hidden; }
.skel::after { content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, var(--bg-elev), transparent);
  transform: translateX(-100%); animation: skel-sweep var(--dur-ambient) ease-in-out infinite; }
@keyframes skel-sweep { to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) { .skel::after { animation: none; opacity: .4; } }

.skel-line { height: 12px; margin: 8px 0; }
.skel-card { border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-elev); padding: 16px; }
.skel-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 16px; }
.skel-rows > * + * { margin-top: 8px; }
.skel-row { height: 56px; border: 1px solid var(--border); border-radius: var(--radius-sm); }

/* --- 기계값 표기 ---------------------------------------------------------------
   사람이 쓴 글과 **기계가 만든 값**(경로·확장자·ID·코드·측정치)은 다르게 보여야 한다.
   본문 폰트로 쓰면 0/O, 1/l 이 구분되지 않고, 자릿수가 흔들려 세로로 비교할 수 없다.
   네온을 쓰지 않고 디지털 느낌을 만드는 방법이기도 하다 — Product 레이어의 규칙이다. */
.mono, .att-item .ext, .art-body code, .att-item .size, code, kbd, samp {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "zero" 1;   /* 0 에 사선 — O 와 구분된다 */
}
.mono { font-size: .92em; letter-spacing: -.01em; }

/* --- 되풀이되는 표기 ------------------------------------------------------------
   같은 인라인 스타일이 40곳 넘게 흩어져 있었다. 흩어진 값은 반드시 갈라진다 —
   한 곳만 고쳐지고 나머지는 남는다.

   색은 **역할**로 고른다. --text-mute 는 3.13:1 이라 가이드가
   '메타·캡션 전용, 본문 금지' 로 묶어 둔 토큰이다. 문장에는 --text-soft(7.56:1)를 쓴다.
   이름을 t- 로 시작하는 이유는 달력의 .cal-cell.muted 와 섞이지 않기 위해서다. */
.t-soft { color: var(--text-soft); }
.t-soft-sm { color: var(--text-soft); font-size: var(--fs-base); }
.t-meta { color: var(--text-mute); font-size: var(--fs-sm); }   /* 날짜·개수·단위 같은 진짜 메타 */

/* 건너뛰기 링크: 평소엔 화면 밖, 탭 키를 누르면 나타난다.
   키보드 사용자가 탭 6개를 매번 지나 본문에 닿지 않도록. */
.skip-link { position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: var(--on-fill);
  padding: 8px 16px; border-radius: 0 0 var(--radius-sm) 0; font-weight: 700; }
.skip-link:focus { left: 0; }

/* 모달이 떠 있는 동안 뒤 배경을 붙잡아 둔다. 같이 스크롤되면 어디를 만지는지 알 수 없다. */
body.modal-open { overflow: hidden; }

/* --- 긴 문자열 -------------------------------------------------------------------
   끊을 자리가 없는 URL·파일경로·해시는 컨테이너를 밀어내 **페이지 전체를 가로로**
   스크롤시킨다. 가이드 §5 는 몸통이 가로로 밀리면 안 된다고 못 박는다.
   한국어·영어 본문의 자연스러운 줄바꿈은 건드리지 않고, 넘칠 때만 강제로 끊는다. */
body { overflow-wrap: anywhere; }
.mono, code, kbd, samp, .bubble, .art-body { overflow-wrap: anywhere; word-break: break-word; }
/* 표·리포트처럼 끊을 수 없는 것은 자기 안에서 스크롤한다 */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* --- Bento 그리드 -----------------------------------------------------------------
   12칸 위에 타일이 자기 폭을 선언한다. 화면마다 다른 열 구성을 만들 필요 없이
   타일 하나를 옮기거나 폭만 바꾸면 된다 — 대시보드가 자라도 구조가 버틴다.

   기본은 **한 칸 전부**(모바일 우선). 900px 부터 선언한 폭을 쓴다.
   간격은 4px 격자 위의 20px — 타일 사이가 패널 안쪽 여백(24px)보다 좁아야
   '한 덩어리 안의 구획' 으로 읽힌다. */
.bento { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 20px; align-items: start; }
.bento > * { grid-column: span 12; min-width: 0; }
@media (min-width: 900px) {
  .b-3 { grid-column: span 3; }
  .b-4 { grid-column: span 4; }
  .b-5 { grid-column: span 5; }
  .b-6 { grid-column: span 6; }
  .b-7 { grid-column: span 7; }
  .b-8 { grid-column: span 8; }
  .b-9 { grid-column: span 9; }
  .b-tall { grid-row: span 2; }
}

/* 패널 제목 줄. 인라인 flex 를 열 곳마다 다시 쓰지 않는다. */
.panel-head { display: flex; align-items: center; gap: 8px; margin-bottom: 16px; }
.panel-head h3 { margin: 0; font-size: var(--fs-md); }
.panel-head .spacer { flex: 1; }

/* --- 누름 피드백 -------------------------------------------------------------------
   눌렀다는 사실이 손에 잡혀야 한다. hover 는 마우스에만 있고 focus 는 키보드에만 있지만,
   :active 는 **세 입력 수단 모두**에 있다 — 터치에서 유일한 즉시 반응이기도 하다.
   1px 은 눈이 알아채되 레이아웃이 흔들리지 않는 크기다. */
.btn:active, .icon-btn:active, .seg button:active,
.menu .dropdown button:active { transform: translateY(1px); }
.btn, .icon-btn, .seg button, .menu .dropdown button { will-change: auto; }

/* 카드는 집어 올린다 — 목록에서 하나를 꺼내는 동작이라 들리는 게 맞다 */
.card { transition: transform var(--dur-fast) var(--ease-out),
                    border-color var(--dur-fast) var(--ease),
                    box-shadow var(--dur-fast) var(--ease); }
.card:active { transform: translateY(0); }

/* 동작 줄이기: 위치 변화까지 없앤다. 위 전역 규칙은 시간만 줄이므로 이동은 남는다. */
@media (prefers-reduced-motion: reduce) {
  .btn:active, .icon-btn:active, .seg button:active,
  .menu .dropdown button:active, .card:hover, .card:focus-visible,
  .art-card:hover, .art-card:focus-visible { transform: none; }
}

/* --- 스크롤 진입 -------------------------------------------------------------------
   **기본은 보이는 상태다.** JS 가 죽거나 IntersectionObserver 가 없거나 동작 줄이기면
   아무 일도 일어나지 않고 내용은 그냥 보인다 — 숨겨놓고 되살리는 방식은
   한 번 실패하면 내용이 영원히 사라진다.

   JS 가 조건을 확인한 뒤에만 .reveal-init 을 붙여 살짝 내려둔다. 8px·150ms —
   '들어왔다' 는 것만 알리고 읽기를 방해하지 않는 크기다. */
/* **투명도는 건드리지 않는다.** 전환은 탭이 백그라운드면 진행되지 않는데,
   opacity 0 에서 멈추면 내용이 영원히 사라진다(로그인 배경에서 한 번 겪었다).
   위치만 8px 움직이므로 전환이 멎어도 글은 그대로 읽힌다 — 최악의 결과가
   '살짝 내려가 있다' 로 끝난다. */
.reveal-init { transform: translateY(8px); }
.reveal-in { transform: none; transition: transform var(--dur-base) var(--ease-out); }
@media (prefers-reduced-motion: reduce) {
  .reveal-init, .reveal-in { transform: none; transition: none; }
}

/* --- 결정 -------------------------------------------------------------------------
   근거가 본문이고 제목이 요약이다. 재검토가 지난 것만 왼쪽 띠로 드러낸다 —
   전부 강조하면 아무것도 강조되지 않는다. */
/* 결정 카드는 **읽는 것**이 됐다. 눌러서 고치던 자리가 없어졌으므로 손 모양 커서도
   지운다 — 눌러도 아무 일이 없는데 누를 수 있어 보이는 것이 가장 나쁜 단서다. */
.dec-card.due { border-left: 3px solid var(--warn); }
.dec-foot { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.dec-foot .t-meta { margin-left: auto; }
@media (max-width: 600px) {
  .dec-foot .t-meta { margin-left: 0; }
}

/* --- 첫 화면(질의) -----------------------------------------------------------------
   질문이 주인공이다. 여백을 넉넉히 주고 다른 것은 아래로 내린다.
   제목은 스케일 상한(22px)을 지킨다 — Product UI 에 display 를 쓰지 않는다. */
.home-ask { max-width: 820px; margin: 0 auto; padding-top: 24px; }
.home-title { font-size: var(--fs-xl); font-weight: 800; letter-spacing: -.4px; margin: 0 0 8px; }
.home-sub { color: var(--text-soft); font-size: var(--fs-base); margin: 0 0 24px; }
.home-row { display: flex; align-items: center; gap: 12px; padding: 8px 0;
  border-bottom: 1px solid var(--border); font-size: var(--fs-base); }
.home-row:last-of-type { border-bottom: 0; }

/* 지난 질문 — 대화창 **위**에 놓는다. 이어서 물을 대상이라 질문의 연장선이고,
   눌러 열면 그때의 답이 바로 아래 대화창에 되살아난다.
   4개까지만 둔다. 그 이상은 목록이지 관문이 아니다. */
.ask-past { display: grid; gap: 10px; margin-bottom: 24px;
  grid-template-columns: repeat(2, minmax(0, 1fr)); }
.ask-card {
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 14px 16px; cursor: pointer;
  display: flex; flex-direction: column; gap: 6px; min-width: 0;
  transition: border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease-out);
}
.ask-card:hover, .ask-card:focus-visible { border-color: var(--border-strong); transform: translateY(-1px); }
/* 질문이 카드의 제목이다. 두 줄까지 보이고 그 뒤는 자른다 — 카드 높이가 들쭉날쭉하면
   네 장이 한 덩어리로 안 읽힌다. */
.ask-q { font-weight: 600; font-size: var(--fs-base); line-height: 1.45;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.ask-a { color: var(--text-soft); font-size: var(--fs-sm); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.ask-foot { display: flex; gap: 10px; align-items: center; margin-top: auto; padding-top: 2px; }
@media (max-width: 600px) { .ask-past { grid-template-columns: 1fr; } }

/* 업로드 안내 — 문서에 무엇을 적어두면 화면이 알아서 읽는지 **보여준다.**
   설명 문장 대신 붙여넣을 수 있는 실물을 준다. */
.upload-hint { background: var(--bg-soft); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 12px 14px; margin-bottom: 16px; font-size: var(--fs-sm); }
.upload-hint b { display: block; margin-bottom: 8px; }
.upload-hint pre { margin: 0 0 8px; font-size: var(--fs-xs); line-height: 1.6;
  color: var(--text-soft); overflow-x: auto; white-space: pre; }

/* --- 노트 (주관적 기록) -------------------------------------------------------
   적는 칸이 화면 맨 위에 **열린 채로** 있다. 버튼을 눌러 창을 여는 단계 하나가
   일기를 안 쓰게 만드는 가장 흔한 이유다. */
.note-write { background: var(--bg-elev); border: 1px solid var(--border-strong);
  border-radius: var(--radius); padding: 12px; margin-bottom: 24px; }
.note-write textarea { width: 100%; border: 0; background: transparent; resize: vertical;
  min-height: 72px; font-size: var(--fs-md); line-height: 1.6; }
.note-write textarea:focus { outline: none; }
.note-write-foot { display: flex; align-items: center; gap: 12px; margin-top: 8px;
  padding-top: 8px; border-top: 1px solid var(--border); }
.note-write-foot input { width: min(240px, 50%); }
.note-day { margin-bottom: 24px; }
/* 일기는 '언제' 가 제목이다. 날짜를 줄 전체에 걸친 선으로 세운다. */
.note-day-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px;
  padding-bottom: 6px; border-bottom: 1px solid var(--border); font-size: var(--fs-sm); }
.note-item { background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 14px 16px; margin-bottom: 8px; }
.note-body { white-space: pre-wrap; line-height: 1.7; }
.note-foot { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
.note-edit { width: 100%; min-height: 96px; resize: vertical; line-height: 1.7; }
.note-edit-foot { display: flex; justify-content: flex-end; gap: 8px; margin-top: 8px; }

/* --- 아카이브의 프로젝트 줄 ----------------------------------------------------
   카드 격자가 아니라 **한 줄에 하나**다. 막대의 시작점이 전부 같은 x 에 있어야
   눈이 길이를 비교할 수 있고, 그래야 "무엇이 밀렸는가" 가 한눈에 보인다. */
.arch-proj { margin-bottom: 24px; }
.arch-proj-row { display: grid; align-items: center; gap: 12px;
  grid-template-columns: minmax(0, 1.4fr) 68px minmax(80px, 1fr) 44px 44px 56px;
  padding: 10px 0; border-bottom: 1px solid var(--border); font-size: var(--fs-sm); }
.arch-proj-row:last-child { border-bottom: 0; padding-bottom: 0; }
.ap-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ap-bar { height: 6px; border-radius: 999px; background: var(--bg-soft); overflow: hidden; }
.ap-pct { text-align: right; font-variant-numeric: tabular-nums; }
.ap-ms { text-align: right; font-variant-numeric: tabular-nums; }
.ap-link { text-align: right; font-size: var(--fs-xs); }
@media (max-width: 900px) {
  /* 좁으면 이름과 막대만 남긴다 — 비교에 필요한 최소한이다. */
  .arch-proj-row { grid-template-columns: minmax(0, 1fr) minmax(70px, .8fr) 44px; }
  .arch-proj-row .pill, .ap-ms, .ap-link { display: none; }
}

/* 캘린더 상태 — 제목줄에 조용히 붙는다. 상태는 알려주되 눈길을 끌지 않는다. */
.gcal { display: inline-flex; align-items: center; gap: 8px; }

/* 가져오기 결과 — 무엇이 늘고 줄었는지 **적용 전에** 같은 모양으로 보여준다. */
.imp-box { margin-top: 16px; border: 1px solid var(--border); border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm); padding: 12px 16px; font-size: var(--fs-sm); }
.imp-box.err { border-left-color: var(--danger); color: var(--text); }
.imp-box b { display: block; margin-bottom: 8px; }
.imp-row { display: flex; align-items: center; gap: 12px; padding: 4px 0; }
.imp-n { font-variant-numeric: tabular-nums; font-size: var(--fs-xs); color: var(--text-soft); }
.imp-n.add { color: var(--ok); font-weight: 700; }
.imp-n.del { color: var(--danger); }

/* --- 연결 탭 -----------------------------------------------------------------
   흐름(위)과 관계(아래)를 한 장에 둔다. 둘 사이의 제목이 유일한 구분선이다. */
.conn-sec { margin: 36px 0 16px; font-size: var(--fs-md); }
.conn-axis { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  margin-top: 6px; font-size: var(--fs-xs); color: var(--text-soft);
  font-variant-numeric: tabular-nums; text-align: center; }
.conn-donut { display: flex; gap: 20px; align-items: center; flex-wrap: wrap; }
.conn-legend { flex: 1; min-width: 140px; display: flex; flex-direction: column; gap: 6px; }
.conn-leg { display: flex; align-items: center; gap: 8px; font-size: var(--fs-sm); }
/* 프로젝트 한 줄 = 이름·상태·퍼센트, 그 아래 막대. 카드로 감싸지 않는다 —
   여러 개를 나란히 읽어야 '어디까지 왔나' 가 한눈에 비교된다. */
.conn-proj { padding: 12px 0; border-bottom: 1px solid var(--border); }
.conn-proj:last-child { border-bottom: 0; padding-bottom: 0; }
.conn-proj-top { display: flex; align-items: center; gap: 10px; font-size: var(--fs-base); margin-bottom: 8px; }
.conn-proj .proj-bar { margin: 0 0 6px; }

/* 근거가 언제 것인지. 답변 옆에 시점이 없으면 '근거 중심' 이 절반만 지켜진다. */
.src-date { margin-left: 6px; opacity: .72; font-size: .92em; }

/* 지난 기록 대비 변화. 방향(좋아짐/나빠짐)을 모르면 색을 쓰지 않는다 —
   색으로 거짓말하지 않는다. 상태색은 판정에만 쓴다(가이드 §1-2). */
.mc-delta { font-size: var(--fs-xs); font-weight: 700; font-variant-numeric: tabular-nums;
  color: var(--text-soft); }
.mc-delta.good { color: var(--ok); }
.mc-delta.bad { color: var(--danger); }

/* 탭 안의 구분 막대. 좁은 화면에서는 한 줄 가로 스크롤로 —
   줄바꿈되면 머리가 화면의 절반을 먹는다(내비에서 겪은 것과 같은 문제). */
.section-bar { margin: 0 0 20px; max-width: 100%; overflow-x: auto;
  scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.section-bar::-webkit-scrollbar { display: none; }
.section-bar .seg-btn { white-space: nowrap; }

/* --- 탭 배경 영상 -----------------------------------------------------------------
   수집과 답 탭 뒤에 브랜드 영상이 흐른다. **작업 화면**이므로 로그인보다 훨씬 눌러야 한다.

   불투명도는 측정으로 정했다. 두 영상 모두 순백 픽셀(YMAX 255)을 가지므로
   최악의 경우 흰 바탕 위에 밝은 글자가 놓인다. 스크림 불투명도별 실측:
       a=.70 → 보조 텍스트 3.66:1 (미달)
       a=.80 → 5.32:1
       a=.86 → 6.60:1
   .82 를 쓴다 — 기준(4.5:1)을 넘기는 최소값 위에 여유를 조금 둔 지점이다.
   영상을 바꾸면 **다시 잰다.**

   본문은 대부분 불투명한 패널 위에 올라가므로 영상에 직접 노출되는 글자는
   페이지 제목·구분 막대·빈 상태뿐이다. 그래도 기준은 같게 적용한다. */
.has-tab-bg body, body.has-tab-bg { background: transparent; }
/* 배경은 **초점 밖**에 있어야 한다.
   선명한 영상은 본문과 눈길을 다툰다. 흐리면 깊이만 남고 경쟁이 사라진다.

   흐리는 방법: 영상을 뷰포트의 1/4 크기로 그린 뒤 4배로 늘린다.
   blur() 는 표면의 픽셀 수에 비례해 비싸므로, 1080p 를 통째로 흐리는 대신
   540p 넓이를 흐리고 확대하면 연산이 약 1/16 로 준다. 확대 자체가 이미
   보간 흐림을 주기 때문에 blur 값도 작게(8px) 끝난다.
   scale(4.2) — 4 가 아니라 4.2 인 이유는 흐린 가장자리가 화면 안으로
   말려들어오지 않게 여유를 두기 위함이다. 넘치는 5%(뷰포트 기준)를 좌우로
   나눠 물리려면 자기 크기의 2.4% 만큼 당겨야 한다(2.4% × 25% × 4.2 ≒ 2.5%). */
.tab-bg { position: fixed; top: 0; left: 0; z-index: -2; width: 25%; height: 25%;
  object-fit: cover; pointer-events: none;
  transform-origin: 0 0; transform: scale(4.2) translate(-2.4%, -2.4%);
  filter: blur(8px) saturate(1.05);
  background: var(--bg) center / cover no-repeat; }
.tab-scrim { position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: rgba(15, 18, 23, .82); }
/* 흐림은 대비를 **낮추지 않는다** — 평균으로 끌어당기므로 최악 프레임의
   극단값이 오히려 완화된다. 위 .82 측정값은 그대로 유효하다. */

/* 배경이 뒤로 물러난 만큼 작업 표면은 앞으로 나와야 한다.
   불투명도는 이미 최대이므로 분리는 **모서리와 그림자**로 만든다. */
body.has-tab-bg :is(.panel, .card, .row, .stat, .login-card) {
  box-shadow: 0 8px 28px -12px rgba(0, 0, 0, .7);
}
/* 영상이 없을 때도 화면이 비지 않는다 — 포스터가 바닥에 깔린다.
   포스터도 같은 깊이에 둔다. 정지 이미지라 흐림은 한 번만 계산되므로
   전체 크기에 그대로 걸어도 된다. scale 은 흐린 가장자리를 화면 밖으로 밀어낸다. */
.tab-bg-still { position: fixed; inset: 0; z-index: -2;
  background-size: cover; background-position: center; pointer-events: none;
  transform: scale(1.06); filter: blur(8px) saturate(1.05); }

/* --- 발광: 선과 점에만 -------------------------------------------------------------
   가이드 §0 — 넓은 면이 빛나면 그 위의 글자가 읽히지 않는다. 빛은 **구조**를 드러내는
   데만 쓴다: 활성 탭 밑줄, 진행바, 포커스 링, 작은 점. 면적 합 10% 이내. */

/* 활성 탭: 배경 대신 **밑줄**로 표시한다. 선이 곧 Link 다. */
.nav .links a.active {
  background: transparent; color: var(--accent-ink);
  box-shadow: inset 0 -2px 0 var(--accent), 0 2px 10px -4px var(--accent);
}
.seg button.active {
  background: transparent; color: var(--accent-ink);
  box-shadow: inset 0 -2px 0 var(--accent);
}

/* 포커스 링에 옅은 빛 — 지금 어디에 있는지가 더 또렷해진다 */
:focus-visible { box-shadow: var(--glow-soft); }

/* 진행·측정 막대: 값이 곧 빛의 길이다 */
.bar-fill, .proj-fill, .up-bar { box-shadow: var(--glow-soft); }

/* 도메인 막대와 작은 점 */
.row .dom-bar, .legend-dot { box-shadow: var(--glow-soft); }

/* 주 CTA — 화면에서 할 일이 하나라면 그게 빛난다 */
.btn.primary { box-shadow: 0 2px 14px -4px var(--accent); }
.btn.primary:hover, .btn.primary:focus-visible { box-shadow: var(--glow-hard); }

