/* AirFact — 2026-07-30 리디자인.
   모바일이 기준이고 데스크톱은 같은 컴포넌트를 2열로 편다.
   토큰은 handoff/HANDOFF-REDESIGN.md §3. */
:root{
  --bg:#07090c;         /* 앱 셸 */
  --bg2:#0d1117;        /* 데스크톱 바깥 */
  --card:#10161e;       /* 카드 */
  --btn:#131a22;        /* 버튼 */
  --line:rgba(255,255,255,.08);
  --line2:rgba(255,255,255,.12);
  --fg:#e8edf2;         /* 주 */
  --mid:#aeb9c4;        /* 본문 */
  --dim:#8b98a5;        /* 보조 */
  --lab:#66727e;        /* 라벨 */
  --good:#49d3c4;       /* 좋음 강조 */
  --info:#5fc8f8;       /* 정보 강조 */
  --warn:#f8963c;
  --bad:#f2523b;
  --r-shell:28px; --r-dock:20px; --r-card:16px; --r-btn:14px; --r-sm:12px;
}
/* ── 서체 (2026-08-18 사용자 지시 "스토리 글자가 촌스럽다") ──
   Pretendard는 여태 **선언만 있고 로드가 없었다** — 맥·아이폰은 Apple SD Gothic Neo로
   멀쩡했지만, 인스타 캡처가 도는 GitHub 러너(Ubuntu)에는 그 폰트가 없어 스토리·릴스가
   폴백 고딕으로 찍혔다. 저장소에 동봉해 어디서 열어도 같은 글자가 나오게 한다.
   ⚠️ CDN이 아니라 **동봉**이다 — 러너 캡처가 외부 CDN 장애·차단에 흔들리면 안 된다.
   가변 1파일(woff2 2MB, /assets/* 7일 캐시). display:swap — 폰트보다 글이 먼저다.
   ⚠️ 릴스 인트로(ffmpeg drawtext)는 같은 서체의 TTF를 pipeline/insta/assets에서 쓴다 —
      한쪽만 바꾸면 인트로와 본편 글자가 다른 서체가 된다. SIL OFL 1.1. */
@font-face{
  font-family:'Pretendard Variable';
  src:url('../assets/fonts/PretendardVariable.woff2') format('woff2-variations'),
      url('../assets/fonts/PretendardVariable.woff2') format('woff2');
  font-weight:45 920;font-style:normal;font-display:swap}
*{box-sizing:border-box}
html{background:var(--bg2)}
body{margin:0;background:var(--bg);color:var(--fg);
  font-family:'Pretendard Variable',Pretendard,'Apple SD Gothic Neo',system-ui,-apple-system,sans-serif;
  -webkit-font-smoothing:antialiased;
  /* 한글은 단어 중간에서 끊으면 안 읽힌다. 한 글자 고아도 막는다. */
  word-break:keep-all;
  /* iOS 더블탭 확대만 끄고 두 손가락 확대는 남긴다 */
  touch-action:manipulation;
  /* 브라우저 기본 '당겨서 새로고침'을 끄고 우리 것(app.js bindPull)으로 일원화 */
  overscroll-behavior-y:contain}
button{font-family:inherit;color:inherit;cursor:pointer}
button,input,select,canvas{touch-action:manipulation}
a{color:var(--info);text-decoration:none}
.dim{color:var(--dim)}
.tabhide{display:none !important}

/* ── 스냅샷 모드 (?snap=1 — 인스타 캡처 전용, app.js가 body에 .snap을 단다) ──
   ① 오존 요소 전부 제외 (2026-08-10 사용자 지시): 판정 카드의 경보 줄(#ozonote) ·
      오존 카드(#o3fact) · 시간띠 아래 보라 띠(.o3bar) · 그 범례 줄(#o3cap)
   ② 상단 여백 56px: 스토리 좌상단의 프로필 아이콘·계정명 UI가 로고를 덮지 않게 */
body.snap #ozonote, body.snap #o3fact, body.snap #o3cap, body.snap .o3bar{display:none !important}
body.snap{padding-top:56px}
/* ③ 하단 스크롤 여백: view=side가 먼지 구성을 **맨 위로** 올리려면 문서가 뷰포트보다
   충분히 길어야 한다 (짧으면 최대 스크롤에 걸려 범례부터 찍힌다 — 실제로 그랬다).
   남는 검은 여백은 '어디서 온 공기' 목록이 길어질 날을 위한 예비 공간이기도 하다. */
body.snap #shell{padding-bottom:1280px}
/* ④ 조작 UI 제거 (2026-08-10 시청자 관점 감사): 스토리 시청자는 아무것도 누를 수 없다.
   메뉴 ☰ · 지도 조작 6버튼 · '지금으로' · 캘린더 · 위험/자연 칩(눌러야 내용이 나와
   캡처에선 빈 상자다) 전부 노이즈. */
body.snap #menuwrap, body.snap #mapbtns, body.snap #bnow, body.snap #daybtn,
body.snap #chips, body.snap #locHint{display:none !important}
/* ⑤ 판독성 확대: 같은 감사에서 잰 값 — 시청자의 '답'인 행동 문장이 폰 환산 9.8pt로
   조작 버튼보다 작았다. 스토리는 5초 스침이라 핵심 줄을 신문 헤드라인 위계로 올린다.
   (확대 합계 ≈ +25px 세로 — 주의 문구 있는 날 시간띠 눈금이 하단 UI에 살짝 물리는
   것은 감수, 색 막대 자체는 보인다) */
body.snap #bandchip{font-size:22px}
body.snap #vsub{font-size:15px}
body.snap #venthead{font-size:19px}
body.snap #moments .moment b{font-size:16px}
body.snap #moments .mlab, body.snap .lab{font-size:13px}
body.snap #hourticks span{font-size:13px}
body.snap #legendtitle{font-size:12.5px}
body.snap #legendbody .lg span{font-size:11.5px}
body.snap #timepill{font-size:14px}
body.snap #cofactline, body.snap #origin{font-size:16px}
body.snap #dockcap{font-size:12.5px}
/* ⑥ 주의 문구는 **캡처에서 뺀다** (2026-08-24 사용자 지시).
   이 자리는 두 번 밟혔다. 처음엔 주의 2줄이 카드 2개(+190px)로 부풀어 시간띠를 화면
   밖으로 밀었고, 패딩·간격을 절반으로 줄여 101px로 **완화만** 했다. 그래도 모자랐다 —
   실측(비 오는 날 재현, 540×960): 맑은 날 시간띠 아래끝 913px(여유 47px) ↔ 비 오는 날
   1029px로 **69px 초과**. advisory는 판정 카드와 지도 **사이**에 있어서, 늘어나면 그
   아래(지도·시간띠)가 통째로 밀린다. 스토리는 스크롤이 없으니 밀린 만큼 잘린다.

   왜 지워도 되나: **캡션이 같은 문장을 이미 말한다** — caption.py가 `advisory`의 앞
   2줄을 `💬 …`로 그대로 재사용한다(검증된 문구 재사용 원칙). 그림에서 빼도 읽는 사람은
   같은 정보를 받고, 대신 컷 높이가 **날씨와 무관하게 일정**해진다.
   ⚠️ 라이브 화면에서는 그대로 보인다 — 여기 규칙은 `body.snap`(캡처)에만 걸린다. */
body.snap #advisory{display:none !important}
/* ⑩ svid = 스토리 1컷 타임랩스 — 배치는 스토리 그대로, 높이는 **앱이** 못 박는다(app.js).
   여기서는 자리를 만든다.

   쓸 수 있는 세로는 960이 아니라 **769px**이다 — 제목(고정 0~125)과 꼬리말(고정 894~960)
   사이. 높이를 못 박으면 모든 블록이 그날 최대치로 동시에 서므로 자연 상태보다 최대
   +72px가 되고, 실측에서 시간띠 아래끝 955px → **시간 라벨(0시~24시)이 꼬리말에 잘렸다.**
   지도·범례는 못 줄인다(캔버스가 10:7 고정이라 폭이 높이를 정한다).

   그래서 영상 컷에서는 #moments(환기 추천 · 가장 맑을 때)를 뺀다 — 87px + 칸 14px.
   정보가 사라지지 않는다: 환기 시점은 판정 카드의 환기 줄이 이미 말하고("12시간 뒤부터
   공기가 좋아집니다"), 언제가 맑은지는 아래 시간띠가 색으로 말한다. 게다가 이 블록은
   66↔87px로 **흔들림의 한 축**이라, 빼면 못 박을 대상도 하나 준다. */
/* ⚠️ #side까지 숨긴다. 1컷은 **시간띠에서 끝난다** — 그 아래 먼지 구성·CO·출처 카드는
   2컷 몫이다. 정지 컷 시절엔 화면 밖이라 안 보였는데, 구간이 짧은 날은 시간띠가 낮아져
   그 아래가 화면에 걸린다(실측 8/25: 시간띠 아래끝 840 · 꼬리말 894 → 먼지 구성이 삐져나옴). */
body.snap.svid #moments,
body.snap.svid #side{display:none !important}

/* ⑪ svid2 = 스토리 2컷 타임랩스 — **보는 시각(시간띠) + 일산화탄소 + 어디서 온 공기**
   (2026-08-25 사용자 지시). 위에 '보는 시각'이 흐르고, 그 아래 두 카드가 그 시각의 공기를
   설명한다: 얼마나 태운 공기인가(CO) · 어디서 왔나(궤적 판정).

   ⚠️ **지도(레이더)는 뺀다** — 1컷이 이미 지도다. 두 컷이 같은 그림이면 넘길 이유가 없다.
   판정 카드·환기 카드도 1컷 몫이고, 먼지 구성 막대는 시간이 지나도 안 움직여 영상에서
   할 일이 없다. (오존 카드는 body.snap이 이미 숨긴다 — ①)
   DOM 순서가 verdict → mapwrap → moments → dock → side라, 앞 셋을 숨기면 시간띠가 맨 위로
   올라오고 그 아래 카드가 따라온다 — 자리를 옮기지 않고 배치가 나온다.
   높이: 시간띠 233 + CO 88 + 출처 94 + 칸 28 = 443 → 오염일 권역 목록에 자리가 넉넉하다. */
body.snap.svid2 #verdict,
body.snap.svid2 #moments,
body.snap.svid2 #mapwrap,
body.snap.svid2 #side > .card:first-child{display:none !important}

/* ⑦ 스냅샷 제목 (?title=머리말|보조줄) — 인스타가 API로 텍스트를 못 얹으므로 굽는다.
   위치는 상단 고정: 스토리는 **첫 1초에 무엇을 보는지**가 정해져야 하고, 하단
   350px은 답장창 UI에 가린다. 위쪽 56px 여백(②)은 프로필 UI를 피하려는 것이다. */
/* ⚠️ 제목·꼬리말은 **고정 배치**다. view=side로 스크롤하면 문서 맨 위에 있던 제목이
   화면 밖으로 밀려 2컷에 제목도 주소도 안 남았다(실제로 그랬다). 고정하면 어느 컷이든
   같은 자리에 있어 스토리 두 장이 한 세트로 읽힌다.
   위 56px은 프로필 UI, 아래 여백은 답장창 UI를 피하는 값이다. */
body.snap #snaptitle{
  display:block;position:fixed;top:0;left:50%;transform:translateX(-50%);
  width:100%;max-width:430px;padding:56px 18px 14px;z-index:40;
  background:linear-gradient(180deg,var(--bg) 92%,rgba(7,9,12,0));
  font-size:27px;font-weight:800;
  letter-spacing:-.02em;line-height:1.22;color:#fff}
/* 고정 제목이 첫 카드를 덮지 않게 — 흐름 콘텐츠는 그만큼 내려서 시작한다.
   scroll-margin-top은 scrollIntoView가 존중하므로 view=side도 제목 아래에 앉는다.
   ⚠️ 제목이 있을 때만이다(`hastitle`). 제목의 56px 상단 패딩이 ②의 body 여백과
   겹치면 빈 띠가 생겨 **시간띠가 화면 밖으로 밀린다**(실제로 그랬다) — 여기서 되돌린다.
   브랜드 줄도 숨긴다: 하단 주소 꼬리말과 같은 말이고, 그 자리가 시간띠 몫이다. */
body.snap.hastitle{padding-top:0}
body.snap.hastitle #shell{padding-top:132px}
body.snap.hastitle header{display:none}
body.snap #side, body.snap #dock{scroll-margin-top:132px}
body.snap #snaptitle i{
  display:block;margin-top:5px;font-size:14px;font-weight:600;
  font-style:normal;letter-spacing:0;color:var(--dim)}
/* 제목이 주인공이 되도록 브랜드 줄은 한 줄로 줄인다 (출처 표기는 유지 — 워터마크다) */
body.snap #logo{font-size:14px}
body.snap #logo .dom{font-size:11.5px}
body.snap #tagline{display:none}
/* ⑧ 2컷 꼬리 정리 (2026-08-12 사용자 지시): 공식 예보 탭부터 아래(출처·약관 푸터)를
   전부 뺀다 — 스토리에서 눌러 볼 수 없는 접힘 카드와 긴 출처 목록이 화면만 먹는다.
   대신 **주소 한 줄**을 남긴다: 스토리를 본 사람이 갈 곳이 있어야 하고,
   출처 표기가 사라지는 자리를 이 워터마크가 대신한다. */
body.snap #officialcard, body.snap footer{display:none !important}
/* ⑨ 릴스 모드 (?view=reel) — 타임랩스는 **지도가 주인공**이어야 한다.
   지도 캔버스는 종횡비가 고정(위경도 비율)이라 세로로만 못 키운다 → 폭을 넓히는 게
   유일한 수단이다. 셸 최대폭 430 → 뷰포트 전체(540)로 열면 지도가 28% 커진다.
   환기·맑을때 카드는 뺀다: 정지 화면용 요약이라 움직이는 그림 옆에서 자리만 먹는다.

   ⭐⭐ 안전 여백이 이 블록의 핵심이다 (2026-08-14 사용자 지적 "많이 잘린다").
   인스타 릴스는 9:16 영상을 **화면 높이에 맞춰 채우고 좌우를 잘라낸다.** 요즘 폰은
   19.5:9~20:9라 1080폭 중 **좌우 97~108px이 통째로 사라진다** — 실제 게시분에서
   '종합'의 종, 'air-fact.com'의 m, 날짜의 8이 잘려 나갔다. 게다가 오른쪽 세로줄
   버튼(좋아요·댓글·공유·더보기)이 x≈880~1010 · y≈1180~1750을 덮고, 아래 ~500px은
   캡션·계정 UI 몫이다. 그래서 규칙은 하나다 — **글자는 안쪽, 그림만 끝까지.**

     --rp     62px(=이미지 124px)  좌우 안전 여백. 20:9 폰의 108px 잘림 + 16px 여유
     --rside  56px 더             지도 **아래** 요소(범례·꼬리말)의 오른쪽 추가 여백
                                  — 그 높이대는 버튼 열과 겹친다
     --rtop  120px(=240px)        인스타 상단 UI 아래에서 제목이 시작하게

   ⚠️ 이 값을 줄이려면 **실제로 게시해 보고** 줄일 것. 잘린 영상은 되돌릴 수 없고,
      우리가 재현할 수 없는 것은 시청자 폰의 종횡비다. 지도는 일부러 끝까지 보낸다 —
      그림이 잘리는 건 무해하고 오히려 꽉 찬 화면으로 보인다. */
body.snap.reel{--rp:62px;--rside:56px;--rtop:120px;
  /* 바탕은 **순검정** (2026-08-19 사용자 지시) — 앱 셸의 남색빛 검정(#07090c/#0d1117)이
     인트로·아웃트로 카드(#000) 사이에서 '남색 페이지'로 보였다. 릴스 모드에서만 --bg를
     덮는다 — 앱 테마는 그대로. 범례 scrim도 같은 검정으로 잇는다. */
  --bg:#000;background:#000}
html:has(body.snap.reel){background:#000}
body.snap.reel #shell{max-width:none;padding-left:var(--rp);padding-right:var(--rp)}
body.snap.reel #verdict, body.snap.reel #advisory, body.snap.reel #moments,
body.snap.reel #dock, body.snap.reel #side{display:none !important}
body.snap.reel.hastitle #shell{padding-top:var(--rtop);padding-bottom:0}
/* ⚠️ transform을 반드시 지운다 — 스토리용 고정 배치의 중앙정렬(left:50% +
   translateX(-50%))이 흐름 배치에서도 살아남아 **제목이 화면 밖(-238px)으로**
   밀려 나갔다. position만 static으로 바꾸면 left는 무시되지만 transform은 안 무시된다. */
body.snap.reel #snaptitle, body.snap.reel #snapfoot{transform:none;left:auto}
body.snap.reel #snaptitle{
  position:static;max-width:none;padding:0;background:none;
  display:flex;flex-direction:column-reverse}   /* 날짜가 위, 큰 제목이 아래 */
body.snap.reel #snaptitle b{
  display:block;font-size:34px;line-height:1.16;letter-spacing:-.03em}
body.snap.reel #snaptitle i{
  margin:0 0 9px;font-size:15px;font-weight:700;color:var(--dim);letter-spacing:.03em}
/* 상황 한 줄 — 판정 카드 자리를 대신한다. 왼쪽 굵은 띠로 '속보'의 인상을 준다.
   띠 색은 app.js가 등급색(?tone)으로 갈아 끼운다 — 제목 글자색과 한 세트다. */
body.snap.reel #snapnote{
  display:block;margin:12px 0 0;padding:3px 0 3px 14px;
  border-left:4px solid var(--line2);
  font-size:18px;font-weight:700;line-height:1.42;color:var(--fg)}
/* 지도는 화면 끝까지 — 셸 좌우 여백을 음수 마진으로 걷어낸다 (가장 큰 그림) */
body.snap.reel #mapwrap{position:relative;margin:12px calc(-1 * var(--rp)) 0}
/* 지도를 세로로 조금 잘라 아래 띠 자리를 만든다 (2026-08-15).
   ⚠️ 자르는 것은 **왜곡이 아니다** — 캔버스는 제 종횡비로 그리고 우리는 그 가운데를
      들여다볼 뿐이라 한반도 모양은 그대로다. 시야를 21°로 넓힌 뒤라 위아래로 각각
      0.4°쯤 덜 보이는 것은 바다뿐이고, 라벨은 세로 클램프(map.js)가 안쪽으로 당긴다. */
body.snap.reel #canvasbox{border-radius:0;height:350px;overflow:hidden}
body.snap.reel #canvasbox canvas{display:block;margin-top:-14px}
/* 레이어 알약(종합/초미세)도 뺀다 — 릴스 시청자는 못 누르고, 지도 위 유일한
   '앱 UI'라 영상에서 가장 티가 난다. 무엇을 보는지는 제목·범례가 말한다. */
body.snap.reel #layerbar{display:none !important}
/* 범례는 **지도 위에 얹는다** (방송 자막식). 두 가지를 한꺼번에 푼다:
   ① 카드 상자가 지도 아래에 따로 뜨면 '웹페이지를 잘라 붙인 것'으로 보인다
   ② 흐름에서 빼면 세로 130px이 뜬다 — 그만큼 꼬리말이 인스타 캡션 UI(아래 ~300px)
      에서 멀어진다. 겹치면 우리가 못 고치는 자리에서 글이 가려진다.
   얹는 자리는 지도 아래쪽 바다(규슈 남쪽)라 가리는 정보가 없다. 그래도 지도 색 위라
   scrim(아래로 갈수록 짙어지는 그라데이션)을 깔아 눈금 글자가 안 묻히게 한다.
   ⚠️ scrim은 **끝까지**, 글자만 안쪽이다 — 어둠을 안전 여백에 맞춰 자르면 지도 위에
      네모 상자가 뜬 것처럼 보인다(한 번 그렇게 만들어 보고 되돌렸다). */
body.snap.reel #legendcard{
  position:absolute;left:0;right:0;bottom:0;
  padding:26px calc(var(--rp) + var(--rside)) 12px var(--rp);
  border:0;border-radius:0;
  background:linear-gradient(0deg,rgba(0,0,0,.97) 58%,rgba(0,0,0,0))}
body.snap.reel #legendhead{padding:0 0 6px}
body.snap.reel #legendtitle{font-size:13.5px;color:var(--lab);letter-spacing:.02em}
body.snap.reel #legendbody{padding:0}
body.snap.reel #legendbar{height:9px}
body.snap.reel #legendbody .lg{margin-top:5px}
/* nowrap 필수 — 안전 여백만큼 색막대가 좁아지면 '75–100'이 두 줄로 접힌다 */
body.snap.reel #legendbody .lg span{font-size:13px;white-space:nowrap}
/* ⑨-b 아래 띠 (2026-08-15 사용자 요청) — **"언제부터 얼마나"**를 그림으로.
   제목·상황 줄은 문턱을 넘는 시각만 말해서, 보는 사람은 좋았다 나빠진 건지 그 반대인지
   몰랐다. 앞으로 48시간을 3시간 칸 16개로 깔면 방향과 폭이 한눈에 읽히고, 영상이
   흐르면 현재 칸(흰 테두리)이 함께 움직여 진행 막대 노릇도 한다.
   자리는 지도 아래 빈 곳 — 인스타 캡션 UI(대략 y>1620)에 닿지 않는 선까지만 쓴다. */
body.snap.reel #reelband{
  margin:8px var(--rside) 0 0;padding:0}
/* 머리말 — 날짜·시각(작게) 위, 색 표식 + 위험도 + 점수(크게) 아래.
   영상이 흐르면 둘 다 바뀐다: 시청자는 '이 날짜 이 시각의 위험도'를 눈으로 따라간다.
   ⚠️ 표식은 **이모지가 아니라 색 사각형**이다 — 이모지는 폰트가 없으면 두부(□)로
      그려지는데, 캡처 러너의 폰트를 우리가 못 정한다. 영상은 글자가 아니라 그림이라
      한 번 잘못 그려지면 그대로 박제된다. 모양을 아래 칸과 같게 두어 눈으로 이어진다. */
body.snap.reel .rbwhen{
  font-size:13px;font-weight:700;color:var(--dim);letter-spacing:.02em}
body.snap.reel .rbrisk{
  display:flex;align-items:center;gap:9px;margin-top:3px;line-height:1.1;
  font-size:23px;font-weight:800;letter-spacing:-.02em;color:#fff}
/* ⚠️ **동그라미다 — 아래 칸(사각형)과 일부러 모양을 다르게 뒀다.**
   머리말은 지도와 같은 *연속 시각*의 등급이고 칸은 *3시간 구간*이라, 구간 경계에서
   둘의 색이 다를 수 있다(실제로 위험도 15 = 보통인데 그 구간 칸은 양호였다).
   같은 모양이면 "칩은 양호인데 막대는 보통"으로 읽혀 버그처럼 보인다 — 이 저장소가
   이미 세 번 겪은 종류다(HANDOFF §8 시간띠 색 사고). 모양을 가르면 '지금 값'과
   '구간 개요'라는 서로 다른 읽을거리로 읽힌다. */
body.snap.reel .rbrisk i{
  width:20px;height:20px;border-radius:50%;flex:none}
body.snap.reel .rbrisk span{font-size:17px;font-weight:700;color:var(--mid)}
body.snap.reel .rbrisk b{font-size:27px;letter-spacing:-.03em}
/* 칸은 **붙여서** 깐다 — 띄우면 16개가 점선처럼 보여 '흐름'이 안 읽힌다 */
/* 머리말과 칸 사이는 좁게(2026-08-18 사용자 지시 — 아래 공간 확보). 11→5px, 머리말
   line-height도 1.1로 붙였다. 칸 높이는 그대로 21px. */
body.snap.reel .rbcells{
  display:flex;gap:2px;margin-top:5px;height:21px}
body.snap.reel .rbcells span{
  flex:1;border-radius:2px}
/* 현재 칸 — 흰 테두리가 영상에서 움직인다. 색을 안 바꾸고 테두리만 얹어
   등급 색을 가리지 않는다. */
body.snap.reel .rbcells span.on{
  outline:3px solid #fff;outline-offset:1px;border-radius:3px}
body.snap.reel .rbticks{
  display:flex;justify-content:space-between;margin-top:5px;
  font-size:12px;color:var(--lab);letter-spacing:.01em}
/* 꼬리말 — 주소를 **왼쪽으로** 옮긴다. 오른쪽 끝은 인스타 버튼 열이 덮는 자리라
   워터마크가 거기 있으면 가려지거나 잘린다(실제 게시분에서 'air-fact.cor'). */
/* ⚠️ width:auto도 함께 — 스토리용 `width:100%`가 남아 있으면 margin-right가 폭을
   못 줄이고 그냥 넘쳐서, 구분선만 버튼 열 밑으로 들어간다(재 보고 알았다). */
/* 2026-08-18 사용자 지시 — 세로 3줄: 안내 / 고지("예측 자료이므로 변경될 수 있습니다") /
   맨 아래 주소 'Air-Fact.com'(크게). 주소가 **마지막 줄**이어야 시선이 거기서 끝난다.
   오른쪽 여백(--rside)은 그대로 — 인스타 버튼 열이 덮는 자리다. */
body.snap.reel #snapfoot{
  position:static;max-width:none;width:auto;background:none;
  flex-direction:column;align-items:flex-start;gap:2px;
  margin:6px var(--rside) 0 0;padding:7px 0 0;
  border-top:1px solid var(--line);font-size:13.5px;line-height:1.3}
body.snap.reel #snapfoot .fnote{font-size:12.5px;color:var(--lab)}
/* 주소가 마지막 줄 — 인스타 캡션 UI(이미지 y≈1620~)에 안 물리게 전체를 위로 조였다.
   실측(라이브 데이터 캡처): 주소 하단 1661 → 조인 뒤 ~1610. */
body.snap.reel #snapfoot b{margin:4px 0 0;font-size:19px;letter-spacing:-.01em}
body.snap #snapfoot{
  display:flex;align-items:baseline;gap:9px;
  position:fixed;bottom:0;left:50%;transform:translateX(-50%);
  width:100%;max-width:430px;padding:16px 18px 30px;z-index:40;
  background:linear-gradient(0deg,var(--bg) 92%,rgba(7,9,12,0));
  font-size:13px;color:var(--lab);letter-spacing:.01em}
body.snap #snapfoot b{
  margin-left:auto;font-size:17px;font-weight:800;color:var(--fg);letter-spacing:-.01em}
.lab{font-size:11.5px;color:var(--lab);letter-spacing:.02em}

#shell{max-width:430px;margin:0 auto;padding:0 16px 28px}

/* ── 헤더 ── */
/* 로고 줄은 위아래로 붙여 잡는다 — 태그라인이 한 줄 늘어난 만큼만 헤더가 커지고
   지도는 그대로 있게. align-items:center면 ☰가 두 줄의 한가운데로 내려가 어색하다. */
header{display:flex;align-items:flex-start;justify-content:space-between;padding:18px 4px 0}
#brand{cursor:pointer;user-select:none;min-width:0}
#brand:active{opacity:.6}
h1{font-size:17px;font-weight:800;margin:0;display:flex;align-items:baseline;gap:8px;
  letter-spacing:-.01em}
h1 .dom{font-size:11px;font-weight:400;color:var(--lab);letter-spacing:0}
/* 태그라인 — 처음 온 사람에게 "이게 뭐고 뭐가 다른가"를 한 줄로.
   화면의 큰 숫자가 '농도'가 아니라 '위험도'라는 것도 여기서 미리 말한다. */
#tagline{margin:2px 0 0;font-size:11px;line-height:1.35;color:var(--lab);letter-spacing:-.01em}

#menuwrap{position:relative}
#menubtn{width:44px;height:44px;background:var(--btn);border:1px solid var(--line2);
  border-radius:var(--r-btn);font-size:17px;display:flex;align-items:center;justify-content:center}
#menubtn:active{background:#1a222c}
#menu{display:none;position:absolute;right:0;top:calc(100% + 6px);z-index:40;
  background:#131a22;border:1px solid var(--line2);border-radius:var(--r-btn);
  padding:6px;min-width:150px;box-shadow:0 16px 40px rgba(0,0,0,.6)}
#menu.show{display:block}
#menu button{display:block;width:100%;text-align:left;background:none;border:none;
  padding:11px 12px;font-size:14px;border-radius:10px;color:var(--mid);min-height:44px}
#menu button:hover{background:#1a222c}
#menu button.on{color:var(--fg);font-weight:700;background:#18212b}

/* ── 낡음 배지 ── 푸터의 데이터 시각 줄 위에 한 줄로.
   경고색 큰 띠를 첫 화면에 두면 멀쩡한 숫자까지 의심하게 만든다. 색은 라벨 톤에
   가깝게 낮추고 왼쪽 띠 하나로만 표시한다 — 안 보이게 하려는 게 아니라, 놀라게
   하지 않으면서 읽히게 하려는 것이다. */
#stale{display:none;color:#c8a63e;border-left:2px solid #6b5220;
  padding:2px 0 2px 8px;font-size:11px;line-height:1.55;margin:0 0 7px}
#stale.show{display:block}
#stale.act{cursor:pointer;text-decoration:underline;text-underline-offset:2px}

/* ── 본문 흐름 ── */
#main{display:flex;flex-direction:column;gap:14px;margin-top:14px}

/* ── 판정 스트립 ── */
#verdict{background:#0e1a1b;border:1px solid rgba(73,211,196,.22);border-radius:var(--r-card);
  overflow:hidden}
/* 등급 8단계(2026-08-01). 같은 농도에는 같은 배경이 나오도록 옛 자리를 그대로 옮겼다:
   옛 b0,b1(아주좋음·좋음) -> b0,b1,b2(+양호) · 옛 b2(보통) -> b3,b4(보통·주의)
   옛 b3(나쁨) -> b5 · 옛 b4,b5(매우나쁨·위험) -> b6,b7 */
#verdict.b0,#verdict.b1,#verdict.b2{background:#0e1a1b;border-color:rgba(73,211,196,.22)}
#verdict.b3,#verdict.b4{background:#1a1a12;border-color:rgba(242,213,78,.22)}
#verdict.b5{background:#1c1510;border-color:rgba(248,150,60,.28)}
#verdict.b6,#verdict.b7{background:#1f1211;border-color:rgba(242,82,59,.3)}
#verdictmain{width:100%;display:flex;align-items:center;gap:12px;background:none;border:none;
  padding:12px 14px;text-align:left;min-height:44px}
#face{flex:none;line-height:0}
svg.face{width:44px;height:44px;display:block;
  stroke:var(--fc);stroke-width:2.4;fill:none;stroke-linecap:round;stroke-linejoin:round}
svg.face .ring{stroke-width:2.4;opacity:.92}
svg.face .f{fill:var(--fc);stroke:none}
svg.face .fill{fill:var(--fc);fill-opacity:.2}
svg.face .lens{fill:#07090c}
#vtext{flex:1;min-width:0;display:block}
#vline1{display:flex;align-items:baseline;gap:7px;flex-wrap:wrap}
#bandchip{font-size:18px;font-weight:800;line-height:1.2}
/* 등급을 PM이 아니라 오존·CO가 올렸을 때 붙는 꼬리표.
   ⚠️ 이게 없으면 "나쁨 21점"과 "주의 27점"이 나란히 놓여 **거꾸로 보인다** —
   숫자는 PM 기준이고 단어는 결합 등급이라는 걸 화면이 말해 주지 않아서다. */
#bandby{font-size:11px;font-weight:700;line-height:1.2;padding:2px 6px;border-radius:6px;
  background:rgba(155,140,255,.16);border:1px solid rgba(155,140,255,.38);color:#cfc4ff}
#vsub{font-size:12px;color:var(--dim);font-weight:500;font-variant-numeric:tabular-nums}
/* 위험도 숫자 — 등급 단어 다음으로 크게 (2026-08-08 사용자 지시 "숫자 더 크게").
   글자색은 밝은 전경색: 칩이 이미 등급색을 말하므로 숫자까지 물들이면 과하다. */
#vsub #score{font-size:16px;font-weight:800;color:var(--fg)}
#venthead{display:block;font-size:13.5px;font-weight:600;margin-top:2px;line-height:1.4}
/* 오존 경고 — 등급과 **별개 채널**이다. 등급 색을 쓰지 않고 청자색으로 구분한다.
   PM 등급이 '좋음'이어도 이 줄만 켜질 수 있다 — 그게 이 설계의 요점이다. */
#ozonote{display:none;font-size:11.5px;line-height:1.5;margin-top:6px;
  padding:6px 9px;border-radius:9px;color:#cfc4ff;
  background:rgba(155,140,255,.12);border:1px solid rgba(155,140,255,.3)}
#ozonote.show{display:block}
#ozonote b{color:#d8cfff;font-weight:700;margin-right:4px}
/* 오존 경보 강조 — 경보·위험(옛 4·5)에만. 8단계에서 **6·7**로 옮겨졌다.
   안 옮기면 '주의'(새 5)가 경보 색을 쓰고 정작 '위험'(새 7)이 밋밋해진다. */
#ozonote.o3-6,#ozonote.o3-7{background:rgba(155,140,255,.2);border-color:rgba(155,140,255,.5)}
.o3est{display:inline-block;margin-left:6px;font-size:10px;color:var(--lab)}
/* 오존 침묵 구간의 사실 카드 — **경보처럼 보이면 안 된다.** 위 #ozonote와 같은 청자색
   계열을 쓰되 배경·테두리를 넣지 않는다. 색으로 놀라게 하는 게 아니라 숫자를 알려 준다. */
/* CO 카드 — 주 지표라 숫자를 크게. 색은 등급 색을 따라간다(인라인 style). */
#cofactline{font-size:15px;font-weight:700;margin-top:3px}
#cofactline .dim{display:block;font-size:11.5px;font-weight:400;line-height:1.5;margin-top:4px;color:var(--dim)}
#cofactline .cog{font-size:12.5px;font-weight:600;margin-left:6px}
#o3factline{font-size:13.5px;font-weight:600;color:#cfc4ff;margin-top:3px}
#o3factline b{font-weight:700}
#o3factline .dim{display:block;font-size:11.5px;font-weight:400;line-height:1.5;margin-top:4px}

/* 시간띠 한 칸 안: 위 4/5 = PM 등급, 아래 1/5 = 오존.
   1/3은 오존이 PM 칸을 너무 먹어 등급 색이 눌려 보였다 (사용자 지시로 1/5). */
#hourstrip button{position:relative;overflow:hidden}
.o3bar{position:absolute;left:0;right:0;bottom:0;height:20%;display:block;pointer-events:none}
#o3cap{display:flex;align-items:center;gap:6px;font-size:10.5px;color:var(--lab);margin-top:6px}
.o3sw{width:14px;height:5px;border-radius:2px;display:inline-block;flex:none}
#modelnote{display:block;font-size:11px;color:var(--lab);margin-top:3px;line-height:1.45}
#vchev{flex:none;color:var(--lab);font-size:17px;transition:transform .18s ease}
#verdictmain[aria-expanded=true] #vchev{transform:rotate(90deg)}
#verdictmore{padding:0 14px 12px;border-top:1px solid var(--line);margin:0 0 0}
.vrow{display:flex;gap:10px;padding:9px 0;border-bottom:1px solid rgba(255,255,255,.05);
  font-size:12.5px;line-height:1.5}
.vrow:last-of-type{border-bottom:none}
.vk{flex:0 0 68px;color:var(--lab);font-size:11.5px;padding-top:1px}
.vrow span:last-child{flex:1;color:var(--mid)}
.vfoot{font-size:11px;color:var(--lab);padding-top:8px}

/* 주의 안내 */
#advisory{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:6px}
#advisory:empty{display:none}
#advisory li{background:var(--card);border:1px solid var(--line);border-radius:var(--r-sm);
  padding:10px 13px;font-size:12.5px;color:var(--mid);line-height:1.55}
/* 안내가 어디 기준인지 밝히는 꼬리표 — 안내 자체보다 약하게, 카드 없이 한 줄로. */
#advisory li.advnote{background:none;border:none;padding:0 4px;font-size:11px;color:var(--lab)}

/* ── 지도 ── */
#mapwrap{display:flex;flex-direction:column;gap:10px}
#canvasbox{position:relative;line-height:0;border-radius:var(--r-card);overflow:hidden}
/* 지도 위 제스처는 우리가 직접 처리한다 (app.js pointerdown/move).
   manipulation으로 두면 두 손가락 확대를 브라우저가 가져가 페이지 전체가 확대된다.
   ⚠️ 이 선언과 app.js 포인터 핸들러는 한 쌍이다. */
#canvasbox canvas{touch-action:none}
#canvasbox canvas{max-width:100%;max-height:44vh;width:auto;height:auto;display:block;margin:0 auto}
canvas#map{background:#2b3634}
canvas#windcv{position:absolute;left:50%;top:0;transform:translateX(-50%);cursor:crosshair}

#timepill{position:absolute;top:12px;left:50%;transform:translateX(-50%);z-index:4;
  background:rgba(7,9,12,.85);backdrop-filter:blur(6px);border:1px solid var(--line2);
  border-radius:99px;padding:7px 15px;font-size:12.5px;font-weight:700;white-space:nowrap;
  line-height:1.2;font-variant-numeric:tabular-nums}
#timepill .nowmark{color:var(--good)}
#timepill .farmark{color:var(--dim);font-weight:500}

/* 레이어는 왼쪽 아래, 지도 버튼은 오른쪽 아래 — 좁은 폰에서 둘이 겹치지 않게
   각자 최대폭을 정해 둔다 (겹치면 레이어 이름이 버튼 밑으로 들어간다). */
/* 자료 받는 중 / 실패 — 지도 가운데에 크게. 알약의 '…' 하나로는 안 보인다. */
#mapbusy{position:absolute;inset:0;z-index:6;display:flex;align-items:center;justify-content:center;
  background:rgba(7,9,12,.55);backdrop-filter:blur(2px);padding:20px;text-align:center}
#mapbusytext{background:rgba(19,26,34,.96);border:1px solid var(--line2);border-radius:var(--r-btn);
  padding:12px 18px;font-size:13px;font-weight:600;line-height:1.5;max-width:280px;
  box-shadow:0 12px 32px rgba(0,0,0,.5);white-space:pre-line}
#mapbusy.err #mapbusytext{border-color:rgba(248,150,60,.5);color:#f8c07a;cursor:pointer;
  pointer-events:auto}

#layerbar{position:absolute;left:12px;bottom:12px;z-index:5;display:flex;gap:6px;align-items:flex-end;
  max-width:calc(100% - 24px - 96px)}
.lypill{background:rgba(7,9,12,.68);color:#cfd8e0;border:1px solid var(--line2);
  font-size:12px;padding:8px 13px;border-radius:99px;line-height:1.2;backdrop-filter:blur(6px);
  white-space:nowrap;min-height:34px}
.lypill.on{background:rgba(232,237,242,.95);color:#07090c;border-color:transparent;font-weight:700}
.lypill .cv{font-size:10px;opacity:.8}
/* 자료 미수신 상태 — 누르면 잠깐 걸린다는 걸 미리 알리는 작은 점 */
.lypill.pend::after{content:'';width:4px;height:4px;border-radius:50%;background:var(--info);
  display:inline-block;margin-left:5px;vertical-align:middle;opacity:.75}
.lypill.pend.on::after{background:#07090c;opacity:.45}
#lywrap{position:relative}
#lymenu{display:none;position:absolute;left:0;bottom:calc(100% + 6px);z-index:12;
  background:#131a22;border:1px solid var(--line2);border-radius:var(--r-btn);padding:6px;
  min-width:132px;box-shadow:0 16px 40px rgba(0,0,0,.6)}
#lymenu.show{display:block}
#lymenu button{display:block;width:100%;text-align:left;background:none;border:none;
  padding:10px 12px;font-size:13.5px;border-radius:9px;color:var(--mid);min-height:44px;
  line-height:1.3}
#lymenu button:hover{background:#1a222c}
#lymenu button.on{color:var(--fg);font-weight:700;background:#18212b}

/* 지도 버튼 5개를 한 줄에 늘어놓으면 폰에서 레이어를 밀어낸다 — 2열 그리드로 세운다.
   오른쪽 아래 모서리에 붙어 엄지 닿는 자리를 지킨다. */
#mapbtns{position:absolute;right:12px;bottom:12px;z-index:5;
  display:grid;grid-template-columns:repeat(2,38px);gap:6px;justify-items:end}
#mapbtns button{width:38px;height:38px;border-radius:var(--r-sm);background:rgba(7,9,12,.78);
  border:1px solid var(--line2);color:var(--fg);font-size:15px;backdrop-filter:blur(6px);
  display:flex;align-items:center;justify-content:center;padding:0;line-height:1}
/* 버튼이 6개(2열 × 3행)로 딱 떨어져서, 5번째만 오른쪽으로 밀던 규칙은 필요 없어졌다
   (2026-08-02에 '공기 출처' 버튼이 바람 왼쪽으로 들어왔다). */
#mapbtns button.on{background:rgba(95,200,248,.2);border-color:rgba(95,200,248,.5);color:#bfe6ff}
/* 재생 중 — 초록(지금/시간 계열)으로 켜진다. 정지 버튼은 재생 중·'지금' 아닐 때만 뜻이 있어
   그 밖에는 흐리게. */
#playbtn.on{background:rgba(73,211,196,.2);border-color:rgba(73,211,196,.55);color:var(--good)}
#stopbtn:disabled{opacity:.35;cursor:default}

/* 어디서 온 공기 — 지나온 배출 권역 목록. 가까운 순.
   ⚠️ 지도 위에서 노란 선을 '짚게' 하던 방식을 대체한 것이다. 폰에서 그 탭이
   지점 이동과 싸워서 사실상 못 썼다 (2026-08-02 사용자 보고). */
#zonelist,#difflist{margin:6px 0 0;padding:0;list-style:none;display:flex;flex-direction:column;gap:6px}
#zonelist li,#difflist li{font-size:11.5px;line-height:1.5;color:var(--mid)}
#zonelist .zn,#difflist .zn{font-weight:700;color:#ffd640}
#zonelist .zd,#difflist .zd{color:var(--lab);margin-left:5px}
#zonelist .kinds,#difflist .kinds{display:flex;flex-wrap:wrap;gap:4px;margin-top:3px}
/* 시설 종류 — 누르면 성분·영향 팝업. 누를 수 있다는 게 보여야 한다. */
#zonelist .kind,#difflist .kind{font-size:11px;line-height:1.2;padding:3px 7px;border-radius:7px;
  background:#18212b;border:1px solid var(--line2);color:var(--mid);min-height:0}
#zonelist .kind:active,#difflist .kind:active{background:#22303d}

/* 확산 가지 — 확정 접촉(노랑)과 달리 '스쳤을 가능성'이라 이름을 눌러 둔다.
   가중 알약(.zw)이 불확실성의 크기를 말한다: 가지 1/5 < 3/5. */
#diffbox{margin-top:10px}
#diffbox .dexp{font-size:10.5px;color:var(--lab);margin:2px 0 0;line-height:1.45}
#difflist .zn{color:#c9b96a}
.zw{display:inline-block;font-size:10.5px;padding:1px 6px;border-radius:6px;
  border:1px solid var(--line2);color:var(--lab);margin-left:5px;vertical-align:1px}

/* 간접영향 — 기원 줄에서 분리한 '주변에서도 N일 전 접촉' (2026-08-07 사용자 지시).
   ⓘ는 진짜 버튼이다 — 링크 흉내 span은 폰에서 눌리는지조차 안 보인다. */
#indirectbox{margin-top:10px}
#indirectline{font-size:13px;font-weight:600;margin-top:2px;line-height:1.4}
.infobtn{background:none;border:none;color:var(--dim);font-size:13px;padding:0 4px;
  min-height:0;vertical-align:0;line-height:1}
.infobtn:active{color:var(--mid)}

#kindpop,#infopop{position:fixed;inset:0;z-index:60;background:rgba(4,6,9,.72);
  display:flex;align-items:center;justify-content:center;padding:18px}
#kindbox,#infobox{position:relative;max-width:400px;width:100%;max-height:82vh;overflow:auto;
  background:var(--card);border:1px solid var(--line2);border-radius:var(--r-card);padding:16px 16px 14px}
#kindclose,#infoclose{position:absolute;right:8px;top:8px;width:32px;height:32px;background:none;
  border:none;color:var(--dim);font-size:15px}
#kindname,#infotitle{display:block;font-size:15px;font-weight:800;color:#ffd640;margin:0 28px 10px 0}
#infobody{font-size:12.5px;line-height:1.65;color:var(--mid)}
.kindrow{display:flex;gap:10px;padding:8px 0;border-top:1px solid rgba(255,255,255,.06);
  font-size:12.5px;line-height:1.6}
.kindk{flex:0 0 66px;color:var(--lab);font-size:11.5px;padding-top:2px}
.kindrow span:last-child{flex:1;color:var(--mid)}
#kindnote{display:block;font-style:normal;font-size:10.5px;line-height:1.5;
  color:var(--lab);margin-top:10px}

#locHint{background:#2a2118;border:1px solid #5c4620;color:#f2d54e;
  border-radius:var(--r-sm);padding:10px 13px;font-size:11.5px;line-height:1.55}
#locHint b{color:#fff}

/* 범례 — 눌러야 펴진다 */
/* 범례는 펼친 채로 두는 카드다 — 세로를 최대한 아낀다 (사용자 지시).
   펼침 높이 약 85px -> 52px. 접었을 때 44px 터치 표적은 지키고, 펼치면
   제목 줄이 28px로 줄면서 색 띠와 눈금만 남는다. */
#legendcard{background:var(--card);border:1px solid var(--line);border-radius:var(--r-sm)}
/* 머리글 — 접히지 않으므로 누를 것이 없다 (2026-08-08). 옛 summary의 '펴진 상태'
   치수를 그대로 물려받는다: 지도 바로 아래에서 카드가 커지지 않게. */
#legendhead{padding:7px 12px 4px;font-size:11px;color:var(--dim);display:flex;align-items:center}
/* 보고 있는 시각에만 그 항목 자료가 없을 때 — 색이 안 보이는 것을 '깨끗하다'로
   읽지 않게 하는 표시다. 경보가 아니므로 조용히 (굵기만 빼고 흐리게). */
#legendtitle.gap{color:var(--lab);font-weight:400}
#legendbody{padding:0 12px 8px}
#legendbar{height:7px;border-radius:99px}
.lg{display:flex;justify-content:space-between;font-size:9.5px;color:var(--lab);margin-top:3px;
  font-variant-numeric:tabular-nums;line-height:1.3}
.lg.seg span{flex:1;text-align:center}

/* ── 핵심 시점 ── */
#moments{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.moment{background:var(--card);border:1px solid var(--line);border-radius:var(--r-btn);
  padding:12px 14px}
.mlab{display:block;font-size:11.5px;color:var(--lab)}
.moment b{display:block;font-size:13.5px;font-weight:700;margin-top:3px;line-height:1.35}

/* ── 시간 탐색 도크 ── */
#dock{background:var(--card);border:1px solid rgba(255,255,255,.09);border-radius:var(--r-dock);
  padding:16px}
#dockhead{display:flex;align-items:baseline;gap:9px}
.dklab{font-size:11px;color:var(--lab);letter-spacing:.06em;flex:none}
#dkhour{font-size:19px;font-weight:800;font-variant-numeric:tabular-nums;line-height:1.2;white-space:nowrap}
#dknow{font-size:12px;color:var(--good);font-weight:700}
#dknow.far{color:var(--dim);font-weight:500}
/* ±1h · 지금으로 — 한 묶음으로 오른쪽 끝에 (2026-08-19). ±1h는 '지금으로'보다 낮은
   위계(테두리만)라 눈이 가운데 초록 버튼에 먼저 간다. 44px 최소 터치 높이는 유지. */
#stepbtns{margin-left:auto;display:flex;gap:6px;align-items:stretch}
#bnow{background:rgba(73,211,196,.14);border:1px solid rgba(73,211,196,.4);
  color:var(--good);font-size:12px;font-weight:700;padding:8px 12px;border-radius:10px;
  min-height:36px;white-space:nowrap}
#bnow:disabled{opacity:.35;cursor:default}
#bm1,#bp1{background:var(--btn);border:1px solid var(--line2);color:var(--mid);
  font-size:12px;font-weight:700;padding:8px 9px;border-radius:10px;min-height:36px;
  font-variant-numeric:tabular-nums;white-space:nowrap}
/* 폰(≤400px)에서는 '지금으로'의 좌우 여백을 조금 줄여 '오전 9시'가 한 줄에 남게 */
@media (max-width:400px){#bnow{padding:8px 9px}#bm1,#bp1{padding:8px 7px}#dockhead{gap:7px}}
#bm1:disabled,#bp1:disabled{opacity:.35;cursor:default}
#bm1:active,#bp1:active{background:#1a222c}
/* 스냅샷(스토리·릴스)에서는 조작 UI라 뺀다 — §④와 같은 이유 */
body.snap #stepbtns,body.snap #playbtn,body.snap #stopbtn{display:none !important}

#daytabs{display:flex;gap:6px;margin-top:12px;align-items:stretch}
#daytabs button{flex:1;background:var(--btn);border:1px solid var(--line);color:var(--dim);
  border-radius:var(--r-sm);font-size:13px;min-height:44px;padding:0 4px;line-height:1.25;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px}
#daytabs button .dnum{font-size:10px;color:var(--lab);font-variant-numeric:tabular-nums}
#daytabs button.on{background:#e8edf2;border-color:transparent;color:#07090c;font-weight:800}
#daytabs button.on .dnum{color:rgba(7,9,12,.6)}
#daytabs button:disabled{opacity:.33;cursor:default}
/* 아카이브 표시 칸은 비활성이지만 '흐리게'가 아니라 '선택됨'으로 보여야 한다 */
#daytabs button.daytab.on:disabled{opacity:1}
#calwrap{position:relative;flex:none}
#daybtn{width:44px;flex:none;font-size:16px}
#calendar{display:none;position:absolute;left:0;top:calc(100% + 6px);z-index:20;
  background:#131a22;border:1px solid var(--line2);border-radius:var(--r-btn);padding:11px;
  width:250px;box-shadow:0 16px 40px rgba(0,0,0,.6)}
#calendar.show{display:block}
#calendar.loading{opacity:.5;pointer-events:none}
.calhead{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;
  font-size:13px;font-weight:700}
.calhead button{background:none;border:1px solid var(--line2);border-radius:8px;
  width:30px;height:30px;font-size:14px;padding:0;display:flex;align-items:center;justify-content:center}
.calgrid{display:grid;grid-template-columns:repeat(7,1fr);gap:3px}
.caldow{font-size:10px;color:var(--lab);text-align:center;padding-bottom:3px}
.calday{font-size:12px;text-align:center;padding:8px 0;border-radius:8px;cursor:pointer;
  color:var(--fg);background:#18212b;font-variant-numeric:tabular-nums}
.calday.off{color:#39434f;background:transparent;cursor:default}
.calday.today{outline:1px solid rgba(95,200,248,.6)}
.calday.sel{background:#1e3a52;color:#9fdcff;font-weight:800}
.calnote{font-size:10.5px;color:var(--lab);margin-top:9px;line-height:1.5}

#dockcap{display:flex;justify-content:space-between;font-size:11px;color:var(--lab);
  margin-top:9px;gap:10px}
#hourstrip{display:flex;gap:2px;height:34px;border-radius:10px;overflow:hidden;margin-top:10px}
#hourstrip button{flex:1;border:none;padding:0;border-radius:0;min-width:0;position:relative;
  transition:filter .12s ease}
/* .far 흐리게는 없앴다 — 아래 #farnote 문구로만 알린다 (사용자 지시) */
#hourstrip button.cur{border-radius:7px;outline:2px solid #e8edf2;outline-offset:-2px;z-index:2}
#hourstrip button:disabled{opacity:.14;cursor:default}
#hourticks{display:flex;justify-content:space-between;font-size:10.5px;color:var(--lab);
  margin-top:5px;font-variant-numeric:tabular-nums}
#farnote{font-size:11px;color:var(--lab);margin-top:9px;line-height:1.5}
#backlive{width:100%;margin-top:10px;background:#2a2118;border:1px solid #5c4620;color:#f2d54e;
  border-radius:var(--r-sm);font-size:12.5px;font-weight:600;min-height:44px}

/* ── 상세 ── */
#side{display:flex;flex-direction:column;gap:10px}
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);padding:14px 16px}
.ctop{display:flex;justify-content:space-between;align-items:baseline;gap:8px}
.cright{font-size:12px;color:var(--dim);font-variant-numeric:tabular-nums}
.cright b{font-weight:700}
#nrisk{color:var(--warn)}
#nnat{color:var(--fg)}
.bar{display:flex;height:10px;border-radius:99px;overflow:hidden;margin-top:10px;background:#3a4552}
#barfine{background:linear-gradient(90deg,var(--warn),var(--bad))}
#barcoarse{background:#3a4552}
#chips{display:flex;gap:8px;margin-top:10px}
.chip{flex:1;border-radius:var(--r-sm);padding:9px 10px;font-size:12px;
  border:1px solid var(--line);background:#131a22;color:var(--dim);text-align:left;min-height:44px}
.chip.on{border-color:rgba(95,200,248,.45);background:#18212b;color:var(--fg)}
#chipdesc{font-size:12px;line-height:1.6;margin-top:9px;color:var(--dim)}
#origin{font-size:15px;font-weight:700;margin-top:4px;line-height:1.4}
#srcnote{margin-top:9px;padding-top:9px;border-top:1px solid rgba(255,255,255,.06)}
#srcline{font-size:13px;color:#f8b26a;line-height:1.5}
#srcwhy{font-size:11.5px;line-height:1.6;margin-top:4px}

#officialcard{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card)}
#officialcard summary{list-style:none;cursor:pointer;padding:13px 16px;font-size:13px;
  color:var(--dim);display:flex;align-items:center;min-height:44px}
#officialcard summary::-webkit-details-marker{display:none}
#officialcard summary::after{content:'＋';margin-left:auto;color:var(--lab)}
#officialcard[open] summary::after{content:'－'}
#official{padding:0 16px 14px;font-size:12.5px;line-height:1.65;color:var(--mid)}

footer{font-size:11px;color:var(--lab);margin-top:18px;line-height:1.7;padding:16px 4px 0;
  border-top:1px solid rgba(255,255,255,.06)}
footer .credit{margin-top:9px;font-size:10.5px;letter-spacing:.03em;color:#5d6874}
footer .credit b{color:#8b98a5;font-weight:600}

/* ── 당겨서 새로고침 ── */
#pull{position:fixed;top:0;left:50%;z-index:70;pointer-events:none;
  width:36px;height:36px;margin-left:-18px;border-radius:50%;
  background:var(--card);border:1px solid var(--line2);
  display:flex;align-items:center;justify-content:center;
  transform:translateY(-48px);opacity:0}
#pullicon{font-size:16px;color:var(--dim);line-height:1;display:block}
#pull.ready{border-color:var(--good)}
#pull.ready #pullicon{color:var(--good)}
#pull.spin{transition:transform .18s ease}
#pull.spin #pullicon{animation:pullspin .7s linear infinite}
@keyframes pullspin{to{transform:rotate(360deg)}}

/* ── 앱 설치 모달 ── */
#installmodal{position:fixed;inset:0;background:rgba(4,6,9,.74);z-index:60;
  display:flex;align-items:center;justify-content:center;padding:18px}
#installbox{position:relative;background:var(--card);border:1px solid var(--line2);border-radius:20px;
  max-width:420px;width:100%;max-height:86vh;overflow-y:auto;padding:20px 18px 16px;
  box-shadow:0 24px 60px rgba(0,0,0,.6)}
#installclose{position:absolute;right:10px;top:10px;background:none;border:none;color:var(--lab);
  font-size:15px;width:36px;height:36px;border-radius:10px;display:flex;align-items:center;
  justify-content:center}
#installclose:active{background:#1a222c}
/* 정보 탭과 같은 언어: 아이콘 + 눈썹 라벨 + 굵은 제목 */
#installtop{display:flex;align-items:center;gap:14px;padding-right:36px}
#installtop img{border-radius:13px;flex:none;display:block}
.ieyebrow{font-size:11px;font-weight:700;letter-spacing:.1em;color:var(--info)}
#installtop b{font-size:18px;font-weight:800;line-height:1.3;display:block;margin-top:2px}
#installbox p{font-size:12.5px;line-height:1.7;margin:12px 0 0;color:var(--dim)}
#installbox p b{color:var(--fg)}
#installnow{width:100%;margin:12px 0 0;padding:12px;border-radius:var(--r-sm);
  border:1px solid rgba(95,200,248,.5);background:rgba(95,200,248,.14);color:#bfe6ff;
  font-size:13.5px;font-weight:700;min-height:44px}
/* 단계 안내는 카드로 — 정보 탭의 .ab-card와 같은 형태 */
.installsec{margin-top:12px;background:#0d1219;border:1px solid var(--line);
  border-radius:var(--r-card);padding:13px 15px}
.isechead{display:flex;align-items:center;gap:8px;font-size:13.5px}
.isechead b{font-weight:700}
.isechead .ios{font-size:15px;line-height:1}
.installsec ol{margin:9px 0 0;padding-left:19px;font-size:12.5px;line-height:1.85;color:var(--dim)}
.installsec ol b{color:#cfd8e0}
.installsec .inote{font-size:11.5px;line-height:1.6;margin-top:9px;padding-top:9px;
  border-top:1px solid rgba(255,255,255,.05);color:var(--lab)}

/* ── 정보 탭 ── */
#about{padding:20px 2px 8px;line-height:1.75;font-size:14px}
#about h1{font-size:22px;font-weight:800;margin:0 0 14px;line-height:1.35;letter-spacing:-.01em}
/* 시안 2b의 '정보' 눈썹 라벨 — 마크다운을 건드리지 않고 여기서 붙인다 */
#about h1::before{content:'정보';display:block;font-size:12px;font-weight:700;
  letter-spacing:.1em;color:var(--lab);margin-bottom:7px}
#about h2{font-size:15px;font-weight:800;margin:26px 0 10px;padding:0 2px}
#about h3{font-size:13.5px;margin:16px 0 6px;color:var(--info)}
#about p{color:var(--mid);margin:10px 0}
#about strong{color:var(--fg)}
#about table{border-collapse:collapse;width:100%;margin:11px 0;font-size:12px}
#about th,#about td{border:1px solid var(--line);padding:7px 9px;text-align:left}
#about th{background:#16202a;color:var(--fg)}
#about code{background:#0d151d;border-radius:5px;padding:1px 5px;font-size:12px}
#about pre{background:#0d151d;border-radius:10px;padding:12px;overflow-x:auto;font-size:12px;
  border:1px solid var(--line)}
#about blockquote{border-left:3px solid var(--line2);margin:12px 0;padding:2px 0 2px 13px;color:var(--dim)}
#about ul,#about ol{padding-left:19px;color:var(--mid)}
#about li{margin:5px 0}
#about hr{border:none;border-top:1px solid var(--line);margin:24px 0}

/* 정보탭 등급표의 등급 알약 — 색은 build_about.py BAND_UI(= data.js BAND_UI 사본).
   화면 칩·시간띠·범례 다리와 같은 색이라 표만 보고도 화면 색을 읽을 수 있다. */
.ab-gband{display:inline-block;padding:1px 8px;border:1px solid;border-radius:8px;
  font-weight:700;font-size:11.5px;white-space:nowrap;background:rgba(255,255,255,.04)}
.ab-lead{font-size:14.5px;line-height:1.78;color:var(--mid);margin:0 0 4px;padding:0 2px}
.ab-lead strong{color:var(--info)}
.ab-cards{display:flex;flex-direction:column;gap:8px;margin:12px 0 4px}
.ab-card{background:var(--card);border:1px solid var(--line);border-radius:var(--r-card);
  padding:14px 16px}
.ab-card h4{margin:0 0 3px;font-size:14px;color:var(--fg);font-weight:700;line-height:1.4}
.ab-card p{margin:0;font-size:12.5px;line-height:1.65;color:var(--dim)}
.ab-card p+p{margin-top:6px}
.ab-card strong{color:#cfd8e0}

/* 실측 수치 히어로 — "구체적 숫자 = 신뢰". 권위보다 증거를 앞세운다 (시안 2b §4) */
.ab-stats{background:linear-gradient(160deg,#101a26,#0b1016);border:1px solid rgba(95,200,248,.25);
  border-radius:18px;padding:16px;margin:14px 0}
.ab-statlab{font-size:11px;color:var(--info);font-weight:700;letter-spacing:.1em}
.ab-statrow{display:flex;gap:12px;margin-top:11px}
.ab-stat{flex:1;min-width:0}
.ab-stat b{display:block;font-size:24px;font-weight:800;color:var(--info);line-height:1.15;
  font-variant-numeric:tabular-nums}
.ab-stat span{display:block;font-size:11.5px;color:var(--dim);line-height:1.5;margin-top:3px}

.ab-note{background:linear-gradient(160deg,#101a26,#0b1016);border:1px solid rgba(95,200,248,.25);
  border-radius:18px;padding:16px;margin:14px 0;font-size:13px;line-height:1.7;color:var(--dim)}
.ab-note strong{color:var(--fg)}
.ab-note p{margin:0;color:var(--dim)}
.ab-note p+p{margin-top:8px}

#about details{border:1px solid var(--line);border-radius:var(--r-card);margin:8px 0;
  background:var(--card);overflow:hidden}
#about details[open]{border-color:rgba(95,200,248,.3)}
#about summary{cursor:pointer;padding:14px 16px;font-size:14px;font-weight:700;color:var(--fg);
  list-style:none;display:flex;align-items:center;gap:8px;user-select:none;min-height:48px}
#about summary::-webkit-details-marker{display:none}
#about summary::after{content:'▾';margin-left:auto;color:var(--lab);font-size:13px}
#about details[open] summary::after{content:'▴';color:var(--info)}
.ab-dbody{padding:0 16px 14px;font-size:12.5px;line-height:1.7;color:var(--dim)}
.ab-dbody strong{color:#cfd8e0}
.ab-dbody p{color:var(--dim);margin:8px 0}
.ab-dbody table{font-size:11.5px}
.ab-dbody ul,.ab-dbody ol{color:var(--dim)}

/* ══════════ 데스크톱 — 같은 컴포넌트를 2열로 편다 ══════════
   모바일 구조가 원본이고, 여기서는 '지도를 크게 / 읽을 것을 오른쪽에'만 바꾼다. */
@media (min-width:900px){
  body{background:var(--bg2)}
  #shell{max-width:1240px;padding:0 24px 32px}
  header{padding:22px 2px 0}
  h1{font-size:19px}

  #main{display:grid;gap:14px;align-items:start;
    grid-template-columns:minmax(0,1fr) 380px;
    grid-template-areas:
      "map verdict"
      "map advisory"
      "map moments"
      "map dock"
      "map side";
    grid-auto-rows:min-content}
  /* 주의 안내가 없을 땐 그 행을 아예 없앤다 — 안 그러면 빈 행의 gap이 하나 더 붙는다.
     :has()를 못 쓰는 브라우저는 gap 14px만 더 생길 뿐 겹치지는 않는다. */
  #main:has(#advisory:empty){grid-template-areas:
      "map verdict" "map moments" "map dock" "map side"}
  #mapwrap{grid-area:map;position:sticky;top:16px}
  #verdict{grid-area:verdict}
  #moments{grid-area:moments}
  #dock{grid-area:dock}
  #side{grid-area:side}
  /* ⚠️ 예전엔 `grid-area:verdict` + `align-self:end`로 **판정 카드와 같은 칸**에
     놓았다. 주석은 "겹치지 않게"라고 적혀 있었지만 실제로는 정반대였다 — 같은 칸의
     그리드 아이템은 겹쳐 쌓인다. 판정 카드가 길어지면(오존 경보 줄이 켜지면)
     주의 안내가 그 위를 덮었다 (2026-08-01 사용자 보고, 화면 캡처로 확인).
     지금은 **자기 행**을 갖는다. */
  #advisory{grid-area:advisory;align-self:start;margin-top:0}

  #canvasbox canvas{max-height:calc(100vh - 150px)}
  #timepill{font-size:13.5px;padding:8px 17px}
  #mapbtns button{width:42px;height:42px;font-size:16px}
  .lypill{font-size:12.5px;padding:9px 15px;min-height:38px}

  #bandchip{font-size:20px}
  svg.face{width:48px;height:48px}
  #venthead{font-size:14px}

  #about{max-width:760px;margin:0 auto;padding:26px 4px 12px}
  #about h1{font-size:26px}
  .ab-cards{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
  .ab-lead{font-size:15.5px}
  footer{max-width:760px;margin-left:auto;margin-right:auto}
}

/* 아주 좁은 폰 — 날짜 탭 6칸이 눌리지 않게 */
@media (max-width:360px){
  #shell{padding:0 12px 24px}
  #daytabs{gap:4px}
  #daytabs button{font-size:12px}
  #bandchip{font-size:17px}
}
