/* web/static/tokens.css -- 디자인 토큰 단일 정의 (R7A, 2026-07-18)
   시스템: 검증 원장(ledger of evidence). 흰 카드 서피스 + 인디고 브랜드색
   (행동·정보 공용). 지면·텍스트는 뉴트럴, 색은 상태·액션·브랜드 포인트에만
   쓴다.
   [W-PR2 웜 스톤 시프트 2026-09-09] 지면 뉴트럴을 순뉴트럴(zinc)에서 웜 스톤으로
   옮겼다. 사유는 장식이 아니라 원칙이다 — docs/design-principles.md 원칙 4
   "액센트 1색, 지면은 틴트 뉴트럴": 등간격 무채색(#nnn)은 라이브러리 기본값의
   지문이라 어느 SaaS와도 구분되지 않는다. 미색 캔버스가 "종이 원장" 은유를
   완성한다. **액센트·의미 채널(방향·판정·상태·주목 램프)은 값 불변**이고 새
   지면 위 대비만 재실측했다(아래 각 값 옆 주석이 그 실측이다).
   [2026-09-12 배경 원복] Kevin 직접 지시로 --bg/--panel(라이트·다크·인쇄 3 블록
   전부)만 W-PR2 이전 순뉴트럴(zinc)로 되돌렸다 — "이 은은한 크림색 배경색 안
   어울린다, 옛날 배경색이 더 어울린다". --ink/--sub/--line/--line-strong·
   --accent 계열·--surface(카드)·--lit-quiet 등 W-PR2 가 옮긴 다른 웜 토큰은
   **값 불변**이다(원칙 4 자체의 재검토가 아니라 배경 계열 단독 원복 — 위 새
   지면 위 대비 재실측 주석들은 이제 일부 stale 하다, 실제 대비는 PR 본문에
   재계산 기록). 배경에서 손파생된 rgba 는 같이 따라간다: 다크 --glass-bg.
   [v2 색복원 2026-09-05] PR-2 의 "잉크 프라이머리(행동) / 인디고 액센트(정보)"
   2색 분리는 **철회**됐다. 전면이 흑백으로 읽힌다는 Kevin 반려 — 잉크 CTA +
   사진 무채화가 겹쳐 브랜드색이 화면에서 사라졌다. --primary 는 다시 --accent
   별칭이고, 인디고가 CTA·활성 탭·링크를 함께 진다.
   SSOT 분담: 이 파일 = 지면·타이포·간격·라운딩·그림자 토큰.
             web/palette.py = 차트·의미색 (--color-*, dist/static/palette.css).
   로드 순서: tokens.css -> (palette.css, 어드민만) -> style.css/app.css/admin.css.
   구 계약 호환: --bg --panel --paper --ink --sub --line --accent
   --accent-soft --sans --mono 이름 유지. --serif 는 삭제 (참조 금지). */

/* -- 자체 호스팅 폰트 (OFL) -- 서브셋 woff2, CDN 아님 -- */
@font-face {
  font-family: "Pretendard Variable";
  src: url("/static/fonts/PretendardVariable-sub.ee12fffc042b.woff2") format("woff2");
  font-weight: 45 920;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("/static/fonts/JetBrainsMono-sub.588d538906ed.woff2") format("woff2");
  font-weight: 100 800;
  font-display: swap;
}

:root {
  color-scheme: light dark;

  /* 지면 -- [2026-09-12 배경 원복] Kevin 직접 지시("이 은은한 크림색 배경색 안
     어울린다, 옛날 배경색이 더 어울린다") — --bg/--panel 만 W-PR2(#1000) 이전
     순뉴트럴(zinc)로 되돌린다. 액센트·타이포(--ink/--sub)·보더(--line 계열)·
     카드 화이트(--surface)는 W-PR2 범위 그대로 두고 건드리지 않는다 — 웜 트랙
     전체 롤백이 아니라 배경 계열 단독 원복이다. 원복 전 실값은 tokens.css 를
     따라 커밋 20e9adc85(#1000)로 대조: `git show 20e9adc85 -- web/static/tokens.css`. */
  --bg: #f7f7f8;            /* 캔버스 -- 순뉴트럴(zinc), 배경 원복 */
  --surface: #ffffff;       /* 카드 서피스 -- 흰색 유지(대비 기준면 불변) */
  --panel: #f1f1f3;         /* 인셋 패널·행 hover -- 배경 원복 */
  --paper: #ffffff;         /* 차트 지면 -- 다크에서도 밝게 (SVG 고정색 가독) */

  /* 텍스트 */
  --ink: #1a1815;           /* 웜 블랙. 17.72:1 on surface, 16.54:1 on bg, 15.56:1 on panel */
  --sub: #57534e;           /* 7.63:1 on surface, 7.12:1 on bg, 6.70:1 on panel */

  /* 보더 -- 웜 헤어라인 */
  --line: #e8e5df;
  --line-strong: #d8d3ca;

  /* 액센트 (인디고 -- DIRECTION 적/청과 충돌하지 않는 제3색). 브랜드색이자
     인터랙션 색이다: 주 CTA·활성 탭·링크 hover·데이터 하이라이트·포커스 링.
     OG 이미지·브랜드 마크와 같은 계열이라 제품 밖 표면과도 정합.
     [v2] "정보 전용" 축소는 철회 — 행동도 인디고가 진다. */
  --accent: #4f46e5;        /* 6.29:1 on surface, 5.87:1 on bg, 5.52:1 on panel (W-PR2 재실측) */
  --accent-strong: #4338ca; /* hover·active, 7.90:1 on surface */
  --accent-soft: #eef0fe;   /* 틴트 배경 -- accent 텍스트 5.55:1, accent-strong 6.97:1 */
  --accent-ink: #ffffff;    /* on accent, 6.29:1 */

  /* 구성 세그먼트 사다리 (기업 개요 "무엇으로 벌까요"·"번 돈은 어디로 갈까요"
     스택/절대금액 바 + 그 범례 스와치). 소비는 style.css `.mix-seg-N`.

     왜 색이 붙는가 — 구 구현은 `--ink` 불투명도 사다리(.82→.07)라 8단이 전부
     무채 회색이었고, 말단 두 단(.12/.07)은 --panel 위에서 사실상 안 보였다
     (Kevin 실사용 반려: "bar 색깔이 흑백이라 알아보기 힘들어"). 세그먼트 구분은
     **정보**이므로 색 사용이 정당하다(디자인 원칙 4 "색은 정보에만"). 다만 신규
     hue 는 0개다 — 전 단이 브랜드 인디고(--accent h≈302.6 in CIELab) 단일
     계열이고, 구분은 색상이 아니라 **명도**가 진다. 무지개 다색 금지 유지.

     방향·판정 의미 없음. index 는 원문 공시 순서(폭 순 아님)이고 순서 인코딩일
     뿐이다 — 색으로 좋고 나쁨을 말하지 않는다(앰버·방향색 금지 규율 그대로).

     색맹 대비 — 인접 단 ΔL* ≥ 7.5(최소 3→4 의 7.5), 인접 대비비 ≥ 1.26. 단일
     hue 사다리라 색상 정보를 전부 지워도(회색조 변환) 8단이 그대로 남는다.

     말단(8단)은 뉴트럴이다(웜 지면과 같은 계열의 저채도 C=4 — 무채 회색을 쓰면
     웜 지면 위에서 푸른 기로 뜬다. --lit-quiet 재파생과 같은 규율). "기타"류
     잔여 항목이 마지막에 오는 관례를 받되 **라벨로 분류하지 않는다** — 실측
     (forward/company_profiles 446개 mix) 상 '기타' 문자열 보유 240건 중 36건은
     마지막이 아니고, '기타자동차부품'·'감가상각비·기타상각비'처럼 실명 부문도
     substring 에 걸린다. 실명 부문을 잔여로 오표기하는 손해가 이득보다 크다.
     뉴트럴은 사다리의 L* 슬롯을 그대로 지키므로(라이트 81.1 / 다크 25.0) 명도
     단조성과 위 색맹 대비 주장은 깨지지 않는다.

     -fg = 그 단 위에 얹히는 온-바 % 오버레이(.seg-num) 글자색. 어두운 단은 흰
     글자, 밝은 단은 잉크. 전 단 ≥ 4.5:1 (아래 실측). 사다리가 L*≈50 부근을
     비켜 가는 것은 의도다 — 그 명도대는 흰 글자·검은 글자 어느 쪽도 4.5:1 에
     못 미치는 사각지대다(이론 최댓값 4.34:1).

     아래 실측은 라이트 지면 --panel(#f2f0ec) 기준이다. 바는 aria-hidden 장식이고
     정보 정본은 범례 텍스트라 세그먼트-지면 대비에 AA(3:1) 를 요구하지 않는다 —
     요구하면 8단이 3:1 안으로 압축돼 인접 ΔL 이 죽는다. 다만 말단이 지면에 녹아
     사라지던 구 결함은 재발 금지이고, 8단 vs panel 1.45:1 이 그 하한이다. */
  --mix-seg-1: #3b3084;     /* L*25.9 · on panel 9.50:1, surface 10.81:1 */
  --mix-seg-2: #4239bc;     /* L*33.4 · on panel 7.26:1 */
  --mix-seg-3: #5147e3;     /* L*40.9 · on panel 5.49:1 (--accent 와 동급 명도) */
  --mix-seg-4: #735fe5;     /* L*48.9 · on panel 4.10:1 */
  --mix-seg-5: #8e78e4;     /* L*56.9 · on panel 3.10:1 */
  --mix-seg-6: #a690e9;     /* L*64.9 · on panel 2.37:1 */
  --mix-seg-7: #bca8f1;     /* L*72.9 · on panel 1.84:1 */
  --mix-seg-8: #cdc9c2;     /* L*81.1 · on panel 1.45:1 — 말단 웜 뉴트럴(C=4) */
  --mix-seg-1-fg: #ffffff;  /* on seg-1, 10.81:1 */
  --mix-seg-2-fg: #ffffff;  /* on seg-2,  8.26:1 */
  --mix-seg-3-fg: #ffffff;  /* on seg-3,  6.25:1 */
  --mix-seg-4-fg: #ffffff;  /* on seg-4,  4.67:1 */
  --mix-seg-5-fg: #1a1815;  /* on seg-5,  5.03:1 */
  --mix-seg-6-fg: #1a1815;  /* on seg-6,  6.57:1 */
  --mix-seg-7-fg: #1a1815;  /* on seg-7,  8.44:1 */
  --mix-seg-8-fg: #1a1815;  /* on seg-8, 10.74:1 */

  /* 노출 주목 세기 램프 (조용→조명, P-explore-redesign) — 판정/방향색 아님, 서수 인코딩.
     앰버 = 주목받는 중(월상 원반 은유). 색은 보조, 텍스트 라벨이 정본. */
  /* [W-PR2 재파생 완료] --lit-quiet 만은 의미 hue 가 아니라 중립 회색이라 웜 지면
     위에서 푸른 기로 읽혔다. 램프의 **서수 역할을 보존하려고 상대 휘도를 고정**하고
     hue 만 옮겼다: 구 #cfd4dc 의 Y=0.6552 → 신 #d9d3c8 의 Y=0.6551(차 0.0001).
     밝기가 같으므로 quiet < mid < lit 순서가 값으로 흔들리지 않는다.
     --lit-mid/--lit-lit 는 의미 hue(인디고·앰버)라 재파생 대상이 아니다 — 불변. */
  --lit-quiet: #d9d3c8;   /* 주목 전 — 웜 뉴트럴. Y=0.6551 (구 zinc #cfd4dc Y=0.6552 동일) */
  --lit-mid:   #8ea0d8;   /* 보통 — 옅은 인디고 (accent 계열, 저채도) */
  --lit-lit:   #f79009;   /* 주목받는 중 — 앰버 */

  /* 파급 방향색 (수혜/피해) — style.css `.delta-benefit/.delta-harm .delta-num`.
     값의 단일 진실 원천은 web/palette.py 의 DIRECTION 이고 여기 라이트 2값은 그
     **사본**이다. 사본을 두는 이유: palette.css(=palette.py 산출물)는 어드민 전용
     링크라 공개면 <head> 에 없어서 공개 카드 34장에서 이 두 변수가 정의되지 않은
     채로 있었다(빈 var() -> 상속색으로 조용히 렌더. 실측: computed color 가 부모와
     동일). palette.css 를 공개면에 링크하는 대안은 다크를 못 푼다 — admin.css 는
     prefers-color-scheme 블록이 0개라 palette.py 는 애초에 라이트 전용이고,
     그 값을 다크 지면에 그대로 쓰면 2.80~3.66:1 로 AA 미달이다.
     그래서 라이트만 사본(정합)이고 **다크는 사본이 아니라 신규 값**이다.
     사본 잠금: tests/test_web_assets.py 가 palette.py DIRECTION 과 대조한다.
     --color-direction-neutral 은 공개면이 안 읽으므로 일부러 안 옮긴다(사본 최소화).
     DESIGN.md §2 "방향색(--color-direction-*)는 의미 채널이므로 역할 불변,
     값만 새 뉴트럴 위 대비 재실측". 아래가 그 재실측이다. */
  /* [W-PR2] 값 불변, 웜 지면 위 대비만 재실측(아래 수치가 새 --bg/--panel 기준이다). */
  --color-direction-plus:  #c0392b;  /* 수혜 — 5.44:1 on surface, 5.08:1 on bg, 4.78:1 on panel */
  --color-direction-minus: #2563a8;  /* 피해 — 6.12:1 on surface, 5.72:1 on bg, 5.38:1 on panel */

  /* 주가 부호색 (오름/내림) — style.css `.attn-move.is-up/.is-down`.
     [주가 인라인 2026-09-14 Kevin] 종목 행에 붙는 주가 변화는 **숫자의 부호**를
     칠한다. 위 방향색은 다른 채널이다: 수혜/피해는 이벤트가 사업에 닿는 방향의
     판정이고, 부호는 그 뒤 실제로 어느 쪽으로 움직였는지다. 둘은 어긋날 수 있어
     (피해 판정 종목이 오르는 경우) 방향색을 그대로 쓰면 "피해인데 빨강"으로
     읽힌다. 그래서 이름을 갈라 둔다.
     값은 **복제하지 않고 var() 로 가리킨다** — 오름=빨강·내림=파랑이라는 한국
     관례가 방향색 값과 마침 같고, 사본을 두면 두 벌이 갈라진다(위 방향색 주석이
     사본 최소화를 말하는 그 이유). var() 는 사용처에서 해석되므로 아래 다크
     블록의 방향색 재정의가 이 별칭에도 그대로 따라온다(다크 재정의 불요). */
  --color-move-up:   var(--color-direction-plus);
  --color-move-down: var(--color-direction-minus);

  /* 판정 상태색 (연결 확인 / 연결 기각) — style.css `.badge-confirmed/.badge-not-confirmed`.
     [W-PR1 2026-09-07] 종전에는 style.css 안 hex 리터럴이었다. 승격은 **값 보존**이다
     (온도 시프트는 W-PR2 소관 — 이 PR 은 시각 diff 0 이 목표).
     방향색·주목 램프와 값이 겹치지 않게 유지한다: 세 채널(파급 방향·서수 주목·판정)이
     한 색을 쓰면 같은 화면에서 서로를 못 가린다. 위 다크 방향색 주석과 같은 논리.
     실측 대비(라이트): 배지는 틴트 면 + 같은 계열 보더 + 텍스트라 텍스트 대비가 기준면이다. */
  --color-verified:        #2e7d32;  /* 4.52:1 on --color-verified-bg */
  --color-verified-bg:     #eaf3ea;
  --color-verified-line:   #cfe3cf;  /* 틴트 면의 보조선(1.35:1 on surface) — 정보 전달 보더가
                                        아니다. 판정은 배지 텍스트로 이중 인코딩된다. */
  --color-unverified:      #c62828;  /* 4.89:1 on --color-unverified-bg */
  --color-unverified-bg:   #faecec;
  --color-unverified-line: #f0d4d4;  /* 1.39:1 on surface — 위와 같은 보조선 */

  /* 상태색 (경고 / 위험). [W-PR1] 종전 panel.css `.sayo-panel` 스코프 전용 리터럴이라
     공개면에는 이 두 역할의 토큰이 아예 없었다(설계 §2.1 D2). 값 보존 승격.
     --color-warn-bg 는 --lit-lit(#f79009)과 **값을 분리한다**: 주목 램프는 서수 채널,
     경고는 상태 채널이라 한 색이면 두 의미가 한 색으로 뭉개진다.
     --color-danger 는 글자·테두리 전용이다 — 면을 채우면 액센트 CTA 와 경쟁한다. */
  --color-warn-bg:   #fff7e6;  /* --ink 16.62:1, --sub 7.16:1 on this fill (W-PR2 새 잉크 기준) */
  --color-warn-line: #eadfae;
  --color-danger:    #d92d20;  /* 4.83:1 on surface, 4.51:1 on bg, 4.24:1 on panel */

  /* 그림자 -- 층은 3개뿐: 지면(0) / 카드(1층) / 떠 있는 크롬(raised).
     카드 그림자는 장식이 아니라 보더의 보조라 1층으로 줄인다 (2층 -> 1층). */
  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.04);
  --shadow-raised: 0 4px 6px -2px rgba(0, 0, 0, 0.05),
                   0 12px 16px -4px rgba(0, 0, 0, 0.10);

  /* ── 질감·입체 층 (UI-TACTILE 파일럿 2026-09-14, Kevin 지시) ────────────────
     "우리 ui 컴포넌트들을 좀 더 트렌디하게 살짝 입체감+질감 있는 형태로".
     **파일럿이다** - 실견 판단용이고 머지·배포 대상이 아니다. 판정이 기각이면
     이 블록과 style.css 의 `[UI-TACTILE]` 블록만 통째로 지우면 원복이다
     (기존 --shadow-card·--shadow-raised 는 한 줄도 안 고쳤다).

     광원은 **한 화면에서 하나**다: 위에서 온다. 그래서 모든 하이라이트는 상단
     1px(--glint), 모든 안쪽 그늘은 하단 1px(--contour), 모든 드롭 섀도는 아래로
     떨어진다(--elev-*). 이 세 규칙을 어기는 신규 선언은 이 파일럿의 실패다 -
     재질이 화면 안에서 제각각 섞이는 것이 네오모피즘형 실패의 첫 증상이다.

     **왜 rgba 오버레이인가(값이 아니라 층이다).** --sheen 은 지면색을 모르는
     투명 그라디언트다: 위는 흰빛, 아래는 잉크빛. 그래서 **같은 한 토큰**이 흰
     카드 위에서도, accent-soft 틴트 배지 위에서도, 인디고 버튼 위에서도 같은
     물리로 동작한다. 표면마다 다른 hex 를 손으로 파생하면 그 순간 재질이 갈린다
     (Kevin 체크리스트 ④ "광원·재질 일관"). 팔레트가 또 움직여도 이 층은
     **따라갈 것이 없다** - 위 --scroll-shade 류 손파생 rgba 의 함정이 여기엔
     원리적으로 없다.

     **깊이는 역할이 있는 곳에만 준다**(Kevin: "모든 카드·입력창을 같은 강도로
     부풀리지 말 것"). 드롭 섀도(--elev-*)와 눌림(--elev-press)은 **누를 수 있는
     것 전용**이다. 배지처럼 정보만 지는 표면은 --sheen/--glint 까지만 받고
     그림자를 못 받는다 - 그래야 "떠 있으면 누를 수 있다" 가 화면의 규칙으로
     선다. (그림자 **단독**으로 클릭 가능을 말하지도 않는다: 커서·포커스 링·
     라벨이 그대로 정본이고, 입체는 그 위의 보조 신호다.)

     대비 영향 실측(라이트, 인디고 --accent #4f46e5 + 흰 글자 6.29:1 기준):
     --sheen 상단 흰빛 10% 를 얹은 최악점(버튼 맨 윗변)이 5.15:1 로 AA 통과.
     글자는 세로 중앙(그라디언트가 46% 에서 이미 0)이라 실제 글자 대비는 불변.
     하단 잉크빛 3% 는 인디고를 더 어둡게 만들어 흰 글자 대비를 **올린다**.
     다크(--accent #a5b4fc + 잉크 글자 9.85:1)는 하단 검정 10% 합성 후 8.18:1.
     1px 짜리 --glint/--contour 는 면이 아니라 모서리라 글자 대비의 기준면이
     아니다. */
  --glint: rgba(255, 255, 255, 0.55);   /* 상단 1px 반사광 - 흰 서피스에선 안 보이고
                                           틴트·채움 표면에서만 모서리로 선다 */
  --contour: rgba(26, 24, 21, 0.10);    /* 하단 1px 안쪽 그늘 - --ink #1a1815 파생 */
  --sheen: linear-gradient(180deg,
           rgba(255, 255, 255, 0.10) 0%,
           rgba(255, 255, 255, 0) 46%,
           rgba(26, 24, 21, 0.03) 100%);
  /* 위만 밝히고 **아래를 어둡게 하지 않는** 변형. 20px 짜리 배지 전용이다.
     왜 따로 있는가 - 실측이 강제했다. --sheen 하단 잉크빛 3% 를 판정 배지
     '연결 확인'(--color-verified #2e7d32 on --color-verified-bg #eaf3ea)에 얹으면
     4.52:1 -> **4.25:1 로 AA 미달**이다. 그 배지는 원래 여유가 0.02 밖에 없어서
     어떤 어둡게-하기도 못 받는다. 게다가 20px 높이에서는 세로 그라디언트가
     읽히지도 않는다 - 못 읽히는 장식 때문에 판정색이 AA 를 잃는 거래다.
     위만 밝히면 어두운 글자/밝은 면 조합에서 대비는 **전 구간 오른다**
     (연결 확인 4.52 -> 4.61, 연결 기각 4.89 -> 5.00, 틴트 6.97 -> 7.09). */
  --sheen-lit: linear-gradient(180deg,
               rgba(255, 255, 255, 0.14) 0%,
               rgba(255, 255, 255, 0) 100%);
  /* 2층 합성: 근거리 접촉 그림자(진하고 좁게) + 원거리 환경 그림자(옅고 넓게).
     한 층짜리 그림자는 스티커처럼 붙어 보이고, 두 층이라야 종이가 지면에서
     떠 있는 것으로 읽힌다. */
  --elev-1: 0 1px 2px rgba(26, 24, 21, 0.05),
            0 2px 5px -2px rgba(26, 24, 21, 0.06);
  --elev-2: 0 1px 2px rgba(26, 24, 21, 0.05),
            0 5px 12px -3px rgba(26, 24, 21, 0.08);
  --elev-3: 0 2px 4px -1px rgba(26, 24, 21, 0.05),
            0 12px 28px -8px rgba(26, 24, 21, 0.12);
  /* 눌림 = 드롭 섀도를 **인셋으로 뒤집는다**. translateY 만으로 내리면 표면이
     여전히 떠 있어서 "움직였다" 이지 "눌렸다" 가 아니다. */
  --elev-press: inset 0 1px 3px rgba(26, 24, 21, 0.14);
  /* 초저농도 그레인. feTurbulence 한 장을 타일로 깐다(외부 파일 0·요청 0·
     의존성 0·약 350 B).

     **세기를 SVG 안 `opacity` 에 굽는다**(별도 --grain-opacity 토큰이 아니라).
     소비가 `background-image` 레이어이기 때문이다 - 배경 레이어에는 레이어별
     불투명도가 없다. 그리고 배경으로 까는 것이 이 파일럿의 하드 요구를 **구조로**
     지킨다: 배경은 정의상 그 요소의 본문 글자 **뒤**에 그려진다. 겹침 순서를
     z-index 로 손으로 세우는 pseudo-element 오버레이 방식은 (a) 한 줄만 지워도
     노이즈가 글리프 위로 올라오고 (b) `inset:0` 절대 오버레이라
     tests/test_web_stretched_link_scope.py 의 클릭 납치 가드에 정면으로 걸린다.
     배경 레이어는 둘 다 원리적으로 못 일어난다.
     (첫 구현이 실제로 그 가드를 빨강으로 만들었다 - 가드가 옳았다.) */
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23g)' opacity='0.045'/%3E%3C/svg%3E");
  /* [손파생 rgba — 팔레트 변경 때 반드시 손으로 재파생] 아래 --scroll-shade 와
     --glass-bg/--glass-border 는 rgba 리터럴이라 var() 로 팔레트를 못 따라간다.
     다크 블록에도 같은 3종이 있고(아래), 패널에는 --sp-ring 이 같은 클래스다
     (web/static/panel/panel.css). 지면·잉크 값을 바꾸는 PR(W-PR2)이 이 넷을
     빠뜨리면 새 지면 위에 옛 색조의 크롬이 남는다 — 증상이 조용하다. */
  --scroll-shade: rgba(26, 24, 21, 0.14);  /* 표 가로 스크롤 섀도 -- --ink #1a1815 파생 (W-PR2 재파생) */

  /* 글래스 (R7B) -- 떠 있는 크롬 전용: sticky 내비 바 / Ask Sayo 런처 / 홈 가치 바.
     본문·카드 서피스에 사용 금지. 적용은 @supports (backdrop-filter) 안에서만 --
     미지원 브라우저는 불투명 기본값이 그대로 남는다 (fail-open).
     prefers-reduced-transparency 오버라이드는 style.css 크롬 블록이 담당. */
  --glass-bg: rgba(255, 255, 255, 0.72);    /* --surface #ffffff 파생 (손파생, 위 주석) — surface 불변이라 값 유지 */
  --glass-border: rgba(26, 24, 21, 0.08);   /* --ink #1a1815 파생 (손파생, 위 주석 — W-PR2 재파생) */
  --glass-blur: 14px;
  --glass-saturate: 160%;

  /* 라운딩 -- shadcn --radius 10px 기준 재파생.
     --radius-pill 은 **인터랙티브 전용**(검색창·필터 버튼·헤더 pill).
     정보성 배지에 필 금지 (DESIGN.md §3.4). */
  --radius-xs: 4px;    /* 포커스 링·미니 요소 */
  --radius-sm: 6px;    /* 배지·인풋·인라인 블록 */
  --radius-md: 8px;    /* 버튼·패널·팝오버 */
  --radius-lg: 10px;   /* 카드 */
  --radius-pill: 999px;

  /* 간격 -- 4px 베이스 스케일 */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;

  /* 타이포 -- Pretendard 단일 패밀리 (명조 폐기) */
  --sans: "Pretendard Variable", "Apple SD Gothic Neo", "Noto Sans KR",
          "Malgun Gothic", -apple-system, "Segoe UI", sans-serif;
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* 스크롤 리빌 모션 토큰 (W-PR14, landing-v2-design-20260909/v2-mobile-first.md
     §6.1/§6.2) -- "지금 살펴볼 사건" 우측 행 + "둘러보기" 타일 전용 IntersectionObserver
     reveal 이 쓰는 이징·거리·시간 3종. 히어로 절(첫 화면)은 이 토큰의 소비 대상이
     아니다(하드 요구: 첫 화면 무모션). 외부 모션 라이브러리(AOS 등) 디폴트를
     그대로 베끼지 않으려고 자체 값으로 둔다 -- 6원칙 ⑥ 역방향 함정.
     네임스페이스 `--motion-reveal-*` 로 한정: W-PR4(모션 토큰 트랙)가 착지하면
     그쪽 체계로 흡수한다는 전제이고, 그 전까지는 이 이름만 유효하다. */
  --motion-reveal-duration: 360ms;
  --motion-reveal-ease: cubic-bezier(.22, .61, .36, 1);
  --motion-reveal-distance: 12px;

  /* 활자 하한 (M-6, 2026-09-11) -- AUDIT.md §M-6(mobile-audit-20260910)
     P2-1/P2-2/P2-4. 메타·라벨·캡션류가 컴포넌트마다 따로 11px/10px/9px 로
     흩어져 있었다(C4 "활자 하한이 컴포넌트마다 따로 정해진다") -- 여기 하나로
     모아 토큰화한다. 소비처는 style.css/app.css EOF 재선언 블록과
     web/charts.py(파이썬 문자열이라 CSS 로 못 잡는 SVG font-size 속성).
     --fs-meta -- 표·라벨·캡션 등 "메타" 활자의 하한. 오라클(19면×360px 최소
     폰트 ≥12px, sup 제외)과 값을 맞춘다.
     --fs-caption -- 각주 마커(`sup`) 전용. 오라클이 sup 를 명시적으로 뺀
     이유가 곧 이 값의 존재 이유다: 위첨자를 12px 까지 올리면 본문과 거의
     같은 크기가 돼 "각주"라는 시각 신호가 사라진다(구현 전 실측 -- .cite >
     sup 가 .7em, 14px 본문 기준 9.8px). 11px 로 승격해 가독은 올리되 본문보다
     확실히 작게 유지한다. */
  --fs-meta: 12px;
  --fs-caption: 11px;

  /* -- shadcn/ui semantic aliases (PR-1, render-invariant) --
     These are ALIASES over the sayo tokens above, not new values. Every one
     resolves through var() to the existing token, so the dark and print
     overrides further down keep working untouched: var() is resolved at use
     time, so redefining --bg in the dark block also moves --background.
     Do NOT restate these aliases inside the dark or print blocks -- that would
     duplicate the value and let the two copies drift apart.

     Naming note -- --accent is NOT introduced here. sayo already defines
     --accent (indigo, 170 call sites) and it carries what shadcn calls
     "primary", which is why --primary aliases it. shadcn's own --accent role
     (a subtle hover/active surface tint) has no counterpart yet; introducing
     it would mean redefining the existing --accent and repainting every one of
     those call sites, so it is deferred to the follow-up visual PR.
     --destructive is likewise omitted: the public stylesheets carry no
     dominant destructive value to derive it from, only two one-off literals.

     v2 note (2026-09-05) -- --primary / --primary-hover / --primary-foreground
     are back HERE as aliases. PR-2 had split them out into real ink values
     restated in the dark and print blocks ("indigo = information, ink =
     action"); Kevin reverted that call -- the whole product read monochrome.
     Aliasing is deliberately the safer shape: a restated value has to be kept
     in sync across three blocks (:root / dark / print) and the dark copy is
     exactly the one a screenshot pass forgets to look at. As aliases they
     resolve at use time, so the dark and print --accent overrides move them
     for free. Do NOT restate these three below. */
  --background: var(--bg);
  --foreground: var(--ink);
  --muted: var(--panel);
  --muted-foreground: var(--sub);
  --border: var(--line);
  --ring: var(--accent);
  --radius: var(--radius-sm);
  --primary: var(--accent);
  --primary-hover: var(--accent-strong);
  --primary-foreground: var(--accent-ink);
}

@media (prefers-color-scheme: dark) {
  :root {
    /* 네이비 틴트 다크 폐기 -> 무채 심연 -> [W-PR2] 웜 심연 -> [2026-09-12 배경 원복]
       --bg/--panel 만 무채 심연으로 되돌린다(라이트와 같은 이유, 위 주석 참조).
       --surface 는 카드 서피스라 범위 밖 — 웜 값 유지(다크 카드가 원복된 다크
       지면보다 살짝 따뜻하게 남는 것은 알려진 이음매, PR 본문에 기록). */
    --bg: #09090b;
    --surface: #151310;
    --panel: #1b1b1f;
    /* [W-PR2 재파생] 라이트 --lit-quiet 과 같은 규율: 서수 보존을 위해 상대 휘도 고정.
       구 #3a4358 Y=0.0562 → 신 #4b4232 Y=0.0562(동일). hue 만 웜으로. */
    --lit-quiet: #4b4232;   /* 주목 전 — 어두운 웜 뉴트럴. Y=0.0562 (구 #3a4358 과 동일) */
    --lit-mid:   #5a6bb0;   /* 보통 — 인디고 */
    --lit-lit:   #f5a623;   /* 주목받는 중 — 앰버(다크 대비 상향) */
    /* 방향색 다크 — palette.py 사본이 아니다(그쪽은 라이트 전용). 라이트 실값을
       그대로 쓰면 최악 2.80:1(minus on panel)로 AA 미달이라 색상(hue)은 지키고
       명도만 올려 재파생했다. .badge-confirmed/.rs-role-* 선례와 같은 톤대이되
       값은 겹치지 않게 뒀다 — #ef9a9a 는 이미 '미검증' 의미라 방향색이 같은 색을
       쓰면 두 의미 채널이 한 색이 된다. */
    --color-direction-plus:  #e8897c;  /* 수혜 — 7.33:1 on surface, 7.77:1 on bg, 6.85:1 on panel (W-PR2 재실측) */
    --color-direction-minus: #7fb3e8;  /* 피해 — 8.40:1 on surface, 8.90:1 on bg, 7.85:1 on panel (W-PR2 재실측) */
    /* 판정·상태색 다크 — style.css/panel.css 다크 리터럴을 값 보존 승격(W-PR1).
       면은 알파 틴트라 지면 위에서 합성된다. 아래 실측은 합성 후 텍스트 대비다. */
    --color-verified:        #81c784;  /* 7.43:1 on surface, 8.10:1 on bg, 6.84:1 on panel (새 지면 합성 후) */
    --color-verified-bg:     rgba(129, 199, 132, 0.12);
    --color-verified-line:   rgba(129, 199, 132, 0.28);
    --color-unverified:      #ef9a9a;  /* 7.06:1 on surface, 7.68:1 on bg, 6.49:1 on panel (새 지면 합성 후) */
    --color-unverified-bg:   rgba(239, 154, 154, 0.12);
    --color-unverified-line: rgba(239, 154, 154, 0.28);
    --color-warn-bg:   #2a2410;  /* --ink 13.59:1, --sub 6.13:1 on this fill (W-PR2 새 잉크 기준) */
    --color-warn-line: #4a3f14;
    --color-danger:    #f97066;  /* 6.66:1 on surface, 7.05:1 on bg, 6.22:1 on panel */
    /* --paper 는 재정의하지 않음 (차트 지면은 다크에서도 흰색 유지) */
    --ink: #f2f0ed;          /* 웜 화이트. 16.30:1 on surface, 17.27:1 on bg, 15.23:1 on panel */
    --sub: #a8a29e;          /* 7.35:1 on surface, 7.79:1 on bg, 6.87:1 on panel */
    --line: #29251f;
    --line-strong: #423d35;
    --accent: #a5b4fc;       /* 9.85:1 on bg, 9.30:1 on surface, 8.69:1 on panel (W-PR2 재실측) */
    --accent-strong: #c7d2fe;
    --accent-soft: #26264a;  /* accent 텍스트 7.22:1 위에서 통과 (틴트 면이라 지면 시프트 무관) */
    --accent-ink: #0d0b09;   /* on accent, 9.85:1 — --bg 파생이라 웜 시프트를 따라간다 */
    /* 구성 세그먼트 사다리 다크 — 라이트의 **반전**이다. 라이트는 어두운 인디고
       →밝은 쪽으로 가고, 다크는 밝은 인디고→어두운 쪽으로 간다: 두 모드 모두
       "1단이 지면에서 가장 멀고 말단이 지면에 가장 가깝다"는 같은 규칙이다.
       라이트 값을 그대로 흘리면 1단(L*25.9)이 웜 심연(--panel L*9.5) 위에서
       사실상 사라진다 — 그래서 이 블록의 존재 자체가 계약이고,
       tests/test_web_assets.py 가 다크 오버라이드 부재를 red 로 잡는다.
       실측 지면 = 웜 심연 --panel #1d1a17 / --surface #151310 / --bg #0d0b09.
       인접 단 ΔL* ≥ 7.9, 인접 대비비 ≥ 1.268 (라이트와 같은 하한). */
    --mix-seg-1: #d1c1f8;    /* L*81.0 · on panel 10.47:1, surface 11.21:1 */
    --mix-seg-2: #bca8f1;    /* L*72.9 · on panel 8.26:1 */
    --mix-seg-3: #a690ec;    /* L*65.0 · on panel 6.45:1 */
    --mix-seg-4: #8e77eb;    /* L*57.0 · on panel 4.94:1 */
    --mix-seg-5: #725fe8;    /* L*49.0 · on panel 3.73:1 */
    --mix-seg-6: #5147e3;    /* L*40.9 · on panel 2.77:1 */
    --mix-seg-7: #4038bb;    /* L*32.9 · on panel 2.06:1 */
    --mix-seg-8: #3e3b35;    /* L*25.0 · on panel 1.55:1 — 말단 웜 뉴트럴(C=4) */
    --mix-seg-1-fg: #0d0b09; /* on seg-1, 11.88:1 */
    --mix-seg-2-fg: #0d0b09; /* on seg-2,  9.36:1 */
    --mix-seg-3-fg: #0d0b09; /* on seg-3,  7.32:1 */
    --mix-seg-4-fg: #0d0b09; /* on seg-4,  5.60:1 */
    --mix-seg-5-fg: #ffffff; /* on seg-5,  4.65:1 */
    --mix-seg-6-fg: #ffffff; /* on seg-6,  6.25:1 */
    --mix-seg-7-fg: #ffffff; /* on seg-7,  8.40:1 */
    --mix-seg-8-fg: #ffffff; /* on seg-8, 11.16:1 */
    /* --primary 3종은 재정의하지 않는다 -- :root 에서 --accent 별칭이라
       위 --accent 재정의를 자동으로 따라간다(다크 CTA = 라이트 인디고 반전). */
    --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.45);
    --shadow-raised: 0 4px 6px -2px rgba(0, 0, 0, 0.4),
                     0 12px 16px -4px rgba(0, 0, 0, 0.5);
    /* ── 질감·입체 층 다크 (UI-TACTILE 파일럿) ────────────────────────────────
       발주 하드 요구: "다크는 그림자 대신 표면 밝기 층으로". 심연(--bg #09090b)
       위에서 드롭 섀도는 원리적으로 안 보인다 - 검정 위의 검정이다. 그래서 다크
       에서 깊이를 지는 것은 그림자가 아니라 **--glint**(상단 모서리가 지면보다
       밝다)와 --sheen 상단의 밝기 층이고, 드롭 섀도는 경계 분리용 보조로만
       남는다(라이트보다 알파를 훨씬 올려야 겨우 보이므로 값이 크다).
       --contour 는 반대로 짙어진다: 밝은 심연이 없으니 아래 모서리는 순검정으로
       눌러야 모서리로 읽힌다.
       광원 방향은 라이트와 **같다**(위). 모드가 바뀌어도 물리는 안 바뀐다. */
    --glint: rgba(242, 240, 237, 0.10);   /* --ink #f2f0ed 파생 */
    --contour: rgba(0, 0, 0, 0.40);
    --sheen: linear-gradient(180deg,
             rgba(242, 240, 237, 0.045) 0%,
             rgba(242, 240, 237, 0) 46%,
             rgba(0, 0, 0, 0.10) 100%);
    /* 다크 배지 면은 알파 틴트라 지면 위에서 합성된 뒤 대비가 정해진다. 밝기층을
       얹으면 **면이 밝아져 밝은 글자와의 대비가 내려간다**(라이트와 부호가 반대).
       그래서 라이트의 0.14 를 그대로 쓰지 않고 0.035 로 낮춘다 - 실측:
       연결 확인 7.43 -> 6.76, 연결 기각 7.06 -> 6.43, 틴트 7.22 -> 6.57.
       전부 AA(4.5:1) 대비 여유가 크다. */
    --sheen-lit: linear-gradient(180deg,
                 rgba(242, 240, 237, 0.035) 0%,
                 rgba(242, 240, 237, 0) 100%);
    --elev-1: 0 1px 2px rgba(0, 0, 0, 0.50),
              0 2px 5px -2px rgba(0, 0, 0, 0.45);
    --elev-2: 0 1px 2px rgba(0, 0, 0, 0.50),
              0 5px 12px -3px rgba(0, 0, 0, 0.50);
    --elev-3: 0 2px 4px -1px rgba(0, 0, 0, 0.50),
              0 14px 30px -8px rgba(0, 0, 0, 0.60);
    --elev-press: inset 0 1px 3px rgba(0, 0, 0, 0.50);
    /* 그레인은 다크에서 더 조용해야 한다 - 어두운 면 위의 밝은 점은 같은 알파도
       더 크게 튄다(라이트의 어두운 점보다 상대 대비가 높다). 라이트 0.045 -> 0.028.
       세기가 SVG 안에 구워져 있으므로 **URI 전체가 다크 사본**이다: 이 줄을
       빠뜨리면 라이트 세기가 심연 위로 그대로 샌다(tests/test_ui_tactile_pilot.py). */
    --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23g)' opacity='0.028'/%3E%3C/svg%3E");
    /* 아래 셋은 rgba 라 var() 로 팔레트를 못 따라간다 -- 새 뉴트럴에서 손으로 재파생.
       (구 값은 네이비 #0d1220·#e6e9f0 파생이라 무채 지면 위에 파란 크롬이 떴다.
        W-PR2 에서 zinc 파생 → 웜 스톤 파생으로 한 번 더 옮겼다.) */
    --scroll-shade: rgba(242, 240, 237, 0.16);  /* --ink #f2f0ed 파생 (W-PR2 재파생, --ink 원복 범위 밖이라 무접촉) */
    --glass-bg: rgba(9, 9, 11, 0.66);           /* --bg #09090b 파생 -- [2026-09-12 배경 원복] --bg 를 따라 손 재파생 */
    --glass-border: rgba(242, 240, 237, 0.10);  /* --ink #f2f0ed 파생 (W-PR2 재파생, --ink 원복 범위 밖이라 무접촉) */
  }
}

/* 인쇄 = 라이트 고정 (구 style.css @media print :root 이관) */
@media print {
  :root {
    color-scheme: light;
    /* [W-PR2] 라이트 실값의 사본이다 — :root 를 옮기면 여기도 같이 옮겨야 한다.
       (사본을 안 옮기면 화면만 웜이고 인쇄물만 zinc 로 남는다.)
       [2026-09-12 배경 원복] --bg/--panel 만 위 :root 원복을 따라간다. */
    --bg: #f7f7f8; --surface: #ffffff; --panel: #f1f1f3; --paper: #ffffff;
    --ink: #1a1815; --sub: #57534e;
    --line: #e8e5df; --line-strong: #d8d3ca;
    --accent: #4f46e5; --accent-strong: #4338ca;
    --accent-soft: #eef0fe; --accent-ink: #ffffff;
    /* --primary 3종은 재정의 금지 (:root 별칭이 위 --accent 를 따라간다). */
    /* 구성 세그먼트 사다리 = 라이트 실값 사본. 지면·액센트와 **같은 이유**로
       여기 적는다: 위에서 --panel 을 라이트로 되돌리므로, 다크 사다리가 그대로
       남으면 "다크모드로 인쇄" 시 흰 지면 위에 다크 말단(#3e3b35)이 찍힌다.
       (판정·상태색을 일부러 안 적는 것과 갈리는 지점 — 그쪽은 승격 전 동작
       보존이 목적이었고, 이쪽은 신규라 보존할 구 동작이 없다.)
       사본 잠금: tests/test_web_assets.py 가 :root 값과 대조한다. */
    --mix-seg-1: #3b3084; --mix-seg-2: #4239bc;
    --mix-seg-3: #5147e3; --mix-seg-4: #735fe5;
    --mix-seg-5: #8e78e4; --mix-seg-6: #a690e9;
    --mix-seg-7: #bca8f1; --mix-seg-8: #cdc9c2;
    --mix-seg-1-fg: #ffffff; --mix-seg-2-fg: #ffffff;
    --mix-seg-3-fg: #ffffff; --mix-seg-4-fg: #ffffff;
    --mix-seg-5-fg: #1a1815; --mix-seg-6-fg: #1a1815;
    --mix-seg-7-fg: #1a1815; --mix-seg-8-fg: #1a1815;
    /* [UI-TACTILE 파일럿] 질감·입체 층은 인쇄에서 전부 꺼진다. 화면의 깊이는
       빛이지만 종이 위에서는 회색 얼룩일 뿐이고, 그레인은 토너를 먹으면서
       본문 가독만 떨어뜨린다. 라이트 사본을 그대로 흘리면(= 이 블록을 빼면)
       인쇄물에 옅은 회색 그림자와 노이즈가 그대로 찍힌다 - 위 지면·사다리
       사본이 여기 있는 것과 **같은 이유**의 세 번째 사본이다.
       `none` 이 아니라 `0 0 transparent` 인 이유: 소비처가
       `box-shadow: inset ..., var(--elev-1)` 처럼 목록 안에서 합성하는데
       목록 항목에 `none` 이 오면 선언 전체가 무효가 된다(조용한 실패). */
    --glint: transparent; --contour: transparent;
    --sheen: none; --sheen-lit: none;
    --elev-1: 0 0 transparent; --elev-2: 0 0 transparent;
    --elev-3: 0 0 transparent; --elev-press: 0 0 transparent;
    /* `none` 이 여기서는 안전하다 - 소비가 box-shadow 목록이 아니라
       background-image 레이어라 `background-image: none, none` 이 유효하다. */
    --grain: none;
    /* [W-PR1] 판정·상태색(--color-verified/-unverified/-warn/-danger)은 **일부러**
       여기 안 적는다. 승격 전 style.css 리터럴도 print 오버라이드가 없어서
       "다크모드로 인쇄" 시 다크 배지가 그대로 나갔다. 여기에 라이트 값을 적으면
       그 동작이 바뀐다 — 이 PR 은 값 보존 리팩터라 인쇄 동작도 보존한다.
       인쇄 팔레트 정합은 별도 판단거리로 남긴다(승격 이전과 동일 상태). */
  }
}
