/* today-content/app-ui.css — 오늘수학 자체 UI(사이드바/상단바/카트바) CSS.
   원래 styles.css의 "1. 오늘수학 전용 UI"+"2. 인쇄 시 UI 숨김" 두 섹션을 그대로
   옮긴 것(2026-08-11). sheet-layout.css와 달리 자유롭게 수정해도 되는 영역. */
/* =========================================================
   1. 오늘수학 전용 UI (사이드바/상단바) — 위 카드 CSS와 이름이 겹치지 않도록
   전부 tm- 접두사 사용(하루수학의 .topbar/.sidebar 등과 무관, 새로 설계)
========================================================= */
/* "오늘수학" 로고 폰트: 손글씨체(TodayMathLogo, insungit Cutelively jisu)가 "유치해
   보인다"는 피드백으로 2026-08-14에 SUIT Variable 볼드로 교체(사용자 확정,
   AskUserQuestion — 문제 숫자와 통일된 서체를 골라 가장 중립적/현대적인 느낌). 인쇄
   헤더(.sheet-brand, sheet-layout.css)도 동일하게 바꿈. TodayMathLogo @font-face는
   더 이상 아무 데서도 안 쓰여서 삭제. */
/* 웹 UI 폰트 프리텐다드 시험 적용(2026-08-17) — 학습지(.sheet) 내부는 여기 영향 안
   받게 sheet-layout.css의 `.sheet{font-family:...}` 방어 규칙으로 상속을 끊어놨다. */
/* Pretendard Variable 로컬 호스팅(2026-08-22, 배포 준비 — jsdelivr CDN 의존 제거,
   OFL-1.1, orioncactus/pretendard 공식 배포본과 동일 파일). 가변 폰트라 굵기 하나로
   45~920(Thin~Black) 전 구간 커버, 오늘수학.html의 jsdelivr <link>도 같이 뺐음. */
@font-face{ font-family:'Pretendard Variable'; src:url('../fonts/PretendardVariable.woff2') format('woff2-variations'); font-weight:45 920; font-style:normal; font-display:swap; }
html, body{ margin:0; height:100%; font-family:'Pretendard Variable','Pretendard','Noto Sans KR','Gothic A1',sans-serif; }
.app{ display:flex; height:100vh; background:var(--bg); color:var(--title); overflow:hidden; }

/* 요청(2026-08-16): "차시명 줄바꿈도 최소화" — 실측 결과 차시 제목 자연 너비 분포가
   50%=125px/90%=183px/95%=236px/최대=351px(극단값 몇 개)였고, 기존 사이드바(340px)에서
   제목이 쓸 수 있는 실제 폭은 ~197px(90%는 이미 안 접힘, 95%부터 접힘 시작). 최댓값까지
   전부 한 줄로 만들려면 사이드바를 500px 가까이 넓혀야 해서(미리보기 영역을 크게 잠식)
   과함 — 95번째 백분위(236px)를 커버하는 선에서 사이드바를 380px로 넓혀 대부분의 줄바꿈을
   없애고, 극단적으로 긴 제목 몇 개만 여전히 2줄로 남긴다("최소화"이지 "완전 제거"가
   아니므로 이 정도가 적절한 절충선).
   2026-08-17 재조정(Claude Design "사이즈 가이드" 참고, 차시명 14px 확대 + 번호·조건이
   같은 줄에 추가되며 폭이 더 필요해짐): 실제 curriculum 데이터(전체 109개 번호 붙은
   행)로 폭별 줄바꿈 개수를 다시 실측 — 380px=14행, 400px=7행, 420px=6행(400→420은
   1행만 더 줄어 체감 차이 작음). 사용자가 확인한 상한(400px)과도 일치해서 400px로
   확정("최소화"이지 "완전 제거"는 아니므로 잔여 7행은 그대로 둠).
   2026-08-17 추가 축소(같은 날, 후속 요청) — "사이드바가 넓어져서 미리보기가 2열이 아니라
   1열로 보인다"는 리포트: 미리보기는 `updatePreviewScale()`이 가로 여유폭이
   `280*2+24=584px` 이상이어야 2열로 그린다(오늘수학.html 참고) — 사이드바가 넓어질수록
   그 여유폭이 줄어드는 게 원인이 맞음. 그런데 "줄바꿈은 다시 늘리지 말 것"이 조건이라
   차시명이 쓰는 실제 텍스트 폭(row-num/gap/stepper를 뺀 나머지)은 그대로 두고, 대신 그
   텍스트 폭과 무관한 "여백"만 줄여서 사이드바 전체 폭을 20px 줄였다 —
   `#tmSidebarScroll`/`.tm-card-head`/`.tm-card-body`의 좌우 padding을 16px→10~12px로
   줄여 정확히 20px를 상쇄(400→380이어도 차시명이 실제로 쓸 수 있는 폭은 400px일 때와
   동일 — Playwright로 줄바꿈 행 수가 늘지 않았음을 재확인함). */
#tmSidebar{ width:380px; flex-shrink:0; border-right:1px solid var(--ui-border); background:var(--surface); display:flex; flex-direction:column; overflow:hidden; }
.tm-brand{ padding:20px 20px 12px; position:relative; }
.tm-brand-name{ font-family:'Pretendard Variable','Pretendard','Noto Sans KR','Gothic A1',sans-serif; font-size:22px; font-weight:700; letter-spacing:-0.02em; color:var(--title); }
.tm-brand-sub{ font-size:12.5px; color:var(--text-secondary); margin-top:2px; }
.tm-brand-div{ height:1px; background:var(--ui-border); margin:4px 20px; }

/* 선택 순서 라벨(1~4단계 안내, 2026-08-15) — 사이드바/상단바 곳곳에 재사용되는 작은
   "N단계" 배지. 숫자 원 + 라벨 텍스트만 있는 최소 구성이라 어디에 놓아도 줄바꿈 걱정이
   적다(topbar처럼 이미 flex-wrap 버그 이력이 있는 좁은 공간에도 안전하게 넣을 수 있게). */
.tm-step-label{ display:flex; align-items:center; gap:6px; padding:14px 20px 6px; font-size:11.5px; font-weight:800; color:var(--text-secondary); letter-spacing:.01em; }
.tm-step-num{ display:inline-flex; align-items:center; justify-content:center; width:16px; height:16px; border-radius:50%; background:var(--primary); color:#fff; font-size:10px; font-weight:800; flex-shrink:0; }

/* 1단계(학년·단원 선택)는 다른 단계로 넘어가기 전 항상 먼저 정해야 하는 값이라, 나머지
   행(2단계 차시 목록 등)보다 눈에 띄게 필드를 키우고 강조 테두리/배경을 준다(요청,
   2026-08-15: "중요한 부분이므로 필드 크기 키우고, 강조 필요"). */
.tm-picker-row{ padding:2px 20px 16px; display:flex; gap:8px; }
/* 2026-08-17(Claude Design 참고): 배경 틴트(color-mix 6% primary) 제거, 테두리만 유지 —
   사용자 지정("학년과 단원 필드 배경색은 없애고"). */
.tm-picker-row select{ padding:11px 10px; border:1.5px solid var(--primary); border-radius:9px; background:#fff; font-size:14.5px; font-family:inherit; font-weight:700; color:var(--title); cursor:pointer; }
#tmGradeSelect{ flex:0 0 92px; min-width:0; }

.tm-unit-dd{ position:relative; flex:1; min-width:0; }
.tm-unit-dd-btn{ width:100%; text-align:left; padding:11px 12px; border:1.5px solid var(--primary); border-radius:9px; background:#fff; font-size:14.5px; font-family:inherit; font-weight:700; color:var(--title); cursor:pointer; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.tm-unit-dd-btn:disabled{ opacity:.5; cursor:default; }
.tm-unit-dd-panel{ position:absolute; top:calc(100% + 4px); left:0; right:0; background:#fff; border:1px solid var(--ui-border); border-radius:8px; box-shadow:0 8px 24px rgba(0,0,0,.14); padding:6px; z-index:30; max-height:360px; overflow-y:auto; }
.tm-unit-dd-panel.tm-hidden{ display:none; }
.tm-unit-dd-sem{ font-size:11px; font-weight:700; color:var(--text-secondary); padding:8px 10px 4px; }
.tm-unit-dd-item{ padding:9px 10px; border-radius:6px; cursor:pointer; font-size:13px; color:var(--title); line-height:1.4; }
.tm-unit-dd-item:hover{ background:var(--ui-divider); }
.tm-unit-dd-item.active{ font-weight:700; color:var(--primary); }

.tm-row-check{ width:18px; height:18px; flex-shrink:0; border-radius:5px; border:1.5px solid #B9C0CC; box-sizing:border-box; display:flex; align-items:center; justify-content:center; background:#fff; }
.tm-row-check span{ font-size:11px; font-weight:800; color:#fff; line-height:1; }
.tm-selectall .tm-row-check{ background:transparent; }
.tm-selectall.tm-checked .tm-row-check{ background:var(--title); border-color:var(--title); }
.tm-subhead{ font-size:11px; font-weight:700; color:var(--text-secondary); padding:10px 12px 2px; }

/* 카드형 드롭다운(fixes.md #45, 2026-08-16 후보, 되돌리려면 git checkout 57ab55a --
   오늘수학.html today-content/app-ui.css) — "학습지 종류 자체가 드롭다운": 기본은
   진단평가/선수학습/이번단원/정리학습 4장이 카드로 나란히 펼쳐져 있고, 하나를 고르면
   그 카드 하나(제목+본문)만 남는다(오늘수학.html의 sidebarCategory/isCardOpen 참고).
   애니메이션은 일부러 안 씀(이전 .tm-sec-body의 grid-template-rows 트릭은 후보 비교
   단계라 단순함 우선 — renderSidebarSections가 매번 innerHTML 통째로 재생성하므로
   부드러운 전환을 하려면 DOM을 보존하는 별도 장치가 다시 필요해짐, 채택되면 고려). */
/* 요청(2026-08-16): "스크롤바는 hover시에만" — 처음엔 scrollbar-width를 none↔thin으로
   hover 시 바꿨더니, 스크롤바 트랙 자체의 폭이 바뀌면서 콘텐츠 영역 폭이 줄어 차시명이
   줄바꿈되는 버그가 남(스크롤바 폭이 바뀌면 scrollbar-gutter:stable이 미리 예약해둔 공간과
   실제 트랙 폭이 어긋남). **폭(scrollbar-width)은 항상 thin으로 고정**하고, thumb "색"만
   투명↔보임으로 토글해야 트랙이 차지하는 공간이 항상 똑같이 유지된다(레이아웃 안 흔들림). */
#tmSidebarScroll{ flex:1; overflow-y:auto; scrollbar-gutter:stable; padding:14px 10px;
  scrollbar-width:thin; scrollbar-color:transparent transparent; }
#tmSidebarScroll:hover{ scrollbar-color:var(--ui-border) transparent; }
#tmSidebarScroll::-webkit-scrollbar{ width:8px; }
#tmSidebarScroll::-webkit-scrollbar-thumb{ background:transparent; border-radius:999px; }
#tmSidebarScroll:hover::-webkit-scrollbar-thumb{ background:var(--ui-border); }
.tm-no-data{ padding:24px 20px; font-size:13px; color:var(--text-secondary); line-height:1.6; }

.tm-card{ border:1px solid var(--ui-border); border-radius:12px; background:#fff; margin-bottom:10px; overflow:hidden; }
.tm-card:last-child{ margin-bottom:0; }
.tm-card-head{ cursor:pointer; padding:14px 12px; display:flex; align-items:center; justify-content:space-between; user-select:none; }
.tm-card-head:hover{ background:var(--ui-divider); }
.tm-card-title{ font-size:16px; font-weight:700; color:var(--title); }
.tm-card-chevron{ font-size:12px; font-weight:800; color:var(--text-secondary); transition:transform .15s ease; }
.tm-card.open .tm-card-chevron{ transform:rotate(180deg); }
.tm-card-body{ display:none; padding:0 12px 14px; }
.tm-card.open .tm-card-body{ display:block; }
.tm-card-desc{ margin:0 0 8px; font-size:12.5px; color:var(--text-secondary); line-height:1.55; }
.tm-card-div{ height:1px; background:var(--ui-border); margin:6px 0 8px; }

/* 요청(2026-08-16 재수정, 정정): "체크박스도 없애고"는 유지하되, 하이라이트는 행 전체가
   아니라 **"N장" 배지(pill) 자체**에만 준다("내 말은 pill 자체에 효과! 차시명 전체 강조
   말고") — 행 배경/제목 색은 원래대로(투명, hover만 옅은 회색, 글씨는 항상 진한 색)
   되돌리고, 대신 오른쪽의 실제 pill 모양 배지(.tm-pages-stepper, 아래)에 미선택=연한
   회색/선택=연한 포인트컬러 대비를 준다. */
/* 2026-08-17(Claude Design "사이즈 가이드" 참고, 범위 축소판 — 카드 박스·sticky는 이번에
   안 가져옴): 차시 행에 번호/조건(O·X 배지 또는 문장형)이 새로 붙었다. 행 전체는
   align-items:center(번호·텍스트블록·스테퍼가 서로 세로 중앙 정렬 — 재수정: 처음엔
   baseline을 썼는데 번호(12px)·스테퍼(14px)처럼 글자 크기가 다른 요소끼리 중심이 안
   맞아 보였음), 제목↔조건처럼 "같은 글줄 안"의 정렬만 안쪽(.tm-row-text)에서
   baseline을 쓴다.
   선택 시 배경색(요청, 2026-08-17 재수정) — Claude Design 값(선택 #EFF4FF, hover
   #F5F6F8) 재사용. 글자색(제목·번호가 파랑 계열로 바뀌는 것)까지는 요청 안 받아서 그대로
   둠 — 필요하면 바로 추가.
   배경색 사이 간격(요청, 2026-08-17 추가): 배경이 깔린 행끼리 위아래로 맞붙으면 하나의
   덩어리로 뭉쳐 보여 몇 개를 골랐는지 세기 어렵다(Claude Design 원본의 margin:3px -6px
   규칙 재사용) — hover/선택 여부와 무관하게 항상 고정 여백으로 두고 배경만 켜지게 해서,
   선택 상태가 바뀌어도 다른 행이 위아래로 밀리지 않는다. 가로 -6px는 padding(6px)을
   상쇄해 카드 폭 기준으론 그대로 정렬되면서 배경만 살짝 더 넓게 칠해지게 하는 값. */
.tm-row{ cursor:pointer; padding:8px 6px; margin:3px -6px; border-radius:8px; display:flex; align-items:center; gap:10px; }
.tm-row:hover{ background:#F5F6F8; }
.tm-row.tm-row-selected{ background:#EFF4FF; }
.tm-row.tm-row-selected:hover{ background:#E4ECFF; }
.tm-row-num{ flex:none; width:16px; text-align:right; font-size:12px; font-weight:500; color:#6B7280; font-variant-numeric:tabular-nums; line-height:1.4; }
/* 차시명+조건을 한 묶음으로 묶어서 이 안에서만 gap 7px(요청 값)을 쓰고, 번호↔이 묶음/
   이 묶음↔스테퍼는 .tm-row 자체의 gap(10px)을 그대로 쓴다 — flex gap은 컨테이너 하나당
   한 값만 되므로, 다른 gap이 필요한 조합은 이렇게 중첩해서 만든다. */
.tm-row-text{ flex:1; min-width:0; display:flex; align-items:baseline; flex-wrap:wrap; row-gap:2px; column-gap:7px; }
.tm-row-title{ font-size:14px; color:var(--title); font-weight:600; white-space:nowrap; }
.tm-row.tm-row-selected .tm-row-title{ font-weight:700; }
.tm-row-note{ display:inline-flex; align-items:baseline; flex-wrap:wrap; row-gap:2px; column-gap:10px; font-size:13px; font-weight:500; color:#4B5563; }
.tm-row-flag{ display:inline-flex; align-items:baseline; gap:3px; }
.tm-row-flag-label{ color:#6B7280; }
.tm-row-flag-o{ font-weight:700; color:#1F2937; }
.tm-row-flag-x{ font-weight:500; color:#6B7280; }
.tm-row-flag-extra{ color:#4B5563; }
/* 요청(2026-08-17): "진단평가/정리학습은 선택됐는지 잘 안 보임" — 이 둘은 장수 스테퍼가
   없는 단일 행 카드라(.tm-pages-stepper 없음), 그동안 선택 표시가 제목 볼드체 하나뿐이라
   눈에 잘 안 띄었다. 스테퍼가 있는 행(개별 차시)은 "행 배경 대신 pill에만 강조"라는 기존
   원칙을 그대로 유지하고(2026-08-16 결정, 위 주석 참고), 스테퍼가 없는 행에만 배경 틴트를
   추가한다 — 스테퍼 pill의 선택 색상(color-mix 14% primary)과 같은 계열로 맞춤. */
.tm-row.tm-row-selected:not(:has(.tm-pages-stepper)){ background:color-mix(in oklab, var(--primary) 14%, white); }
.tm-row.tm-row-selected:not(:has(.tm-pages-stepper)):hover{ background:color-mix(in oklab, var(--primary) 20%, white); }
.tm-row-meta{ font-size:11px; color:var(--text-secondary); white-space:nowrap; }
.tm-row-check{ width:18px; height:18px; flex-shrink:0; border-radius:5px; border:1.5px solid #B9C0CC; box-sizing:border-box; display:flex; align-items:center; justify-content:center; background:#fff; }
.tm-row.tm-checked .tm-row-check, .tm-row.tm-row-selected .tm-row-check{ background:var(--title); border-color:var(--title); }
.tm-row-check span{ font-size:11px; font-weight:800; color:#fff; line-height:1; }
.tm-row-summary{ font-weight:700; }

/* 2026-08-17(Claude Design "사이즈 가이드" 참고, 범위 축소판) — 스테퍼를 pill 배경 없는
   3열 고정 그리드(28/34/28=94px)로 재설계. 가운데 34px 슬롯이 "0장의 +"와 "1장+의
   N장" 자리를 항상 공유해서, 예전(2026-08-16)에 고쳤던 "상태 바뀔 때 숫자 위치가
   흔들리는 문제"의 재발을 구조적으로 막는다(마크업 자체가 슬롯 3개로 고정이라 위치가
   흔들릴 수가 없음). 0장일 땐 가운데 슬롯에 "+"만 상시 노출(미선택 행이 뭘 눌러야
   하는지 알려주는 유일한 단서라 hover 뒤에 안 숨김, rowHTML의 .tm-stepper-plus-zero).
   1장 이상이면 가운데엔 "N장" 텍스트만 기본 노출되고, 좌우 −/+ 버튼은 행을 hover해야만
   보인다(자리는 항상 차지하되 opacity로만 전환). 터치 기기는 hover 자체가 없으므로
   @media(hover:none)에서 −/+를 상시 노출로 폴백 — 안 그러면 터치 사용자는 장수를 아예
   조정 못 하게 됨.
   0장 "+" 버튼도 hover 시에만 노출로 변경(요청, 2026-08-17) — 원래는 "미선택 행이 뭘
   눌러야 하는지 스스로 말해주는 유일한 단서"라 상시 노출이었는데, Claude Design 원본대로
   −/+와 동일하게 hover-게이트로 통일. `.tm-stepper-plus-zero`를 `-l`/`-r`과 같은 opacity
   규칙 두 곳(기본 hover 전환, @media(hover:none) 터치 폴백) 모두에 같이 넣을 것 —
   하나만 넣으면 터치 사용자가 0장 행에서 "+"를 아예 못 보게 됨. */
.tm-pages-stepper{ display:flex; align-items:center; width:94px; flex-shrink:0; align-self:center; }
.tm-stepper-slot{ display:flex; align-items:center; justify-content:center; height:28px; flex:none; }
.tm-stepper-slot-l, .tm-stepper-slot-r{ width:28px; }
.tm-stepper-slot-m{ width:34px; }
.tm-pages-cnt{ font-size:14px; font-weight:700; color:var(--primary-hover); font-variant-numeric:lining-nums tabular-nums; }
.tm-stepper-btn{ width:28px; height:28px; border:none; background:transparent; font-size:16px; font-weight:600; line-height:1; padding:0; cursor:pointer; color:var(--primary-hover); font-family:inherit; display:flex; align-items:center; justify-content:center; }
.tm-stepper-slot-l .tm-stepper-btn, .tm-stepper-slot-r .tm-stepper-btn, .tm-stepper-btn.tm-stepper-plus-zero{ opacity:0; transition:opacity .12s ease; }
.tm-row:hover .tm-stepper-slot-l .tm-stepper-btn:not(.tm-stepper-btn-disabled),
.tm-row:hover .tm-stepper-slot-r .tm-stepper-btn:not(.tm-stepper-btn-disabled),
.tm-row:hover .tm-stepper-btn.tm-stepper-plus-zero{ opacity:1; }
.tm-stepper-btn-disabled{ opacity:0 !important; pointer-events:none; }
@media (hover:none){
  .tm-stepper-slot-l .tm-stepper-btn, .tm-stepper-slot-r .tm-stepper-btn, .tm-stepper-btn.tm-stepper-plus-zero{ opacity:1; }
}

#tmMain{ flex:1; display:flex; flex-direction:column; min-width:0; }
#tmTopbar{ padding:14px 28px; border-bottom:1px solid var(--ui-border); display:flex; align-items:center; gap:16px; flex-wrap:wrap; background:var(--surface); }
/* 모바일 사이드바 드로어를 여는 버튼 — PC에서는 사이드바가 항상 보이므로 숨겨두고, 768px
   이하 미디어쿼리(아래)에서만 보이게 한다. */
.tm-hamburger-btn{ display:none; flex-shrink:0; width:36px; height:36px; border:1px solid var(--ui-border); border-radius:8px; background:#fff; font-size:16px; cursor:pointer; align-items:center; justify-content:center; color:var(--title); }
/* 사이드바 안의 닫기(X) 버튼 — 드로어 모드에서만 보임(PC에서는 사이드바가 항상 열려있어
   "닫기"라는 개념 자체가 없음). */
.tm-sidebar-close-btn{ display:none; position:absolute; top:16px; right:14px; width:30px; height:30px; border:none; background:transparent; font-size:16px; cursor:pointer; color:var(--text-secondary); align-items:center; justify-content:center; }
/* 드로어가 열렸을 때 뒤를 덮는 반투명 배경 — 탭하면 닫힘. PC에서는 사이드바 자체가
   position:fixed가 아니라서(일반 flex 레이아웃 안에 있음) 이 배경이 나설 일이 없으므로
   기본 상태에선 항상 숨김. */
.tm-sidebar-backdrop{ display:none; }
.tm-topbar-label{ font-size:14px; color:var(--text-secondary); white-space:nowrap; flex-shrink:0; }
.tm-topbar-label b{ font-weight:700; color:var(--title); }
.tm-topbar-badge{ font-size:13px; color:var(--text-secondary); background:var(--ui-divider); padding:4px 10px; border-radius:6px; }
/* 카트 바와 같은 종류의 버그(2026-08-11) — #tmTopbar 전체가 flex-wrap:wrap이라, 안의
   요소(라벨/배지/보기토글/인쇄버튼)가 각자 독립적으로 줄바꿈되면 "보기토글은 첫 줄에 남고
   인쇄버튼만 다음 줄로" 같은 어중간한 상태가 폭 800~890px·600~610px 구간에서 실제로
   재현됐다(Playwright 실측). 보기토글+인쇄버튼을 한 컨테이너(.tm-topbar-actions)로 묶어서
   "둘이 항상 같이 붙어있거나, 둘이 같이 다음 줄로 넘어가거나" 둘 중 하나만 가능하게 함
   (margin-left:auto도 개별 .tm-view-toggle이 아니라 이 컨테이너로 옮김). */
.tm-topbar-actions{ display:flex; align-items:center; gap:12px; margin-left:auto; }
.tm-view-toggle{ display:flex; gap:2px; background:var(--ui-divider); padding:3px; border-radius:999px; }
.tm-view-toggle button{ border:none; padding:7px 16px; border-radius:999px; font-size:13px; cursor:pointer; background:transparent; color:var(--text-secondary); font-weight:600; white-space:nowrap; font-family:inherit; }
.tm-view-toggle button.active{ background:#fff; color:var(--title); box-shadow:0 1px 2px rgba(0,0,0,.08); }
.tm-print-btn{ padding:8px 18px; border:1px solid var(--title); border-radius:999px; background:var(--title); color:#fff; font-size:13px; cursor:pointer; font-weight:700; white-space:nowrap; flex-shrink:0; font-family:inherit; }
.tm-print-btn:disabled{ opacity:.4; cursor:default; }

/* 사이드바 하단에 고정되는 학습지 생성 버튼(카트 바의 것과 같은 클래스 공유) — 요청: "학습지
   생성 버튼은 사이드바와 가까운 곳에". 문제 수/체크는 즉시 반영되고 용지 크기만 다시 생성이
   필요해서, 평소엔 수수하다가(회색) 용지를 바꾼 직후에만 강조된다(파란 테두리+배경). */
.tm-gen-btn{ width:100%; padding:11px; border:1px solid var(--ui-border); border-radius:8px; background:#fff; font-size:14px; cursor:pointer; color:var(--body-text); font-weight:700; white-space:nowrap; font-family:inherit; }
.tm-gen-btn:disabled{ opacity:.45; cursor:default; }
.tm-gen-btn.tm-pending{ border-color:var(--primary); color:var(--primary); background:color-mix(in oklab, var(--primary) 8%, white); box-shadow:0 0 0 3px color-mix(in oklab, var(--primary) 15%, transparent); }
.cart-make.tm-gen-btn{ width:auto; background:#fff; color:oklch(0.2 0.01 85); border:none; padding:10px 20px; border-radius:999px; font-weight:700; }
.cart-make.tm-gen-btn.tm-pending{ background:var(--primary); color:#fff; box-shadow:none; }

/* 2026-08-17: "같은 꽉찬 화면인데 어느 땐 2열, 어느 땐 1열로 보인다"는 리포트 —
   updatePreviewScale()(오늘수학.html)이 #tmPreviewScroll의 clientWidth로 2열 여부를
   정하는데, overflow-y:auto만 있고 scrollbar-gutter가 없으면 세로 스크롤바 등장 여부에
   따라 clientWidth 자체가 바뀌는 히스테리시스가 생긴다: 1열(카드 큼→내용 김)이면
   스크롤바가 생겨 폭이 줄고, 그 줄어든 폭이 다시 "2열 못 함" 판정을 강화해 1열에
   눌러앉는다 — 반대로 2열(카드 작음→내용 짧음)로 시작하면 스크롤바가 안 생겨 넓은 폭이
   그대로 유지되며 2열에 눌러앉는다. 똑같은 창 폭에서 시작 상태에 따라 결과가 갈리는
   게 이 때문. `#tmSidebarScroll`에 이미 쓰던 것과 같은 처방(scrollbar-gutter:stable)으로
   스크롤바 자리를 항상 예약해서 clientWidth를 스크롤바 유무와 무관하게 고정시킨다. */
#tmPreviewScroll{ flex:1; overflow-y:auto; scrollbar-gutter:stable; padding:28px 28px 120px; background:var(--bg); }
.tm-empty-state{ max-width:560px; margin:60px auto; text-align:center; color:var(--text-secondary); font-size:14px; line-height:1.7; }
/* 요청: "가로로 2개 보이고 아래 2개는 살짝 잘려보일 정도로 크게" — 하루수학의 3열
   그리드(.multi-page)를 그대로 안 쓰고, 항상 가로 2열로 감싸는 전용 클래스를 씀(내용 양과
   무관하게 항상 이 배치 — updatePreviewScale이 배율을 "2열이 꽉 차는 만큼"으로만 계산하고
   세로는 일부러 제한하지 않아서 다음 줄이 자연스럽게 잘려 보이며 스크롤된다).
   반드시 @media screen 안에만 둘 것 — 인쇄는 기존 .sheet-wrap(세로 스택, 한 쪽씩)을 그대로
   써야 한다(하루수학의 --preview-zoom과 같은 이유로 스코프 분리, CLAUDE.md 참고). */
@media screen{
  .tm-grid2{ display:flex; flex-direction:row; flex-wrap:wrap; align-items:flex-start; justify-content:center; gap:24px; max-width:100%; }
  .tm-grid2 .sheet{ transform-origin:top left; flex-shrink:0; }
}


/* ====== 예전 카트 바(화면 하단 고정 알약) CSS — 2026-08-15에 사이드바 통합(#tmCartbarWrap
   아래 참고)으로 대체되어 비활성화. 삭제하지 않고 주석으로 보존(사용자 요청) — 되돌릴 일이
   생기면 이 블록의 주석만 풀고 아래 새 `.tm-side-cart-*` 블록을 다시 주석 처리하면 됨.
   원래 로드맵.html의 카트 바(.cartbar-wrap 등) CSS 그대로 가져온 것이었음.
  .cartbar-wrap{ position:fixed; left:340px; right:0; bottom:0; display:flex; justify-content:center; padding:16px 0 20px; background:linear-gradient(to top, oklch(0.975 0.006 85) 60%, transparent); pointer-events:none; z-index:20; container-type:inline-size; container-name:cartbarwrap; }
  .cartbar{ pointer-events:auto; display:flex; align-items:center; gap:14px; background:oklch(0.2 0.01 85); color:#fff; padding:10px 12px 10px 20px; border-radius:999px; box-shadow:0 12px 32px rgba(0,0,0,.25); flex-wrap:wrap; }
  .cart-total{ font-size:15px; font-weight:700; white-space:nowrap; display:flex; align-items:center; gap:0; }
  .cart-total b{ display:inline-block; min-width:2.4em; text-align:right; }
  .cart-sep{ width:1px; height:20px; background:rgba(255,255,255,.2); }
  .seg{ display:flex; background:rgba(255,255,255,.1); border-radius:8px; padding:2px; gap:1px; }
  .seg button{ border:none; background:transparent; color:oklch(0.8 0.01 85); font-size:12px; font-weight:600; padding:6px 10px; border-radius:6px; cursor:pointer; font-family:inherit; white-space:nowrap; margin:0; }
  .seg button.active{ background:#fff; color:oklch(0.2 0.01 85); font-weight:700; }
  .cart-reset{ background:transparent; border:none; color:oklch(0.85 0.01 85); font-size:14px; font-weight:600; cursor:pointer; font-family:inherit; white-space:nowrap; }
  .cart-make{ background:#fff; color:oklch(0.2 0.01 85); border:none; padding:10px 20px; border-radius:999px; font-size:14px; font-weight:700; cursor:pointer; font-family:inherit; white-space:nowrap; }
  (원래 .toast{ bottom:96px; ... } — 카트 바가 화면 하단에 떠 있던 시절 그 위에 뜨도록 잡은
  여백. 새 .toast 규칙은 아래 3단계 블록 안에 있음.)
  @container cartbarwrap (max-width:720px){
    .cartbar{ justify-content:center; border-radius:20px; row-gap:8px; max-width:calc(100cqw - 24px); }
    .cart-total{ flex:1 1 100%; justify-content:center; order:-1; }
    .cart-sep{ display:none; }
  }
  1100px 미디어쿼리 안: .cartbar-wrap{ left:280px; }
  768px 미디어쿼리 안: .cartbar-wrap{ left:0; padding:12px 12px 18px; }
====== */

/* ---------- 3단계: 인쇄 형태(용지·문제 수·생성하기) — 사이드바 하단 고정 패널 ----------
   2026-08-15(fixes.md #45): 예전엔 화면 하단에 떠 있는 알약 모양 "카트 바"(로드맵.html에서
   그대로 가져온 것)였는데, "사이드바 안으로 통합해서 1~4단계가 한눈에 이어지게" 요청받아
   사이드바 안의 고정 패널로 옮김. `#tmSidebarScroll`(flex:1, 아코디언 목록)의 형제로 두면
   flex-column인 `#tmSidebar` 안에서 자동으로 하단에 붙는다 — 예전처럼 viewport 전체 기준
   `position:fixed` + 사이드바 폭만큼 left를 미는 계산이나, 사이드바 폭이 줄어들 때마다
   같이 좁아지는 걸 감안한 @container 반응형 계산이 전혀 필요 없어졌다(사이드바 자체가
   이미 반응형을 갖고 있어서 그 안의 자식은 저절로 따라감). 차시를 하나도 안 담았으면(요청:
   "차시를 선택하면 그 아래 3단계가 나타남") JS(updateCartbar)가 그대로 display:none 처리 —
   id(#tmCartbarWrap)는 안 바뀌었으니 그 JS는 손 안 댐. */
  #tmCartbarWrap{ flex-shrink:0; border-top:1px solid var(--ui-border); background:var(--surface); padding:0 0 14px; }
  .tm-side-cart-total{ padding:0 20px; font-size:13.5px; font-weight:700; color:var(--title); }
  .tm-side-cart-total b{ color:var(--primary); }
  .tm-side-cart-row{ padding:8px 20px 0; display:flex; align-items:center; gap:10px; }
  .tm-side-cart-row-label{ font-size:12px; color:var(--text-secondary); font-weight:600; flex-shrink:0; width:44px; }
  .tm-side-seg{ flex:1; display:flex; background:var(--ui-divider); border-radius:8px; padding:2px; gap:1px; }
  /* QR 코드·이름 칸을 한 줄에(요청, 2026-08-16) — 각 라벨+토글을 하나의 그룹으로 묶어서
     justify-content:space-between이 라벨-토글 사이가 아니라 두 그룹 사이에만 여백을 주게 함. */
  .tm-side-cart-row-pair{ justify-content:space-between; }
  .tm-side-cart-row-pair-item{ display:flex; align-items:center; gap:8px; }
  .tm-side-cart-row-pair-item .tm-side-cart-row-label{ width:auto; }
  .tm-side-seg button{ flex:1; border:none; background:transparent; color:var(--text-secondary); font-size:12px; font-weight:600; padding:6px 4px; border-radius:6px; cursor:pointer; font-family:inherit; white-space:nowrap; margin:0; }
  .tm-side-seg button.active{ background:#fff; color:var(--title); font-weight:700; box-shadow:0 1px 2px rgba(0,0,0,.08); }
  /* QR 코드·이름 칸 토글(fixes.md #45, 2026-08-15) — 스위치 모양의 작은 체크박스.
     실제 켜고 끄는 효과는 sheet-layout.css의 `#sheetWrap.qr-off`/`.name-off` 클래스가
     담당(순수 CSS 숨김, 캐시/재생성과 무관 — 이유는 그 CSS 옆 주석 참고). */
  .tm-toggle{ position:relative; display:inline-block; width:36px; height:20px; flex-shrink:0; }
  .tm-toggle input{ position:absolute; inset:0; opacity:0; margin:0; cursor:pointer; }
  .tm-toggle-track{ position:absolute; inset:0; background:var(--ui-border); border-radius:999px; transition:background .15s ease; pointer-events:none; }
  .tm-toggle-track::before{ content:''; position:absolute; top:2px; left:2px; width:16px; height:16px; border-radius:50%; background:#fff; box-shadow:0 1px 2px rgba(0,0,0,.25); transition:transform .15s ease; }
  /* 요청(2026-08-16): "설정의 토글이 그렇게 중요한 정보는 아님" — 포인트 컬러(--primary,
     차시 체크·활성 탭에 이미 쓰는 파란색)를 여기 또 쓰면 너무 강조돼 보임. 켜짐 상태는
     중립적인 진한 회색(--title, "인쇄하기" 버튼과 같은 톤)만으로 표시. */
  .tm-toggle input:checked + .tm-toggle-track{ background:var(--title); }
  .tm-toggle input:checked + .tm-toggle-track::before{ transform:translateX(16px); }

  /* 요청(2026-08-17): "소책자는 빈페이지 생기면... 안내 주자" — 제책이 4쪽 단위라 뒷표지
     앞에 빈 페이지가 몇 장 끼는지 "학습지 생성"을 누르기 전이 아니라 누른 직후(실제 값이
     확정된 시점)에 알려준다. 경고라기보단 참고 정보라 강한 경고색 대신 옅은 배경+톤다운된
     글씨(다른 보조 설명, .tm-card-desc와 같은 계열)로 조용히 보여준다. */
  .tm-booklet-filler-note{ margin:8px 20px 0; padding:8px 10px; border-radius:8px; background:var(--ui-divider); color:var(--text-secondary); font-size:11.5px; line-height:1.5; }
  .tm-side-cart-actions{ padding:10px 20px 0; display:flex; gap:8px; }
  .tm-side-cart-reset{ flex-shrink:0; background:transparent; border:1px solid var(--ui-border); border-radius:8px; color:var(--text-secondary); font-size:13px; font-weight:600; cursor:pointer; font-family:inherit; white-space:nowrap; padding:0 14px; }
  #tmCartbarWrap .tm-gen-btn{ flex:1; }

  /* 예전 카트 바가 화면 하단 중앙에 떠 있을 때 만들던 여백(bottom:96px)을 그대로 두면
     이제 아무것도 없는 빈 공간에 토스트가 붕 떠 보인다 — 화면 맨 아래로 낮춤. */
  .toast{ position:fixed; left:50%; bottom:24px; transform:translateX(-50%); background:oklch(0.2 0.01 85); color:#fff; padding:10px 18px; border-radius:12px; font-size:14px; font-weight:600; box-shadow:0 8px 20px rgba(0,0,0,.2); z-index:40; opacity:0; transition:opacity .15s ease; pointer-events:none; }
  .toast.show{ opacity:1; }

  /* 사용자 피드백은 인쇄 영역과 분리한 편지 모양 플로팅 버튼으로 둔다. */
  .tm-feedback-floater{ position:fixed; right:18px; bottom:18px; z-index:50; display:inline-flex; align-items:center; gap:8px; padding:12px 15px 12px 13px; border-radius:999px; background:var(--primary); color:#fff; text-decoration:none; font-size:13px; font-weight:700; letter-spacing:-.01em; box-shadow:0 8px 22px color-mix(in srgb, var(--primary) 30%, transparent); transition:transform .16s ease, box-shadow .16s ease, background .16s ease; }
  .tm-feedback-floater:hover{ transform:translateY(-2px); background:var(--primary-dark, var(--primary)); box-shadow:0 11px 26px color-mix(in srgb, var(--primary) 38%, transparent); }
  .tm-feedback-floater:focus-visible{ outline:3px solid color-mix(in srgb, var(--primary) 30%, white); outline-offset:3px; }
  .tm-feedback-floater svg{ width:19px; height:19px; fill:none; stroke:currentColor; stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; }

  /* 개발자용 문제 수 플로터(fixes.md #45, 2026-08-16) — 사이드바에서 문제 수 선택지를
     빼면서, 레이아웃 테스트용으로 qty를 여전히 바꿔볼 수 있게 화면 오른쪽 아래에 작게 따로
     띄워둔 도구. 점선 테두리·연한 색으로 "정식 UI가 아님"을 시각적으로 구분함. */
  #devQtyFloater{ position:fixed; right:12px; bottom:78px; z-index:45; background:#fff; border:1px dashed var(--ui-border); border-radius:10px; padding:8px; box-shadow:0 4px 16px rgba(0,0,0,.12); }
  .tm-dev-floater-label{ font-size:9.5px; color:var(--text-secondary); font-weight:600; margin-bottom:4px; text-align:center; }
  #devQtyFloater .tm-side-seg{ width:180px; }
  #devLayoutFloater{ position:fixed; right:12px; bottom:158px; z-index:45; background:#fff; border:1px dashed var(--primary); border-radius:10px; padding:8px; box-shadow:0 4px 16px rgba(0,0,0,.12); }
  #devLayoutFloater .tm-side-seg{ width:180px; }
  .tm-dev-floater-note{ margin-top:5px; font-size:9px; line-height:1.35; color:var(--text-secondary); text-align:center; }

/* =========================================================
   1-b. 반응형(2026-08-11, fixes.md #1) — PC 반응형 설정 + 모바일 대응.
   세 단계로 나눔(좁은 순서대로): "PC 반응형"(사이드바만 살짝 좁힘, 나머지는 그대로) →
   "카트 바 두 줄 대응"(사이드바는 아직 그대로, 카트 바 pill이 두 줄로 밀릴 때를 대비해
   모양 자체를 두 줄에 맞게 바꿈) → "모바일"(사이드바를 오버레이 드로어로 전환).
   실측(Playwright, 여러 뷰포트 폭)으로 확인한 문제:
   - 사이드바가 항상 고정 340px라서, 좁은 화면에서는 본문(#tmMain)이 짓눌려 상단바가 여러
     줄로 밀리고(375px에서는 본문 폭이 35px까지 줄어 글자가 한 글자씩 줄바꿈됨) 워크시트
     미리보기도 제대로 안 보임.
   - 카트 바(.cartbar-wrap)는 border-radius:999px(알약 모양)를 전제로 한 줄 레이아웃인데,
     폭이 좁아 두 줄로 밀리면 알약 모양 그대로 두 줄이 되어 아래쪽 모서리만 둥글게 남고
     위쪽은 각져 보이는 등 모양이 깨짐 + 총 개수 라벨이 다른 조각과 뒤섞여 읽기 어려움.
   ⚠️(재수정) 처음엔 이 "두 줄 대응"을 @media (max-width:860px)로 걸었는데, 사용자가 실제
   창 크기를 조절해보고 "1열→중앙정렬 2열 사이에 2열인데 중앙정렬 아닌 구간이 있다"고
   재현함 — 사이드바 폭이 구간마다(340/280/0) 달라서 "실제 줄바꿈이 시작되는 폭"과 고정
   860px 임계값이 어긋났던 것. 뷰포트 미디어쿼리를 버리고 @container(아래 .cartbar-wrap의
   container-type)로 바꿔서, 사이드바 폭이 무엇이든 "이 요소 자신의 실제 가용 폭"을 직접
   기준으로 삼도록 근본적으로 고침 — 자세한 설명은 .cartbar-wrap 옆 주석 참고.
========================================================= */

/* ── PC 반응형(1100px 이하, 아직 사이드바 드로어 아님) ──
   좁은 노트북/창 분할 사용을 겨냥 — 사이드바만 340→280px로 줄여서 본문에 여유를 더 준다.
   상단바의 flex-wrap은 이미 있어서(줄바꿈 자체는 원래도 됨) 폭만 넉넉해지면 대부분
   한 줄로 돌아온다. */
@media (max-width:1100px){
  #tmSidebar{ width:280px; }
}

/* ── 모바일(768px 이하) — 사이드바를 오버레이 드로어로 전환 ──
   사용자 지정(2026-08-11, AskUserQuestion): "햄버거 버튼 + 오버레이 드로어" 방식 채택.
   평소엔 숨겨져 있다가 상단바의 햄버거 버튼을 누르면 사이드바가 왼쪽에서 슬라이드로
   나타나 화면을 덮는다. **자동으로 안 닫힘**(요청: "자동으로 닫지 말고 사용자가 선택
   끝나면 닫도록") — 체크박스로 여러 차시를 담는 흐름이라 행을 누를 때마다 닫히면 오히려
   불편하다. 닫기는 햄버거 재클릭/배경(backdrop) 탭/사이드바 안 닫기(X) 버튼 세 가지
   경로로만 이뤄진다(JS의 setSidebarOpen 참고) — 이 미디어쿼리는 시각적 상태(열림/닫힘에
   따른 위치)만 담당하고, 언제 열고 닫을지 판단은 전혀 안 함. */
@media (max-width:768px){
  /* 사이드바(z-index:60)가 열리면 position:fixed라 문서 흐름과 무관하게 화면 왼쪽을
     뒤덮는데, 햄버거 버튼이 topbar 왼쪽 끝(사이드바와 같은 x좌표 범위)에 있어서 그 밑에
     깔려버린다 — "햄버거 재클릭으로 닫기"가 실제로는 안 눌리는 버그였음(Playwright로
     실측 발견: 클릭이 사이드바의 다른 요소에 가로채짐). 햄버거 버튼만 사이드바보다 높은
     z-index로 올려서 열려있을 때도 계속 눌리게 함. */
  .tm-hamburger-btn{ display:flex; position:relative; z-index:70; }
  .tm-sidebar-close-btn{ display:flex; }
  #tmTopbar{ padding:12px 16px; gap:10px; }
  #tmPreviewScroll{ padding:16px 16px 120px; }

  #tmSidebar{
    position:fixed; top:0; left:0; bottom:0; z-index:60;
    width:min(320px, 85vw);
    transform:translateX(-100%);
    transition:transform .22s ease;
  }
  .app.tm-sidebar-open #tmSidebar{
    transform:translateX(0);
    box-shadow:8px 0 28px rgba(0,0,0,.2);
  }
  .tm-sidebar-backdrop{
    display:block; position:fixed; inset:0; background:rgba(15,15,20,.42);
    z-index:55; opacity:0; pointer-events:none; transition:opacity .22s ease;
  }
  .app.tm-sidebar-open .tm-sidebar-backdrop{ opacity:1; pointer-events:auto; }
  /* 3단계 인쇄 형태 패널(#tmCartbarWrap)이 이제 카트 바 시절과 달리 viewport 기준
     position:fixed가 아니라 #tmSidebar 안의 평범한 flex 자식이라, 사이드바가 드로어로
     밀려 나갔다 들어왔다 해도(위 #tmSidebar의 transform) 별도 처리 없이 항상 같이 따라간다. */
}


/* =========================================================
   2. 인쇄 시 오늘수학 자체 UI(사이드바/상단바/카트바)도 하루수학과 동일한 원칙으로 완전히
   레이아웃에서 제거 — 이 원칙(display:none으로 문서 전체 높이에 아예 기여하지 않게)이
   3일간의 인쇄 여백 버그를 잡은 근본 해법이었음(하루수학 CLAUDE.md "최종 3차" 항목 참고).
========================================================= */
@media print{
  #tmSidebar, #tmTopbar, #tmCartbarWrap, #devQtyFloater, #devLayoutFloater, .tm-feedback-floater, .toast, .tm-sidebar-backdrop{ display:none !important; }
  .app{ min-height:0 !important; display:block !important; height:auto !important; overflow:visible !important; }
  body{ min-height:0 !important; }
  #tmMain{ display:block !important; }
  #tmPreviewScroll{ overflow:visible !important; height:auto !important; padding:0 !important; }
  /* 2026-08-22 발견(사용자 리포트: "Safari 인쇄 미리보기에서 내용이 넘쳐, 위 학습지가
     잘려서 다음 학습지 위에 이어짐") — #sheetWrap의 실제 클래스는 sheet-layout.css가
     인쇄 시 되돌리는 `.sheet-wrap.multi-page`(하루수학.html 시절 이름)가 아니라
     `.tm-grid2`(오늘수학 전용, 화면에서 여러 쪽을 가로로 나란히 보여주는 배치)다 — 이름이
     달라서 그 되돌림 규칙이 한 번도 안 먹었다. `.tm-grid2`(1클래스, 이 파일)와
     `.sheet-wrap`(1클래스, sheet-layout.css)는 우선순위가 같아 나중에 로드되는 이 파일이
     이겨서, 인쇄 시에도 화면용 가로 배치(flex-direction:row/flex-wrap:wrap)가 그대로
     남아 여러 쪽이 겹쳐 보였다. `.sheet-wrap.multi-page` 인쇄 규칙과 같은 값으로,
     실제 클래스 이름에 맞춰 되돌린다(2클래스 선택자라 위 1클래스 `.tm-grid2`보다 우선함). */
  .sheet-wrap.tm-grid2{ flex-direction:column !important; flex-wrap:nowrap !important; align-items:flex-start !important; justify-content:flex-start !important; }
}

/* QR 스캔으로 들어온 "#c=..." 정답 뷰(오늘수학.html의 tryHandleQrDecodeRoute/
   renderDecodedAnswerView, 플랜 §5) — 카트를 담을 사이드바/상단바/카트바 자체가 무의미하므로
   완전히 숨긴다. 인쇄 시 숨기는 위 규칙과 같은 요소 집합, 화면(screen)에서도 항상 숨겨야
   하므로 미디어 쿼리 없이 별도 클래스로 둠. */
body.qr-decode-mode #tmSidebar,
body.qr-decode-mode #tmTopbar,
body.qr-decode-mode #tmCartbarWrap,
body.qr-decode-mode #devQtyFloater,
body.qr-decode-mode #devLayoutFloater,
body.qr-decode-mode .tm-feedback-floater,
body.qr-decode-mode .tm-sidebar-backdrop{ display:none !important; }
body.qr-decode-mode .app{ display:block !important; }
body.qr-decode-mode #tmMain{ display:block !important; }
body.qr-decode-mode #tmPreviewScroll{ height:100vh !important; }
