/* ==========================================================================
   Nautilus - 공통 베이스 스타일 (정본)
   이 파일이 Nautilus 전 서비스의 단일 출처다. 서비스 app.css 에 복사하지 말고
   맨 위에서 @import 로 가져다 쓴다 (_design/README.md).

   값의 출처는 언제나 tokens.css 다. 여기서 색·radius·폰트를 새로 정하지 않는다.
   컴포넌트 사양의 출처: DESIGN.dark.md

   내용의 출처: Poll/app/src/styles/app.css 의 범용 구간(7~879행)을 그대로 옮긴 것.
   2026-08-13 실측 기준 Converter·Spellcheck·Summary 의 같은 구간은 이 파일과
   바이트 단위로 동일했다 - 즉 4벌이 이미 같은 것을 4번 복사해 두고 있었다.
   ========================================================================== */

/* 스크롤바 자리를 **항상 비워 둔다** (2026-08-19 대표님: "탭에 들어가면 헤더가 미세하게 바뀐다").
   원인은 헤더가 아니라 스크롤바였다 - 홈처럼 긴 화면에는 스크롤바가 생기고 짧은 탭에는 없어서,
   그때마다 뷰포트 폭이 15px 안팎 달라지고 **가운데 정렬된 것이 전부 좌우로 밀린다**(헤더 포함).
   stable 은 스크롤이 없어도 그 자리를 남겨 두므로 화면을 옮겨도 폭이 변하지 않는다.
   ※ 헤드리스 크롬은 오버레이 스크롤바라 이 어긋남이 실측에 안 잡힌다 - 실기기로 봐야 보인다. */
html { scrollbar-gutter: stable; }


*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100svh;
  font-family: var(--font-sans);
}

h1, h2, h3, h4, p, figure, blockquote { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
button, input, textarea, select { font: inherit; font-family: var(--font-sans); color: inherit; }
img, svg { display: block; max-width: 100%; }

/* 접근성: 시각적으로 숨기되 스크린리더에는 남긴다 */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip-link {
  position: absolute; left: var(--space-lg); top: -100px; z-index: 100;
  background: var(--color-surface-overlay); color: var(--color-ink);
  padding: var(--space-md) var(--space-lg); border-radius: var(--radius-md);
  font: var(--text-button-md); font-family: var(--font-sans);
  transition: top var(--dur-state) var(--ease);
}
.skip-link:focus { top: var(--space-lg); }

/* ==========================================================================
   레이아웃
   ========================================================================== */

.page { display: grid; grid-template-rows: auto 1fr auto; min-height: 100svh; }

.wrap { width: 100%; max-width: var(--container-max); margin: 0 auto; padding: 0 var(--space-xl); }

/* 컴포저·투표·결과가 앉는 좁은 계기판 컬럼 - 질문은 산문이라 75자를 넘기면 안 읽힌다 */
.instrument { width: 100%; max-width: var(--container-instrument); margin: 0 auto; }
/* 로그인·가입·비밀번호 재설정만 쓰는 좁은 기둥. Poll 의 로그인 폭이 기준이다. */
.instrument--auth { max-width: var(--container-auth); }
.instrument--pricing { max-width: var(--container-pricing); }

main { padding: var(--space-section) 0; }
@media (max-width: 768px) { main { padding: 48px 0; } .wrap { padding: 0 var(--space-lg); } }
@media (max-width: 480px) { main { padding: var(--space-xxl) 0; } }

.stack     { display: grid; gap: var(--space-lg); }
.stack-sm  { display: grid; gap: var(--space-sm); }
.stack-md  { display: grid; gap: var(--space-md); }
.stack-lg  { display: grid; gap: var(--space-lg); }
.stack-xl  { display: grid; gap: var(--space-xl); }
.stack-section { display: grid; gap: var(--space-section); }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--space-md); flex-wrap: wrap; }
.row-gap { display: flex; align-items: center; gap: var(--space-md); flex-wrap: wrap; }

/* ==========================================================================
   내비게이션 / 푸터
   ========================================================================== */

.nav {
  background: var(--color-canvas); height: 64px;
  display: flex; align-items: center;
  border-bottom: 1px solid var(--color-hairline);
}
.nav--bare { border-bottom: 0; }

/* 탭 나열형 헤더 (`.nav.nav--tabs`) — 가운데 칸이 **기능 탭 여러 개**인 사이트용.
 *
 * 노틸러스 사이트의 헤더는 가운데가 [주기능 · 목록 · 요금제] 셋으로 고정이라 좁은 화면에서도
 * 한 줄에 선다. 고객사 인스턴스(99_Membership)는 사정이 다르다 - 그 사이트의 **주 내비게이션이
 * 곧 기능 탭들**이라(매일신문: 유튜브 모니터링·컨버터·여론조사·개표 결과) 좁은 화면에서
 * 감출 수가 없다. 감추면 기능으로 가는 길이 아예 없어진다(.nav__secondary 를 못 쓰는 이유).
 *
 * 그래서 좁은 화면에서는 **헤더를 두 줄로 편다**: 윗줄 [워드마크][계정 칸], 아랫줄에 탭을
 * 가로로 흘린다(줄바꿈 대신 스크롤 - 줄바꿈은 탭 수에 따라 헤더 높이가 널을 뛴다).
 * 640px 은 탭 넷이 워드마크와 한 줄에 서지 못하는 폭이다(실측 390px 에서 두 줄로 깨졌다).
 * 색·타이포는 그대로다 - 이 변형이 정하는 것은 배치뿐이다. */
/* 항목 사이는 노틸러스 헤더(.row-gap = 12px)보다 넓다. 그 헤더는 가운데가 셋이고 그중 하나가
   빨간 단추라 덩어리가 저절로 갈리지만, 탭 나열형은 **같은 모양의 글자 넷**이라 12px 로는
   한 줄로 이어져 읽힌다 (대표님 2026-08-19 "구분을 위해 띄어쓰기 명확하게"). */
.nav--tabs .nav__center { gap: var(--space-xxl); }

@media (max-width: 640px) {
  .nav--tabs { height: auto; }
  .nav--tabs .nav__inner {
    grid-template-columns: minmax(0, 1fr) auto;
    row-gap: var(--space-sm);
    padding-block: var(--space-sm);
  }
  /* 계정 칸을 첫 줄 오른쪽에 **명시로** 앉힌다. 자동 배치에 맡기면 탭 줄이 한 행을 통째로
     가져간 뒤라서 세 번째 줄로 밀린다 (실측 390px: 로고 / 탭 / 계정 이렇게 석 줄이 됐다). */
  .nav--tabs .nav__account { grid-row: 1; grid-column: 2; }
  .nav--tabs .nav__center {
    grid-row: 2; grid-column: 1 / -1; justify-self: stretch;
    flex-wrap: nowrap; overflow-x: auto;
    /* 스크롤 막대가 헤더 밑줄과 겹쳐 두 줄처럼 보인다 - 막대는 감추고 스크롤은 남긴다 */
    scrollbar-width: none;
  }
  .nav--tabs .nav__center::-webkit-scrollbar { display: none; }
  .nav--tabs .nav-link { white-space: nowrap; }
}
/* 헤더 3열 (대표님 2026-08-19) — [워드마크] [가운데 메뉴] [계정].
 *
 * 왜 grid 인가: 전에는 flex + space-between 이라 **계정 이메일 길이가 메뉴 위치를 정했다** —
 * 사이트를 옮기거나 다른 계정으로 들어가면 헤더 단추들이 좌우로 밀렸다. `minmax(0,1fr) auto minmax(0,1fr)` 은
 * 양쪽 칸이 아무리 넓거나 좁아도 **가운데 그룹을 화면 중앙에 고정**한다.
 * minmax 의 0 이 핵심이다 — 기본 `1fr`(=minmax(auto,1fr))이면 긴 이메일이 칸을 밀어 가로 스크롤이 생긴다
 * (실측 2026-08-19: 390px 화면에서 여덟 서비스 전부 434px 로 넘쳤다). */
.nav__inner { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: var(--space-lg); }
.nav__center { justify-self: center; }
/* 칸을 **늘려 둔 채** 안에서만 오른쪽으로 붙인다. `justify-self:end` 로 하면 그리드 칸을 무시하고
   내용 크기(=이메일 전체 길이)로 커져 좁은 화면에서 워드마크 위로 올라탄다 (실측 390px: 94px 칸에 312px 단추). */
.nav__account { min-width: 0; display: flex; justify-content: flex-end; }
/* 이메일이 길면 말줄임 — 헤더를 밀거나 넘치지 않는다. 단추 안의 글자만 줄인다(단추 높이·정렬은 그대로).
   제약은 `.menu` 까지 내려가야 한다 — 단추의 부모가 그 div 라, 거기서 폭이 풀리면 max-width:100% 가 무의미해진다(실측). */
.nav__account .menu { min-width: 0; max-width: 100%; }
.nav__account .btn { min-width: 0; max-width: 100%; }
.nav__account .btn > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
/* 주기능(빨강)은 **전 서비스 같은 폭** — 가장 긴 라벨 기준(실측 `+ 만들기` 83px, 2026-08-19 대표님 지시) */
.nav__cta { min-width: 5.25rem; }

.wordmark {
  /* min-height 는 로고를 크게 보이게 하려는 게 아니라 누를 자리를 만드는 것이다.
     글자 높이만 두면 21px 이라 WCAG 2.5.8(최소 24px) 에 미달한다. */
  display: inline-flex; align-items: center; gap: var(--space-sm); min-height: 32px;
  font: var(--text-heading-md); font-family: var(--font-sans);
  color: var(--color-ink); text-decoration: none;
}
/* 마크는 이제 제 색을 안에 담는다(파비콘과 같은 그림이라 currentColor 를 못 쓴다) — 이 규칙은 옛 마크의 잔재다 */
.wordmark svg { color: var(--color-primary); }
/* `Nautilus` 만 브랜드색. 뒷말은 붙여 쓰고 색으로 나눈다 (대표님 2026-08-19).
   색은 **버튼과 같은 --color-primary** — 대표님이 분홍(#ff5c72)이 아니라 빨강이어야 한다고 정했다.
   토큰 문서는 이 값을 '배경 전용' 으로 두지만, 워드마크는 heading-md(600 18px)라 대비 4.0:1 — 본문 기준(4.5:1)엔 살짝 못 미치고 큰 글자 기준(3:1)은 넘는다. */
.wordmark__brand { color: var(--color-primary); }

/* 헤더 보조 링크 - 헤더에서만 쓴다. 문장 속 링크(.link-inline)와 다른 물건이다.
   .link-inline 은 산문 한가운데서 "여기가 링크다" 를 혼자 알려야 해서 브랜드 레드
   (--color-primary-text)를 쓴다. 헤더는 사정이 반대다 - 링크만 넷이 나란히 서 있어서
   눈에 띌 필요가 없고, 그 자리에 레드를 넷 두면 헤더에서 가장 무거운 덩어리가 빨강이 된다.
   실측(2026-08-15): 네 서비스 헤더가 #ff5c72 링크 4개를 나란히 그리고 있었다.
   이건 DESIGN.dark.md 의 두 규정을 동시에 어긴다 -
     · primary-nav: "text {colors.body}, active {colors.ink} + 2px {colors.primary} 밑줄"
     · Do 첫 줄: 브랜드 레드는 CTA·활성 탭 표시·선택된 행·워드마크 전용. 장식이 아니다.
   그래서 문서에 이미 적혀 있던 그 규격을 클래스로 만든 것이지, 새 디자인이 아니다.

   타이포는 --text-body-strong(600 16px/1.65). --text-link-md 와 자·간격이 같은 값이라
   글자 폭이 바뀌지 않는다 - 클래스를 바꿔도 헤더 배치는 그대로다(실측 확인).
   min-height 32px 는 .wordmark·.footer__links a 와 같은 이유다(WCAG 2.5.8 최소 24px). */
.nav-link {
  display: inline-flex; align-items: center; min-height: 32px;
  font: var(--text-body-strong); font-family: var(--font-sans);
  color: var(--color-body); text-decoration: none;
  /* 투명 2px 를 늘 깔아 둔다. 현재 위치에서만 색을 넣으면 그 항목만 2px 밀린다. */
  border-bottom: 2px solid transparent;
  transition: color var(--dur-state) var(--ease), border-color var(--dur-state) var(--ease);
}
.nav-link:hover, .nav-link:focus-visible { color: var(--color-ink); }
/* 현재 위치 - 색만으로 알리지 않는다. 밑줄이 함께 서므로 색을 못 보는 눈에도 남는다.
   AppHeader 가 이미 aria-current="page" 를 붙이고 있어서 마크업을 늘리지 않고
   상태를 속성에서 읽는다. 상태의 출처가 하나면 어긋날 자리가 없다. */
.nav-link[aria-current='page'] {
  color: var(--color-ink); border-bottom-color: var(--color-primary);
}

/* (.nav__item 은 2026-08-18 에 뗐다 - 헤더가 Poll 과 같은 구성이 되면서 보조 칸이 둘로 줄어
   현재 위치 밑줄이 할 일이 없어졌다. 헤더 항목의 마우스 반응은 .btn--tertiary:hover 하나다.) */

.footer {
  background: var(--color-canvas); border-top: 1px solid var(--color-hairline);
  padding: var(--space-xxl) 0;
  font: var(--text-body-sm); font-family: var(--font-sans);
  letter-spacing: var(--track-body-sm); color: var(--color-mute);
}
.footer__inner { display: flex; align-items: center; justify-content: space-between; gap: var(--space-lg); flex-wrap: wrap; }
.footer a { color: var(--color-mute); text-decoration: none; }
.footer a:hover, .footer a:focus-visible { color: var(--color-ink); text-decoration: underline; }
.footer__links { display: flex; gap: var(--space-lg); flex-wrap: wrap; align-items: center; }

/* 손가락으로 누를 수 있어야 한다. 글자 높이만 두면 22px 이라 WCAG 2.5.8(최소 24px)에
   미달하고, 실제로도 옆 링크를 잘못 누른다. 문장 속 링크(.link-inline)는 규격상 예외라
   건드리지 않는다 - 줄 사이가 벌어져 본문이 망가진다. */
.footer__links a { display: inline-flex; align-items: center; min-height: 32px; }

/* 푸터 메모 한 줄 - 그 서비스의 **주의사항**이다 (산출물 보관 기간·다루는 범위 따위).
   왜 링크 줄과 다른 줄인가: 링크와 한 줄에 두면 `.footer__inner` 의 space-between 이
   세 덩이를 벌려 메모가 가운데 끼고, 좁은 화면에서 링크 사이로 접힌다.
   왜 `베타` 로 시작하지 않는가: 헤더 배지(`.pill.pill--info` "베타")가 이미 그 사실을 말한다.
   실측(2026-08-15)에서 네 서비스 중 셋이 `베타 서비스 - …` 로 시작해 같은 말을 두 번 하고
   있었고, 그 셋과 하나가 3:1 로 갈려 있었다. 남길 말은 그 서비스에서만 참인 문장이다.
   (`DESIGN.dark.md` > 공지를 어디에 두는가) */
.footer__note {
  margin-top: var(--space-md);
  font: var(--text-caption-md); font-family: var(--font-sans);
  letter-spacing: var(--track-caption); color: var(--color-mute);
}

/* ==========================================================================
   타이포
   ========================================================================== */

.display-lg { font: var(--text-display-lg); font-family: var(--font-sans);
  letter-spacing: var(--track-display-lg); color: var(--color-ink); }
.heading-xl { font: var(--text-heading-xl); font-family: var(--font-sans);
  letter-spacing: var(--track-heading-xl); color: var(--color-ink); }
.heading-lg { font: var(--text-heading-lg); font-family: var(--font-sans); color: var(--color-ink); }
.heading-md { font: var(--text-heading-md); font-family: var(--font-sans); color: var(--color-ink); }
.body      { font: var(--text-body-md); font-family: var(--font-sans); color: var(--color-body); }
/* 본문 강조 - 제목은 아니지만 눈이 먼저 닿아야 하는 줄(결과 카드의 제목 줄, 접이식 머리).
   2026-08-19 실측: 5개 서비스 8개 파일이 `.body-strong` 을 쓰고 있었는데 **규칙이 없었다** -
   토큰 이름(--text-body-strong)만 있고 클래스는 아무 데도 정의돼 있지 않아, 그 자리들이
   전부 그냥 본문으로 보였다. 한 서비스에 넣으면 나머지 넷이 계속 죽은 채로 남는다. */
.body-strong { font: var(--text-body-strong); font-family: var(--font-sans); color: var(--color-ink); }
/* 접어 두는 카드의 머리줄 - 누를 수 있는 곳으로 보여야 한다 (details > summary) */
summary.body-strong { cursor: pointer; }
.body-sm   { font: var(--text-body-sm); font-family: var(--font-sans);
  letter-spacing: var(--track-body-sm); color: var(--color-mute); }
.caption   { font: var(--text-caption-md); font-family: var(--font-sans);
  letter-spacing: var(--track-caption); color: var(--color-mute); }
.lede { font: var(--text-body-md); font-family: var(--font-sans); color: var(--color-body); max-width: 52ch; }
.ink { color: var(--color-ink); }
.mute { color: var(--color-mute); }

/* 뷰당 정확히 하나. 큰 단독 숫자에는 tabular-nums 를 쓰지 않는다. */
.hero-figure {
  font: var(--text-hero-figure); font-family: var(--font-sans);
  letter-spacing: var(--track-hero-figure); color: var(--color-ink);
  font-variant-numeric: proportional-nums;
}
@media (max-width: 480px) {
  .display-lg { font-size: 32px; }
  .hero-figure { font-size: 36px; }
}

.link-inline {
  color: var(--color-primary-text); text-decoration: none;
  font: var(--text-link-md); font-family: var(--font-sans);
  /* 세로 여백으로 누를 자리를 24px 이상으로 넓힌다. 인라인 요소의 세로 패딩은
     줄 높이를 밀지 않으므로 본문 레이아웃은 그대로다 - 손가락 판정만 커진다. */
  padding-block: 3px;
}
.link-inline:hover, .link-inline:focus-visible { text-decoration: underline; }

/* ==========================================================================
   버튼
   ========================================================================== */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-sm);
  height: 40px; padding: 6px 14px; border: 0; border-radius: var(--radius-md);
  font: var(--text-button-md); font-family: var(--font-sans);
  cursor: pointer; text-decoration: none; white-space: nowrap; text-align: center;
  transition: background-color var(--dur-state) var(--ease), color var(--dur-state) var(--ease);
}
.btn--lg { height: 48px; padding: 6px 20px; }
.btn--block { width: 100%; }

.btn--primary { background: var(--color-primary); color: var(--color-on-primary); }
.btn--primary:hover:not(:disabled) { background: var(--color-primary-hover); }
.btn--primary:active:not(:disabled) { background: var(--color-primary-pressed); }

.btn--secondary { background: var(--color-secondary-bg); color: var(--color-ink); }
.btn--secondary:hover:not(:disabled) { background: var(--color-secondary-hover); }
.btn--secondary:active:not(:disabled) { background: var(--color-secondary-pressed); }

.btn--tertiary { background: transparent; color: var(--color-ink); }
.btn--tertiary:hover:not(:disabled) { background: rgba(245, 245, 240, 0.06); }

/* 파괴적 동작은 브랜드 레드가 아니라 critical + 경고 아이콘 */
.btn--destructive { background: var(--color-critical); color: #fff; }
.btn--destructive:hover:not(:disabled) { filter: brightness(1.08); }

.btn:disabled { background: var(--color-surface-raised); color: var(--color-stone); cursor: not-allowed; }

.btn-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border: 0; border-radius: var(--radius-full);
  background: var(--color-surface-raised); color: var(--color-body); cursor: pointer;
  transition: background-color var(--dur-state) var(--ease), color var(--dur-state) var(--ease);
}
.btn-icon:hover { background: var(--color-surface-overlay); color: var(--color-ink); }

/* ==========================================================================
   입력
   ========================================================================== */

.field { display: grid; gap: var(--space-sm); }

/* legend 는 fieldset 의 캡션이라 grid/flex 아이템이 아니다 - 부모의 gap 이 적용되지 않아
   제목이 본문에 딱 붙는다. 명시적으로 띄운다. (.sr-only legend 는 제외) */
fieldset > legend:not(.sr-only) { margin-bottom: var(--space-md); }
.field__label { font: var(--text-body-strong); font-family: var(--font-sans); color: var(--color-ink); }
.field__hint { font: var(--text-body-sm); font-family: var(--font-sans);
  letter-spacing: var(--track-body-sm); color: var(--color-mute); }
.field__error {
  display: flex; align-items: center; gap: var(--space-xs);
  font: var(--text-body-sm); font-family: var(--font-sans);
  letter-spacing: var(--track-body-sm); color: var(--color-critical-text);
}

.input, .textarea {
  width: 100%; background: var(--color-surface-card); color: var(--color-ink);
  border: 1px solid var(--color-hairline); border-radius: var(--radius-md);
  font: var(--text-body-md); font-family: var(--font-sans);
  padding: 11px 15px; min-height: 44px;
  transition: border-color var(--dur-state) var(--ease);
}
.textarea { min-height: 96px; resize: vertical; line-height: 1.5; }
.input::placeholder, .textarea::placeholder { color: var(--color-ash); }
.input:hover, .textarea:hover { border-color: var(--color-stone); }
.input:focus, .textarea:focus { border-color: var(--color-focus-outer); }
.input[aria-invalid='true'], .textarea[aria-invalid='true'] { border-color: var(--color-critical); }
.input:disabled, .textarea:disabled {
  background: var(--color-surface-soft); color: var(--color-stone); border-color: var(--color-hairline-soft);
}

.input--code {
  font-variant-numeric: tabular-nums; letter-spacing: 0.4em; text-align: center;
  font-size: 24px; font-weight: 600; height: 56px; padding-left: 0.4em;
}

/* ==========================================================================
   표면
   ========================================================================== */

.card { background: var(--color-surface-card); border-radius: var(--radius-md); padding: var(--space-xl); }
.card--lg { border-radius: var(--radius-lg); padding: var(--space-xxl); }
.card--raised { background: var(--color-surface-raised); }
.card--soft { background: var(--color-surface-soft); }

@media (max-width: 480px) { .card, .card--lg { padding: var(--space-lg); } }

.divider { height: 1px; background: var(--color-hairline-soft); border: 0; margin: 0; }

/* ==========================================================================
   상태 필 / 배지 - 항상 아이콘 + 라벨. 점만으로 상태를 전달하지 않는다.
   ========================================================================== */

.pill {
  display: inline-flex; align-items: center; gap: var(--space-xs);
  padding: 6px 12px; border-radius: var(--radius-full);
  font: var(--text-button-sm); font-family: var(--font-sans);
  background: var(--color-surface-raised); color: var(--color-mute);
}
.pill--live    { background: var(--color-success-bg); color: var(--color-success-text); }
.pill--closing { background: var(--color-warning-bg); color: var(--color-warning); }
.pill--closed  { background: var(--color-surface-raised); color: var(--color-mute); }
.pill--draft   { background: var(--color-surface-raised); color: var(--color-ash); }
.pill--info    { background: var(--color-surface-raised); color: var(--color-accent-blue); }
/* 실패·오류 상태. banner--critical 과 같은 critical 토큰 쌍을 쓴다 - 같은 뜻은 같은 색이어야 한다.
   Converter·Monitoring 이 각자 app.css 맨 아래에 똑같이 정의해 두고 있던 것을 여기로 올렸다.
   Poll 은 이 필을 쓰지 않으므로 규칙이 하나 늘어도 화면은 그대로다. */
.pill--error   { background: var(--color-critical-bg); color: var(--color-critical-text); }

/* 데이터 색 마크 - **글자 옆에 서는 색 조각 하나.**
   `DESIGN.dark.md` 의 "Text never wears the data color. Identity comes from the colored mark
   *beside* the text." 가 이 물건이다. 색은 마크업에서 온다(계열마다 다르므로) - 여기서는
   크기와 모양만 정한다. 배경색을 여기에 적지 않는 것이 이 규칙의 핵심이다.

   2026-08-15: 같은 선언이 **세 벌** 있었다 -
     · `.legend__mark`(base.css, Poll 추이 범례)
     · `.spell-dot`(Spellcheck app.css, 필터 칩)
     · `.kw-dot`(Monitoring app.css, 키워드 행·피드 행)
   셋 다 `width:10px; height:10px; border-radius:3px; flex:none` 로 값까지 같았다. 세 서비스가
   쓰는 개념이므로 README 의 "둘 이상이 쓸 것 같으면 그 순간 _design 으로 올린다" 에 해당한다.
   `3px` 은 어느 토큰에도 없던 리터럴이라 `--radius-mark` 로 이름을 줬다(tokens.css).

   `.legend__mark` 를 같이 적어 둔 이유: Poll 의 `TrendChart.tsx` 가 그 이름을 쓰고 있는데
   이번 작업은 **Poll 소스를 고치지 않는다**. Poll 을 다음에 손댈 때 `.swatch` 로 바꾸고
   이 줄에서 `.legend__mark` 를 지운다 - 그때까지는 별칭 한 줄이 사본 한 벌보다 싸다. */
.swatch, .legend__mark {
  width: 10px; height: 10px; border-radius: var(--radius-mark);
  flex: none; display: inline-block;
}

.trust {
  display: inline-flex; align-items: center; gap: var(--space-xs);
  padding: 6px 12px; border-radius: var(--radius-full);
  font: var(--text-button-sm); font-family: var(--font-sans);
  background: var(--color-surface-raised);
}
.trust--anonymous       { color: var(--color-mute); }
.trust--browser-checked { color: var(--color-ash); }
.trust--email-verified  { color: var(--color-accent-blue); }
.trust--phone-verified,
.trust--identity-verified { color: var(--color-success-text); }

/* ==========================================================================
   option-row - 이 시스템에서 가장 중요한 컴포넌트
   행 전체가 히트 타깃이고, 최소 높이 56px.
   ========================================================================== */

.options { display: grid; gap: var(--space-md); }

.option {
  position: relative;
  display: grid; grid-template-columns: 24px 1fr; align-items: center; gap: var(--space-md);
  background: var(--color-surface-card); border: 1px solid var(--color-hairline);
  border-radius: var(--radius-md); padding: var(--space-lg) var(--space-xl);
  min-height: 56px; cursor: pointer; width: 100%; text-align: left;
  transition: background-color var(--dur-state) var(--ease), border-color var(--dur-state) var(--ease);
}
.option:hover { background: var(--color-surface-raised); border-color: var(--color-stone); }
.option--selected {
  background: var(--color-primary-tint); border-color: var(--color-primary-tint-border);
}
.option__label { font: var(--text-body-md); font-family: var(--font-sans); color: var(--color-ink); overflow-wrap: anywhere; }

/* 실제 input 은 숨기지 않고 투명하게 겹쳐 두어 키보드·스크린리더 동작을 그대로 유지한다 */
.option__control {
  width: 24px; height: 24px; display: grid; place-items: center;
  border: 2px solid var(--color-stone); background: transparent;
  transition: border-color var(--dur-state) var(--ease), background-color var(--dur-state) var(--ease);
}
.option__control--radio { border-radius: var(--radius-full); }
.option__control--check { border-radius: 6px; }
.option:hover .option__control { border-color: var(--color-mute); }
.option--selected .option__control { border-color: var(--color-primary); background: var(--color-primary); }
.option__control svg { color: var(--color-on-primary); opacity: 0; }
.option--selected .option__control svg { opacity: 1; }
.option__control--radio::after {
  content: ''; width: 8px; height: 8px; border-radius: var(--radius-full);
  background: var(--color-on-primary); opacity: 0; transition: opacity var(--dur-state) var(--ease);
}
.option--selected .option__control--radio::after { opacity: 1; }

.option input { position: absolute; opacity: 0; width: 0; height: 0; }
.option:has(input:focus-visible) { outline: 2px solid var(--color-focus-outer); outline-offset: 2px; }

/* ==========================================================================
   result-bar-row - 시그니처 요소
   한 폴의 모든 막대는 한 시리즈(파랑). 강조는 명도로만.
   ========================================================================== */

.results { display: grid; gap: var(--space-md); }

.result { display: grid; gap: var(--space-sm); }
.result__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-md); }
.result__name {
  font: var(--text-body-md); font-family: var(--font-sans); color: var(--color-ink);
  display: flex; align-items: baseline; gap: var(--space-sm); flex-wrap: wrap; overflow-wrap: anywhere;
}
.result__value {
  font: var(--text-numeric-md); font-family: var(--font-sans); color: var(--color-ink);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* "내가 찍은 것"은 라벨 칩으로 표시한다 - 막대를 다시 칠하지 않는다(1시리즈 규칙) */
.result__mine {
  font: var(--text-button-sm); font-family: var(--font-sans);
  color: var(--color-primary-text); background: var(--color-primary-tint);
  border: 1px solid var(--color-primary-tint-border);
  padding: 2px 8px; border-radius: var(--radius-full);
}

.result__track {
  height: 8px; width: 100%; background: var(--color-surface-raised);
  border-radius: var(--radius-bar);
}
.result__fill {
  height: 100%; background: var(--viz-series-1);
  /* 데이터 끝만 둥글게, 기준선 쪽은 각지게 */
  border-radius: 0 var(--radius-bar) var(--radius-bar) 0;
  transition: width var(--dur-enter) var(--ease);
}
.result__fill--dim { background: var(--viz-series-1-dim); }

/* 첫 페인트에만 0에서 자란다. 표가 들어올 때마다 다시 자라면 읽을 수 없다. */
@media (prefers-reduced-motion: no-preference) {
  .results--enter .result__fill { animation: bar-grow 320ms var(--ease) both; }
}
@keyframes bar-grow { from { transform: scaleX(0); transform-origin: left; } to { transform: scaleX(1); } }

/* 320px 에서는 값이 라벨 아래로 내려간다. 막대는 절대 줄이지 않는다. */
@media (max-width: 360px) {
  .result__head { flex-direction: column; align-items: flex-start; gap: var(--space-xxs); }
}

/* ── 표 보기 쌍둥이 - 모든 차트에 필수 ───────────────────────────── */

.table-twin { width: 100%; border-collapse: collapse; }
.table-twin caption { text-align: left; padding-bottom: var(--space-md);
  font: var(--text-body-sm); font-family: var(--font-sans);
  letter-spacing: var(--track-body-sm); color: var(--color-mute); }
.table-twin th, .table-twin td {
  text-align: left; padding: var(--space-md);
  border-bottom: 1px solid var(--color-hairline-soft);
}
.table-twin th {
  font: var(--text-body-sm-strong); font-family: var(--font-sans);
  letter-spacing: var(--track-body-sm); color: var(--color-body);
  position: sticky; top: 0; background: var(--color-surface-raised);
}
.table-twin td { font: var(--text-body-md); font-family: var(--font-sans); color: var(--color-ink); }
.table-twin td.num { font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
/* 숫자 열의 **머리글도 오른쪽**이다 (2026-08-18). td.num 만 있고 th.num 이 없어서
   전 사이트의 숫자 열이 "머리글 왼쪽 · 값 오른쪽" 으로 어긋나 있었다 - 표가 엉망으로 보이는
   주범이었다. 머리글의 정렬은 언제나 그 열의 값과 같아야 한다. */
.table-twin th.num { text-align: right; }
/* 셀 세로 정렬은 가운데. 기본값(baseline)은 등락 태그·막대처럼 높이가 다른 내용이 섞이는 순간
   줄이 들쭉날쭉해진다. */
.table-twin th, .table-twin td { vertical-align: middle; }
.table-twin tbody tr:nth-child(even) { background: var(--color-surface-soft); }
.table-scroll { overflow-x: auto; }

/* ── 뷰 전환 칩 ───────────────────────────────────────────────── */

.chips { display: inline-flex; gap: var(--space-sm); }

/* 같은 층위의 선택지는 폭을 통일하고 한 줄로 둔다.
   핵심은 minmax(0, 1fr) - 1fr 만 쓰면 트랙의 최소 크기가 "내용 폭"이라 라벨이 길면
   컨테이너 밖으로 넘친다(Monitoring 피드 탭에서 실측, 2026-08-12). 0 을 최소로 주면
   좁은 카드에서도 트랙이 같이 줄어든다.
   ※ 옛 판은 width:max-content + @media(560px) 였는데, 뷰포트가 넓어도 **카드가 좁을 수 있다**는
     경우를 못 본 설정이라 그 카드에서 칩이 넘쳤다. 뷰포트가 아니라 컨테이너 기준이 맞다. */
.chips--equal {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
  width: 100%; gap: var(--space-sm);
}
/* 칸보다 라벨이 긴 극단에서도 컨테이너를 밀지 않는다 - 말줄임으로 흡수 */
.chips--equal .chip {
  min-width: 0; padding-inline: 10px;
  overflow: hidden; text-overflow: ellipsis; display: block; line-height: 20px;
  /* display:block 이 되는 순간 .chip 의 flex 중앙정렬이 죽는다 - 그걸 몰라서
     균등 칩의 글자가 전부 왼쪽에 붙어 있었다(2026-08-18 대표님 지적, 실측 dH -334px).
     블록에서의 중앙은 text-align 이 맡는다. */
  text-align: center;
}
.chip {
  /* inline-flex 여야 한다 - 리셋의 svg{display:block} 때문에 인라인 칩 안에서
     아이콘이 블록이 되어 라벨을 다음 줄로 밀어낸다. */
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  background: var(--color-surface-raised); color: var(--color-body);
  border: 0; border-radius: var(--radius-full); padding: 8px 16px; min-height: 36px;
  font: var(--text-button-md); font-family: var(--font-sans); cursor: pointer;
  white-space: nowrap;
  transition: background-color var(--dur-state) var(--ease), color var(--dur-state) var(--ease);
}
.chip:hover { background: var(--color-surface-overlay); color: var(--color-ink); }
.chip[aria-pressed='true'] { background: var(--color-ink); color: var(--color-canvas); }
/* 링크로 쓰는 칩(NavLink) - 서비스 안쪽 이동(2026-08-18 신규 8종의 서브내비).
   <a> 라 밑줄이 붙는데 칩은 버튼 모양이라 밑줄이 어색하다. 현재 위치(aria-current)는
   눌린 칩과 같은 모양이다 - "고른 것" 과 "지금 있는 곳" 을 다른 색으로 두면 두 규칙이 된다. */
a.chip { text-decoration: none; }
.chip[aria-current='page'] { background: var(--color-ink); color: var(--color-canvas); }

/* ==========================================================================
   stat-tile
   ========================================================================== */

.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--space-md); }

/* 타일이 4~5개까지 늘어나는 화면(Monitoring)에서 한 줄을 지키는 변형.
   기본 .stats(최소 150px)는 좁은 컨테이너에서 마지막 하나가 아랫줄로 접힌다 -
   Monitoring 좌측 열은 1280px 뷰포트에서 724.8px 이고, 여기서 5개 중 4개만 한 줄이었다
   (실측 2026-08-14). 150×5 + 12×4 = 798px 이 필요한데 컨테이너가 725px 이기 때문.

   최소 트랙을 130px 로 내린다. 근거는 내용 실측이다 -
   가장 넓은 값 "123,456"(28px) 이 97.6px, 타일 좌우 패딩 16+16 = 32px → 129.6px.
   이 값이면 725px 컨테이너에 5개가 한 줄로 들어가고(각 136.6px), **동시에**
   트랙이 130px 밑으로는 절대 안 내려가므로 숫자가 잘리는 일도 없다.

   접히는 판정을 미디어쿼리(뷰포트)가 아니라 auto-fit(컨테이너)에 맡긴 이유는
   .chips--equal 이 배운 것과 같다 - 뷰포트가 넓어도 **컨테이너는 좁을 수 있다**.
   컨테이너가 130×5+48 = 698px 밑으로 내려가면 스스로 접히고, 접힌 뒤에도
   모든 줄의 타일 폭이 같다.
   ※ 기본 .stats 는 그대로 둔다. Poll 관리 화면의 "참여 코드"(자간 0.08em, 124.5px 실측)는
     타일이 156px 밑으로 내려가면 잘리므로, 130px 을 전역으로 내리면 그 화면이 깨진다. */
@media (min-width: 761px) {
  .stats--fit { grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); }
}
/* ≤760px(모바일·태블릿)에서는 기본 .stats 규칙(최소 150px)으로 되돌아간다 -
   작은 화면에서 5개를 한 줄에 욱여넣으면 읽을 수 없다. */

.stat { background: var(--color-surface-card); border-radius: var(--radius-md); padding: var(--space-lg); display: grid; gap: var(--space-xs); }
.stat__label { font: var(--text-caption-md); font-family: var(--font-sans);
  letter-spacing: var(--track-caption); color: var(--color-mute); }
.stat__value { font: var(--text-heading-xl); font-family: var(--font-sans);
  letter-spacing: var(--track-heading-xl); color: var(--color-ink);
  font-variant-numeric: proportional-nums; }

/* ==========================================================================
   배너 / 토스트
   ========================================================================== */

.banner {
  display: flex; align-items: flex-start; gap: var(--space-md);
  padding: var(--space-lg); border-radius: var(--radius-md);
  font: var(--text-body-sm); font-family: var(--font-sans); letter-spacing: var(--track-body-sm);
}
.banner svg { flex: none; margin-top: 1px; }
.banner--info     { background: var(--color-surface-raised); color: var(--color-body); }
.banner--success  { background: var(--color-success-bg);  color: var(--color-success-text); }
.banner--warning  { background: var(--color-warning-bg);  color: var(--color-warning); }
.banner--serious  { background: var(--color-serious-bg);  color: var(--color-serious); }
.banner--critical { background: var(--color-critical-bg); color: var(--color-critical-text); }

.toast-region { position: fixed; left: 50%; bottom: var(--space-xl); transform: translateX(-50%); z-index: 60;
  display: grid; gap: var(--space-sm); width: min(480px, calc(100vw - 32px)); }
.toast {
  display: flex; align-items: center; gap: var(--space-md);
  background: var(--color-surface-overlay); border: 1px solid var(--color-hairline);
  border-radius: var(--radius-md); padding: var(--space-md) var(--space-lg);
  box-shadow: var(--shadow-l4);
  font: var(--text-body-sm); font-family: var(--font-sans);
  letter-spacing: var(--track-body-sm); color: var(--color-ink);
}
.toast--critical { color: var(--color-critical-text); }
.toast--success { color: var(--color-success-text); }

/* ==========================================================================
   컴포저 - 선택지 편집
   ========================================================================== */

.composer__option { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: var(--space-sm); }
.composer__handle {
  display: grid; place-items: center; width: 32px; height: 44px;
  color: var(--color-ash); cursor: grab; background: none; border: 0; border-radius: var(--radius-sm);
}
.composer__handle:hover { color: var(--color-body); }
.composer__handle:active { cursor: grabbing; }
.composer__option--dragging { opacity: 0.5; }
.composer__option--over .input { border-color: var(--color-focus-outer); }

.settings-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--space-lg); }

.radio-cards { display: grid; gap: var(--space-sm); }
.radio-card {
  position: relative;
  display: grid; grid-template-columns: 20px 1fr; gap: var(--space-md); align-items: start;
  background: var(--color-surface-card); border: 1px solid var(--color-hairline);
  border-radius: var(--radius-md); padding: var(--space-md) var(--space-lg);
  cursor: pointer; min-height: 56px; align-content: center;
}
.radio-card:hover { background: var(--color-surface-raised); border-color: var(--color-stone); }
.radio-card--selected { background: var(--color-primary-tint); border-color: var(--color-primary-tint-border); }
.radio-card:has(input:focus-visible) { outline: 2px solid var(--color-focus-outer); outline-offset: 2px; }
.radio-card input { position: absolute; opacity: 0; width: 0; height: 0; }
.radio-card__dot {
  width: 20px; height: 20px; border-radius: var(--radius-full);
  border: 2px solid var(--color-stone); display: grid; place-items: center; margin-top: 2px;
}
.radio-card--selected .radio-card__dot { border-color: var(--color-primary); background: var(--color-primary); }
.radio-card__dot::after { content: ''; width: 6px; height: 6px; border-radius: var(--radius-full);
  background: var(--color-on-primary); opacity: 0; }
.radio-card--selected .radio-card__dot::after { opacity: 1; }
/* block 이 아니면 제목과 설명이 한 줄로 붙는다 - span 이라 기본이 inline 이다 */
.radio-card__title { display: block; font: var(--text-body-strong); font-family: var(--font-sans); color: var(--color-ink); }
.radio-card__desc { display: block; font: var(--text-body-sm); font-family: var(--font-sans);
  letter-spacing: var(--track-body-sm); color: var(--color-mute); margin-top: var(--space-xs); }

.switch-row {
  position: relative;
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-lg);
  background: var(--color-surface-card); border: 1px solid var(--color-hairline);
  border-radius: var(--radius-md); padding: var(--space-md) var(--space-lg); min-height: 56px;
  cursor: pointer;
}
.switch-row:hover { background: var(--color-surface-raised); }
.switch-row:has(input:focus-visible) { outline: 2px solid var(--color-focus-outer); outline-offset: 2px; }
.switch-row input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch {
  flex: none; width: 44px; height: 26px; border-radius: var(--radius-full);
  background: var(--color-surface-overlay); border: 1px solid var(--color-hairline);
  position: relative; transition: background-color var(--dur-state) var(--ease);
}
.switch::after {
  content: ''; position: absolute; top: 2px; left: 2px; width: 20px; height: 20px;
  border-radius: var(--radius-full); background: var(--color-mute);
  transition: transform var(--dur-state) var(--ease), background-color var(--dur-state) var(--ease);
}
.switch-row--on .switch { background: var(--color-primary); border-color: var(--color-primary); }
.switch-row--on .switch::after { transform: translateX(18px); background: var(--color-on-primary); }

/* ==========================================================================
   공유 (QR / 링크 / 룸코드)
   ========================================================================== */

.share-grid { display: grid; grid-template-columns: auto 1fr; gap: var(--space-xl); align-items: start; }
@media (max-width: 560px) { .share-grid { grid-template-columns: 1fr; } }

.qr { background: #fff; padding: var(--space-md); border-radius: var(--radius-md); width: 168px; height: 168px; }
.qr svg { width: 100%; height: 100%; }

.copyfield { display: flex; gap: var(--space-sm); align-items: center; }
.copyfield .input { font-size: 14px; }

.roomcode {
  font-variant-numeric: tabular-nums; letter-spacing: 0.22em;
  font-size: 32px; font-weight: 700; color: var(--color-ink);
}

/* ==========================================================================
   기타
   ========================================================================== */

.spinner {
  width: 20px; height: 20px; border-radius: var(--radius-full);
  border: 2px solid var(--color-surface-overlay); border-top-color: var(--color-mute);
  animation: spin 700ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.empty-state { display: grid; gap: var(--space-md); justify-items: start;
  padding: var(--space-xxl); background: var(--color-surface-card); border-radius: var(--radius-md); }

/* 리페치 중에는 스켈레톤을 번쩍이지 않고 직전 렌더를 흐리게 유지한다 */
.is-stale { opacity: 0.6; transition: opacity var(--dur-state) var(--ease); }

.bullets { display: grid; gap: var(--space-md); }
.bullets li { display: grid; grid-template-columns: 20px 1fr; gap: var(--space-md);
  font: var(--text-body-md); font-family: var(--font-sans); color: var(--color-body); align-items: start; }
.bullets li > svg { color: var(--color-primary-text); margin-top: 3px; }
.bullets b { color: var(--color-ink); font-weight: 600; }

/* ==========================================================================
   meter - 쿼터/진행률. result-bar-row 와 다른 물건이다.
   채움이 심각도를 나르고, 트랙은 같은 램프의 밝은 단계를 쓴다
   (파랑 위 파랑 트랙이 옳은 유일한 자리 - DESIGN.dark.md).
   결과 표시에는 절대 쓰지 않는다.
   ========================================================================== */

.meter {
  height: 8px; width: 100%; border-radius: var(--radius-bar);
  /* 채움과 같은 램프의 물러난 단계. seq-600 은 채움과 2.4:1 밖에 안 벌어져
     낮은 사용률이 "거의 꽉 참"으로 오독된다 - 650 으로 내려 약 3:1 을 확보한다. */
  background: var(--viz-seq-650); overflow: hidden;
}
.meter__fill {
  height: 100%; border-radius: 0 var(--radius-bar) var(--radius-bar) 0;
  background: var(--viz-series-1);
  transition: width var(--dur-enter) var(--ease), background-color var(--dur-state) var(--ease);
}
.meter:has(.meter__fill--warning)  { background: var(--color-warning-bg); }
.meter:has(.meter__fill--critical) { background: var(--color-critical-bg); }
.meter__fill--warning  { background: var(--color-warning); }
.meter__fill--critical { background: var(--color-critical); }

/* ==========================================================================
   요금제
   ========================================================================== */

/* 패널 격자 - 기관 카드 같은 **정해진 개수**의 카드. 3열이 기본이고 좁아지면 2·1열.
   Web 여론조사 화면(3×2)을 옮긴 것(2026-08-18 대표님 "1줄에 3개, 2줄로 딱 맞아떨어지게").
   auto-fit 을 쓰지 않는 이유: 6개가 컨테이너 폭에 따라 4+2 로 갈라지면 두 번째 줄이 비어 보인다.
   개수가 정해진 격자는 열 수도 정해져야 한다. */
.panel-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-lg); }
@media (max-width: 1100px) { .panel-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px)  { .panel-grid { grid-template-columns: 1fr; } }

/* 정치 데이터 색 어휘 - tokens.css --pol-* 의 클래스판. 데이터에만 붙인다.
   **글자는 -text 변형을 쓴다** - 원색은 검정 바탕에서 읽히지 않는다(민주 1.32:1 실측).
   **칠(-bg-)은 원색 그대로** - Web 과 같은 색으로 보여야 하고, 칠은 3:1 기준이라 통과한다. */
.pol-dpk    { color: var(--pol-dpk-text); }     .pol-bg-dpk    { background: var(--pol-dpk); }
.pol-ppp    { color: var(--pol-ppp-text); }     .pol-bg-ppp    { background: var(--pol-ppp); }
.pol-reform { color: var(--pol-reform-text); }  .pol-bg-reform { background: var(--pol-reform); }
.pol-rebuild{ color: var(--pol-rebuild-text); } .pol-bg-rebuild{ background: var(--pol-rebuild); }
.pol-etc    { color: var(--pol-etc-text); }     .pol-bg-etc    { background: var(--pol-etc); }
.pol-up     { color: var(--pol-up-text); }      .pol-down      { color: var(--pol-down-text); }
.pol-approve   { background: var(--pol-approve); }
.pol-disapprove{ background: var(--pol-disapprove); }
/* 정당 점 - 이름 앞의 작은 색 표식 (Web 의 w-2.5 rounded-full) */
.pol-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; flex: none; }
/* 진영 칸 - 선거 히트맵의 셀 배경 */
.pol-cell-ppp { background: var(--pol-ppp-bg); }
.pol-cell-dpk { background: var(--pol-dpk-bg); }
.pol-cell-etc { background: var(--pol-etc-bg); }

.plan-grid {
  display: grid; gap: var(--space-md);
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
/* 4개가 3+1 로 갈라지면 마지막 플랜만 외톨이가 된다 - 데스크톱에서는 한 줄로 고정 */
@media (min-width: 900px) { .plan-grid { grid-template-columns: repeat(4, 1fr); } }
.plan { display: grid; gap: var(--space-lg); align-content: start; }
.plan--current { outline: 1px solid var(--color-primary-tint-border); background: var(--color-surface-raised); }

.plan__price {
  font: var(--text-heading-xl); font-family: var(--font-sans);
  letter-spacing: var(--track-heading-xl); color: var(--color-ink);
  font-variant-numeric: proportional-nums;
}
.plan__cycle {
  font: var(--text-body-sm); font-family: var(--font-sans);
  letter-spacing: var(--track-body-sm); color: var(--color-mute);
}
.plan__features li {
  display: grid; grid-template-columns: 16px 1fr; gap: var(--space-sm); align-items: start;
  font: var(--text-body-sm); font-family: var(--font-sans);
  letter-spacing: var(--track-body-sm); color: var(--color-body);
}
.plan__features svg { color: var(--color-success-text); margin-top: 3px; }

/* ==========================================================================
   계정 메뉴 (level 4 - 링 + 그림자가 붙는 유일한 단계)
   ========================================================================== */

.menu { position: relative; }
.menu__panel {
  position: absolute; right: 0; top: calc(100% + var(--space-sm)); z-index: 50;
  min-width: 200px; display: grid; gap: var(--space-xxs);
  background: var(--color-surface-overlay); border: 1px solid var(--color-hairline);
  border-radius: var(--radius-md); padding: var(--space-sm);
  box-shadow: var(--shadow-l4);
}
.menu__item {
  display: block; width: 100%; text-align: left;
  background: none; border: 0; cursor: pointer; text-decoration: none;
  padding: var(--space-md); border-radius: var(--radius-sm);
  font: var(--text-body-sm); font-family: var(--font-sans);
  letter-spacing: var(--track-body-sm); color: var(--color-ink);
}
.menu__item:hover { background: rgba(245, 245, 240, 0.06); }

/* 세션 확인 전 자리 지킴 - "로그인"이 떴다 사라지는 깜빡임 방지 */
.nav__slot { display: inline-block; width: 64px; height: 40px; }

/* range 입력 - 견적 슬라이더 */
input[type='range'] { accent-color: var(--color-primary); height: 44px; }

/* ==========================================================================
   순위 투표 입력 - 선택 순서가 곧 순위다
   ========================================================================== */

.option__rank {
  width: 24px; height: 24px; border-radius: var(--radius-full);
  display: grid; place-items: center;
  border: 2px solid var(--color-stone); background: transparent;
  font: var(--text-button-sm); font-family: var(--font-sans);
  font-variant-numeric: tabular-nums; color: var(--color-mute);
  transition: background-color var(--dur-state) var(--ease), border-color var(--dur-state) var(--ease);
}
.option:hover .option__rank { border-color: var(--color-mute); }
.option__rank--on {
  background: var(--color-primary); border-color: var(--color-primary);
  color: var(--color-on-primary);
}

/* IRV 라운드 배지 - 서수 램프. 라운드는 순서가 있는 값이라 카테고리컬 팔레트를 쓰지 않는다. */
.irv-round {
  display: inline-block; width: 10px; height: 10px; border-radius: 3px;
  margin-right: var(--space-sm); vertical-align: baseline;
}

/* ==========================================================================
   법적 문서 (약관 · 처리방침)
   ========================================================================== */

.legal-body p { font: var(--text-body-md); font-family: var(--font-sans); color: var(--color-body); }
.legal-body b { color: var(--color-ink); font-weight: 600; }
.legal-ul { display: grid; gap: var(--space-sm); padding-left: var(--space-lg); list-style: disc; }
.legal-ul li { font: var(--text-body-md); font-family: var(--font-sans); color: var(--color-body); }
.legal-dl {
  display: grid; grid-template-columns: minmax(140px, auto) 1fr;
  gap: var(--space-md) var(--space-lg); margin: 0;
}
.legal-dl dt { font: var(--text-body-sm-strong); font-family: var(--font-sans); color: var(--color-body); }
.legal-dl dd { margin: 0; font: var(--text-body-md); font-family: var(--font-sans); color: var(--color-ink); }
.legal-th { font-weight: 400 !important; color: var(--color-ink) !important;
  position: static !important; background: none !important; }

/* ==========================================================================
   모바일 내비 - 항목이 많아 줄바꿈되면 64px 고정 높이를 넘어 겹친다.
   디자인 문서는 햄버거 드로어를 규정하지만(§Responsive), 그 전 단계로
   보조 링크를 접어 워드마크 + 계정 + 빨간 CTA 만 남긴다.
   빨간 CTA 는 모든 브레이크포인트에서 보여야 한다는 규칙은 지킨다.
   ========================================================================== */

@media (max-width: 560px) {
  .nav { height: auto; min-height: 64px; padding: var(--space-sm) 0; }
  .nav__inner { gap: var(--space-sm); }
  .nav__secondary { display: none; }
  .wordmark { font-size: 15px; min-width: 0; }
  .wordmark > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .nav .btn { padding: 6px 12px; }
  /* 좁은 화면에서는 세 칸을 내용 크기대로 — 1fr 로 두면 계정 칸이 이름을 밀어낸다 */
  .nav__inner { grid-template-columns: minmax(0, auto) auto minmax(0, 1fr); gap: var(--space-sm); }
  .nav__cta { min-width: 0; }
}

/* ==========================================================================
   공개 상황판 - 첫 화면 카드 그리드
   ========================================================================== */

.board {
  display: grid; gap: var(--space-md);
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}
/* 칸마다 **내용 높이 그대로**, 위로 붙인다 (`.board.board--top`).
 *
 * 기본 `.board` 는 칸을 늘려 카드 높이를 맞춘다 - 카드 목록에서는 그게 맞다(제목·버튼이 줄 맞춰 선다).
 * 그런데 칸 안에 **제목 + 표**처럼 세로로 쌓인 것이 들어오면, 늘어난 높이를 그 둘이 나눠 가져
 * 제목과 표 사이가 벌어진다. 실측(2026-08-19, 여론조사 상세 교차분석): 6행짜리 표가 있는 줄에서
 * 3행짜리 표들이 100px 아래로 밀려 "표가 엉망" 으로 보였다(대표님 지적).
 * 카드 격자는 그대로 두고, 표 격자만 이 변형을 붙인다. */
.board--top { align-items: start; }

.board-card {
  display: grid; gap: var(--space-md); align-content: start; height: 100%;
  background: var(--color-surface-card); border-radius: var(--radius-md);
  padding: var(--space-lg); text-decoration: none;
  transition: background-color var(--dur-state) var(--ease);
}
.board-card:hover { background: var(--color-surface-raised); }

.board-card__title {
  font: var(--text-heading-md); font-family: var(--font-sans); color: var(--color-ink);
  margin: 0; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; overflow-wrap: anywhere;
}

.board-card__options { display: grid; gap: var(--space-md); }

.board-opt { display: grid; gap: var(--space-xs); }
.board-opt--photo { grid-template-columns: 36px 1fr; gap: var(--space-sm); align-items: center; }
.board-opt__photo {
  width: 36px; height: 36px; border-radius: var(--radius-sm);
  object-fit: cover; background: var(--color-surface-raised);
}
.board-opt__photo--empty { display: block; }
.board-opt__body { display: grid; gap: var(--space-xs); min-width: 0; }
.board-opt__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-sm); }
.board-opt__label {
  font: var(--text-body-sm); font-family: var(--font-sans); color: var(--color-ink);
  letter-spacing: var(--track-body-sm);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.board-opt__symbol {
  display: inline-block; min-width: 18px; text-align: center;
  background: var(--color-surface-overlay); color: var(--color-mute);
  border-radius: var(--radius-sm); padding: 0 4px; margin-right: 6px;
  font: var(--text-caption-md); font-family: var(--font-sans);
}
.board-opt__pct {
  font: var(--text-numeric-md); font-family: var(--font-sans); font-size: 14px;
  color: var(--color-ink); font-variant-numeric: tabular-nums;
}

.board-card__foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-sm);
  font: var(--text-caption-md); font-family: var(--font-sans);
  letter-spacing: var(--track-caption); color: var(--color-mute);
  margin-top: auto; padding-top: var(--space-xs);
}
.board-card__go { display: inline-flex; align-items: center; gap: 4px; color: var(--color-primary-text); }

/* ==========================================================================
   순위 교체 - FLIP 으로 행이 미끄러지고, 바뀐 행만 잠깐 표시된다
   ========================================================================== */

.result__rank {
  display: inline-grid; place-items: center; min-width: 20px; height: 20px;
  border-radius: var(--radius-full); background: var(--color-surface-raised);
  color: var(--color-mute); font: var(--text-caption-md); font-family: var(--font-sans);
  font-variant-numeric: tabular-nums; margin-right: var(--space-xs);
}
.result__symbol {
  background: var(--color-surface-raised); color: var(--color-mute);
  border-radius: var(--radius-sm); padding: 1px 6px; margin-right: var(--space-xs);
  font: var(--text-caption-md); font-family: var(--font-sans);
}
/* 색만으로 알리지 않는다 - 화살표와 글자가 같이 붙는다 */
.result__shift {
  font: var(--text-button-sm); font-family: var(--font-sans);
  padding: 2px 8px; border-radius: var(--radius-full);
}
.result__shift--up   { background: var(--color-success-bg); color: var(--color-success-text); }
.result__shift--down { background: var(--color-surface-raised); color: var(--color-mute); }

@media (prefers-reduced-motion: no-preference) {
  .result--moved-up  { animation: rank-flash 900ms var(--ease); }
  .result--moved-down { animation: rank-flash-dim 900ms var(--ease); }
}
@keyframes rank-flash {
  from { background: var(--color-primary-tint); }
  to   { background: transparent; }
}
@keyframes rank-flash-dim {
  from { background: rgba(245, 245, 240, 0.05); }
  to   { background: transparent; }
}
.result--moved-up, .result--moved-down { border-radius: var(--radius-sm); }

/* ==========================================================================
   선거 모드 - 기호 + 사진 + 이름
   ========================================================================== */

.result--election { grid-template-columns: auto 1fr; gap: var(--space-md); align-items: center; }
.result__body { display: grid; gap: var(--space-sm); min-width: 0; }

.cand { display: grid; justify-items: center; gap: var(--space-xs); }
.cand__symbol {
  font: var(--text-body-sm-strong); font-family: var(--font-sans);
  color: var(--color-ink); font-variant-numeric: tabular-nums;
}
.cand__photo {
  width: 52px; height: 64px; object-fit: cover;
  border-radius: var(--radius-sm); background: var(--color-surface-raised); display: block;
}
.cand__photo--empty { background: var(--color-surface-raised); }

/* ==========================================================================
   컴포저 - 선거 모드 입력 (기호 · 이름 · 사진)
   ========================================================================== */

.composer__option--election { grid-template-columns: auto 72px 1fr auto auto; }
.composer__symbol { text-align: center; font-variant-numeric: tabular-nums; }

.composer__photo {
  display: grid; place-items: center; cursor: pointer;
  width: 44px; height: 54px; border-radius: var(--radius-sm);
  background: var(--color-surface-raised); border: 1px dashed var(--color-hairline);
  overflow: hidden; flex: none;
}
.composer__photo:hover { border-color: var(--color-stone); }
.composer__photo:focus-within { outline: 2px solid var(--color-focus-outer); outline-offset: 2px; }
.composer__photo img { width: 100%; height: 100%; object-fit: cover; }
.composer__photo-empty {
  font: var(--text-caption-sm); font-family: var(--font-sans); color: var(--color-ash);
}

/* 투표 화면의 선거 모드 행 - 컨트롤 · 후보카드 · 이름 */
.option--election { grid-template-columns: 24px auto 1fr; }

@media (max-width: 560px) {
  .composer__option--election { grid-template-columns: auto 60px 1fr auto; }
  .composer__option--election .composer__photo { width: 38px; height: 46px; }
}

.board-card__empty {
  font: var(--text-body-sm); font-family: var(--font-sans);
  letter-spacing: var(--track-body-sm); color: var(--color-mute);
  padding: var(--space-lg) 0;
}

/* ==========================================================================
   시간대별 추이 - 인라인 SVG. 마크 규격은 DESIGN.dark.md > Mark specs.
   ========================================================================== */

.trend { width: 100%; min-width: 520px; height: auto; display: block; }

.legend {
  display: flex; flex-wrap: wrap; gap: var(--space-sm) var(--space-lg);
  font: var(--text-body-sm); font-family: var(--font-sans);
  letter-spacing: var(--track-body-sm); color: var(--color-body);
}
.legend li { display: inline-flex; align-items: center; gap: var(--space-xs); }
/* 색 마크(`.legend__mark`)의 선언은 위 `.swatch` 로 올라갔다 - 같은 것이 Spellcheck·Monitoring
   에도 각각 있었기 때문이다. 여기서 다시 정의하면 그 순간 다시 세 벌이 된다. */
.legend__symbol {
  background: var(--color-surface-raised); color: var(--color-mute);
  border-radius: var(--radius-sm); padding: 0 5px;
  font: var(--text-caption-md); font-family: var(--font-sans);
}

/* ==========================================================================
   댓글
   ========================================================================== */

.comment {
  background: var(--color-surface-card); border-radius: var(--radius-md);
  padding: var(--space-lg); display: grid; gap: var(--space-sm);
}
.comment--hidden { background: var(--color-surface-soft); }
.comment--hidden .comment__body { color: var(--color-ash); font-style: italic; }

.comment__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-md); }
.comment__name {
  font: var(--text-body-strong); font-family: var(--font-sans); color: var(--color-ink);
}
.comment__body {
  font: var(--text-body-md); font-family: var(--font-sans); color: var(--color-body);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.comment__actions { display: flex; flex-wrap: wrap; gap: var(--space-xs); }
.comment__actions .btn { height: 32px; padding: 4px 10px; }

/* 답글은 1단계까지만 - 들여쓰기도 한 번뿐이다 */
.comment-replies {
  margin-left: var(--space-xl);
  border-left: 1px solid var(--color-hairline);
  padding-left: var(--space-lg);
}
@media (max-width: 480px) {
  .comment-replies { margin-left: var(--space-sm); padding-left: var(--space-md); }
}

/* 칩 묶음이 나란히 올 때는 **컨테이너 gap 으로만** 간격을 준다.
   여기에 margin 까지 더하면 flex gap 과 이중으로 먹어 간격이 두 배가 된다.
   (묶음 사이는 묶음 안 8px 보다 확실히 넓어야 별개 묶음으로 읽힌다 - 근접성이 곧 그룹이다) */
