/**
 * 콘텐츠 액셀러레이터 — 다크 오버라이드 레이어
 *
 * 규칙 하나: **팔레트는 tokens.css, 이 파일은 셀렉터 오버라이드.**
 *   라이트·다크 팔레트 모두 v2 tokens.css 가 `data-theme` 으로 가른다. 이 파일은 theme.css **뒤에**
 *   실려서 이 앱의 공유 JS·어드민 화면이 박아 둔 색 리터럴을 다크 스코프에서만 중화한다.
 *   → 롤백은 각 페이지의 <link> 한 줄 삭제.
 *
 * 라이트는 1픽셀도 안 변한다:
 *   이 파일의 모든 규칙은 `[data-theme="dark"]` 아래에만 있다.
 *   기본값은 각 페이지의 인라인 가드가 light 로 고정하므로,
 *   저장값이 없는 수강생에겐 이 파일이 한 줄도 적용되지 않는다.
 *
 * 팔레트 출처 (2026-09-13 브랜드 v2 편입): Ryan Brand System v2 tokens.css `[data-theme="dark"]`
 *   (design-tokens.creator-os.app/tokens.css?v=2.1.2). 이 파일은 팔레트를 **정의하지 않는다** —
 *   v2 가 정의하지 않는 앱 전용 토큰 넷(--neutral-bg/--bg-subtle/--brand-soft + --color-border/--text-danger)의
 *   다크값과, 아래 셀렉터 오버라이드(공유 JS 중화 · 어드민 6화면 중화)만 남는다.
 *   타이포 토큰(--display-* 등)은 손대지 않는다:
 *   다크에서만 선언하면 같은 화면이 테마에 따라 다른 크기로 조판된다.
 *
 * ✅ 2026-09-07(S5-2) — 어드민 6화면(admin·consultation·lecturer·index·catalog·home)도
 *    이 레이어를 싣는다. 못 싣던 이유 셋을 각각 이렇게 풀었다:
 *    · Tailwind 색유틸 → tailwind-dark.css 가 토큰으로 리맵한다(S5-1 에서 91종 확장).
 *      🔴 그 파일을 **안 싣는 화면이 둘** — home(tw.css 만) · lecturer(둘 다 없음).
 *         싣게 하면 무조건 블록이 **라이트를 같이 바꾸므로**(실측: home 계열에서
 *         bg-white·text-gray-500 등 9종이 걸린다) 링크를 늘리지 않았다.
 *         home 몫은 아래 「tailwind-dark 미탑재 화면」 절이 다크 스코프로만 준다.
 *    · 색 리터럴 → **값을 안 고치고 여기서 덮는다**(아래 S5-2 절). 라이트 0픽셀.
 *    · 🔴 메일·공지 미리보기 iframe → iframe **안**은 별개 문서라 이 시트가 애초에
 *      안 닿는 것이 보호막이고, 그건 실측으로 확인됐다(17.40~21.00:1). 셸이 비쳐 들면
 *      본문 잉크(#1a1a1a)가 #211d16 위 **대비 1.04** 로 안 보인 채 「비었네」 하고
 *      발송하는 사고가 된다.
 *      🔴 2026-09-07 정정 — 「renderEmailIntoIframe 이 흰 종이를 못 박고 있다」는
 *      **사실이 아니었다.** 그 함수의 `el.style.background='#fff'` 는 **인라인 normal**
 *      선언이라, tailwind-dark.css:61 의 `.bg-white{... !important}`(author !important)
 *      에 **진다**. 다크에서 실제 `#mp-frame` 의 computed 배경은 `rgb(33,29,22)` 였다.
 *      지금 안 터지는 건 iframe **안쪽 문서가 스스로 배경을 칠하기 때문**이지 이 못박기
 *      덕분이 아니다 — 배경 없는 fullDoc 이 한 번만 들어오면 그대로 1.25:1 이 된다.
 *      → 아래 「미리보기 iframe 흰 종이 못박기」 절이 다크 스코프에서 실제로 닫는다.
 *      (JS 쪽을 `setProperty(...,'important')` 로 고치면 **라이트도** 크림 → 흰색으로
 *       바뀐다 — 라이트 0픽셀 불변식을 깨므로 CSS 다크 스코프로만 닫았다.)
 *      tests/test_dark_admin.py 가 iframe 안쪽 불변을 지킨다.
 *
 * ═══ 액센트 역할 분리 규칙 (S1 · 어기면 다크에서 AA 가 깨진다) ═══
 *
 *   **글자엔 --accent-text · 면과 테두리엔 --color-accent.**
 *
 *   왜 둘로 가르나 — 다크에서 같은 바탕 위 대비가 갈린다 (2026-09-06 실측):
 *
 *              바탕            --color-accent   --accent-text
 *     --bg-base      #15130f        4.03            5.34   ✅
 *     --bg-surface   #211d16        3.65            4.83   ✅
 *     --bg-elevated  #262019        3.50            4.64   ✅
 *     --bg-elevated-2 #2a241a       3.34            4.42   ⚠️
 *
 *   면·테두리는 대비 기준이 3:1(비텍스트)이라 --color-accent 로 충분하고,
 *   오히려 --accent-text 로 올리면 버튼·배지가 들뜬다.
 *
 *   🔴 아직 못 넘은 천장 둘 — 「S1 했으니 전부 AA」가 아니다:
 *     1. 가장 높은 면 위 — --bg-elevated-2 에서 4.42:1. 지금 그 조합은 0곳이고
 *        test_accent_text_is_never_placed_on_elevated_2 가 지킨다.
 *     2. 액센트 틴트 배지 바탕 — 이게 더 넓다. 액센트를 낮은 알파로 깐 바탕
 *        (--accent-bg 12% = #35231b, 칩 14~15% = #38241c~#39251c) 위에서
 *        --accent-text 는 4.29~4.15:1 로 여전히 AA 미달이다
 *        (치환 전 --color-accent 는 3.24~3.14 였으니 좋아지긴 했다).
 *        해당하는 곳: announcements `.an-badge`·`.an-cm .coach` ·
 *        challenge-feed `.cf-chip.reels`·`.cf-cm .coach` · private-my `.status-badge.active` ·
 *        home `.rq-chip-n` 계열 · lecture-spot `.ls-chip`.
 *        푸는 길은 둘 — 틴트를 낮추거나(면이 흐려진다) 글자를 한 칸 더 올리거나.
 *        후자면 #da7461 (HSL 명도 +3%) 이 두 천장을 **동시에** 넘는 최소값이다(실측).
 *        SSOT 팔레트를 또 한 칸 이탈하는 일이라 이 단계에선 안 건드렸다.
 *
 *   라이트는 두 토큰이 **같은 값(#9c2618)** 이라 이 분리가 0픽셀이다(theme.css 35·37행).
 *   → 그래서 치환을 라이트 무변화로 할 수 있었다. 이 등식이 깨지면 라이트가
 *     조용히 바뀌므로 test_dark_accent_split.py 가 지키고 있다.
 *
 *   적용 범위 (2026-09-06 · 88곳):
 *     · 수강생 17화면 HTML 67곳 · 공유 JS 11모듈 20곳 · theme.css 의 `a{}` 1곳
 *     · `background:` / `border*:` / `accent-color:` 는 **안 건드렸다**
 *       (학생 화면에 면·테두리 122곳 그대로 남아 있다)
 *
 *   🔴 **토글을 붙이는 단계에서 할 일** — 캐시 버스터(`?v=`) 올리기.
 *      S1 은 공유 JS 11모듈과 theme.css 의 *내용*을 바꿨는데 `?v=` 는 안 올렸다.
 *      지금은 무해하다: 라이트에선 두 토큰이 같은 값이라 낡은 사본이든 새 사본이든
 *      **화면이 같고**, 다크는 아직 켤 길이 없어 아무도 못 본다.
 *      토글이 생기는 순간 이야기가 달라진다 — 캐시에 낡은 모듈이 남은 사람은
 *      다크에서 예전 액센트(3.65:1)를 그대로 본다. 그때 반드시 같이 올릴 것:
 *        theme.css · app-shell · live-render · person-detail · consult-manage ·
 *        consult-scheduler · guest-cta · lecture-spot · materials ·
 *        challenge-submit · waitlist-cta · live-waitlist-cta
 *
 * 남은 빚 — 이 레이어가 아직 못 덮는 것 (2026-09-06 실측 · S2 에서 갱신):
 *    · 수강생 HTML 안의 색 리터럴 97건 (my 34 · grow 20 · challenge-feed 9 …)
 *      → S2 에서 전수로 다시 쟀다. **다크에서 깨지는 것은 0건**이다.
 *        대부분이 `var(--토큰, #폴백)` 이라 토큰이 정의된 지금 폴백이 아예 안 뜨고,
 *        맨 리터럴은 액센트 면 위 흰 글자·검정 비디오 바탕·골드(#b8860b, 5.18:1)다.
 *    · 그 화면들이 싣는 static/js 28모듈 안의 색 리터럴 216건
 *      → 그중 다섯 곳이 실제로 깨졌다. 아래 「S2 공유 JS 중화」에서 덮는다.
 *    · ~~consult-manage/consult-scheduler 의 Tailwind 색유틸 51건~~
 *      ✅ 2026-09-07 갚았다 — tailwind-dark.css(S5-1)가 `hover:text-gray-700`(1.80:1)·
 *        `text-gray-600`(2.45:1) 을 포함해 91종을 다크 스코프로 덮는다.
 *        단 그 파일을 안 싣는 home 은 아래 「tailwind-dark 미탑재 화면」 절이 맡는다.
 *    · live-room-theme.js 는 CSS 로 못 닿는다 — **JS 객체를 벤더 SDK 에 넘긴다**.
 *      그래서 S2 에서 그 파일만 예외로 직접 고쳤다(사유는 그 파일 주석에).
 */

/* ── 팔레트는 이제 v2 tokens.css 가 준다 (2026-09-13 브랜드 v2 편입) ──────────
   `[data-theme="dark"]` 팔레트(배경 4단·텍스트 4단·액센트·시맨틱·보더·그림자·차트·별칭)는
   https://design-tokens.creator-os.app/tokens.css?v=2.1.2 이 정의한다. 예전에 이 파일이
   「SSOT 이탈」로 적어 뒀던 세 가지는 v2 가 흡수했다:
     ① 표면 위계 — v2 다크는 base/surface/elevated/elevated-2 가 네 단으로 갈린다.
     ② 시맨틱 네온 — v2 다크는 sage/amber/slate 저채도다.
     ③ 액센트 버튼 글자 — v2 규칙 «레드 위 글자는 언제나 종이(--on-accent: #faf7ef)». 잉크를 얹지 않는다.
   그래서 여기엔 **v2 가 정의하지 않는 앱 전용 토큰의 다크값**만 남는다.
   인쇄는 여전히 라이트로 — 이 블록은 화면에서만 산다. */
@media screen {

[data-theme="dark"] {
    /* 네이티브 위젯(체크박스·date picker·select 팝업·스크롤바)도 같이 뒤집는다.
       tokens.css 도 같은 값을 주지만, 이 파일이 뒤에 실려 어떤 로컬 시트가 끼어들어도 보증한다. */
    color-scheme: dark;

    /* theme.css 도 tokens.css 도 정의하지 않아 라이트 리터럴로 폴백되던 것 —
       라이트는 폴백 그대로(0픽셀), 다크에서만 값을 준다. */
    --neutral-bg: rgba(240, 235, 224, 0.06);   /* tailwind-dark 의 neutral 배지 바탕 */
    --bg-subtle:  rgba(240, 235, 224, 0.05);   /* my.html 읽기전용 입력·live-render 버튼 */
    --brand-soft: var(--accent-bg);            /* deck-plugin.js */

    /* 🔴 S5-2 — `var(--토큰, 폴백)` 의 폴백이 라이트·다크 양쪽에서 늘 그려지던 둘.
         · --color-border → #e5e7eb : 어두운 면 위 흰 실선 (admin.html `.ocs`·`.ocw-tog`·`.occ-row`)
         · --text-danger  → #dc2626 : 표면 위 3.47:1 (consultation.html 오류 문구)
       값을 여기서 주면 폴백이 안 뜬다. 라이트는 토큰이 여전히 없으므로 리터럴 그대로 = 0픽셀.
       ⚠️ theme.css 에 이 둘을 넣으면 라이트가 바뀐다 — 넣지 말 것. */
    --color-border: var(--border-subtle);
    --text-danger:  var(--color-danger);
}

}   /* @media screen */

/* ── select 화살표 — 색이 SVG data-URI 에 박혀 있다 ──────────────
   theme.css 는 stroke='%23171717'(잉크)로 구운 화살표를 쓴다.
   다크에서 그대로 두면 검은 배경에 검은 화살표가 된다.
   특이도 (0,1,1) > theme.css `html select`(0,0,2) — 순서와 무관하게 이긴다. */
[data-theme="dark"] select:where(:not([multiple], [size])) {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23f0ebe0' stroke-opacity='0.55' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}

/* ── 토글 순간 body 만 늦게 따라오는 것 ──────────────────────────
   theme.css 의 body 는 `transition: background var(--duration-slow) …` 를 단다.
   카드·표·보더에는 transition 이 없어서, 테마를 바꾸면 나머지는 즉시 뒤집히고
   **바탕만 300ms 동안 혼자 넘어온다** — 한 프레임 동안 다크 카드가
   크림 바탕 위에 뜬다.
   ① 다크 쪽에서 아예 끈다(라이트→다크 전환의 after-change style 이 다크다).
   ② 다크→라이트는 after-change 가 라이트라 ①이 안 걸린다 → 토글이 전환 직전
      html 에 `data-theme-switching` 을 붙였다 한 프레임 뒤 떼는 규약으로 막는다.
      지금은 그 속성을 붙이는 코드가 없어 **라이트에서 0픽셀** 변화다. */
[data-theme="dark"] body { transition: none; }
html[data-theme-switching] body,
html[data-theme-switching] body::before { transition: none !important; }


/* ═══════════════════════════════════════════════════════════════════════
   S2 — 공유 JS 중화 (2026-09-06)

   🔴 **여기 있는 규칙들은 부채다.** 색이 JS 안에 값으로 박혀 있어서
      토큰 교체로는 못 뒤집고, 이 시트가 밖에서 덮는 수밖에 없다.
      그 JS 를 고치는 사람에게: 「내가 준 색이 왜 안 먹지」 싶으면 여기다.
      각 규칙에 **대상 파일·줄번호**를 적어 뒀다. JS 쪽을 토큰으로 바꾸면
      여기 짝이 되는 규칙을 같이 지워야 한다(안 지우면 이번엔 이 시트가 이긴다).

   ⚠️ 안 고친 것도 여기 적어 둔다 — 「빠뜨린 것」과 「재 보고 놔둔 것」은 다르다.
      · gold-ticket-share.js `.gts-preview`(#f4f0e6) — **메일 미리보기**다.
        메일 본문은 라이트로 렌더되는 게 맞아서 그대로 둔다(어드민 iframe 과 같은 이유).
      · attach-ui.js:97 / attach-viewer.js:73,112 PDF iframe(#fff) · challenge-feed
        `.cf-ig`(#fff, 인스타 임베드) · `video{background:#000}` — 전부 **콘텐츠**다.
      · vod-player `.vp-modal`·`.vp-stage`·`.vq`(vod-questions) — 검정 극장 안이라
        라이트에서도 같은 색이다. 테마와 무관 → 다크 파손이 아니다(실측: vod-player.js:671
        이 유일한 mount 호출부이고 `#vp-qbox` 는 모달 안이다).
      · live-render.js — 잰 값이 전부 통과다(`#b8860b` 초대 버튼 5.75:1 ·
        `#ff5a4d` 라이브 점은 장식 · `rgba(255,255,255,.16)` 칩은 배너 안).
        아래 `#live-event-banner` 를 고치면 그 칩 글자도 같이 풀린다.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── 웹앱 설치 안내바 — app-shell.js:183 (cssText) · 링크 색은 :188 ────────
   `background: var(--ink)` 다. 다크에서 --ink 는 --text-primary 를 참조하므로
   **크림(#f0ebe0)** 이 된다 — 실측 L=0.833, 지면과 대비 15.88. 어두운 화면 맨 위에
   폭 전체 크림 슬래브가 눕는다.
   그게 의도가 아니라는 증거는 바 안에 있다: 안내 링크가 `#f0c9a0`(:188) 인데
   크림 위에서 **1.3:1** 로 사라진다. 저 살구색은 「잉크 바」를 전제로 고른 색이다.
   → 라이트의 의도(가장 대비 큰 바 + 밝은 글자)를 다크에서도 지키게 **토큰만** 바꾼다.
     `!important` 가 아니라 커스텀 프로퍼티 재정의라 인라인 스타일이 그대로 산다.
     덤으로 #f0c9a0 은 #2a241a 위에서 10.4:1 이 되어 **저절로 풀린다**(규칙 추가 없음).
   ✅ 2026-09-07 갱신 — 여기 있던 「0.16 나빠진 천장」이 **없어졌다.**
      안드로이드 「홈에 추가」 버튼은 액센트 면 위 글자인데, SSOT 이탈 ③ 으로 면이
      #c4503f → #ce6c5e 가 되면서 이 자리도 같이 풀렸다(3.87 → AA 통과).
      «면을 밝히면 목록에 없는 버튼도 자동으로 풀린다» 는 안 A 의 근거가
      실제로 작동한 예다 — 이 버튼은 인라인 style 이라 선택자 목록으로는 못 잡던 자리다. */
[data-theme="dark"] #a2hs-bar {
    --ink: var(--bg-elevated-2);
    /* 글자(--on-accent)는 v2 다크에서도 종이(#faf7ef)라 되돌릴 것이 없다 (2026-09-13) */
}

/* ── 라이브 이벤트 배너 — app-shell.js:344 (cssText) ──────────────────────
   `background:" + accent + ";color:var(--on-accent)`. 예전 다크 팔레트는 --on-accent 가
   잉크(#15130f)라 배너 글자가 1.03:1 로 사라져 여기서 종이색으로 되돌렸었다.
   v2(2026-09-13) 는 라이트·다크 모두 --on-accent = 종이(#faf7ef) — 규칙이 없어도 라이트와 같은
   계약이 그대로 선다. 되돌림 규칙을 걷어냈다(test_dark_shared_js_neutralize 가 v2 값으로 잰다). */

/* ── VOD 미니 플레이어 — vod-player.js:106 ────────────────────────────────
   `.vp-float{background:#000}` 이 지면(#15130f)과 **1.11:1**(실측),
   그 안 `.vp-fbar{background:#111}`(:112)은 **1.00:1**. 라이트에선 크림 위 검정
   상자라 저절로 떠 있었는데, 다크에선 화면 오른쪽 아래에 **경계 없이 잠긴다**
   — 어디를 잡아 옮기고 어디를 눌러 닫는지가 안 보인다.
   그림자(0 10px 34px rgba(0,0,0,.5))는 어두운 지면 위에서 아무 일도 안 한다.
   → 색을 바꾸지 않는다(영상 레터박스는 검정이 맞다). **테두리 한 겹**만 얹는다.
     border 대신 0-blur box-shadow 라 레이아웃이 1px 도 안 밀린다. */
[data-theme="dark"] .vp-float {
    box-shadow: 0 0 0 1px var(--border-emphasis), 0 10px 34px rgba(0, 0, 0, .5) !important;
}

/* ── 강의 스팟 카드 — lecture-spot.js:31 ──────────────────────────────────
   `.ls{background:#111}` 이 지면과 **1.02:1**(실측). 포스터가 붙기 전(또는 못 불러올 때)
   170px 검정 사각형이 지면에 그대로 녹는다 — 「카드가 하나 있다」는 사실 자체가 안 보인다.
   포스터는 콘텐츠라 안 건드리고, 카드 테두리만 세운다. */
[data-theme="dark"] .ls {
    box-shadow: 0 0 0 1px var(--border-subtle) !important;
}

/* ── 지난 시간 안개 — consult-scheduler.js:48 ─────────────────────────────
   `.cspc-pastfog{background:rgba(244,240,230,.65)}` — **크림 안개**다(실측 L=0.365).
   라이트에선 「지난 시간은 종이색으로 덮는다」가 맞지만, 다크에선 달력 위에
   밝은 띠가 얹힌다. 같은 뜻(지면색으로 덮기)을 다크 지면색으로 옮긴다.
   ⚠️ 이 화면은 코치·어드민 동선이다(학생 4화면이 모듈을 싣기만 한다). 그래도
      싣는 이상 다크에서 뜰 수 있어 한 줄로 막아 둔다. */
[data-theme="dark"] .cspc-pastfog {
    background: color-mix(in srgb, var(--bg-base) 65%, transparent) !important;   /* 지면색 65% — 팔레트가 바뀌어도 따라온다 */
}

/* ── 상단 바 두 개의 아랫선 — app-shell.js:183(그림자) · :344(그림자) ─────────
   둘 다 `box-shadow: … rgba(0,0,0,.28|.14)` 로 자기 경계를 만든다. 검정 번짐은
   크림 지면 위에서만 보인다 — 어두운 지면에선 **아무 일도 안 한다**(실측: 다크에서
   설치바 지면대비 1.21 · 라이브 배너 1.03). 글자는 위에서 이미 풀었으니
   남은 건 「여기까지가 바다」라는 선 하나다. 번짐을 1px 선으로 바꾼다.
   ⚠️ 라이브 배너의 바탕색은 서버가 정하는 값이라 그대로 둔다 — 운영자가 고른 색을
      우리가 덮으면 그 기능이 죽는다. 선만 얹는다. */
[data-theme="dark"] #a2hs-bar,
[data-theme="dark"] #live-event-banner {
    box-shadow: 0 1px 0 var(--border-emphasis) !important;
}

/* ═══════════════════════════════════════════════════════════════════════
   S5-2 — 어드민 6화면 중화 (2026-09-07)

   🔴 **원칙: 파일의 색을 안 고치고 여기서 덮는다.**
      어드민은 라이언이 매일 쓰는 화면이라 «라이트 0픽셀» 이 이 트랙의 최우선
      불변식이다. 리터럴을 토큰으로 바꾸면 라이트도 같이 바뀐다 —
      예: `.ocs{background:#fff}` → `var(--bg-surface)` 는 라이트에서
      #fff → #faf7ef 로 **실제로 변한다**. 그래서 값을 그대로 두고
      다크 스코프에서만 덮는다. 라이트는 이 절 전체가 한 줄도 안 닿는다.

   ⚠️ 여기 규칙들은 S2 절과 같은 **부채**다. 각 규칙에 대상 파일·줄번호를 적었다.
      그 파일을 토큰으로 바꾸는 사람은 여기 짝을 같이 지워라(안 지우면 이 시트가 이긴다).
      tests/test_dark_admin.py 가 «출처 문자열이 아직 그 파일에 있는지» 를 잰다 —
      JS 를 고치고 여기를 안 고치면 그 테스트가 먼저 빨간불이 된다.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── 성과 카드 3종 — admin.html:3054 `.ocs` · :3059 `.ocw-tog` · :3060 눌린 상태 ──
   `.ocs{background:#fff}` 은 다크에서 **상속 잉크(#f0ebe0) on #fff = 1.19:1** 이다
   (실측). 26px·800 짜리 성과 숫자「315개」가 통째로 안 보인다. 부제도 3.48:1.
   `.ocw-tog` 는 흰 알약 버튼 7개(글자 2.64:1), 눌린 상태는 액센트 면 위 흰 글자다.

   🔴 `:not([aria-pressed="true"])` 는 장식이 아니다 — 한 번 밟은 자리다.
      `.ocw-tog` 를 통째로 덮었더니 `!important` 가 눌린 상태의 액센트 면
      (`.ocw-tog[aria-pressed="true"]{background:var(--color-accent…)}`)까지 지워서
      **눌린 버튼이 안 눌린 것처럼** 보였다(실측 1.11:1 — 어두운 면에 어두운 잉크).
      «다크에서 깨진 걸 고치다 멀쩡하던 상태를 깨는» 종류라 조건을 붙였다. */
[data-theme="dark"] .ocs,
[data-theme="dark"] .ocw-tog:not([aria-pressed="true"]) { background: var(--bg-surface) !important; }
[data-theme="dark"] .ocw-tog[aria-pressed="true"] { color: var(--on-accent) !important; }

/* ── 퍼널 해설 상자 — admin.html:1694 `#jny-note` (인라인 style) ────────────
   `style="background:#f8fafc"` + 안쪽 글자가 `text-gray-700`(tailwind-dark 가
   --text-primary 로 바꾼다) → **1.14:1**. 배경 리터럴만 봐서는 «흰 상자» 로 보이고,
   글자가 리맵을 타고 밝아지는 것까지 합쳐야 이 숫자가 나온다.
   인라인 style 은 `!important` 로만 이긴다. */
[data-theme="dark"] #jny-note { background: var(--bg-elevated) !important; }

/* ── 액센트 면 위 흰 글자 ────────────────────────────────────────────────
   🔴 «라이트에서 멀쩡했으니 다크도 되겠지» 가 정확히 뒤집히는 자리다.
      라이트는 #fff on #9c2618 = 7.77:1. 다크는 SSOT 이탈 ③ 으로 면을 밝혔으므로
      (#c4503f → #ce6c5e, «어두운 잉크를 받으려고» 한 조정이다) #fff 를 얹으면
      **3.54:1** 로 떨어진다. --on-accent 로 뒤집으면 5.24:1.
   ① 인라인으로 `color:#fff` 를 박은 것 — 5곳:
        admin-assets-outcomes.js:105·109·112·713 (공개 상태 배지)
        consult-followup.js:106 (최종 승인 버튼)
      두 조건을 **함께** 요구해 다른 액센트 면(글자 없는 막대·이미 --on-accent 를
      쓰는 버튼)은 건드리지 않는다.
   ② Tailwind `.text-white` 로 흰 글자를 얹은 것 — **18곳**(2026-09-07 실측).
      admin.html 10 · deck-plugin.js 4 · admin-vod.js 2 · admin-newsletter-send.js 2.
      어드민의 **주 실행 버튼 가족**이다(가져오기·저장·발행·보내기).
      ⚠️ 이건 실측 목록에 없던 것이다 — 실측이 인라인 `color:#fff` 만 셌다.
      tailwind-dark.css 의 「컬러 면 위 글자」 계약과 같은 판단인데, lecturer 는
      그 파일을 안 실으므로 deck-plugin 몫을 위해 여기(dark.css)에 둔다. */
[data-theme="dark"] [style*="background:var(--color-accent"][style*="color:#fff"],
[data-theme="dark"] .text-white[style*="background:var(--color-accent"] {
    color: var(--on-accent) !important;
}

/* ── 경고 노랑 자족 쌍 — 어두운 카드 위에 눕는 크림 슬래브 ──────────────────
   consult-followup.js:166(화자 게이트 경고) · consultation.html:2845 · :3081.
   박스 **안**은 4.51:1 로 읽히지만, 다크 화면에 밝은 노랑 패치가 통째로 얹힌다.
   맨 `#b45309` 하나만 쓰는 자리도 있다 — consult-followup.js:171
   「승인하면 이미 받으신 N명에게도 고친 내용이 바로 보여요」(면 없이 카드 위 3.34:1).
   되돌리기 어려운 작업을 알리는 문장이라 흐리면 안 된다.
   ⚠️ 색값으로 잡으므로 question.html 의 `var(--color-warning,#b45309)` 폴백에도
      걸리는데, 거기 computed 값이 이미 --color-warning 이라 **결과가 같다**(0픽셀). */
[data-theme="dark"] [style*="#fef3c7"] { background: var(--warning-bg) !important; }
[data-theme="dark"] [style*="#b45309"] { color: var(--color-warning) !important; }

/* ── 면이 사라지는 자리 둘 ───────────────────────────────────────────────
   ① admin-newsletter-send.js:1296 — 점검 실패 알림 상자.
      `background:rgba(156,38,24,.07)` 는 라이트 크림슨 7% 라 어두운 지면에서
      **상자와 카드가 구분되지 않는다**. 안쪽 글자는 `color:var(--color-accent)` 인데
      다크에서 글자에 --color-accent 를 쓰면 3.65:1 이다(S1 규칙: 글자는 --accent-text).
      🔴 그런데 --accent-text 로도 이 틴트 위에선 **4.40:1 로 AA 를 못 넘는다**(실측).
         액센트 틴트 위 액센트 글자라 색이 서로 가깝기 때문이고, dark.css 머리말이
         「아직 못 넘은 천장 ②」로 적어 둔 바로 그 자리다. 여기선 뜻이 이미 «실패 알림»
         이므로 --color-danger 로 간다 — 같은 밴드 안이면서 5.38:1 로 넘는다.
   ② deck-plugin.js:703 — 영상 업로드 진행 막대의 **트랙**.
      `rgba(0,0,0,.1)` 은 어두운 지면에서 안 보여서 «0% 인지 안 그려진 건지» 를
      구분할 수 없다. 채움(--color-accent)만 보이고 남은 길이가 안 보인다.
   둘 다 클래스가 없어 인라인 값으로 잡는다. */
[data-theme="dark"] [style*="background:rgba(156,38,24,.07)"] {
    background: var(--danger-bg) !important;
}
[data-theme="dark"] [style*="background:rgba(156,38,24,.07)"] p {
    color: var(--color-danger) !important;
}
[data-theme="dark"] [style*="background:rgba(0,0,0,.1)"] {
    background: var(--bg-elevated-2) !important;
}

/* ── 어드민 배너 미리보기 — admin-live.js:794 `.evp-banner` ─────────────────
   🔴 미리보기가 **거짓말을 하고 있었다.** 학생 화면 배너(`#live-event-banner`)는
   위에서 --on-accent 를 라이트값으로 되돌리는데, 어드민 미리보기 컨테이너
   (`#evp-body`)는 그 셀렉터를 못 받아 다크 잉크(#15130f)로 그려졌다.
   결과가 **양방향으로 뒤집혔다**(실측): 블루(#1d4ed8) 미리보기 2.77 vs 학생 6.26 ·
   기본 레드 미리보기 5.24 vs 학생 3.31. 「미리보기가 멀쩡하니 발행」이 안 통했다.
   → 학생 배너와 **같은 규칙**을 받게 한다. 이제 미리보기 = 실물이다.
   ✅ v2(2026-09-13) — 다크 --on-accent 가 종이(#faf7ef)라 학생 배너·미리보기 모두 되돌림 규칙이
      필요 없다. 둘 다 tokens.css 값을 그대로 받으므로 «같은 것» 이 저절로 성립한다. */

/* ═══════════════════════════════════════════════════════════════════════
   tailwind-dark 미탑재 화면 — home.html 몫 (2026-09-07)

   🔴 home.html 은 tw.css 는 싣고 **tailwind-dark.css 는 안 싣는다.**
      싣게 하면 그 파일의 「무조건 리맵」 블록이 **라이트까지 바꾼다** — 실측으로
      9종이 걸린다(bg-white·bg-gray-50·bg-amber-50·text-gray-400/500/600·
      text-amber-600·text-red-500/600 · consult-scheduler·consult-manage 가 그린다).
      라이트 0픽셀이 이 트랙의 불변식이므로 링크를 늘리지 않고, **다크 몫만** 여기서 준다.

   왜 `[data-page="home"]` 로 좁히나 — lecturer.html 은 tw.css **조차** 안 실어서
   Tailwind 클래스가 라이트에서 아무 일도 안 한다. 좁히지 않으면 다크에서만
   그 화면에 없던 카드 면이 생겨 «라이트와 다른 화면» 이 된다.

   ⚠️ 값은 tailwind-dark.css 의 같은 클래스와 **글자까지 같아야 한다**(사본이다).
      tests/test_dark_admin.py::test_home_mirror_matches_tailwind_dark 가 대조한다.
      ✅ 근본 해결은 이 사본이 아니라 home.html 의 <link> 를 늘리는 것이다 —
         그때 이 절을 통째로 지운다. 라이트가 같이 바뀌는 걸 감수할지가 판단 지점이고,
         지금은 «감수하지 않는다» 로 정했다.
   ═══════════════════════════════════════════════════════════════════════ */
[data-theme="dark"] [data-page="home"] .bg-white       { background-color: var(--bg-surface) !important; }
[data-theme="dark"] [data-page="home"] .bg-gray-50     { background-color: var(--bg-elevated) !important; }
[data-theme="dark"] [data-page="home"] .bg-amber-50    { background-color: var(--warning-bg) !important; }
[data-theme="dark"] [data-page="home"] .bg-gray-900    { background-color: var(--border-strong) !important; }
[data-theme="dark"] [data-page="home"] .text-gray-600,
[data-theme="dark"] [data-page="home"] .text-gray-500  { color: var(--text-secondary) !important; }
[data-theme="dark"] [data-page="home"] .text-gray-400  { color: var(--text-tertiary) !important; }
[data-theme="dark"] [data-page="home"] .text-gray-300  { color: var(--text-ghost) !important; }
[data-theme="dark"] [data-page="home"] .text-red-500,
[data-theme="dark"] [data-page="home"] .text-red-600   { color: var(--color-danger) !important; }
[data-theme="dark"] [data-page="home"] .text-amber-600,
[data-theme="dark"] [data-page="home"] .text-amber-700 { color: var(--color-warning) !important; }
[data-theme="dark"] [data-page="home"] .border-gray-50 { border-color: var(--border-subtle) !important; }
[data-theme="dark"] [data-page="home"] .border-amber-200 { border-color: rgba(188, 141, 47, 0.28) !important; }
[data-theme="dark"] [data-page="home"] .hover\:text-gray-700:hover { color: var(--text-primary) !important; }

/* ═══════════════════════════════════════════════════════════════════════
   인쇄 — 다크로 보고 있어도 종이는 흰 종이다
   ═══════════════════════════════════════════════════════════════════════
   🔴 2026-09-06 독립 검사 실측: 다크 상태에서 print 로 넘기면 본문 색이
   rgb(240,235,224)(거의 흰색) 그대로 남고 배경만 브라우저 기본으로 빠져
   **흰 종이에 흰 글자**가 된다. 대시보드 CSS 에 @media print 가 0건이었다.

   이 앱엔 실제 인쇄 동선이 있다(상담 자료·자료 페이지). 밝기는 «화면을 보는 방식»
   이지 «문서의 정체»가 아니므로, 인쇄에서는 밝기 선택과 무관하게 라이트로 되돌린다.

   🔴 2026-09-07 — **되돌리는 방식을 바꿨다.** 여기서 토큰을 손으로 나열하고 있었는데
   14개만 적혀 있어 **39개가 다크값 그대로 종이에 나갔다**(독립 검사 실측). 나열은
   구조적으로 어긋난다 — 다크에 토큰을 하나 더할 때마다 여기도 고쳐야 하는데 그걸
   강제할 방법이 없다. 이제 팔레트 정의 자체가 `@media screen` 안에 있어서
   **인쇄에는 애초에 안 먹고** theme.css 의 라이트 값이 그대로 살아난다.
   → 여기 남는 건 토큰을 안 타는 자리 못박기 하나뿐이다. */
@media print {
    /* 지면·본문은 토큰을 안 타는 곳이 있어 못 박는다 */
    [data-theme="dark"] body {
        background: #f4f0e6 !important;
        color: #171717 !important;
    }
}

/* ── 미리보기 iframe 흰 종이 못박기 ────────────────────────────────────────
   🔴 2026-09-07 독립 검사가 잡았다. 「renderEmailIntoIframe 이 흰 종이를 못 박는다」는
   머리말의 약속이 **실제로는 지켜지지 않고 있었다** — `el.style.background='#fff'` 는
   인라인 **normal** 이라 `.bg-white{... !important}`(author !important)에 진다.
   다크에서 실측한 `#mp-frame` computed 배경이 `rgb(33,29,22)` 였다.

   지금 사고가 안 난 건 iframe **안쪽 문서가 스스로 배경을 칠하기 때문**이다
   (fragment·self-styled 는 리셋이 `body{background:#fff}`, fullDoc 은 서버 템플릿이
   `body style="...background:{PAPER}"`, 자료 미리보기는 BASE_CSS 가 `#f4f0e6`).
   배경 없는 fullDoc 이 한 번만 들어오면 검정 잉크 on #211d16 = **1.25:1** 이다.
   그게 정확히 머리말이 막겠다던 「비었네 하고 발송」 사고다.

   ⚠️ 라이트는 안 건드린다. 라이트에서도 `.bg-white` 가 이 자리를 크림(#faf7ef)으로
      바꾸고 있었고, JS 를 `setProperty(...,'important')` 로 고치면 **라이트가 크림 →
      흰색으로 변한다**(0픽셀 불변식 위반). 그래서 다크 스코프에서만 닫는다.
   ⚠️ 특이도로 이긴다 — `[data-theme="dark"] #id` (1,1,0) > `.bg-white` (0,1,0).
      id 가 없는 admin-vod.js 의 발송본 미리보기는 `iframe.bg-white` 로 잡는다.

   보내질 것을 보여주는 자리 전부: 메일(mp) · 공지 메일(bcp) · 뉴스레터(nl-pv) ·
   코스 메일(ca-pv) · 자료(lm-preview, 인라인 `background:var(--on-accent)` 라
   다크에서 #15130f 였다) · VOD 발송본(id 없음). */
[data-theme="dark"] #mp-frame,
[data-theme="dark"] #bcp-email-frame,
[data-theme="dark"] #nl-pv-frame,
[data-theme="dark"] #ca-pv-frame,
[data-theme="dark"] #lm-preview-frame,
[data-theme="dark"] iframe.bg-white { background: #fff !important; }
