/* 학습계획(StudyPlan) 화면 규칙 — 2026-10-18 학습계획·이력·캘린더 도메인 이전으로 index.html 첫 style 블록에서 바이트 그대로 옮겼다.
   index.html에서 첫 style 블록 바로 뒤(회수 인라인 스타일 kbgReclaimedInline 앞)에 실린다 — 옮기며 지나친 규칙은 .lib-pdf-*·.library-relation-body .pr-sub뿐이고
   그 선택자는 학습계획 마크업에 쓰이지 않는다(계산 스타일 대조로 확인). */
/* ===== StudyPlan (학습계획) — 앱 토큰 기반 · 다크/라이트 · 반응형 (v3.91) ===== */
.sp-root{display:flex;flex-direction:column;gap:14px;max-width:1080px;margin:0 auto}
.sp-topline{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.sp-mode-badge{font-size:12px;font-weight:700;color:var(--add-active-text);background:var(--add-active-bg);border:1px solid var(--add-active-border);border-radius:999px;padding:4px 12px}
.sp-sub{font-size:12px;color:var(--text3)}
.sp-panel{background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:16px}
.sp-panel h3{margin:0 0 12px;font-size:13px;color:var(--text);font-weight:700;display:flex;align-items:center;gap:8px}
.sp-avg{margin-left:auto;font-size:11px;color:var(--text3);font-weight:600}
.sp-empty{color:var(--text3);font-size:12.5px;padding:6px 0}
.sp-prog-head{display:flex;align-items:baseline;gap:10px;margin-bottom:8px}
.sp-prog-head b{font-size:13px;color:var(--text)}
.sp-prog-pct{font-size:22px;font-weight:800;color:var(--accent);font-family:"Playfair Display",serif;line-height:1}
.sp-prog-num{margin-left:auto;font-size:12px;color:var(--text3)}
.sp-prog-bar{height:9px;border-radius:999px;background:var(--surface2);overflow:hidden}
.sp-prog-bar>span{display:block;height:100%;background:linear-gradient(90deg,var(--accent),var(--accent2));border-radius:999px;transition:width .3s}
.sp-dots{display:flex;flex-wrap:wrap;gap:6px;margin-top:12px}
.sp-dot{width:18px;height:18px;border-radius:50%;border:2px solid;display:inline-flex;align-items:center;justify-content:center;font-size:10px;color:#fff;font-weight:800}
.sp-grid2{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.sp-cat-row{display:flex;align-items:center;gap:9px;margin-bottom:9px;font-size:12px}
.sp-cat-ic{font-size:14px}
.sp-cat-l{color:var(--text2);min-width:66px}
.sp-cat-bar{flex:1;height:8px;border-radius:999px;background:var(--surface2);overflow:hidden}
.sp-cat-bar>span{display:block;height:100%;border-radius:999px}
.sp-cat-v{color:var(--text3);font-size:11px;min-width:42px;text-align:right;font-family:"Playfair Display",serif}
.sp-ach{display:flex;align-items:flex-end;gap:8px;height:120px}
.sp-ach-col{flex:1;display:flex;flex-direction:column;align-items:center;gap:4px;height:100%}
.sp-ach-bar-wrap{flex:1;width:60%;display:flex;align-items:flex-end}
.sp-ach-bar{width:100%;border-radius:6px 6px 0 0;background:linear-gradient(180deg,var(--accent),var(--accent2));min-height:3px;transition:height .3s}
.sp-ach-pct{font-size:10px;color:var(--text3);font-weight:700}
/* 0인 날은 막대를 세우지 않고 바닥선만 남긴다 — 0은 「작다」가 아니라 「없다」다.
   수치도 한 단계 낮춰, 값이 있는 날과 눈으로 갈린다. */
.sp-ach-bar.is-zero{height:2px;background:var(--border);border-radius:2px;opacity:.85}
.sp-ach-pct.is-zero{color:var(--text3);opacity:.6;font-weight:600}
.sp-ach-d{font-size:10px;color:var(--text3)}
.sp-goal-wrap{display:flex;flex-wrap:wrap;gap:7px;margin-bottom:10px}
.sp-goal-chip{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--text);background:var(--surface2);border:1px solid var(--border);border-radius:999px;padding:4px 6px 4px 12px}
.sp-goal-x{border:0;background:transparent;color:var(--text3);cursor:pointer;font-size:15px;line-height:1;width:18px;height:18px;border-radius:50%}
.sp-goal-x:hover{background:var(--red);color:#fff}
.sp-goal-add{display:flex;gap:8px}
.sp-goal-add input{flex:1;background:var(--add-input-bg);border:1px solid var(--border);border-radius:8px;padding:8px 12px;color:var(--text);font-size:13px;font-family:inherit;outline:none}
.sp-goal-add input:focus{border-color:var(--accent)}
.sp-clock-panel{display:flex;flex-direction:column;align-items:center;gap:12px}
.sp-clock{width:320px;height:320px;max-width:100%}
.sp-banner{min-height:44px;font-size:13px;color:var(--text2);display:flex;align-items:center;gap:8px;flex-wrap:wrap;justify-content:flex-start;text-align:left}
.sp-banner-ic{font-size:18px}
.sp-banner-now b{color:var(--text)}
.sp-banner-idle{color:var(--text3)}
.sp-cat-tag{font-size:11px;font-weight:700;margin-left:6px}
.sp-toolbar{display:flex;flex-wrap:wrap;gap:8px}
.sp-btn{display:inline-flex;align-items:center;justify-content:center;gap:7px;min-height:44px;background:var(--surface);border:1px solid var(--border);border-radius:9px;padding:9px 14px;color:var(--text2);font-size:12.5px;cursor:pointer;font-family:inherit;transition:border-color .15s,color .15s}
.sp-btn>.ui-icon,.sp-mini>.ui-icon{width:16px;height:16px;flex:0 0 16px}
.sp-btn:hover{border-color:var(--accent);color:var(--text)}
.sp-btn.sm{padding:6px 11px;font-size:12px}
.sp-btn.primary{background:var(--accent);border-color:var(--accent);color:#0d1117}
.sp-btn.primary:hover{color:#0d1117;opacity:.92}
html[data-theme="light"] .sp-btn.primary,html[data-theme="light"] .sp-btn.primary:hover{color:#fff}
/* ⚠️ hover만 두면 **터치에는 위험 신호가 0**이다 — 실측에서 `🗑 삭제합니다`와 `취소`의 계산 스타일이
   완전히 같았다(2026-07-28 독립 리뷰). 평상시에도 위험색을 입힌다. */
.sp-btn.danger{color:var(--red);border-color:var(--red)}
.sp-btn.danger:hover{border-color:var(--red);color:var(--red);background:var(--danger-soft-bg,rgba(248,81,73,0.12))}
html[data-theme="light"] .sp-btn.danger{color:var(--confirm-danger);border-color:var(--confirm-danger)}
html[data-theme="light"] .sp-btn.danger:hover{color:var(--confirm-danger);border-color:var(--confirm-danger);background:var(--confirm-danger-bg)}
.sp-datenav{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.sp-date{font-size:14px;font-weight:700;color:var(--text);font-family:"Playfair Display",serif}
.sp-date-done{font-size:11px;color:var(--text3);background:var(--surface2);border-radius:999px;padding:3px 10px}
.sp-table-wrap{padding:0;overflow-x:auto}
.sp-table{width:100%;border-collapse:collapse;font-size:12.5px}
.sp-table th{color:var(--text3);font-size:11px;text-align:left;padding:11px 12px;border-bottom:1px solid var(--border);font-weight:700;white-space:nowrap}
.sp-table td{padding:11px 12px;border-bottom:1px solid var(--border);vertical-align:top;color:var(--text2)}
.sp-table tr:last-child td{border-bottom:0}
.sp-table tr.sp-done{opacity:.62}
.sp-td-ic{width:40px}
.sp-cat-pill{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:9px;border:1px solid;font-size:15px}
.sp-td-time{white-space:nowrap;font-family:"Playfair Display",serif;color:var(--text)}
.sp-dur{display:block;font-size:10px;color:var(--text3);font-family:"Noto Sans KR",sans-serif}
.sp-td-task b{color:var(--text);font-weight:700;display:block}
.sp-td-strat{max-width:280px;line-height:1.45}
.sp-td-chk{width:44px;text-align:center}
.sp-chk{width:18px;height:18px;accent-color:var(--accent2);cursor:pointer}
.sp-td-act{white-space:nowrap;width:104px}
.sp-mini{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:44px;min-height:44px;background:transparent;border:1px solid var(--border);border-radius:7px;padding:7px 9px;color:var(--text2);cursor:pointer;font-size:12px;margin-left:2px}
.sp-mini:hover{border-color:var(--accent)}
.sp-mini:focus-visible,.sp-btn:focus-visible,.sp-analysis>summary:focus-visible{outline:2px solid var(--ui-focus);outline-offset:2px}
.sp-plan-schedule{display:flex;flex-direction:column;gap:12px;min-width:0}
.sp-schedule-head{display:flex;align-items:flex-end;justify-content:space-between;gap:16px;flex-wrap:wrap}
.sp-schedule-head h2{margin:2px 0 0;color:var(--text);font-size:18px;line-height:1.35}
.sp-eyebrow{display:block;color:var(--text2);font-size:11px;font-weight:700;letter-spacing:.04em}
.sp-schedule-head .sp-toolbar{justify-content:flex-end}
.sp-time-overview{position:relative;min-width:0;padding:28px 14px 14px;background:var(--surface);border:1px solid var(--border);border-radius:14px;overflow:hidden}
.sp-time-ticks{position:relative;height:18px;margin:0 1px;color:var(--text2);font-size:10px}
.sp-time-ticks span{position:absolute;top:0;transform:translateX(-50%)}
.sp-time-ticks span:first-child{transform:none}.sp-time-ticks span:last-child{transform:translateX(-100%)}
.sp-time-track{position:relative;height:68px;border:1px solid var(--border);border-radius:10px;background:var(--surface2);overflow:hidden}
.sp-time-track::before{content:"";position:absolute;inset:0;background:repeating-linear-gradient(90deg,transparent 0,transparent calc(12.5% - 1px),var(--border) calc(12.5% - 1px),var(--border) 12.5%);opacity:.55;pointer-events:none}
.sp-time-block{position:absolute;top:10px;height:46px;min-width:2px;display:flex;align-items:center;padding:0 7px;border-left:3px solid var(--sp-block-color);background:color-mix(in srgb,var(--sp-block-color) 16%,var(--surface));color:var(--text);font-size:11px;font-weight:700;line-height:1.25;overflow:hidden}
.sp-time-block>span{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:clip}
.sp-time-block.done{opacity:.55}
.sp-time-now{position:absolute;top:0;bottom:0;width:2px;background:var(--accent);z-index:3;pointer-events:none}
.sp-time-now i{position:absolute;top:0;left:50%;width:8px;height:8px;border-radius:50%;background:var(--accent);transform:translate(-50%,-50%)}
.sp-cards{display:none;flex-direction:column;gap:0;position:relative}
.sp-cards::before{content:"";position:absolute;top:18px;bottom:18px;left:86px;width:1px;background:var(--border)}
.sp-card{display:grid;grid-template-columns:72px 16px minmax(0,1fr);gap:8px;align-items:start;position:relative;background:transparent;border:0;border-radius:0;padding:0 0 12px}
.sp-card.sp-done{opacity:.62}
.sp-card-when{padding-top:13px;text-align:right;min-width:0}
.sp-card-time{display:block;font-size:11.5px;line-height:1.4;color:var(--text);font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
.sp-card-node{position:relative;z-index:1;width:11px;height:11px;margin-top:19px;border:3px solid var(--accent);border-radius:50%;background:var(--bg)}
.sp-card-body{min-width:0;background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:13px}
.sp-card-h{display:flex;align-items:flex-start;gap:9px;margin-bottom:8px}
.sp-card-chk{margin-left:auto;font-size:11px;color:var(--text3);display:inline-flex;align-items:center;gap:5px;cursor:pointer}
.sp-card-task{flex:1;min-width:0;font-size:13px;color:var(--text);margin-bottom:5px;overflow-wrap:anywhere}
.sp-card-task b{display:block}
.sp-card-strat{font-size:12px;color:var(--text2);line-height:1.5;margin-bottom:9px}
.sp-card-act{display:flex;flex-wrap:wrap;gap:8px}
.sp-analysis{background:var(--surface);border:1px solid var(--border);border-radius:14px;overflow:hidden}
.sp-analysis>summary{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:52px;padding:12px 16px;color:var(--text);font-size:13px;font-weight:700;cursor:pointer;list-style:none}
.sp-analysis>summary::-webkit-details-marker{display:none}
.sp-analysis>summary::after{content:"＋";flex:0 0 auto;color:var(--text2);font-size:18px;font-weight:400}
.sp-analysis[open]>summary::after{content:"−"}
.sp-analysis-body{display:flex;flex-direction:column;gap:14px;padding:0 14px 14px}
.sp-analysis-body>.sp-grid2>.sp-panel,.sp-analysis-body>.sp-clock-panel{background:var(--surface2)}
/* 화면 축 탭(v7.17) — 학습계획표 ↔ 학습이력관리. 아래 .sp-modebar와 **토큰·기하는 공유하되 클래스는 분리**한다.
   같은 모양의 바가 위아래로 둘이면 "어느 바가 무엇을 바꾸는지"를 매번 다시 읽어야 한다: 이쪽은 아이콘+이름+배지,
   저쪽은 라벨만이라 형태로 구별된다. sticky를 안 쓰는 것도 의도다 — 화면 축은 스크롤과 함께 지나가면 된다.
   .sp-root가 max-width:980px·margin:0 auto라 1280에서도 거대 버튼 두 개가 되지 않는다(좌우 여백은 상속). */
.sp-viewbar{display:grid;grid-template-columns:1fr 1fr;gap:8px;background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:8px}
/* 비선택 라벨은 --text3이 아니라 --text2다. --text3(#6e7681) on --surface(#161b22)는 3.76:1로 AA 미달이고
   --text2는 5.63:1(다크)·7.5:1(라이트)이다. 아래 .sp-mode-btn의 --text3는 선재 조건이며 여기서 복제하지 않는다. */
.sp-view-btn{display:flex;align-items:center;flex-wrap:wrap;gap:8px;min-width:0;min-height:44px;padding:10px 12px;background:transparent;border:1px solid transparent;border-radius:9px;color:var(--text2);font-size:13px;font-weight:700;line-height:1.35;font-family:inherit;text-align:left;cursor:pointer;transition:border-color .15s,color .15s}
.sp-view-btn:hover:not(.active){border-color:var(--border);color:var(--text)}
/* 선택 라벨은 --accent가 아니라 --add-active-text. --accent 라이트(#2563eb) on --add-active-bg(#eaf2ff)는 4.46:1로 아슬하게 미달. */
.sp-view-btn.active{background:var(--add-active-bg);border-color:var(--add-active-border);color:var(--add-active-text)}
.sp-view-ic{flex:0 0 auto;font-size:15px;line-height:1}.sp-view-ic .ui-icon{width:17px;height:17px}
/* 말줄임(text-overflow)을 쓰지 않는다 — 한국어·우즈벡어가 영어보다 길어지면 잘리는 게 아니라 줄바꿈으로 흡수한다. */
.sp-view-name{flex:1 1 auto;min-width:0;overflow-wrap:anywhere}

/* .sp-root의 gap 14px 리듬을 패널 안으로 옮긴다 — 패널이 새 flex 아이템이라 이게 없으면 본문이 통째로 붙는다. */
.sp-viewpanel{display:flex;flex-direction:column;gap:14px}
.sp-hx-workspace{display:grid;grid-template-columns:minmax(0,1fr);gap:14px;align-items:start;min-width:0}
.sp-hx-today,.sp-hx-queues{min-width:0}.sp-hx-queues{display:flex;flex-direction:column;gap:14px}
@media(min-width:1180px){.sp-hx-workspace{grid-template-columns:minmax(0,1.08fr) minmax(320px,.92fr)}.sp-hx-today>.sp-hx-day{margin-top:0;position:sticky;top:8px}}
.sp-hx-sum{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}
/* 2026-09-30에 「복습 후보」가 넷째 칸으로 들어왔는데 열이 3개라 넷째 칸만 다음 줄로 혼자 내려갔다(실측 top 672/672/672/753).
   2×2로 바꾸면 375에서 한 줄이 더 생겨 숫자가 접힘선 밖으로 밀리므로 한 줄 네 칸을 유지하고 좁은 폭은 안쪽 여백만 줄인다. */
@media(max-width:520px){.sp-hx-sum{gap:6px}.sp-hx-sum .sp-hx-sum-cell{padding:8px 8px}}
/* 셀을 세로 플렉스로 두고 라벨이 남는 높이를 먹는다 — 375에서 라벨 하나만 2줄이 되면 그 칸의 숫자만
   15px 내려앉아 세 값이 같은 선에 안 놓였다(실측 top 533/518/518). 값을 아래 정렬로 고정한다. */
.sp-hx-sum-cell{min-width:0;display:flex;flex-direction:column;padding:10px 12px;background:var(--surface2);border:1px solid var(--border);border-radius:10px}
.sp-hx-sum-l{flex:1 1 auto;font-size:11.5px;line-height:1.35;color:var(--text2);overflow-wrap:anywhere}
/* 빈 상태 안내 — .sp-empty(--text3)는 다크에서 3.77:1로 AA 미달이라 주 문장에 쓰지 않는다. */
.sp-hx-hint{font-size:12.5px;line-height:1.55;color:var(--text2);overflow-wrap:anywhere}
.sp-hx-sum-v{margin-top:4px;font-size:22px;font-weight:800;line-height:1.1;color:var(--text)}
.sp-hx-sum-foot{margin-top:10px;font-size:11.5px;color:var(--text2)}
/* 요약 3칸이 무슨 날짜로 세어졌는지 말하는 줄. --text3(다크 3.77:1)를 쓰지 않는 이유는 이 문장이
   장식이 아니라 **숫자의 전제**이기 때문이다 — 안 읽히면 숫자가 거짓말을 하게 둔다. */
.sp-hx-sum-basis{margin-top:8px;font-size:11.5px;line-height:1.45;color:var(--text2);overflow-wrap:anywhere}
.sp-hx-sec{background:var(--surface);border:1px solid var(--border);border-radius:14px;overflow:hidden}
.sp-hx-sec>summary{display:flex;align-items:center;flex-wrap:wrap;gap:8px;min-width:0;min-height:44px;padding:13px 16px;font-size:13px;font-weight:700;color:var(--text);cursor:pointer;list-style:none}
.sp-hx-sec>summary::-webkit-details-marker{display:none}
.sp-hx-caret{flex:0 0 auto;color:var(--text3);transition:transform 150ms}
.sp-hx-sec[open]>summary .sp-hx-caret{transform:rotate(90deg)}
.sp-hx-ic{flex:0 0 auto;font-size:14px;line-height:1}
.sp-hx-t{flex:1 1 auto;min-width:0;overflow-wrap:anywhere}
/* ⚠️ margin-left:auto를 배지 자체에서 떼어 섹션 헤더에만 준다 — 배지를 행의 칩으로 재사용하려면
   그 속성이 걸리면 안 된다. **두 줄은 반드시 짝으로 유지한다**(뗀 쪽만 바꾸면 섹션 헤더 배지가
   왼쪽으로 붙는 시각 회귀). */
/* 칩 한 벌 — 예전엔 .sp-view-badge와 .sp-hx-badge가 바이트 단위로 같은 선언 둘이었다. */
.sp-view-badge,.sp-hx-badge{flex:0 0 auto;font-size:11px;font-weight:700;color:var(--text2);background:var(--surface2);border:1px solid var(--border);border-radius:999px;padding:2px 9px;white-space:nowrap}
.sp-hx-sec>summary .sp-hx-badge{margin-left:auto}
.sp-hx-sec-body{padding:0 16px 14px;display:flex;flex-direction:column;gap:12px}
/* 기록 모달 본문은 세로 flex+스크롤이다. 마지막 details가 기본 flex-shrink:1로 2px까지 눌리면
   44px 숙달 버튼이 그려져도 부모가 hit-test를 가로챈다. 기록 폼의 직계 섹션만 내용 높이를 보존한다. */
#spHxRecBody>.sp-hx-sec{flex:0 0 auto}
/* 목록 행(v7.18) — 접힘 아코디언. 스캔 앵커 = 아이콘 + 이름 + 키 배지. 색은 color-neutral로 두고
   급함은 **정렬**이 전달한다(고확신 오답이 맨 위). 목록은 스캔용이지 경고용이 아니다. */
.sp-hx-list{display:flex;flex-direction:column}
.sp-hx-row{border-top:1px solid var(--border)}
.sp-hx-list>.sp-hx-row:first-child{border-top:0}
.sp-hx-row>summary{display:flex;align-items:flex-start;flex-wrap:wrap;gap:8px;min-width:0;min-height:44px;padding:11px 0;cursor:pointer;list-style:none}
.sp-hx-row>summary::-webkit-details-marker{display:none}
.sp-hx-row[open]>summary .sp-hx-caret{transform:rotate(90deg)}
.sp-hx-row>summary .sp-hx-caret,.sp-hx-row>summary .sp-hx-ic{margin-top:2px}
.sp-hx-row-main{flex:1 1 200px;min-width:0;display:flex;flex-direction:column}
/* 말줄임 금지 — 한국어·우즈벡어는 잘리는 게 아니라 줄바꿈으로 흡수한다. */
/* 3줄 클램프. 저장소의 '말줄임 금지'는 **라벨** 규칙이고 여기 제목은 문장이다 — 전문은 펼친 상세의
   재현 문장에 그대로 있고 행 자체가 아코디언이라 정보가 사라지지 않는다. 50자부터 148px로
   밀도가 무너지는 것이 실측됐다(15자 108 · 30자 129 · 50자 148 · 110자 187). */
.sp-hx-row-title{font-size:13px;font-weight:700;line-height:1.5;color:var(--text);overflow-wrap:anywhere;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.sp-hx-row-meta{margin-top:3px;font-size:11.5px;line-height:1.5;color:var(--text2);overflow-wrap:anywhere}
.sp-hx-tags{flex:0 1 auto;margin-left:auto;display:flex;flex-wrap:wrap;gap:6px;min-width:0}
/* 긴 question_id가 375에서 오버플로를 낸다 — 행 안에서만 nowrap을 푼다. */
.sp-hx-row .sp-hx-badge{white-space:normal;overflow-wrap:anywhere}
.sp-hx-row-body{padding:2px 0 12px;display:flex;flex-direction:column;gap:8px}
.sp-hx-fr{display:flex;flex-wrap:wrap;gap:4px 10px;min-width:0}
.sp-hx-fl{flex:0 0 auto;font-size:11.5px;color:var(--text2)}
.sp-hx-fv{flex:1 1 200px;min-width:0;font-size:12.5px;line-height:1.55;color:var(--text);overflow-wrap:anywhere}
.sp-hx-rbtn{align-self:flex-start;min-height:44px;padding:10px 14px;background:var(--surface2);border:1px solid var(--border);border-radius:9px;color:var(--text2);font-size:12.5px;font-family:inherit;cursor:pointer}
.sp-hx-rbtn:hover{border-color:var(--accent);color:var(--text)}
.sp-hx-trunc{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:10px 0 0;border-top:1px solid var(--border);font-size:11.5px;color:var(--text2);overflow-wrap:anywhere}
.sp-hx-loop{font-size:11.5px;line-height:1.55;color:var(--text2);overflow-wrap:anywhere}
/* 가져오기 모달 */
.sp-hx-imp-ta{width:100%;box-sizing:border-box;min-height:150px;background:var(--add-input-bg);border:1px solid var(--border);border-radius:8px;padding:10px;color:var(--text);font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;line-height:1.5;resize:vertical;outline:none}
.sp-hx-imp-ta:focus{border-color:var(--accent)}
.sp-hx-imp-sum{font-size:12.5px;line-height:1.55;color:var(--text);overflow-wrap:anywhere}
/* 스크롤은 .sp-modal-body가 맡는다 — 여기 max-height를 두면 스크롤이 3겹이 된다. */
.sp-hx-imp-list{display:flex;flex-direction:column}
.sp-hx-imp-item{display:flex;flex-wrap:wrap;align-items:flex-start;gap:8px;min-width:0;padding:9px 0;border-top:1px solid var(--border);font-size:12.5px;line-height:1.5;color:var(--text);overflow-wrap:anywhere}
.sp-hx-imp-list>.sp-hx-imp-item:first-child{border-top:0}
/* 제목은 낭독을 위해 프로그램적으로 포커스를 받지만 **누를 수 있는 것이 아니다** — 링이 뜨면
   입력칸으로 오해된다. 낭독은 그대로이므로 스크린리더 쪽 손해가 없다. */
.sp-modal-head [tabindex="-1"]:focus{outline:none}
.sp-hx-imp-mid{flex:1 1 200px;min-width:0}
.sp-hx-imp-why{flex:1 1 100%;font-size:11.5px;line-height:1.5;color:var(--text2);overflow-wrap:anywhere}
/* 8875줄과 **짝**이다. 항목 왼쪽 배지에는 레코드 id가 들어가는데(`att:Q-USMLE-STEP2CK-2411-CARDIO-0042`
   36자) 공용 배지가 nowrap이라 375에서 본문 폭 307px를 넘긴다. 행에서 이미 같은 이유로 푼 것을
   가져오기 항목에서도 푼다 — 지워짐 목록이 같은 헬퍼(hxImpItem)를 쓰므로 한쪽만 고치면 다시 샌다. */
.sp-hx-imp-item .sp-hx-badge{white-space:normal;overflow-wrap:anywhere}
.sp-hx-import-modal{width:min(760px,calc(100vw - 24px))}
.sp-hx-imp-file{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:10px}
.sp-hx-imp-file .sp-hint{min-width:0;overflow-wrap:anywhere}
.sp-hx-v2-warning{flex:1 1 100%;padding:8px 10px;border:1px solid var(--warning);border-radius:8px;color:var(--text);background:var(--surface2);font-size:11.5px;overflow-wrap:anywhere}
.sp-hx-v2-decision{flex:1 1 100%;min-width:0;margin:2px 0 0;padding:8px 10px;border:1px solid var(--border);border-radius:8px}
.sp-hx-v2-decision legend{padding:0 4px;color:var(--text2);font-size:11.5px}
.sp-hx-v2-options{display:flex;flex-wrap:wrap;gap:8px 14px;align-items:center}
.sp-hx-v2-options label{display:inline-flex;align-items:center;gap:5px;min-height:44px;color:var(--text);font-size:12px;cursor:pointer}
.sp-hx-v2-options input{accent-color:var(--accent)}
.sp-hx-v2-options .sp-btn{min-height:44px}
.sp-hx-v2-facts{flex:1 1 100%;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:4px 12px;padding:6px 0 2px}
.sp-hx-v2-fact{min-width:0;color:var(--text2);font-size:11.5px;overflow-wrap:anywhere}
.sp-hx-v2-fact b{color:var(--text);font-weight:650}
.sp-hx-v2-summary{grid-column:1/-1;min-width:0;color:var(--text2);font-size:11.5px}
.sp-hx-v2-summary summary{display:flex;align-items:center;min-height:44px;color:var(--text);font-weight:650;cursor:pointer}
.sp-hx-v2-summary-body{padding:0 0 6px;line-height:1.55;overflow-wrap:anywhere}
.sp-hx-v2-diff-wrap{flex:1 1 100%;min-width:0;overflow-x:auto}
.sp-hx-v2-diff{width:100%;min-width:560px;table-layout:fixed;border-collapse:collapse;font-size:11.5px}
.sp-hx-v2-diff caption{text-align:left;padding:7px 0 4px;color:var(--text);font-weight:700}
.sp-hx-v2-diff th,.sp-hx-v2-diff td{padding:6px;border-top:1px solid var(--border);text-align:left;vertical-align:top;overflow-wrap:anywhere;word-break:break-word}
.sp-hx-v2-diff th:first-child{width:22%}
@media(max-width:520px){.sp-hx-v2-options{align-items:stretch}.sp-hx-v2-options label,.sp-hx-v2-options .sp-btn{flex:1 1 100%}}
@media(max-width:520px){.sp-hx-v2-facts{grid-template-columns:minmax(0,1fr)}.sp-hx-v2-diff th,.sp-hx-v2-diff td{padding:5px 3px}.sp-hx-v2-diff th:first-child{width:25%}}
/* 계획 머리띠(v7.18) — 요약 3칸 **위**, 같은 .sp-panel 안. 별도 패널을 만들면 375에서 gap+padding으로
   약 48px이 더 들어가 이 화면의 존재 이유인 세 숫자가 접힘선 아래로 밀린다. */
/* ⚠️ 위아래 여백이 6px다(v7.19). 10/10일 때 긴 시험명에서 요약 숫자 하단이 675로 **여전히 8px 밖**이었다
   — v7.18의 2줄 클램프는 687 → 675로 12px만 회수했을 뿐 문제를 없애지 못했다. 여기서 8px을 더 회수한다.
   375x667 실측(v7.19): 짧은 이름 하단 649 · 90자 667 · 183자 667. **2줄 클램프가 상한이라 이름이 더
   길어져도 머리띠 높이는 95px에서 안 자란다**(90자와 183자가 같은 값인 것으로 확인).
   ⚠️ 다만 긴 이름 쪽은 **여유가 0px다** — 하단이 정확히 뷰포트 경계에 닿는다. 실기기 글꼴 지표가
   조금만 달라도 다시 밖으로 나간다. 이 줄 아래의 `.sp-hx-sum-foot`(처리율 문장)은 애초에 접힘선 밖이다.
   여기를 더 건드릴 사람은 세 점(짧은 이름·90자·183자)을 다시 재고, 통과 기준을 `<= 667`이 아니라
   여유를 남기는 값으로 잡아라. */
.sp-hx-plan{display:flex;flex-wrap:wrap;align-items:center;gap:8px;min-width:0;padding-bottom:6px;margin-bottom:6px;border-bottom:1px solid var(--border)}
/* ⚠️ 2줄 클램프. 103자 시험명에서 요약 3칸의 **숫자가 통째로 접힘선 밖**이었다(실측 하단 687 · 뷰포트 667).
   이 화면이 존재하는 이유가 그 세 숫자이므로 이름이 그것을 밀어내면 안 된다.
   ⚠️ **전문은 `title`로 남기지 않는다** — 이 저장소는 "title 속성은 터치에서 안 뜬다"를 이미 판단으로
   적어 뒀다(47180줄 근처). 잘린 전문은 `✏️ 편집` 버튼의 `aria-label`과 편집 폼의 `시험` 칸에 있다.
   (v7.18의 이 주석은 "전문은 title로 남긴다"고 단언했는데 그런 속성은 렌더된 적이 없다 — 정정한다.) */
.sp-hx-plan-name{flex:1 1 160px;min-width:0;font-size:13px;font-weight:700;color:var(--text);overflow-wrap:anywhere;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
/* 펼친 상세의 액션 줄 — 접힘 summary는 한 글자도 바꾸지 않는다(측정된 밀도 유지). */
.sp-hx-row-acts{display:flex;flex-wrap:wrap;gap:8px}
/* 기록 폼(v7.18) — 세그먼트·칩. 44px는 터치 하한이라 라벨이 길어지면 폭을 줄이지 않고 줄바꿈으로 흡수한다. */
.sp-hx-seg,.sp-hx-chips{display:flex;flex-wrap:wrap;gap:6px}
.sp-hx-chip{min-height:44px;display:flex;align-items:center;padding:6px 12px;background:var(--surface2);border:1px solid var(--border);border-radius:999px;color:var(--text2);font-size:12px;font-weight:700;font-family:inherit;line-height:1.35;cursor:pointer;overflow-wrap:anywhere}
.sp-hx-chip:hover{border-color:var(--accent);color:var(--text)}
/* 선택색은 .sp-view-btn.active 레시피 그대로. --accent를 쓰면 라이트에서 4.46:1로 AA 미달이며
   그 결함은 8826줄에서 이미 한 번 고쳤다 — 여기서 되살리지 않는다. */
.sp-hx-chip[aria-pressed="true"],.sp-hx-chip[aria-checked="true"]{background:var(--add-active-bg);border-color:var(--add-active-border);color:var(--add-active-text)}
/* 실시간 안내 줄 — **본문이 아니라 모달 열의 형제**다. 본문에 넣으면 "왜 아직 처리 대기인지"가
   저장 버튼과 400px 떨어져 375x667에서 스크롤 밖으로 나간다. */
.sp-hx-recfoot{flex:0 0 auto;padding:10px 18px;border-top:1px solid var(--border);font-size:11.5px;line-height:1.55;color:var(--text2);overflow-wrap:anywhere}
/* ⚠️ 세 메시지를 **각자 줄로** 쪼갠다. 인라인 span으로 두었더니 마침표 없는 힌트(`D-35`·`상태: 해결`)
   뒤에 다음 메시지가 그대로 붙어 `D-35식별 값을 바꾸면…` 이 됐다. 이 화면에서 가장 중요한 문장
   (삭제 확인)이 안내문 꼬리에 매달린다(2026-07-28 독립 리뷰 실측). 빈 span은 자리를 안 먹는다. */
.sp-hx-recfoot>span{display:block}
.sp-hx-recfoot>span:empty{display:none}
.sp-hx-recfoot>span+span:not(:empty){margin-top:6px}
/* 파괴적 동작은 왼쪽으로 떼어 놓는다 — 취소·저장과 손가락이 겹치지 않게.
   ⚠️ `:not(:last-child)`가 필수다. 인라인 확인 상태에서는 danger가 **마지막** 자식이라
   auto 여백이 자기 오른쪽으로 붙어 버튼 줄이 통째로 왼쪽으로 튄다(실측: 375에서 전진 버튼이
   184px · 1280에서 361px 점프 — 앱의 다른 모달은 전부 오른쪽 정렬이다). */
.sp-modal-foot .sp-btn.danger:not(:last-child){margin-right:auto}
.sp-btn[disabled]{opacity:.55;cursor:not-allowed}
.sp-btn[disabled]:hover{border-color:var(--border);color:var(--text2)}
.sp-modebar{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;position:sticky;bottom:0;z-index:3;background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:8px}
.sp-mode-btn{min-width:0;min-height:44px;background:transparent;border:1px solid transparent;border-radius:9px;padding:8px;color:var(--text2);font-size:12.5px;line-height:1.35;overflow-wrap:anywhere;cursor:pointer;font-family:inherit;font-weight:600}
.sp-mode-btn.active{background:var(--add-active-bg);border-color:var(--add-active-border);color:var(--add-active-text)}
.sp-modal-overlay{position:fixed;inset:0;background:var(--overlay-bg);z-index:2200;display:flex;align-items:center;justify-content:center;padding:16px}
/* ⚠️ 세로 스크롤은 **본문만** 한다. 예전엔 .sp-modal 자체가 overflow-y:auto라 foot이 스크롤 콘텐츠
   맨 아래에 얹혀 같이 밀려났다 — 목록이 길면 375x667에서 적용 버튼이 모달 밖 76px, 1280x720에서
   29px로 나갔다(2026-07-28 독립 리뷰 실측). 사용자는 적용할 방법이 없고 화면은 거기서 끝난 것처럼
   읽혔다(스크롤 힌트도 없었다). flex 열 + 본문만 스크롤로 foot을 항상 붙여 둔다. */
.sp-modal{display:flex;flex-direction:column;min-width:0;max-width:100%;background:var(--surface);border:1px solid var(--border);border-radius:16px;width:min(520px,100%);max-height:90vh;box-shadow:var(--shadow-2)}
.sp-modal-head{display:flex;flex:0 0 auto;align-items:center;justify-content:space-between;padding:16px 18px;border-bottom:1px solid var(--border)}
.sp-modal-head b{font-size:15px;color:var(--text)}
.sp-modal-x{width:44px;height:44px;border-radius:9px;border:1px solid var(--border);background:var(--surface2);color:var(--text2);font-size:18px;cursor:pointer}
.sp-modal-body{padding:18px;display:flex;flex-direction:column;gap:12px;overflow-y:auto;min-width:0;min-height:0}
.sp-f2{display:grid;grid-template-columns:1fr 1fr;gap:12px 10px}
/* ⚠️ `--text3`가 아니라 `--text2`다(v7.19 후속). 폼 라벨은 **무엇을 적는 칸인지 말하는 유일한 글자**라
   장식이 아니다. `--text3`(#6e7681)는 모달 배경 위에서 다크 3.77:1로 AA 미달이었고, 그 값은
   v7.18 독립 리뷰가 선재 조건으로 지목한 뒤 두 판을 그대로 남아 있었다. */
/* ⚠️ 라벨과 힌트를 **시각적으로 갈라 둔다.** 2026-08-09 실측: 둘 다 `rgb(139,148,158)`에 12px/11px이라
   색이 같고 크기가 1px 차이였다 — 대비는 5.62로 WCAG AA를 통과하는데도 「계획 열쇠」(라벨)와
   「한번 만들면 바꾸지 않습니다」(힌트)가 한 덩어리 회색으로 읽혀, 사용자가 「라벨 없는 빈 칸」으로
   보고했다. 대비를 올리는 문제가 아니라 **두 층위를 구분하는** 문제다. */
.sp-modal label,.sp-fl{display:flex;flex-direction:column;gap:5px;font-size:13px;font-weight:600;color:var(--text)}
.sp-modal input,.sp-modal select,.sp-modal textarea{background:var(--add-input-bg);border:1px solid var(--border);border-radius:8px;padding:8px 11px;color:var(--text);font-size:13px;font-family:inherit;outline:none}
.sp-modal input:focus,.sp-modal select:focus,.sp-modal textarea:focus{border-color:var(--accent)}
.sp-modal textarea{resize:vertical}
.sp-hint{font-size:11px;font-weight:400;color:var(--text2);line-height:1.4}
/* 입력칸 바로 아래 도움말은 라벨→입력칸(5px)보다 한 칸 띄운다(8px). 포커스 테두리가 도움말 글자에 닿지 않게 한다. */
.sp-modal label>:is(input:not([type=checkbox]):not([type=radio]),select,textarea)+:is(.sp-hint,small){margin-top:3px}
/* 라벨 밖에 붙는 보조 문구의 자리. `<label>`이 감싼 것은 전부 입력칸의 접근가능 이름에 합쳐지므로,
   실시간으로 바뀌는 판정 문구는 라벨 밖에 두고 aria-describedby로 잇는다(2026-08-09 리뷰 · a11y).
   `.sp-fl`과 같은 세로 흐름·같은 간격을 써서 배치가 달라지지 않게 한다. */
.sp-fw{display:flex;flex-direction:column;gap:5px;min-width:0}
/* --ok는 이 저장소에 정의가 없어 폴백 raw hex가 고정됐고, 라이트 흰 배경에서 2.54:1이었다.
   같은 규칙 블록의 거부는 --confirm-danger를 타서 6.47:1인데 통과만 안 보였다 — 「거부는 보이는데
   통과는 안 보이는」 상태라 이 기능의 목적 자체가 무효였다. var(--X, 폴백)은 정의 없는 var가 아니라
   폴백 있는 var라 check-css-tokens가 원리적으로 못 본다(2026-08-09 독립 리뷰 두 렌즈가 함께 잡음). */
#spHxPlanKeyLive[data-state="good"]{color:var(--accent2);font-weight:600}
#spHxPlanKeyLive[data-state="bad"]{color:var(--confirm-danger,#f85149);font-weight:600}
.sp-modal-err{color:var(--red);font-size:12px;min-height:16px}
.sp-modal-foot{display:flex;flex:0 0 auto;justify-content:flex-end;gap:9px;padding:14px 18px;border-top:1px solid var(--border)}
.sp-modal .sp-btn{min-height:44px}
@media (max-width:1179px){
  .sp-grid2{grid-template-columns:1fr}
  .sp-time-overview{display:none}
  .sp-table-wrap{display:none}
  .sp-cards{display:flex}
}
@media (max-width:600px){
  .sp-clock{width:268px;height:268px}
  .sp-schedule-head{align-items:stretch}
  .sp-schedule-head .sp-toolbar{justify-content:stretch}
  .sp-schedule-head .sp-toolbar .sp-btn{flex:1 1 calc(50% - 4px)}
  .sp-datenav .sp-date{order:-1;flex:1 1 100%}
  .sp-datenav .sp-date-done{margin-left:auto}
  .sp-card{grid-template-columns:62px 14px minmax(0,1fr);gap:7px}
  .sp-cards::before{left:75px}
  .sp-card-h{flex-wrap:wrap}
  .sp-card-chk{min-height:44px}
  .sp-mini{min-height:44px;padding:8px 12px}
  .sp-toolbar .sp-btn{flex:1;min-width:calc(50% - 4px)}
  .sp-f2{grid-template-columns:1fr}
}
