/* ── Liquid Glass 실험 레이어 (iOS 26 감도) ──────────────────────────────────
   app.css 뒤에 로드한다. **`html[data-glass="on"]` 일 때만** 규칙이 붙으므로,
   이 파일을 링크만 해두고 토글을 끄면 기존 화면과 픽셀 단위로 동일하다.
   → 2026-08-19: 실서비스 **9장 전부**가 이 파일을 링크한다(처음엔 /glasslab 만, 그다음
     매크로 메인 `/` 만이었다). 되돌리기 = 해당 html 의 두 줄(css/js) 삭제.

   재료의 정체 — iOS 26 의 "리퀴드 글래스"는 반투명 + 흐림만이 아니다. 네 겹이다:
     ① 매질(medium)   backdrop-filter: blur + saturate  → 뒤가 번지고 색이 살짝 진해짐
     ② 틴트(tint)     반투명 흰/검 채움                → 글자 대비를 지키는 바닥
     ③ 림(rim)        위쪽 밝은 1px + 아래쪽 어두운 1px → 유리 두께(굴절)의 인상
     ④ 스페큘러       포인터를 따라 미끄러지는 하이라이트 → "젖은 유리" 느낌의 정체
   ①②③ 은 정적이고, ④ 만 JS 가 --gx/--gy 를 갱신한다(CSSOM 이라 CSP 무관).

   ⚠️ 가장 중요한 한 가지 — **뒤에 아무것도 없으면 유리는 보이지 않는다.**
      blur 는 뒤 픽셀을 섞는 필터라, 평면 단색(--page) 위에 얹으면 같은 단색이 나온다.
      즉 유리를 쓸 자리는 "무언가 위에 떠 있는 것"뿐이다 — 레일 · 가장자리 손잡이 ·
      스티키 헤더 · 모달 · 툴팁. 카드를 유리로 만들면 비용만 내고 아무 일도 안 난다.
      /glasslab 이 이 둘을 나란히 보여준다.

   접근성: prefers-reduced-transparency 를 존중해 불투명으로 되돌린다(사파리 26+·
   크롬 미지원이지만, 지원 브라우저에서 먼저 옳게 동작하는 쪽을 택한다). */

html[data-glass="on"] {
  /* 두께·곡률 — 동심(concentric) 규칙: 자식 반경 = 부모 반경 − 부모 패딩 */
  --g-r: 20px;
  /* 13px → 20px (2026-08-21, B안). iOS 26 홈화면 사진 실측 비율 0.2237(= 애플 아이콘
     스퀘어클 규격)에 대해 우리 카드가 **0.39배**로 각져 있었다(probe_radius_ios.py):
       매크로 카드 200x150 · r13 → 비율 0.087 (iOS 라면 33.5px)
     ⚠️ 그렇다고 0.2237 을 **비율로** 적용하면 안 된다. 그 규격은 **정사각 아이콘** 것이고,
        이 토큰을 쓰는 판들은 세로가 제각각이라 비율이 요소마다 갈린다:
          매크로 카드 200x150 → 0.134 (0.60배)
          타일        413x 84 → 0.238 (1.06배) ← **가장 짧은 판이 제약**
          선택기      220x880 → 0.091 · 캘린더 목록 320x880 → 0.062 (큰 판은 낮아도 정상)
        ⚠️ 정직하게: 20px 은 타일에서 iOS 비율을 **1.06배로 살짝 넘는다**. 안 넘기려면
           18px(0.96배)이어야 한다. 84px 판에서 18.8 ↔ 20 은 1.2px 차이라 20 을 택했다 —
           카드(0.60배)가 여전히 iOS 보다 각지므로 카드 쪽 이득이 크다. 더 올리면
           타일이 알약으로 가고, 반대로 큰 판에 33px 을 주면 창처럼 보인다.
     🎁 곁가지: 림광 띠 상한 r×(1−1/√2) 이 3.81 → 5.86px 로 넓어진다(현재 띠 1.5px).
     손잡이는 이 한 줄뿐이다 — 값만 바꾸면 9장이 같이 따라온다. */
  --g-r-in: 20px;
  /* 칩·버튼(.glass-thin)은 --g-r-in 을 **따라가지 않는다**. 높이가 30px 안쪽이라
     20px 을 주면 비율 0.7 = 알약이 된다. 위 판들과 갈라 두는 이유가 그것이다. */
  --g-r-chip: 13px;
  --g-blur: 20px;
  --g-sat: 175%;
  /* 라이트: 흰 유리. 틴트 알파를 0.62 로 잡은 이유 — 그 아래로는 본문 글자
     (--text-secondary #52514e)가 뒤 내용과 겹쳐 읽히기 시작한다. */
  --g-tint: rgba(255, 255, 255, 0.62);
  --g-tint-hi: rgba(255, 255, 255, 0.78);
  --g-rim-top: rgba(255, 255, 255, 0.95);
  --g-rim-bot: rgba(11, 11, 11, 0.07);
  --g-hair: rgba(11, 11, 11, 0.10);
  --g-shadow: 0 10px 30px rgba(11, 11, 11, 0.10), 0 2px 6px rgba(11, 11, 11, 0.06);
  --g-spec: rgba(255, 255, 255, 0.55);
  --g-spring: cubic-bezier(.34, 1.56, .64, 1);
  /* 카드용 얕은 그림자 — 카드가 20장 넘으므로 --g-shadow(0 10px 30px)를 그대로 쓰면
     화면이 그림자로 뿌옇게 덮인다. 떠 있다는 인상만 남기는 최소치. */
  --g-shadow-sm: 0 3px 10px rgba(11, 11, 11, 0.07), 0 1px 2px rgba(11, 11, 11, 0.05);
  /* 배경(bed) — 아래 '왜 배경이 필요한가' 참고. body 는 .viz-root 밖이라
     --page/--series-* 를 못 본다(팔레트가 .viz-root 에 선언돼 있음) → 여기서 따로 둔다.
     값은 app.css 의 body 배경(#f9f9f7)과 팔레트 슬롯 1·3·4 를 그대로 옮긴 것. */
  /* 📜 이력 (2026-08-20 1차, **지금 값은 아니다** — 아래 2차 블록이 정본):
     #f9f9f7 → #e8e5de 로 낮췄던 이유는 «밝기곱 배율 > 1» 이 흰 바닥에서 255 에 잘려
     색이 통째로 사라지기 때문이었다(흰 바닥의 최대 배율은 255/249 = 1.024 뿐).
     2차에서 배율이 0.28(어둡게)로 뒤집혀 그 제약 자체가 사라졌다 — 검증기 [1b] 가
     «배율 ≤ 1 이면 클리핑 없음» 으로 통과시키는 게 그 얘기다. 근거: probe_macro_matB.py */
  /* 🔴 2026-08-20 2차: #e8e5de → **#d4cec1** (⑨절 W2). 재료 B(밝은 카드)로는
     라이트에서 유리가 안 읽혔다 — 원인은 재료가 아니라 **침대**였다:
       brightness() 는 순수 곱셈이라 상대채도 (max−min)/max 가 **불변량**이다
       ((b·max − b·min)/(b·max) = (max − min)/max). 즉 카드를 어떻게 만져도
       상대채도는 안 움직이고, 그 값은 침대의 성질이다 — 라이트 침대(248,243,238)는
       4%, 다크 침대는 50%. 그래서 다크만 유리로 보였다(실측 라이트 4.1% ↔ 다크 27.6%).
     ⇒ 올릴 수 있는 축은 둘뿐: ① 침대를 물들인다(여기) ② saturate 를 올린다
       (아래 --g-mat-* — 어둡게 **먼저** 하면 천장이 멀어져 700% 까지 간다).
     근거·스윕: tools/probe_face_regime.py · probe_dark_card.py --sweep
     ⚠️ 벽지는 body 라 **9장이 같이 깊어진다.** 화면 간 점프를 없애려면 한 값이어야
        한다(⑨절에서 세운 규칙). 카드 밖 글자는 아래 .viz-root 에서 갚는다. */
  --g-bed: #d4cec1;
  --g-a1: rgba(42, 120, 214, 0.304);  /* slot 1 blue */
  /* 🔴 slot 3 분홍(#e87ba4)이 아니라 **보라(#7b6fd4)** 다. 채도를 700% 로 올리면
     카드가 실제로 물드는데, 분홍 얼룩이 지나는 자리의 카드가 **붉게** 나온다 —
     시장 화면에서 붉은 카드는 «하락» 으로 읽힌다(상승 초록·하락 빨강 규칙).
     파랑·호박은 등락색으로 읽히지 않으므로 그대로 둔다. */
  --g-a2: rgba(123, 111, 212, 0.256);
  --g-a3: rgba(237, 161, 0, 0.224);   /* slot 4 yellow */
  /* 카드 전용 틴트 — 레일·팝업(--g-tint)보다 얇다. 레일/팝업은 뒤에 글자·차트가
     지나가므로 두껍게 덮어야 읽히지만, 카드 뒤에는 배경 얼룩뿐이라 얇게 둘수록
     색이 비쳐 유리로 보인다. 첫 판은 카드에도 0.62 를 써서 배경이 거의 하얗게
     지워졌다(스크린샷으로 확인). 대비는 tools/verify_glass.py [1] 이 잰다. */
  --g-tint-card: rgba(255, 255, 255, 0.55);
  --g-tint-card-hi: rgba(255, 255, 255, 0.72);

  /* ── 재료 B (밝기곱) — 매크로 카드 전용 (2026-08-20) ──────────────────────
     iOS 26 홈화면 사진을 픽셀로 재서 나온 재료다(tools/probe_ios_fingerprint.py):
       유리 밖→안 **채널별 배율** 1.182 / 1.153 / 1.132, 편차 0.039
       흰 틴트로 환산한 알파 0.20 / 0.29 / 0.43, 편차 0.25  ← 전혀 안 맞는다
     ⇒ iOS 유리는 흰색을 섞는 게 아니라 **뒤 배경을 곱해서 밝힌다.** 곱셈은 세 채널을
       같은 비율로 올려 채도를 보존하고, 흰 틴트는 채널마다 다른 양을 더해 채도를
       죽인다 — 사용자가 말한 "덜 반짝인다"가 정확히 그 값이었다(우리 0.37 ↔ iOS 1.05).

     그래서 세 가지를 같이 바꿔야 한다. 하나만 바꾸면 아무 일도 안 난다:
       ① 틴트를 걷는다   (덮지 말고 비추게)
       ② 곱셈  backdrop-filter: brightness() + saturate()  ← 요소 background 로는 불가
       ③ 스페큘러를 낮춘다 (흰 하이라이트가 140×100 이나 카드 얼굴을 덮고 있었다.
          ①을 걷어도 이게 남으면 색은 그대로 지워진다. iOS 스페큘러는 모서리의 얇은 띠다.)

     🔴 흐림(blur)은 **넣지 않는다.** 재료 B 의 iOS 지문에는 강한 흐림이 있지만,
        우리 카드 뒤에 있는 건 완만한 그라디언트다 — 실측으로 B+흐림 ↔ B·흐림0 의
        평균 채널차가 라이트 **0.000**(잡음 바닥 0.000) · 다크 0.142 인데 프레임은
        33ms → 53ms 로 뛴다. 즉 값을 치를 그림이 없다.
        ⚠️ 다만 brightness/saturate 만으로도 backdrop-filter 가 되살아나므로 비용이
           **0 은 아니다**: 27.3ms → 32.8ms (끔 16.7ms). 이건 곱셈의 대가다.

     📜 이력 — 1차는 라이트도 **밝히는** 방향이었다(틴트 0.08 · 배율 1.08 · 채도 108% ·
        스페큘러 0.14, hover 는 클리핑 직전이라 배율을 못 올리고 틴트만 0.16 으로 올렸다).
        2차에서 라이트만 «어두운 유리» 로 뒤집혔다 — 아래 블록이 정본이고, 위 ①②③ 논리와
        흐림 판정은 두 판에 그대로 유효하다. 다크는 1차 그대로다(밝히는 게 아니라
        어둡게 하는 쪽이라 애초에 클리핑 여유가 있었다: 배율 0.62 → hover 0.72). */
  /* ── 2026-08-20 2차: 라이트 카드는 «밝은 페이지 위의 **어두운 유리**» 다 ─────
     위 침대 주석의 결론 — 상대채도는 침대의 성질이라 밝은 얼굴로는 못 올린다.
     그런데 라이트에서 카드를 어둡게 하는 건 대비를 **파는 게 아니다**: 카드 안
     글자를 밝은 판으로 뒤집으면(아래 .mcard 잉크 블록) 본문 13.3:1 · 흐린 5.5:1 로
     오히려 좋아진다. iOS 26 사진의 폴더 타일도 얼굴 휘도 0.362 의 어두운 유리였다.
     🔑 필터 **순서가 결과를 가른다.** saturate() 는 채널을 휘도에서 멀어지는 방향으로
        밀어서 밝은 면에서는 우세 채널이 255 에 잘린다 — 그래서 지금까지 채도를 220%
        위로 못 올렸다. **먼저 어둡게** 하면 천장이 멀어져 700% 가 들어간다
        (실측 400%: 밝기먼저 12.2% ↔ 채도먼저 11.2%). 그래서 라이트만 순서를 뒤집는다.
     실측 얼굴 (51,56,55) 휘도 0.038 · 상대채도 28.6% (다크 27.6% 를 넘는다). */
  --g-mat-tint: rgba(58, 58, 62, 0.12);
  --g-mat-tint-hi: rgba(72, 72, 78, 0.22);
  --g-mat-b: 0.28;
  --g-mat-b-hi: 0.34;     /* 어둡게 하는 방향이라 hover 에서 배율을 올릴 여유가 있다 */
  --g-mat-sat: 700%;
  --g-mat-filter: brightness(var(--g-mat-b)) saturate(var(--g-mat-sat));
  --g-mat-filter-hi: brightness(var(--g-mat-b-hi)) saturate(var(--g-mat-sat));
  /* ── 판 재료 (2026-08-21) ─────────────────────────────────────────────────
     사용자 지적: «메인 카드는 유리 느낌인데 다른 화면 판은 메인보다 더 짙은 색이다».
     실측으로 방향이 확정됐다(tools/probe_panel_parity.py · 1280px):
       다크  .mcard 얼굴 (30,29,33) 상대채도 12.7%
             .chart-card    (58,57,60)  5.2%  ← 얼굴휘도 3.3배 · 채도는 절반
       즉 판은 «밝다» 가 아니라 **틴트가 두꺼워 뒤가 안 비친다** = 짙은 회색 패널이다
       (--g-tint-card 0.68). 카드가 재료 B(밝기곱)로 옮겨간 뒤 판만 재료 A 에 남았다.
     ⇒ 판도 재료 B 로 옮긴다. 다크는 카드와 **같은 값**을 쓴다(완전 동일).

     🔴 라이트는 카드 값을 쓸 수 없다. 라이트 카드는 M4 «어두운 유리»(배율 0.28)이고,
        그 안 잉크는 glass.css 가 `.mcard` 에만 밝은 판으로 뒤집어 놨다. 판에 그대로
        걸면 판 얼굴이 0.76 → 0.05 로 어두워지는데 **캔버스 잉크가 따라오지 않는다** —
        chartutil.js/stack.js/flow.js/deriv.js/app.js 의 cssColor() 는 항상 `.viz-root`
        에서 읽고, 호출 지점이 112곳이다(macro.js 만 inkColor() 로 카드에서 읽게 고쳐뒀다).
        그래서 라이트 판은 재료 B 의 **밝히는 쪽**(1차 값 — 틴트 0.08 · 배율 1.08 ·
        채도 108%)을 쓴다. 곱셈이라 채도가 보존되므로 «유리» 성질은 얻고, 얼굴 밝기는
        지금과 같아 잉크 문제가 없다. 라이트 판을 어두운 유리로 만들려면 잉크 소스부터
        고쳐야 하고, 그건 별건이다(README 참고).
     ⚠️ 배율 1.08 은 클리핑 상한에서 나온 값이다 — 침대(#d4cec1, 최대채널 212)에서
        212×1.08 = 229 로 여유가 있다. 올리려면 probe 로 최악 얼룩 합성부터 다시 잴 것. */
  --g-pan-tint: rgba(255, 255, 255, 0.08);
  --g-pan-tint-hi: rgba(255, 255, 255, 0.16);
  --g-pan-b: 1.08;
  --g-pan-sat: 108%;
  --g-pan-filter: brightness(var(--g-pan-b)) saturate(var(--g-pan-sat));
  --g-pan-filter-hi: brightness(var(--g-pan-b)) saturate(var(--g-pan-sat));
  /* 🔴 0.16 → 0.12 (2026-08-20 3차, 추적 부활과 **짝**이다).
     정지였을 때 peak 은 늘 카드 위쪽 가운데였고 흐린 라벨·등락 배지는 그보다 아래라
     «peak 이 안 닿는 자리» 에 앉아 있었다. 추적을 켜면 peak 이 포인터를 따라 그 글자
     **위로 온다** — 커서를 얹은 카드가 곧 읽고 있는 카드다.
     실측(잉크마다 자기 사각형 안 최악 배경 · probe_spec_ink.py):
       흐린 라벨  정지 대조군 3.86 → 추적 0.16 에서 2.46  (−1.40)
       상승 배지  정지 대조군 3.84 → 추적 0.16 에서 2.69  (−1.15)
     ⚠️ «추적최악 ≥ 정지대조군» 을 바로 잡고 알파를 0.04 까지 낮춰도 회복되지 않는다
        (−0.40). 당연하다 — 그 바는 «움직이는 빛이 글자 자리에서 보이지 않을 것» 을
        요구하므로 이 효과 자체와 모순이다. 그래서 바를 «절반으로 줄인다» 로 바꿨다.
     0.12 실측 최악 2.74(라이트) · 2.39(다크). 남는 하락은 [5b] 가 기록하고 감시한다.
     ⚠️ **잉크 갚음(이 저장소의 상투 수단)은 여기서 막혔다.** 흐린 라벨을 추적최악에서
        3.5:1 로 올리려면 #a8a6a0 → **#c0beb7** 이 필요한데(실측), 그건 보조색
        `--text-secondary #c3c2b7` 과 사실상 같은 색이다 — 흐린/보조 위계가 통째로
        무너진다. 벽지를 팠을 때 갚음이 통했던 것과 달리, 이 얼굴은 위로 갈 자리가
        없다(«글자 사막»). 그래서 알파를 절반으로 줄이는 선에서 멈추고 남는 하락을
        사실로 남긴다. 더 줄이려면 효과 자체를 포기해야 하고, 그건 요청과 모순이다.
     🔴 4차(2026-08-20)에 이 변수를 **삭제했다.** 카드가 면 스팟을 안 쓰게 됐으므로
        (아래 림광) 값을 조절해도 아무 일이 일어나지 않는다 — 죽은 손잡이는 없는 것보다
        나쁘다(다음 사람이 돌려보고 «효과가 없네» 로 끝난다). 위 실측은 **왜 면 스팟을
        버렸는지의 근거**로 남긴다: 갇히지 않은 빛은 글자 자리에서 대비를 사는 값이
        비싸고, 갚음으로 되살 수도 없었다(«글자 사막»). 새 손잡이는 --g-rim-lit 이다.
     ── 카드 림광 (2026-08-20 4차) ─────────────────────────────────────────
     «면 위의 손전등» 이 아니라 «테두리 띠가 광원 쪽에서 빛난다». 사용자가 /glasslab
     ⑦절 B 에서 보고 고른 그 움직임이고, gloss.css ③ 의 어법을 그대로 가져온다.
     🔑 이 알파가 --g-spec-card(0.12) 보다 훨씬 클 수 있는 이유 — 림은 마스크로
        **테두리 띠에 갇혀** 있어서 글자 자리에 절대 닿지 않는다. 면 하이라이트는
        peak 이 글자 위로 와서 0.12 까지 깎아야 했다([5b] 의 그 값). 갇힌 빛은
        그 대가를 안 낸다.
     ⚠️ 띠 두께는 모서리 수식에 묶인다: ring < r×(1−1/√2) = 13×0.2929 = **3.81px**.
        넘으면 띠의 직각 꼭지가 둥근 모서리 밖으로 튀어나와 «액자선» 이 되살아난다
        (8/20 에 --gl-ring 을 17→6px 로 내린 그 실측). 1.5px 는 그 안이다.
     🔑 5차(2026-08-20) — 2.5px → **1.5px**. 사용자: «그 전엔 테두리가 얇고 진짜
        유리 같았는데 지금은 너무 두껍다». 4차는 «무엇이 움직이나»(면 → 띠)는 맞췄고
        «얼마나 얇나» 를 틀렸다. probe_rim_thin.py 로 ⑦절 B 카드와 **같은 잣대**로 재니:

          대상              밝은띠   비율    정점80%   정점
          ⑦B  라이트        2.00px  1.09%   1.33px   0.947
          카드 라이트        3.00px  2.00%   3.00px   0.783   ← 비율 1.84×
          ⑦B  다크          2.33px  1.27%   1.00px   0.843
          카드 다크          3.00px  2.00%   3.00px   0.670   ← 비율 1.58×

        🔴 진짜 지문은 폭이 아니라 **모양**이다. 카드는 «정점80% = 밝은띠 = 3.00px»
           — 띠 전체가 같은 밝기인 **평탄한 판**이다. ⑦B 는 2.0px 띠 안에 1.0~1.33px
           밝은 심이 있고 나머지는 떨어진다 — 눈이 «얇은 유리 모서리» 로 읽는 게 그
           심이다. 균일한 3px 판은 «두꺼운 둔한 테두리» 로 읽힌다.
        📌 1.5px 은 눈대중이 아니라 ⑦B 자신의 --gl-rim-w(가장 바깥 흰 선) 값이다.
           비율로도 1.5/150 = 1.0% 로 ⑦B(1.09~1.27%) 안에 든다.
        ⚠️ 얇게 하면서 알파(--g-rim-lit 0.95)는 **건드리지 않았다.** 띠가 얇아지면
           정점도 같이 죽으므로 둘을 같이 내리면 림이 통째로 사라진다. 폭만 줄인다.
           그리고 림은 마스크로 띠에 갇혀 있어 글자 자리에 닿지 않으므로, 얇게 하는
           것이 잉크 대비를 **깎지 않는다**(검증기 [5b] 가 이걸 못박는다). */
  --g-ring-card: 1.5px;
  /* 🔦 **정지 각도** — 포인터가 요소 밖에 있을 때 림이 어디서 빛나나.
     conic 정점은 `from` 에서 0.05turn(18°) 뒤에 있으므로 **정점 = 이 값 + 18** 이고,
     각도 규약은 conic 그대로다(0°=위, 시계방향).
     📜 이 값은 4차부터 `var(--ga, 205)` 의 **폴백 자리에 숨어 있었다.** 이름이 없으니
        시안에서 각도만 바꿔볼 방법이 없었고(`::after` 에 --ga 를 적으면 추적이 죽는다),
        주석은 «왼쪽 위» 라고 적혀 있는데 실제 정점은 223° = **왼쪽 아래** 였다 —
        코드와 주석이 90° 어긋난 채로 남아 있었다(probe_rim_angle.py 실측 226°).
     ⚠️ 그래서 손잡이를 밖으로 뺀다. 값을 바꾸려면 여기만 고치면 되고, JS 가 요소에
        박는 인라인 --ga 가 이 폴백을 이기므로 **추적은 그대로 산다.**
     ✅ 7차(2026-08-27) — 사용자가 /glasslab ⑫ 에서 **R5 · 11시** 를 골랐다.
        205 → 312 (정점 330° = 11시). 근거는 취향이 아니라 셋이다:
          ① 사람 시각은 «빛은 위에서 온다» 를 전제로 깔아서, 아래에서 든 빛은
             면이 파인 것으로 읽힌다 — 205(정점 226° = 아래-왼쪽)의 문제가 그것.
          ② 정확히 12시면 좌우 대칭이라 오히려 평평해 보인다 → 살짝 왼쪽.
          ③ 🎁 덤 — 정적 상단선(--g-rim-top, «빛은 위»)과 conic 정점이 **같은
             반구**로 모인다. 4차부터 이 둘은 서로 반대(위 ↔ 아래-왼쪽)였고,
             ⑫절이 «광원이 둘이고 서로 반대» 라고 진단한 그 상태가 풀린다.
        📌 이 토큰은 카드·타일·차트판·모달판·레일이 **전부** 참조한다(폴백 포함).
           그래서 이 한 줄이 곧 «모두 적용» 이다. 폴백 리터럴도 같이 312 로 맞춘다 —
           한쪽만 고치면 유리를 끈 상태에서만 옛 각이 나오고, 그건 화면에서
           «가끔 이상하다» 로만 드러나 재현이 안 된다. */
  --g-ga-rest: 312;
  --g-rim-lit: rgba(255, 255, 255, 0.95);    /* 광원 쪽 모서리 */
  --g-rim-lit2: rgba(255, 255, 255, 0.30);   /* 반대쪽 내부 반사 (유리는 두 번 빛난다) */
  /* 🔦 두 정점이 **어디에** 앉나 — conic 스톱 위치(turn). `from` 기준 오프셋이다.
     📜 6차(2026-08-27)에 이름을 붙였다. 그전엔 gradient 안에 숫자로 박혀 있어서
        «광원이 하나뿐인가 · 상단 두 모서리에 둘인가» 같은 **방향 모델** 자체를
        시안에서 바꿔볼 수가 없었다(각도만 돌릴 수 있었다). 기본값은 박혀 있던 값
        그대로라 이 커밋은 행동 중립이고, verify_glass 가 그걸 못박는다.
     ⚠️ 스톱은 **오름차순**이어야 한다(p1 < e1 ≤ s2 < p2 < e2). 어긋나면 브라우저가
        앞 값으로 끌어올려 lobe 가 조용히 사라진다 — 화면은 멀쩡해 보인다. */
  --g-rim-p1: 0.05turn;   /* 직접광 정점 (정점 = --g-ga-rest + 18°) */
  --g-rim-e1: 0.18turn;   /* 직접광이 꺼지는 자리 */
  --g-rim-s2: 0.44turn;   /* 반사광이 켜지기 시작하는 자리 */
  --g-rim-p2: 0.53turn;   /* 반사광 정점 (직접광에서 ~172° 반대) */
  --g-rim-e2: 0.67turn;   /* 반사광이 꺼지는 자리 */
  /* 어두운 카드에서는 검은 헤어라인이 안 보인다 → 밝은 쪽으로 뒤집는다
     (다크 테마가 --g-hair 를 흰 0.12 로 두는 것과 같은 어법). 윗림(0.95)은 그대로 —
     시안에서 사용자가 고른 판이 그 림을 가진 상태였다. */
  --g-hair-card: rgba(255, 255, 255, 0.12);
}
html[data-glass="on"][data-theme="dark"] {
  /* 다크는 "검은 유리"가 아니라 **연기 낀 유리**다. 순검정 틴트는 뒤가 안 비쳐
     그냥 불투명 패널로 보인다 → 회색(58,58,62)을 올려 뒤 색이 남게 한다.
     ⚠️ 처음엔 "다크가 같은 알파에서 더 탁해 보인다" 는 눈대중으로 알파를 **낮춰**
        잡았는데(0.48), 대비를 재보니 정반대였다. 중간 회색 틴트는 밝은 배경색을
        거의 못 눌러서, 최악 배경(--series-4 #c98500) 위 보조색이 3.15:1 로 떨어진다.
        4.5:1 을 넘는 지점이 0.74 라 그 값을 기본으로 잡았다 — 라이트(0.62)보다 높다.
        /glasslab 의 대비 실측 줄이 이 계산을 화면에서 그대로 보여준다. */
  --g-tint: rgba(58, 58, 62, 0.74);
  --g-tint-hi: rgba(72, 72, 78, 0.86);
  --g-rim-top: rgba(255, 255, 255, 0.22);
  --g-rim-bot: rgba(0, 0, 0, 0.55);
  --g-hair: rgba(255, 255, 255, 0.12);
  --g-shadow: 0 12px 34px rgba(0, 0, 0, 0.55), 0 2px 8px rgba(0, 0, 0, 0.45);
  --g-spec: rgba(255, 255, 255, 0.16);
  --g-shadow-sm: 0 4px 12px rgba(0, 0, 0, 0.40), 0 1px 3px rgba(0, 0, 0, 0.35);
  /* 다크 배경은 알파를 올려야 같은 세기로 보인다 — 어두운 바닥 위에서 색이 덜 드러난다 */
  --g-bed: #0d0d0d;
  --g-a1: rgba(57, 135, 229, 0.20);
  --g-a2: rgba(213, 81, 129, 0.16);
  --g-a3: rgba(201, 133, 0, 0.14);
  --g-tint-card: rgba(58, 58, 62, 0.68);
  --g-tint-card-hi: rgba(72, 72, 78, 0.82);

  /* 재료 B (다크) — ⚠️ **사진 실측이 아니다.** 받은 iOS 사진이 라이트 배경 한 장뿐이라,
     다크는 위 블록의 연기유리 논리를 그대로 쓴다: 빛을 더하는 대신 배경을 흡수해
     어둡게(배율 < 1). 어두운 바닥(#0d0d0d)은 낮추는 방향이라 클리핑 걱정이 없다.
     🔴 다크만 채도를 올린다(라이트 108% ↔ 다크 150%). 배율 0.62 는 **채도까지 같은
        비율로 줄이므로**(곱셈이니까) 그냥 두면 "배경 색이 살아 있다"는 성질이
        라이트만큼 안 나온다. 실측 채도보존 0.78~0.90 (재료 A 는 0.11~0.52).
     대비는 오히려 좋아진다 — 흐린색 4.89:1 → 6.95:1 (카드가 어두워지고 글자는 밝다). */
  --g-mat-tint: rgba(58, 58, 62, 0.12);
  --g-mat-tint-hi: rgba(72, 72, 78, 0.22);
  --g-mat-b: 0.62;
  --g-mat-b-hi: 0.72;     /* 다크 hover = 덜 어둡게 = 밝아진다 */
  --g-mat-sat: 150%;
  /* ⚠️ 다크는 **순서를 안 바꾼다**(채도 먼저). 두 필터는 둘 다 선형이라 클램프가
     없으면 교환법칙이 성립하지만, 클램프가 끼면 갈린다 — 다크 바닥에서는 잘릴 일이
     없어 결과가 같다. 그래도 «지금 화면과 픽셀이 같다» 를 보장하려면 손대지 않는
     쪽이 맞다. 라이트만 밝기 먼저다(위 블록 참고). */
  --g-mat-filter: saturate(var(--g-mat-sat)) brightness(var(--g-mat-b));
  --g-mat-filter-hi: saturate(var(--g-mat-sat)) brightness(var(--g-mat-b-hi));
  /* 판 재료 (다크) — 카드와 **같은 값**이다. 라이트와 달리 잉크를 뒤집을 필요가 없어서
     (다크 잉크는 이미 밝다) 완전 동일 재료를 쓸 수 있다. 위 라이트 블록의 주석 참고.
     ⚠️ 별칭이 아니라 var() 참조로 둔다 — 카드 값을 고치면 판도 같이 움직여야 한다.
        복제하면 다음에 한쪽만 고쳐진다(이 저장소가 반복해서 당한 사고다). */
  --g-pan-tint: var(--g-mat-tint);
  --g-pan-tint-hi: var(--g-mat-tint-hi);
  --g-pan-b: var(--g-mat-b);
  --g-pan-sat: var(--g-mat-sat);
  --g-pan-filter: var(--g-mat-filter);
  --g-pan-filter-hi: var(--g-mat-filter-hi);
  /* (--g-spec-card 는 4차에서 삭제 — 위 블록 참고) */
  /* 카드 림광 (다크) — 띠 두께는 반경이 같으므로 라이트와 같다. 세기만 낮춘다:
     다크 얼굴(휘도 0.012)은 라이트 어두운 카드(0.038)보다 더 어두워서 같은 흰빛이
     더 세게 읽힌다. gloss.css 가 0.95→0.82 로 내린 것과 같은 어법이다. */
  --g-rim-lit: rgba(255, 255, 255, 0.82);
  --g-rim-lit2: rgba(255, 255, 255, 0.24);
  --g-hair-card: var(--g-hair);   /* 다크는 이미 흰 0.12 라 뒤집을 게 없다 */
}

/* ── 재료 본체 ────────────────────────────────────────────────────────────
   .glass 를 붙인 요소가 유리가 된다. isolation:isolate 로 스페큘러(::after)가
   자기 안에서만 합성되게 묶는다 — 안 묶으면 형제 요소 위로 새어 나간다.

   position 은 **:where() 로 특이도 0** 에 둔다. ::after(inset:0)가 붙을 자리가
   필요해서 relative 를 줘야 하는데, 그냥 주면 특이도(0,2,1)가 이겨서 요소가 원래
   갖고 있던 sticky/absolute 를 조용히 덮는다. 실제로 /glasslab 의 떠 있는 판이
   `position: sticky` 였는데 유리를 켜자 relative 로 바뀌어 **스크롤할 때 판이 통째로
   사라졌다**(화면은 멀쩡하고 기능만 없어지는 종류의 사고다). :where() 는 특이도가
   0 이라 `.lab-float { position: sticky }` 같은 평범한 클래스가 그대로 이긴다. */
:where(html[data-glass="on"] .glass) { position: relative; }
html[data-glass="on"] .glass {
  isolation: isolate;
  border: 0;
  border-radius: var(--g-r);
  background: var(--g-tint);
  -webkit-backdrop-filter: blur(var(--g-blur)) saturate(var(--g-sat));
  backdrop-filter: blur(var(--g-blur)) saturate(var(--g-sat));
  box-shadow:
    inset 0 0 0 1px var(--g-hair),      /* 유리 테두리 헤어라인 */
    inset 0 1.5px 0 var(--g-rim-top),   /* ③ 위쪽 밝은 림 = 빛을 받는 모서리 */
    inset 0 -1px 0 var(--g-rim-bot),    /* ③ 아래쪽 어두운 림 = 두께 */
    var(--g-shadow);                    /* 떠 있는 그림자 */
}

/* ④ 스페큘러 — 포인터 위치(--gx/--gy)를 중심으로 한 부드러운 하이라이트.
   기본값 50%/0% 는 "위에서 빛이 든다" 는 정지 상태. JS 가 없어도 자연스럽다. */
html[data-glass="on"] .glass::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  z-index: -1;
  background:
    radial-gradient(120px 90px at var(--gx, 50%) var(--gy, 0%),
                    var(--g-spec), transparent 72%);
  opacity: .9;
  transition: opacity .2s ease;
}
/* 유리 안의 내용은 스페큘러 위로 — z-index:-1 인 ::after 가 배경 쪽에 깔린다 */
html[data-glass="on"] .glass > * { position: relative; }

/* 얇은 유리(칩·버튼) — 같은 재료, 두께만 줄인다 */
html[data-glass="on"] .glass-thin {
  --g-blur: 12px;
  --g-r: var(--g-r-chip);
  background: var(--g-tint-hi);
  box-shadow:
    inset 0 0 0 1px var(--g-hair),
    inset 0 1px 0 var(--g-rim-top),
    0 2px 8px rgba(11, 11, 11, .07);
}

/* 누름 반응 — iOS 는 스케일이 살짝 오버슈트한다(스프링). 유리가 "눌리는 물체"로
   읽히는 건 거의 이 한 줄에서 온다. */
html[data-glass="on"] .glass-press { transition: scale .18s var(--g-spring); }
html[data-glass="on"] .glass-press:active { scale: .965; }
html[data-glass="on"] .glass-press:hover::after { opacity: 1; }

/* ── 실제 대시보드 요소에 얹기 ─────────────────────────────────────────────
   위에서 적은 대로 **떠 있는 것만** 대상이다. */

/* 좌측 레일 — 본문 위에 겹쳐 열리므로 유리가 제 몫을 한다 */
html[data-glass="on"] .navrail {
  border-right: 0;
  background: var(--g-tint);
  -webkit-backdrop-filter: blur(24px) saturate(var(--g-sat));
  backdrop-filter: blur(24px) saturate(var(--g-sat));
  box-shadow:
    inset -1px 0 0 var(--g-hair),
    inset 0 1.5px 0 var(--g-rim-top),
    12px 0 40px rgba(11, 11, 11, .12);
}
html[data-glass="on"][data-theme="dark"] .navrail {
  box-shadow:
    inset -1px 0 0 var(--g-hair),
    inset 0 1.5px 0 var(--g-rim-top),
    12px 0 40px rgba(0, 0, 0, .6);
}
/* 레일 스페큘러 — 여기는 **추적한다**. 레일은 본문 위에 겹쳐 있는 조작 요소라
   애플이 사이드바에 스페큘러를 쓰는 그 자리와 같다(카드와 갈리는 지점).
   position 을 따로 안 주는 이유: .navrail 은 app.css 에서 이미 `position: fixed`
   + `z-index: 40` 이라 ::after(inset:0)가 붙을 자리와 자기 쌓임 문맥이 둘 다 있다
   → z-index:-1 이 레일 밖으로 새지 않는다(isolation 을 덧붙일 필요도 없다). */
html[data-glass="on"] .navrail::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: -1;
  background:
    radial-gradient(220px 220px at var(--gx, 50%) var(--gy, 0%),
                    var(--g-spec), transparent 70%);
  opacity: .85;
}
html[data-glass="on"] .navrail-item {
  border-radius: 11px;
  transition: background .16s ease, scale .18s var(--g-spring);
}
html[data-glass="on"] .navrail-item:active { scale: .97; }
html[data-glass="on"] .navrail-item.active {
  background: var(--g-tint-hi);
  border-color: transparent;
  box-shadow: inset 0 0 0 1px var(--g-hair), inset 0 1px 0 var(--g-rim-top);
}

/* 가장자리 손잡이 — 유리 막대. 전체 높이라 hover 시 폭만 부푼다(app.css 규칙 유지) */
html[data-glass="on"] .navrail-edge:hover::before,
html[data-glass="on"] .navrail-edge:focus-visible::before {
  background: var(--g-tint-hi);
  -webkit-backdrop-filter: blur(14px) saturate(var(--g-sat));
  backdrop-filter: blur(14px) saturate(var(--g-sat));
  box-shadow: inset -1px 0 0 var(--g-hair), inset 1.5px 0 0 var(--g-rim-top);
}

/* 백드롭 — 완전 불투명 회색막 대신 아주 약한 흐림. 뒤가 보여야 "겹쳐 있다"가 읽힌다 */
html[data-glass="on"] .navrail-backdrop {
  background: rgba(11, 11, 11, .18);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}

/* ── 우측 이벤트 레일 — 좌측 레일과 같은 재료(거울상) ─────────────────────
   여기도 **떠 있는 것**이라 흐림이 제 몫을 한다. 카드에서 흐림을 뺀 것과
   갈리는 지점: 카드 뒤는 완만한 그라디언트뿐이라 흐려도 같은 픽셀이 나오지만,
   레일 뒤로는 본문 글자·차트가 지나간다. */
html[data-glass="on"] .evrail {
  border-left: 0;
  background: var(--g-tint);
  -webkit-backdrop-filter: blur(24px) saturate(var(--g-sat));
  backdrop-filter: blur(24px) saturate(var(--g-sat));
  box-shadow:
    inset 1px 0 0 var(--g-hair),
    inset 0 1.5px 0 var(--g-rim-top),
    -12px 0 40px rgba(11, 11, 11, .12);
}
html[data-glass="on"][data-theme="dark"] .evrail {
  box-shadow:
    inset 1px 0 0 var(--g-hair),
    inset 0 1.5px 0 var(--g-rim-top),
    -12px 0 40px rgba(0, 0, 0, .6);
}
/* 스페큘러 — 레일은 추적한다(glass.js TRACK_SEL 참조). .evrail 은 app.css 에서
   이미 position:fixed + z-index:40 이라 ::after(inset:0)가 자기 쌓임 문맥 안에
   갇힌다 → z-index:-1 이 레일 밖으로 새지 않는다. */
html[data-glass="on"] .evrail::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: -1;
  background:
    radial-gradient(220px 220px at var(--gx, 50%) var(--gy, 0%),
                    var(--g-spec), transparent 70%);
  opacity: .85;
}
/* 안쪽 목록/링크는 유리 위 유리가 되지 않게 **틴트를 얹지 않는다** — 부모가 이미
   backdrop-filter 를 가지면 자식은 흐려진 틴트를 한 번 더 필터링할 뿐이고,
   납작한 사각형만 남는다(실측으로 잡아 걷어낸 그 증상). 테두리만 유리 결에 맞춘다. */
html[data-glass="on"] .evrail-list,
html[data-glass="on"] .evrail-more {
  background: transparent;
  border-color: var(--g-hair);
  border-radius: var(--g-r-in);
}
html[data-glass="on"] .ev-dh { background: var(--g-tint-hi); }
html[data-glass="on"] .evrail-edge:hover::before,
html[data-glass="on"] .evrail-edge:focus-visible::before {
  background: var(--g-tint-hi);
  -webkit-backdrop-filter: blur(14px) saturate(var(--g-sat));
  backdrop-filter: blur(14px) saturate(var(--g-sat));
  box-shadow: inset 1px 0 0 var(--g-hair), inset -1.5px 0 0 var(--g-rim-top);
}
html[data-glass="on"] .evrail-backdrop {
  background: rgba(11, 11, 11, .18);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}

/* 모달 — 내용 위에 진짜로 떠 있는 유일한 기존 요소.
   백드롭도 같이 손본다: 지금은 rgba(0,0,0,.55) 로 뒤를 거의 지우는데, 유리를 쓰는
   이유가 "뒤가 남아 있다"는 인상이라 새까맣게 덮으면 앞뒤가 모순된다. */
html[data-glass="on"] .modal-bd {
  background: rgba(11, 11, 11, .34);
  -webkit-backdrop-filter: blur(6px) saturate(120%);
  backdrop-filter: blur(6px) saturate(120%);
}
html[data-glass="on"] .modal {
  border: 0;
  border-radius: 24px;
  background: var(--g-tint);
  -webkit-backdrop-filter: blur(30px) saturate(var(--g-sat));
  backdrop-filter: blur(30px) saturate(var(--g-sat));
  box-shadow:
    inset 0 0 0 1px var(--g-hair),
    inset 0 2px 0 var(--g-rim-top),
    0 24px 60px rgba(11, 11, 11, .22);
}

/* 칩·탭 — 얇은 유리 + 누름 스프링. 배경이 단색이라 흐림은 거의 안 보이지만,
   림 + 스프링만으로도 "만질 수 있는 물체" 감이 붙는다(비용이 거의 0인 부분). */
html[data-glass="on"] .chip,
html[data-glass="on"] .tab {
  border-color: transparent;
  background: var(--g-tint-hi);
  box-shadow: inset 0 0 0 1px var(--g-hair), inset 0 1px 0 var(--g-rim-top);
  transition: scale .18s var(--g-spring), background .16s ease;
}
html[data-glass="on"] .chip:active,
html[data-glass="on"] .tab:active { scale: .955; }
html[data-glass="on"] .chip.active,
html[data-glass="on"] .tab.active {
  box-shadow: inset 0 0 0 1px var(--g-hair), inset 0 1px 0 var(--g-rim-top),
              0 2px 10px rgba(11, 11, 11, .10);
}

/* ── 매크로 메인(/) 에 얹기 ─────────────────────────────────────────────────
   ⚠️ 여기가 이 파일에서 제일 중요한 판단이다.

   위(13행)에 적은 대로 **뒤에 아무것도 없으면 유리는 보이지 않는다.** 그런데 매크로
   메인은 단색(--page) 위에 카드가 깔린 화면이라, 카드만 유리로 바꾸면 blur 가 같은
   단색을 되돌려주고 **비용만 내고 아무것도 안 달라진다**(실험실 ②절이 그 비교다).

   그래서 카드를 유리로 만들기 **전에 배경을 만든다** — iOS 가 유리를 벽지 위에
   올리는 것과 같은 순서다. 저채도 색 얼룩 3개를 화면에 고정(background-attachment)
   으로 깔면, 스크롤할 때 카드 뒤로 배경이 흐르면서 "떠 있는 판" 이 읽힌다.

   대신 데이터 화면이므로 두 가지를 지켰다:
     · 얼룩 색은 **새로 만들지 않고** 팔레트 슬롯 1·3·4 를 그대로 쓴다
     · 알파를 낮게(0.10~0.13) 잡아 **글자 대비를 건드리지 않는다** — 카드 바닥은
       틴트(흰 0.62)가 한 겹 더 덮으므로 최악에도 --page 와 거의 같은 밝기다.
       tools/verify_macro.py 가 이 대비를 실측으로 잰다(눈대중 금지). */

/* 배경은 body 에 깔고, .viz-root 의 불투명 배경을 걷는다 — 안 걷으면 본문 폭(860px)
   안쪽에서 배경이 통째로 가려져 카드 뒤에 아무것도 없는 상태로 되돌아간다. */
html[data-glass="on"] body {
  background-color: var(--g-bed);
  background-image:
    radial-gradient(900px 620px at 12% -6%,  var(--g-a1), transparent 70%),
    radial-gradient(820px 700px at 94% 16%,  var(--g-a2), transparent 68%),
    radial-gradient(760px 560px at 46% 106%, var(--g-a3), transparent 66%);
  background-repeat: no-repeat;
  background-attachment: fixed;  /* 벽지처럼 고정 → 스크롤하면 카드가 배경 위를 지난다 */
}
html[data-glass="on"] .viz-root { background: transparent; }

/* 카드 — 재료는 .glass 와 같지만 **backdrop-filter 는 일부러 뺐다.** 실측 근거:
   ① tools/probe_glass_cost.py — 카드 22장에 blur 를 걸면 스크롤 프레임 간격이
      16.7ms → 42.5ms(2.6배)가 된다. 고정 배경을 끄면 45.5ms 로 그대로라 범인이
      아니었고, 카드 blur 만 끄면 22.5ms 로 떨어졌다. 즉 비용의 대부분이 여기다.
   ② tools/probe_glass_blurdiff.py — 그런데 **그림은 거의 안 바뀐다.** 카드 뒤에
      있는 건 완만한 그라디언트이고, 흐림은 이웃 픽셀을 평균내는 연산이라 이미
      매끈한 면을 흐리면 같은 픽셀이 나온다. 평균 채널 차이 1.3(라이트)/1.7(다크),
      8배 증폭 이미지로 보면 차이가 **글자 안티에일리어싱 테두리에만** 남는다.
      대조군인 레일(뒤에 카드·글자가 있는 자리)은 4.2 로 두 배 이상 차이가 나므로
      이 측정이 무감각한 게 아니다.
   → 레일·팝업·백드롭처럼 **뒤에 진짜 내용이 있는 곳만** 흐린다. 카드는 틴트로
     배경색이 비치고, 림 + 스페큘러 + 누름 스프링으로 유리로 읽힌다.
   되돌리려면 아래 두 줄의 주석을 풀면 된다(비용은 위 숫자 그대로 돌아온다).

   .glass 클래스를 마크업에 붙이지 않고 CSS 로만 얹은 이유: macro.js 를 건드리지
   않으면 되돌리기가 **glass.css 링크 한 줄 삭제**로 끝난다. */
:where(html[data-glass="on"] .mcard) { position: relative; }
html[data-glass="on"] .mcard {
  isolation: isolate;
  /* ⚠️ `border: 0` 이 아니라 **투명 1px**. box-sizing:border-box 라도 카드 높이는
     내용이 정하므로, 테두리를 지우면 카드가 2px 짧아진다 — 유리를 켜고 끌 때
     레이아웃이 움직인다(검증기 [3] 이 실제로 잡았다). 자리는 남기고 색만 없앤다. */
  border: 1px solid transparent;
  border-radius: var(--g-r-in);
  /* 재료 B — 위 --g-mat-* 블록 참고. 틴트로 덮는 대신 뒤 배경을 곱해서 밝힌다.
     ⚠️ blur 는 여기 넣지 않는다(그림 차이 0.000 · 프레임 33→53ms). 검증기 [2] 가
        blur 재등장을 실패로 잡고, brightness/saturate 는 있어야 통과한다. */
  background: var(--g-mat-tint);
  -webkit-backdrop-filter: var(--g-mat-filter);
          backdrop-filter: var(--g-mat-filter);
  box-shadow:
    inset 0 0 0 1px var(--g-hair-card),
    inset 0 1.5px 0 var(--g-rim-top),
    inset 0 -1px 0 var(--g-rim-bot),
    var(--g-shadow-sm);
  transition: scale .18s var(--g-spring), box-shadow .16s ease;
}
/* hover 는 테두리색이 아니라 **유리가 밝아지는** 것으로 바꾼다 — border 를 0 으로
   뒀으므로 app.css 의 `.mcard:hover{border-color}` 는 이제 아무 일도 하지 않는다. */
html[data-glass="on"] .mcard:hover {
  border-color: transparent;   /* app.css 의 `:hover{border-color:var(--baseline)}` 무효화 */
  background: var(--g-mat-tint-hi);
  -webkit-backdrop-filter: var(--g-mat-filter-hi);
          backdrop-filter: var(--g-mat-filter-hi);
  box-shadow:
    inset 0 0 0 1px var(--g-hair-card),
    inset 0 1.5px 0 var(--g-rim-top),
    inset 0 -1px 0 var(--g-rim-bot),
    var(--g-shadow);
}
html[data-glass="on"] .mcard:active { scale: .985; }
/* 카드 스페큘러 — **포인터를 따라간다**(2026-08-20 3차, 사용자 요청으로 부활).
   📜 8/19 에 `50% 0%` 로 못박아 정지시켰던 자리다. 그때 근거는 «iOS 26 도 광원이
      커서가 아니라 자이로이고, 애플은 내용 카드에 안 바른다» — 즉 **애플 흉내**였다.
      그런데 사용자가 그 움직임을 좋아했다고 직접 지적했다. 자이로가 없는 데스크톱에서
      포인터를 광원으로 쓰는 건 대체이고, 애플에 없다는 게 나쁘다는 뜻은 아니다.
      되돌리는 근거는 취향이며, 그게 이 자리에서는 애플 대조보다 앞선다.
      ⚠️ 다시 정지로 되돌리려면 «애플은 안 한다» 가 아니라 사용자 지적이 근거여야 한다.
   🔴 4차(2026-08-20) — 면 위의 **radial 스팟(손전등)** 을 버리고 **테두리 림광** 으로
      바꿨다. 사용자 지적: «7절의 B 를 보면 마우스에 따라 테두리 광택이 움직이던데
      지금은 손전등 비추듯 움직인다. 내가 원하는 건 전자다.»
      즉 3차는 «움직인다» 는 맞췄고 «무엇이 움직이나» 를 틀렸다. ⑦절 B(gloss.css ③)는
      처음부터 conic-gradient 를 **띠에 갇힌** 채 돌리고 있었고, 면은 정지였다.
      🎁 덤이 크다 — 3차가 지불한 대비 하락(흐린 4.73 → 2.74)이 **통째로 사라진다.**
         빛이 글자 자리에 못 오기 때문이다. 그래서 알파도 0.12 가 아니라 0.95 를 쓴다.
   각도는 --ga(도)다. glass.js 가 포인터 방향으로 갱신하고, 기본값 --g-ga-rest(312,
   정점 330° = 11시)는 «위에서 살짝 왼쪽으로 빛이 든다» 는 정지 상태다 —
   JS 가 죽거나 모션 감소 설정이면 그 자리에 남는다.
   ⚠️ 여기에는 backdrop-filter 를 **넣지 않는다.** gloss.css ③ 은 띠가 몸통 재료를
      대체하므로 필터를 다시 걸어야 했지만, 카드는 요소 자신이 이미 재료 B 를 띠까지
      깔고 있다. 여기 또 걸면 «유리 위 유리» 가 되어 8/20 에 실측한 그 함정을 밟는다
      (부모가 backdrop-filter 를 가지면 자식이 보는 «뒤» 는 부모가 그린 결과다 →
      필터가 죽고 프레임만 먹는다). 림은 순수 그라디언트다. */
html[data-glass="on"] .mcard::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  z-index: -1;                  /* 요소 배경 위 · 내용 아래 (isolate 로 밖에 안 샌다) */
  padding: var(--g-ring-card);
  box-sizing: border-box;
  /* 광원 쪽 모서리가 밝고, 그 반대쪽에 약한 내부 반사가 한 번 더 뜬다 — 실제 유리가
     두 번 빛나는 이유와 같다(입사면 + 반대면 전반사). 정점이 `from` 에서 0.05turn
     뒤에 있으므로 glass.js 가 그만큼 당겨서 각을 넣는다. */
  background:
    conic-gradient(from calc(var(--ga, var(--g-ga-rest, 312)) * 1deg),
                   transparent 0turn,
                   var(--g-rim-lit) var(--g-rim-p1, 0.05turn),
                   transparent var(--g-rim-e1, 0.18turn),
                   transparent var(--g-rim-s2, 0.44turn),
                   var(--g-rim-lit2) var(--g-rim-p2, 0.53turn),
                   transparent var(--g-rim-e2, 0.67turn),
                   transparent 1turn);
  /* 띠만 남기는 마스크 — padding-box 전체에서 content-box 안쪽을 뺀다.
     ⚠️ 사파리는 `mask-composite: exclude` 대신 `-webkit-mask-composite: xor` 다.
        둘 다 적는다(모르는 쪽은 무시된다). */
  -webkit-mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  opacity: .85;
  transition: opacity .2s ease;
}
html[data-glass="on"] .mcard:hover::after { opacity: 1; }
html[data-glass="on"] .mcard > * { position: relative; }

/* ── 라이트 어두운 카드의 잉크 (2026-08-20 2차) ────────────────────────────
   카드 얼굴이 휘도 0.038 이 됐으므로 카드 **안** 잉크는 전부 밝은 판으로 뒤집는다.
   🔴 `.viz-root` 가 아니라 **`.mcard` 에만** 건다. 위에 걸면 그룹 제목·부제처럼
      벽지 위에 바로 놓인 글자까지 흰색이 되어 페이지가 통째로 안 읽힌다(시안 1판이
      그랬다). 카드 밖 글자는 아래 .viz-root 블록에서 «벽지가 깊어진 만큼» 갚는다.
   🔴 표준 등락색을 그대로 쓸 수 없다 — app.css `--pos #008300` 은 이 얼굴에서
      2.41:1 이다. 다크 테마에서 같은 색이 통하는 건 그쪽 얼굴이 0.012 로 더 어둡기
      때문이다. **색상은 그대로 두고 명도만 올린다**(상승 초록·하락 빨강 규칙 유지).
   실측 대비(얼굴 51,56,55): 본문 11.9 · 보조 6.7 · 흐린 4.9 · pos 5.3 · neg 4.3 */
html[data-glass="on"][data-theme="light"] .mcard {
  --text-primary: #ffffff;
  --text-secondary: #c3c2b7;
  --muted: #a8a6a0;
  --pos: #39c46b;
  --neg: #ff6b6b;
  --pos-bg: rgba(57, 196, 107, 0.22);
  --neg-bg: rgba(255, 107, 107, 0.22);
  --spark-base: #a8a6a0;
  --grid: #4e4e4a;
  --baseline: #5c5a56;
  --border: rgba(255, 255, 255, 0.18);
  --vol-fill: #4e4e4a;
  --day-band: rgba(255, 255, 255, 0.045);
  /* 계열색도 갈아야 한다 — 라이트 팔레트를 그대로 두면 실제 쓰이는 색이 미달한다
     (실측 얼굴 50,56,57 위에서 s1 2.70:1 · s7 1.39:1).
     🔑 그런데 **다크 팔레트를 그대로 쓸 수도 없다.** 이 얼굴(휘도 0.038)은 다크 표면
        (#1a1a19, 0.011)보다 **밝기 때문에**, 다크용으로 고른 색들이 여기서는 한 단계
        모자란다: s1 3.28 · s3 3.02 · s6 3.07 (3:1 을 간신히 넘는 값들이다. 얼룩 알파를
        한 번 건드리면 넘어간다).
     ⇒ 규칙: 카드가 **실제로 쓰는** 슬롯은 두 팔레트 중 밝은 쪽을 쓰거나, 둘 다
       모자라면 한 단계 밝혀서 **4:1 이상**을 확보한다. 실측 s1 4.40 · s3 4.43 · s4 5.51.
       (지금 카드가 주는 cols 는 s1·s4·s3 과 --text-secondary 뿐이다.)
     ⚠️ 나머지 5개는 다크 팔레트 값을 그대로 둔다 — 아무도 안 쓰는 색 5개를 화면에서
        확인도 못 하고 새로 만드는 건 근거 없는 값을 늘리는 일이다. 대신 «미달» 이라는
        사실을 여기 남기고 검사로 막는다: s2 #008300 **2.41:1** · s6 #d95926 3.07:1.
        verify_glass [1c] 가 «카드가 실제로 쓰는 색» 만 잣대로 잡으므로, 나중에 어떤
        카드가 s2 를 쓰기 시작하면 그때 실패로 잡힌다(3.0~3.5 구간은 경고로 찍는다). */
  --series-1: #5aa0f2;
  --series-2: #008300;
  --series-3: #e87ba4;
  --series-4: #eda100;
  --series-5: #199e70;
  --series-6: #d95926;
  --series-7: #9085e9;
  --series-8: #e66767;
}

/* 그룹 제목은 배경 위에 바로 놓이는 글자다 → 배경 얼룩 위에서도 읽히게 한 단계 진하게 */
html[data-glass="on"] .mg-t { color: var(--text-primary); }

/* 가장 흐린 글자색만 보정한다 — 배경 얼룩이 카드 바닥을 살짝 물들이는 값을 되돌려준다.
   --muted(#898781)는 **평면에서도 3.50:1** 로 이미 빠듯한 색이라, 얼룩을 얹으면
   2.94:1 로 3:1 아래로 떨어진다(verify_glass [1] 이 실제로 잡았다).
   설계를 약하게 만들어 이 색을 지키는 대신, 색을 갚아준다 — 얼룩을 걷는 게 아니라
   글자를 진하게(다크는 밝게) 해서 **평면 기준선을 회복**하는 쪽이 읽기에 낫다.
   값은 계산으로 골랐다: 라이트 −14, 다크 +36 에서 각각 3.56:1 / 4.88:1 로
   평면(3.50 / 4.85)을 넘어선다. 본문·보조색은 여유가 커서 손대지 않는다. */
/* 🔴 2026-08-20 재료 B 로 한 칸 더 갚았다: #7b7973 → #76746e (−5).
   재료 B 는 틴트를 0.55 → 0.08 로 걷으므로 **카드 바닥이 배경 얼룩 색을 그대로
   받는다** = 최악 지점에서 바닥이 어두워진다(판 245,242,238 → 카드 228,211,203).
   그래서 흐린색이 3.00:1 로 3:1 선 아래로 내려갔다(검증기 [1] 이 실제로 잡았다).
   −5 에서 3.22:1 로 회복한다. 평면 위 4.55:1 로 기준선(#898781 · 3.50:1)보다도 좋다. */
/* 🔴 2026-08-20 2차 — 벽지를 #e8e5de → #d4cec1 로 팠으므로 **한 번 더 갚는다.**
   여기서 판정 방법을 바꿨다: verify_glass [1] 의 `worst_bg()`(얼룩 3개 완전 겹침)를
   맨 벽지에 그대로 쓰면 지금 실서비스조차 «보조 4.41 · 흐린 2.60 미달» 이 나온다 —
   그 모델로는 판정이 안 된다. 그래서 **실제 렌더 픽셀**로 잰다(tools/probe_dcard_wall.py):
   `.viz-root` 를 visibility:hidden 으로 지워 벽지만 남긴 뒤, 카드 밖 글자 사각형 안의
   가장 어두운 벽지 점을 뽑는다. (사각형 밖 픽셀을 쓰면 **글자 잉크 자신**을 배경으로
   집는다 — 첫 판이 그랬고 대비 1.00:1 이 나왔다.)
     실측 최악 벽지  지금 (203,212,220) → W2 (171,185,197)
     흐린 #76746e   3.11:1 → **2.33:1** ✗   → #5e5c56 로 3.34:1
     보조 #52514e   5.29:1 → **3.96:1** ✗   → #46453f 로 4.80:1
   ⚠️ 보조색까지 갚아야 하는 건 ⑨절 W2 판정에는 없던 항목이다(그때는 스테이지 기준으로
      쟀다). 픽셀 기준이 더 보수적이라 이쪽을 따른다.
   ⚠️ 카드 **안**에서는 이 두 값이 위 .mcard 블록에서 다시 뒤집힌다(밝은 판). */
html[data-glass="on"] .viz-root { --muted: #5e5c56; --text-secondary: #46453f; }
html[data-glass="on"][data-theme="dark"] .viz-root {
  --muted: #adaba5;
  --text-secondary: #c3c2b7;   /* 다크는 app.css 값 그대로 복구 (위 줄이 덮으므로) */
}

/* ── 나머지 8장에 얹기 (2026-08-19) ─────────────────────────────────────────
   위(240행) 의 결론이 그대로 적용된다 — **배경(bed)은 body 에 깔려 있으므로 이미 9장
   전부에 있다**(`html[data-glass="on"] body`). 즉 여기서 할 일은 각 화면의 "면"을
   유리 재료로 바꾸는 것뿐이다.

   면을 세 종류로 갈랐다. 같은 재료를 전부에 바르지 않은 이유가 각각 있다:

   ① 판(panel) — `.tile · .chart-card · .picker · .cal-list · .modal-chart · .cal-day`
      배경 얼룩 위에 떠 있는 데이터 면. `.mcard` 와 같은 재료이고 **backdrop-filter 는
      같은 이유로 뺐다** — 판 뒤에 있는 건 완만한 그라디언트뿐이라 흐려도 같은 픽셀이
      나오고(probe_glass_blurdiff), 비용만 낸다(probe_glass_cost 2.6배).

   ② 붙박이 머리말 — `.rank thead th · .cal-dh`
      여기는 **흐린다.** 스크롤 컨테이너 안에서 행이 이 밑으로 지나가므로 뒤에 진짜
      내용이 있다 — 흐리지 않으면 글자가 머리말을 뚫고 겹쳐 읽힌다(반투명 틴트만으로는
      가릴 수 없다). iOS 가 붙박이 헤더에 유리를 쓰는 바로 그 자리다.

   ③ 떠 있는 것 — `.hold-tip`
      `position: fixed` 툴팁. 뒤에 표·차트가 그대로 있으므로 ①②와 달리 흐림이 제 몫을
      한다(레일·모달과 같은 부류).

   ⚠️ position 은 ①에서도 :where() 로 **특이도 0** 에 둔다. `.chart-layout > .picker`
      (chart 좌측 레일)와 `.cal-side` 가 `position: sticky` 인데, 그냥 relative 를 주면
      특이도가 이겨서 **레일이 스크롤에 안 붙는다** — /glasslab 에서 이미 한 번 겪은
      "화면은 멀쩡하고 기능만 조용히 사라지는" 사고다.

   ⚠️ border 는 `0` 이 아니라 **투명 1px**. 지우면 판이 2px 짧아져 유리를 켜고 끌 때
      레이아웃이 움직인다(검증기 [3] 이 매크로 카드에서 실제로 잡았다). */

/* ① 판 — 스크롤·붙박이가 없는 것만 정지 스페큘러(::after)를 갖는다.
   `.picker`(max-height+overflow)·`.cal-list`(overflow-y)는 제외한다 — 스크롤 컨테이너
   안의 absolute 자식은 내용과 함께 스크롤되므로 하이라이트가 위로 사라진다. */
:where(html[data-glass="on"] .tile),
:where(html[data-glass="on"] .chart-card),
:where(html[data-glass="on"] .modal-chart) { position: relative; }
html[data-glass="on"] .tile,
html[data-glass="on"] .chart-card,
html[data-glass="on"] .modal-chart {
  isolation: isolate;   /* ::after 의 z-index:-1 이 판 배경 아래로 빠지지 않게 묶는다 */
}
html[data-glass="on"] .tile,
html[data-glass="on"] .chart-card,
html[data-glass="on"] .modal-chart,
html[data-glass="on"] .picker,
html[data-glass="on"] .cal-list {
  border: 1px solid transparent;
  border-radius: var(--g-r-in);
  background: var(--g-pan-tint);
  box-shadow:
    inset 0 0 0 1px var(--g-hair),
    inset 0 1.5px 0 var(--g-rim-top),
    inset 0 -1px 0 var(--g-rim-bot),
    var(--g-shadow-sm);
  transition: background .16s ease, box-shadow .16s ease;
}
/* 재료 B 의 곱셈 — 틴트를 걷은 만큼 뒤 배경을 곱해서 되살린다(위 --g-pan-* 주석).
   ⚠️ `.modal-chart` 는 **제외**다. 조상 `.modal` 이 이미 backdrop-filter 를 갖고 있어서
      여기 또 걸면 «유리 위 유리» 가 된다 — 부모가 보여주는 «뒤» 는 부모가 그린 결과이므로
      자식 필터는 죽고 프레임만 먹는다(8/20 실측: 링 밝기를 2.6배 흔들어도 변화량 0.026,
      부모 필터를 떼면 6.995). 아래에서 재료 A(틴트)로 되돌린다.
   ⚠️ blur 는 넣지 않는다 — 카드와 같은 이유(뒤가 완만한 그라디언트라 그림이 안 바뀌는데
      프레임만 뛴다). 검증기가 판에서도 blur 재등장을 실패로 잡는다. */
html[data-glass="on"] .tile,
html[data-glass="on"] .chart-card,
html[data-glass="on"] .picker,
html[data-glass="on"] .cal-list {
  -webkit-backdrop-filter: var(--g-pan-filter);
          backdrop-filter: var(--g-pan-filter);
}
/* 상세 팝업 차트판만 재료 A(두꺼운 틴트) 로 남는다 — 위 ⚠️ 참고. 뒤가 흐려진 본문
   화면이라 틴트를 걷으면 뒤 차트가 앞 차트에 겹쳐 읽히는 문제도 그대로다(8/20 판정). */
html[data-glass="on"] .modal-chart { background: var(--g-tint-card); }
/* 판의 광택 — 2026-08-21 에 **면 스페큘러에서 띠 림광으로** 갈았다.
   왜 갈아야 했나: 위 재료 B 는 틴트(0.55→0.08)를 걷어서 뒤 색이 비치게 하는 것인데,
   면 위의 흰 radial(--g-spec, 라이트 0.55)이 남아 있으면 **그 색을 그대로 다시 지운다.**
   카드에서 이미 실측한 함정 ③ 이다(8/20 재료 B 주석의 «①을 걷어도 이게 남으면 색은
   그대로 지워진다»). 림은 마스크로 테두리 띠에 갇혀 있어 면과 글자를 건드리지 않는다.
   🎁 덤 — 카드·상세팝업·판이 **같은 recipe 한 벌**을 쓰게 된다. 모양이 갈리면 같은
      페이지에서 서로 다른 유리로 보이므로, 값을 복제하지 않고 이 블록만 둔다.
   ⚠️ 판은 **정지**다(--ga 기본값 312 = 정점 330° = «11시에서 빛이 든다»). `.modal-chart` 만
      glass.js 가 --ga 를 갱신해 추적한다 — 모달은 «내용 위에 진짜로 떠 있는» 유일한
      요소이고 조상 `.modal` 이 이미 추적 대상이다. 페이지 흐름에 박힌 타일·차트카드까지
      추적하면 한 화면에 판이 7~8개라 포인터를 옮길 때마다 전부 다시 그린다.
   ⚠️ 띠 두께는 카드와 같은 --g-ring-card(1.5px). 모서리 수식 ring < r×(1−1/√2) =
      20×0.2929 = 5.86px 안이다. */
html[data-glass="on"] .tile::after,
html[data-glass="on"] .chart-card::after,
html[data-glass="on"] .modal-chart::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  z-index: -1;
  padding: var(--g-ring-card);
  box-sizing: border-box;
  background:
    conic-gradient(from calc(var(--ga, var(--g-ga-rest, 312)) * 1deg),
                   transparent 0turn,
                   var(--g-rim-lit) var(--g-rim-p1, 0.05turn),
                   transparent var(--g-rim-e1, 0.18turn),
                   transparent var(--g-rim-s2, 0.44turn),
                   var(--g-rim-lit2) var(--g-rim-p2, 0.53turn),
                   transparent var(--g-rim-e2, 0.67turn),
                   transparent 1turn);
  -webkit-mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
}
/* 상세 팝업 차트판 — 여기만 포인터 추적(2026-08-20 3차).
   왜 위 블록을 덮어쓰는 방식인가 — 구조(content·inset·z-index)는 판 셋이 공유하고
   **위치만** 갈린다. 블록을 복제하면 다음에 구조를 고칠 때 한쪽만 고쳐진다.
   ⚠️ 세기(--g-spec)는 손대지 않았다. 이 판은 카드와 달리 **밝은 얼굴**(틴트 유지)이라
      0.55 흰빛이 색을 지울 재료 B 가 애초에 안 걸려 있다. 실측 대비도 여유가 있다 —
      추적최악 본문 17.26 · 흐린 5.86(라이트) / 7.55 · 3.29(다크). 카드와 달리 알파를
      깎을 이유가 없다.
   🔴 `backdrop-filter: saturate()` 는 **일부러 안 넣었다.** «안전안» 으로 제안했던
      그 항목인데, 재보니 효과의 방향이 틀렸다: 픽셀은 실제로 바뀌지만(라이트 평균
      채널차 16.35 · 잡음 0.0000) 얼굴 휘도가 0.854 → **0.947** 로 흰색에 붙는다.
      즉 «유리답게» 가 아니라 **더 불투명하게** 보이는 변화다(요청은 광택이었다).
      대비는 오히려 좋아지지만(본문 +1.43) 그건 판이 하얘져서 생긴 값이라 근거가 못
      된다. 다크는 애초에 거의 무효과(1.71 · 대비 −0.02). 광택은 위 추적으로 얻는다.
      ⚠️ 나중에 넣고 싶어지면 이 실측부터 다시 볼 것 — probe_spec_track.py 가 재준다.
   🔴 4차(2026-08-20) — 카드와 **같이** 면 스팟에서 띠 림광으로 갈았다. 사용자가 고른
      방식이 하나여야 하고(«손전등이 아니라 테두리»), 재보니 이 판에서 이득이 더 크다:
        다크  띠 비대칭 0.041 → **0.416** (10배). 3차의 면 스팟은 이 판에서 거의 안 보였다.
        라이트 0.095 → 0.126 (약간 낫다. 얼굴이 휘도 0.87 로 흰색에 붙어 있어 흰 림이
              올라갈 자리가 별로 없다 — 이건 재료가 아니라 밝은 판의 성질이다.)
      🎁 그리고 얼굴이 포인터에 **안 흔들린다**(Δ 0.016 → 0.000) → 잉크 대비가 포인터
         자리에 따라 오르내리지 않는다.
      띠 두께는 카드와 같다(--g-ring-card). 이 판도 반경이 --g-r-in(13px) 이라 상한이
      같다(3.81px). 4px 도 재봤는데 띠 최대 휘도가 **똑같았다**(면적만 늘고 정점은
      그대로) → 얇은 쪽을 쓴다. 5차에 2.5 → 1.5px 로 더 내렸다(위 --g-ring-card 주석
      의 ⑦B 대면 실측). 여기서 값을 따로 적지 않는 이유는, 두 판의 «얇음» 이 갈리면
      같은 페이지에서 카드와 상세팝업이 서로 다른 유리로 보이기 때문이다. */
/* 🔴 2026-08-21 — 이 블록의 선언을 **위 공용 블록으로 올렸다.** 판(타일·차트카드)도
   같은 림광을 쓰게 됐으므로(재료 B 가 면 스페큘러를 못 쓰게 만든다 — 위 주석), 여기
   같은 값을 한 번 더 적으면 다음에 한쪽만 고쳐진다. 위 실측·판단 기록은 그대로 남긴다.
   `.modal-chart` 가 판과 갈리는 건 **CSS 가 아니라 --ga 를 누가 갱신하나**(glass.js)뿐이다. */
html[data-glass="on"] .tile > *,
html[data-glass="on"] .chart-card > *,
html[data-glass="on"] .modal-chart > * { position: relative; }

/* 달력 날짜칸 — 42개짜리 작은 셀이라 스페큘러·그림자를 뺀다(넣으면 격자가 얼룩진다).
   ⚠️ hover 를 **다시 적어야 한다**. app.css 의 `.cal-day:hover{background:--day-band}`
      는 특이도 (0,2,0) 이고 아래 규칙이 (0,2,1) 이라, 안 적으면 hover 반응이 사라진다. */
html[data-glass="on"] .cal-day {
  border: 1px solid transparent;
  /* 반경도 판과 같은 계열로 맞춘다 — 다른 판은 유리를 켜면 --g-r-in(20px)이 되는데
     날짜칸만 app.css 의 6px 로 남아 있어서 같은 화면에서 모서리가 갈렸다.
     ⚠️ 20px 은 쓸 수 없다. 칸 높이가 44px 이라 20/44 = 0.45 로 알약이 된다
        (iOS 비율 0.2237 기준 9.8px). 작은 면용 토큰 --g-r-chip(13px)이 이 자리다. */
  border-radius: var(--g-r-chip);
  /* 판과 같은 재료 B (2026-08-21). 여기는 캔버스도 붙박이 머리말도 없어서 판을 그대로
     따라갈 수 있다. ⚠️ 셀이 42개라 backdrop-filter 도 42장이다 — 프레임 비용을 실측하고
     넣었다(tools/probe_glass_cost.py, /calendar). 되돌리려면 이 두 줄만 지우면
     --g-pan-tint 만 남아 «틴트만 얇아진» 상태가 된다(다크에서 격자가 흐려진다). */
  background: var(--g-pan-tint);
  -webkit-backdrop-filter: var(--g-pan-filter);
          backdrop-filter: var(--g-pan-filter);
  box-shadow: inset 0 0 0 1px var(--g-hair), inset 0 1px 0 var(--g-rim-top);
  transition: background .14s ease;
}
html[data-glass="on"] .cal-day:hover {
  background: var(--g-pan-tint-hi);
  -webkit-backdrop-filter: var(--g-pan-filter-hi);
          backdrop-filter: var(--g-pan-filter-hi);
}
/* ⚠️ `.today`(오늘 테두리)·`.sel`(고른 날) 도 (0,2,0) 이라 위 규칙에 먹힌다 — 되살린다.
   테두리를 투명으로 뒀으므로 표시는 box-shadow 쪽만 쓴다. 색은 app.css 원본 그대로. */
html[data-glass="on"] .cal-day.today {
  box-shadow: inset 0 0 0 1px var(--text-primary);
}
html[data-glass="on"] .cal-day.sel {
  background: var(--warn-bg);
  box-shadow: inset 0 0 0 1px var(--baseline);
}
html[data-glass="on"] .cal-day.sel.today {
  box-shadow: inset 0 0 0 1px var(--text-primary);
}

/* 표 — 둥근 모서리는 border-collapse 가 클리핑하지 않으므로 틴트만 얹는다.
   🔴 여기는 재료 B(곱셈)로 **안 옮긴다.** 표 안에 붙박이 머리말(`.rank thead th`)이
      있고 그것만 blur(14px)를 쓴다 — 행이 그 밑을 지나가므로 흐리지 않으면 글자가
      머리말을 뚫고 겹쳐 읽힌다. 표에 backdrop-filter 를 걸면 머리말은 «유리 위 유리» 가
      되어 그 blur 가 죽는다(8/20 실측: 부모 필터를 떼기 전에는 변화량 0.026).
      머리말의 가독성이 표 배경의 «유리다움» 보다 앞선다. */
html[data-glass="on"] .rank table,
html[data-glass="on"] .table-view table { background: var(--g-tint-card); }

/* ② 붙박이 머리말 — 여기만 흐린다(위 설명) */
html[data-glass="on"] .rank thead th,
html[data-glass="on"] .cal-dh {
  background: var(--g-tint-hi);
  -webkit-backdrop-filter: blur(14px) saturate(var(--g-sat));
  backdrop-filter: blur(14px) saturate(var(--g-sat));
}

/* ③ 떠 있는 툴팁 — **여기만 일부러 불투명하게 남긴다.**
   처음엔 레일·모달과 같은 부류로 보고 `--g-tint` + blur(18px) 를 걸었는데, 되돌렸다:
     · 이 툴팁은 **숫자 위에 뜨는 숫자**다. 뒤에 있는 게 차트선·표 숫자라 흐려도
       형태가 남고, 반투명 바닥 위에서 툴팁의 숫자와 겹쳐 읽힌다. 모달은 백드롭이
       뒤를 덮어주지만 툴팁에는 백드롭이 없다 — 같은 부류가 아니었다.
     · verify_us [23] 이 예전부터 **툴팁 바닥의 불투명성**을 단정한다. 그 검사가 잡는
       진짜 버그가 있다(툴팁을 `.viz-root` 밖에 붙이면 --surface-1 이 정의되지 않아
       바닥이 통째로 투명해진다). 유리로 바꾸면 그 감시가 무력해진다.
   그래서 **바닥은 불투명하게 두고 유리의 윤곽(림·곡률·그림자)만** 얹는다. */
html[data-glass="on"] .hold-tip {
  border-color: transparent;   /* 자리는 남긴다 — 0 으로 두면 툴팁이 2px 줄어든다 */
  border-radius: var(--g-r-in);
  background: var(--surface-1);
  box-shadow:
    inset 0 0 0 1px var(--g-hair),
    inset 0 1.5px 0 var(--g-rim-top),
    var(--g-shadow);
}

/* 작은 컨트롤 — .chip/.tab 은 위에서 이미 처리했다. 같은 생김새인데 클래스만 다른
   것들(선택 계열 알약 · 달력 월 이동 · 차트 레일 여닫기)을 같은 재료로 맞춘다. */
html[data-glass="on"] .selchip,
html[data-glass="on"] .cal-nav,
html[data-glass="on"] .rail-toggle {
  border-color: transparent;
  background: var(--g-tint-hi);
  box-shadow: inset 0 0 0 1px var(--g-hair), inset 0 1px 0 var(--g-rim-top);
  transition: scale .18s var(--g-spring), background .16s ease;
}
html[data-glass="on"] .cal-nav:active:not(:disabled),
html[data-glass="on"] .rail-toggle:active { scale: .955; }

/* 접근성 · 성능 ------------------------------------------------------------
   투명도를 줄이라는 OS 설정이면 재료를 불투명으로 되돌린다. 유리의 정체가
   ①매질이므로 blur 를 빼면 나머지(②③)만 남아 평범한 카드가 된다 — 그게 맞다. */
@media (prefers-reduced-transparency: reduce) {
  html[data-glass="on"] .glass,
  html[data-glass="on"] .mcard,
  html[data-glass="on"] .navrail,
  html[data-glass="on"] .evrail,
  html[data-glass="on"] .modal,
  html[data-glass="on"] .tile,
  html[data-glass="on"] .chart-card,
  html[data-glass="on"] .modal-chart,
  html[data-glass="on"] .picker,
  html[data-glass="on"] .cal-list,
  html[data-glass="on"] .cal-day,
  html[data-glass="on"] .rank table,
  html[data-glass="on"] .table-view table,
  html[data-glass="on"] .rank thead th,
  html[data-glass="on"] .cal-dh {
    /* .hold-tip 은 빠져 있다 — 이미 불투명 바닥이라 되돌릴 게 없다(위 ③ 참고) */
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    background: var(--surface-1);
  }
  /* ⚠️ hover 도 같이 걷어야 한다. 재료 B 부터 `.mcard:hover` 가 backdrop-filter 를
     **자기도** 선언하는데, 그 셀렉터(0,3,1)가 위 목록(0,2,1)을 이긴다 — 안 적으면
     투명도를 줄이라는 설정에서도 마우스를 올린 카드만 유리로 되살아난다. */
  html[data-glass="on"] .mcard:hover {
    background: var(--surface-1);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  /* 🔴 라이트 어두운 카드의 **잉크도 같이 되돌린다.** 위에서 카드 바닥을
     --surface-1(#fcfcfb, 거의 흰색)로 만드는데 잉크는 밝은 판(흰 글자)이 남아 있으면
     **흰 배경에 흰 글자**가 되어 카드가 통째로 안 읽힌다. 화면은 «빈 카드» 로 보이고
     콘솔 에러도 없다 — 눈으로도 검사로도 놓치기 쉬운 종류다.
     값을 다시 적지 않고 `inherit` 로 둔다: 부모(.viz-root)의 값을 그대로 받으므로
     app.css 라이트 팔레트가 자동으로 돌아오고, 나중에 팔레트를 바꿔도 안 갈린다. */
  html[data-glass="on"][data-theme="light"] .mcard {
    --text-primary: inherit; --text-secondary: inherit; --muted: inherit;
    --pos: inherit; --neg: inherit; --pos-bg: inherit; --neg-bg: inherit;
    --spark-base: inherit; --grid: inherit; --baseline: inherit;
    --border: inherit; --vol-fill: inherit; --day-band: inherit;
    --series-1: inherit; --series-2: inherit; --series-3: inherit;
    --series-4: inherit; --series-5: inherit; --series-6: inherit;
    --series-7: inherit; --series-8: inherit;
  }
  /* ⚠️ `.cal-day:hover` 도 `.mcard:hover` 와 같은 이유로 필터를 따로 걷어야 한다 —
     2026-08-21 에 이 hover 가 backdrop-filter 를 자기도 선언하게 됐고, 그 셀렉터
     (0,3,1)가 위 목록(0,2,1)을 이긴다. 안 적으면 마우스 올린 날짜칸만 유리로 되살아난다. */
  html[data-glass="on"] .cal-day:hover {
    background: var(--day-band);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  html[data-glass="on"] .glass::after,
  html[data-glass="on"] .navrail::after,
  html[data-glass="on"] .evrail::after,
  html[data-glass="on"] .tile::after,
  html[data-glass="on"] .chart-card::after,
  html[data-glass="on"] .modal-chart::after,
  html[data-glass="on"] .mcard::after { display: none; }
  /* 배경 얼룩도 함께 걷는다 — 유리를 불투명으로 되돌리면 벽지가 할 일이 없고,
     투명도를 줄이라는 설정의 취지는 "배경이 내용에 섞이지 않게" 이기도 하다. */
  html[data-glass="on"] body { background-image: none; }
}
@media (prefers-reduced-motion: reduce) {
  html[data-glass="on"] .glass-press,
  html[data-glass="on"] .chip,
  html[data-glass="on"] .tab,
  html[data-glass="on"] .navrail-item,
  html[data-glass="on"] .tile,
  html[data-glass="on"] .chart-card,
  html[data-glass="on"] .modal-chart,
  html[data-glass="on"] .picker,
  html[data-glass="on"] .cal-list,
  html[data-glass="on"] .cal-day,
  html[data-glass="on"] .selchip,
  html[data-glass="on"] .cal-nav,
  html[data-glass="on"] .rail-toggle,
  html[data-glass="on"] .mcard { transition: none; }
  html[data-glass="on"] .glass-press:active,
  html[data-glass="on"] .chip:active,
  html[data-glass="on"] .tab:active,
  html[data-glass="on"] .mcard:active,
  html[data-glass="on"] .cal-nav:active:not(:disabled),
  html[data-glass="on"] .rail-toggle:active,
  html[data-glass="on"] .navrail-item:active { scale: 1; }
  html[data-glass="on"] .glass::after,
  html[data-glass="on"] .mcard::after { transition: none; }
  /* 하이라이트 추적도 멈춘다 — 이건 CSS 로 못 막는다(glass.js 가 --gx 를 안 쓰는
     쪽으로 처리한다). 여기서는 남는 정지 하이라이트를 기본 위치에 고정해, JS 가
     이미 값을 박아둔 상태에서 설정이 바뀌어도 어긋난 자리에 멈춰 있지 않게 한다.
     ⚠️ 추적 대상이 늘면(2026-08-20: .mcard · .modal-chart) 여기도 같이 늘려야 한다.
        안 늘리면 «설정을 켠 순간 빛이 마지막 마우스 자리에 얼어붙는다» — 화면은
        멀쩡해 보이고 한 장으로는 구분도 안 된다. 검증기 [10] 이 이 목록을 단정한다.
        크기·세기는 각자의 원래 값을 그대로 쓴다(카드 140×100/0.16 · 판 70%×120%). */
  html[data-glass="on"] .glass::after,
  html[data-glass="on"] .navrail::after,
  html[data-glass="on"] .evrail::after {
    background:
      radial-gradient(160px 120px at 50% 0%, var(--g-spec), transparent 72%);
  }
  /* 카드는 4차부터 면 스팟이 아니라 **띠 림광**이다 → 위치가 아니라 **각도**를 못박는다.
     `::after` 에 --ga 를 직접 적으면 JS 가 요소에 박아둔 인라인 값을 이긴다(상속보다
     자기 선언이 앞선다) — gloss.css 가 쓰는 것과 같은 어법. */
  html[data-glass="on"] .mcard::after { --ga: var(--g-ga-rest, 312); }
  /* 모달 차트판도 4차부터 띠 림광이다 → 각도를 못박는다(카드와 같은 어법). */
  html[data-glass="on"] .modal-chart::after { --ga: var(--g-ga-rest, 312); }
}
