/* ==========================================================================
   CADWISE — 앱스타일 5테마 시스템
   기본 골격(카드/그리드/다이얼로그)은 공유하고, body[data-theme]로
   색상·반경·그림자·장식 토큰을 완전히 교체하는 구조.
   테마: blueprint(청사진) · mono(모노크롬) · neon(네온글래스)
         pastel(파스텔) · warm(웜프로페셔널)
   ========================================================================== */

:root{
  /* ---- 기본값 = blueprint 테마 (JS 미로딩 시 폴백) ---- */
  --bg:#0b0f14;
  --bg-panel:#10161d;
  --bg-elevated:#151d27;
  --bg-inset:#0c1218;
  --bg-hover:#1a2330;
  --line:#232d3a;
  --line-soft:#1a222c;
  --ink:#e7edf4;
  --ink-dim:#8695a8;
  /* [실산 근거 — 상색 대비 재산으로 수정]
     이전 #576375 는 --bg-elevated(#151d27) 위에서 2.79:1 로 WCAG AA(4.5:1) 반이하여
     '결과물/확인/주의' 섹션 라벨, 상태표시줄, placeholder, 근거 목록이 전부
     읽혀지지 않았다. 이 토큰은 46곳에서 사용되며 그 중 39곳이 color: 로서
     장식이 아니라 '실제 텍스트'다 → AA 4.5:1 적용 대상.
     가장 어려운 배경 기준으로 맞춘다. 다효토마에선 --bg-elevated,
     라이트 테마에선 --bg-inset(상태표시줄)이 가장 어렵다 — 테마마다
     어떤 서페이스가 어려운지가 반대라서 세 배경 전부를 검사해야 한다. */
  --ink-faint:#7c8593;
  --accent:#ffb020;
  /* [실측 근거 — 자기합성 배경 전용 전경색]
     .command-badge 와 #ai-form button 은 글자가 var(--accent) 이고
     배경이 color-mix(in srgb, var(--accent) 10~14%, transparent) 다.
     즉 글자와 배경이 '같은 색에서 파생'되므로 --accent 를 아무리 조정해도
     이 경로의 대비는 함께 움직여 개선되지 않는다. 실측으로 확인한 반례:
       합성비율 p 를 0.10 → 0(완전 투명)까지 낮춰도
       pastel 4.22:1 / warm 4.09:1 로 AA(4.5:1) 미달 → 합성비율 조정은 해가 없다.
     그래서 이 경로에만 쓰는 별도 전경색을 둔다. --accent 를 직접 낮추면
     이미 맞춰둔 두 제약(글자 on 카드 / 버튼글자 on accent배경)이 깨진다.
     세 부모 배경(--bg / --bg-panel / --bg-inset) × 두 합성비율(10%,14%)
     여섯 경우의 최악값이 4.5:1 이상이 되도록, 색조·채도를 유지하고
     명도만 낮춘 해를 탐색했다. 다크 테마는 이미 충족하므로 --accent 와 동일. */
  --accent-text:var(--accent);
  --accent-dim:#a87a2c;
  --accent-cyan:#4fd1ff;
  --accent-green:#33d189;
  --accent-red:#ff5d6c;
  /* [실측 근거] --accent-blue 는 글자 5곳 / 배경 0곳 — 전경색 전용 토큰이다.
     .confidence-official(16%) · .history-card .command-badge(12%) · .ai-mode(12%) ·
     .hcard-n(12%) 은 배경이 이 색 자신의 반투명 합성이라
     --bg-elevated 위에서 4.16:1 로 AA 미달이었다. 배경 사용처가 없으므로
     별도 토큰 없이 값 자체를 조정해도 부작용이 없다. */
  --accent-blue:#5895ff;
  --accent-amber:#ffb020;
  /* [D5 실측 근거 — 한글이 낯선 글꼴로 그려지고 있었다]
     JetBrains Mono 에는 한글 글리프가 없다. 그래서 --mono 가 걸린 요소의 한글은
     스택 끝의 generic monospace 로 폴백됐다. 실측(같은 문자열 20px):
       "기본 그리기 명령행 시퀀스"  mono 스택 256px  vs  sans 스택 215px  (19% 넓음)
     화면 전체로는 한글 리프 876개 중 299개(34%)가 이 상태였다
     (footer 158건 · sim-mark 80건 · course-progress .label 12건 등).
     [해결책도 재서 골랐다] 스택 중간에 한글 폰트를 끼우면 글자별로 갈린다:
       · 한글만(공백 제외) — mixed 200px = sans 200px  → 차이 0.0px, 완전 정상화
       · 라틴 "LINE PLINE 0/85 100%" — mono 240px = mixed 240px → 모노 성격 유지
     남는 21px 차이는 전부 '공백 폭'(mono 12px vs sans 5px)이며, 숫자·명령어를
     칸 맞춰 세우는 데 필요하므로 그대로 둔다. 즉 --mono 를 없애는 게 아니라
     '한글에만' 비례 글꼴이 닿게 한다. */
  --mono:'JetBrains Mono','IBM Plex Sans KR',ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  --sans:'IBM Plex Sans KR',-apple-system,sans-serif;
  --radius:16px;
  --radius-sm:11px;
  --shadow-lg:0 30px 70px rgba(0,0,0,.5);
  --shadow-md:0 14px 34px rgba(0,0,0,.36);
  --shadow-sm:0 4px 14px rgba(0,0,0,.28);
  --card-blur:none;
  --bg-decor:none;

  /* ==== 디자인 토큰: 유동 타이포 스케일 ====================================
     WCAG 1.4.4(200% 확대) 준수 조건에 맞춰 rem 기준값 + 소량의 vw 로 구성한다.
     각 단계의 max/min 비율은 모두 2.5배 이하이므로 사용자가 브라우저
     기본 글꼴을 키워도 텍스트가 vw 에 고정되어 확대를 무시하는 일이 없다.
     기준 산식: clamp(최소, rem기준 + n*vw, 최대)  (루트 16px 가정) */
  --fs-3xs:clamp(0.625rem,0.600rem + 0.12vw,0.6875rem);   /* 10 → 11 */
  --fs-2xs:clamp(0.6875rem,0.660rem + 0.14vw,0.750rem);   /* 11 → 12 */
  --fs-xs:clamp(0.750rem,0.720rem + 0.15vw,0.8125rem);    /* 12 → 13 */
  --fs-sm:clamp(0.8125rem,0.780rem + 0.16vw,0.875rem);    /* 13 → 14 */
  --fs-md:clamp(0.875rem,0.840rem + 0.18vw,0.9375rem);    /* 14 → 15 */
  --fs-base:clamp(0.9375rem,0.900rem + 0.19vw,1rem);      /* 15 → 16 */
  --fs-lg:clamp(1rem,0.950rem + 0.25vw,1.125rem);         /* 16 → 18 */
  --fs-xl:clamp(1.125rem,1.050rem + 0.38vw,1.3125rem);    /* 18 → 21 */
  --fs-2xl:clamp(1.375rem,1.250rem + 0.63vw,1.750rem);    /* 22 → 28 */
  --fs-3xl:clamp(1.625rem,1.350rem + 1.38vw,2.500rem);    /* 26 → 40 */

  /* ==== 디자인 토큰: 8pt 기반 여백 스케일 ================================ */
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:20px;
  --sp-6:24px; --sp-7:28px; --sp-8:32px; --sp-10:40px; --sp-12:48px; --sp-14:56px;
  /* 화면 폭에 따라 늘어나는 좌우 여백(거터). 360px에서 16px, 1440px에서 64px */
  --gutter:clamp(1rem,0.318rem + 3.03vw,4rem);
  --gutter-sm:clamp(0.75rem,0.466rem + 1.26vw,2rem);

  /* ==== 디자인 토큰: 브레이크포인트 =======================================
     @media 는 var() 를 못 쓰므로 이 값들은 '문서화용 단일 출처'다.
     CSS 안의 모든 미디어쿼리는 반드시 이 5개 값만 사용한다.
     xs=360(소형 폰) sm=480(대형 폰) md=640(폰 가로/소형 태블릿)
     lg=960(태블릿) xl=1280(데스크톱) */
  --bp-xs:360px; --bp-sm:480px; --bp-md:640px; --bp-lg:960px; --bp-xl:1280px;

  /* ==== 디자인 토큰: 터치 타깃 (WCAG 2.2 SC 2.5.5 강화 = 44px) ========== */
  --touch:44px;
  --touch-min:24px; /* SC 2.5.8 최소 기준 */

  /* ==== 디자인 토큰: 셸 높이 / 안전영역 ================================= */
  --h-titlebar:56px;
  --safe-t:env(safe-area-inset-top,0px);
  --safe-r:env(safe-area-inset-right,0px);
  --safe-b:env(safe-area-inset-bottom,0px);
  --safe-l:env(safe-area-inset-left,0px);
}
@media (min-width:640px){ :root{ --h-titlebar:60px } }

*{box-sizing:border-box}
html{
  scroll-behavior:smooth;
  /* iOS 가로↔세로 전환 시 브라우저가 임의로 글자를 키우는 것을 막는다 */
  -webkit-text-size-adjust:100%;
  text-size-adjust:100%;
}
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--sans);font-size:var(--fs-base);line-height:1.55;-webkit-font-smoothing:antialiased;transition:background .25s,color .25s;overflow-x:hidden;overscroll-behavior-y:none}
img,svg,video,canvas{max-width:100%;height:auto}
button,input,select,textarea{font:inherit;color:inherit}
/* 모바일에서 폰트 16px 미만 입력창에 포커스하면 iOS Safari 가 페이지를 확대한다.
   640px 미만에서만 16px 을 강제해 자동 확대를 차단한다(데스크톱 디자인은 유지).

   [브라우저 실측으로 드러난 결함 — 이 규칙은 원래 전혀 작동하지 않았다]
   과제 2 에서 위 요소 선택자만 써 두고 '방어 완료'로 기록했으나,
   390px 실측 결과 6개 입력요소가 모두 13.104px 였다:
     catalog-search, detail-filter, pgp-search, pgp-mode, cmdline-input, ai-input
   원인은 명시도다. `input,select,textarea` (요소, 0-0-1) 는
     .catalog-search input (0-1-1)  /  #cmdline-input · #ai-input (1-0-0)
   의 font-size:var(--fs-sm) 에 전부 밀린다. --fs-sm 은
   clamp(0.8125rem,...) 이므로 390px 에서 13.104px 로 계산된다.
   → CSS 텍스트만 읽는 정적 검사로는 '규칙이 있다'까지만 알 수 있고
     '규칙이 이긴다'는 알 수 없었다. 실제 계산값 관찰이 필요했던 사례다.

   [해결 방식 선택 근거] !important 대신 명시도를 맞춘다. 후자는 개별 규칙이
   왜 지는지 남기지 못하고, 이후 다른 규칙과 다시 충돌할 때 추적이 어렵다.
   여기서는 실제 위반자를 ID/클래스 명시도로 직접 겨냥한다.

   [2차 실측 — 명시도만으로는 부족했다]
   위 방식으로 검색/필터 4개(.catalog-search input 0-1-1 을 ID 1-0-0 으로 이김)는
   해결됐지만 #cmdline-input, #ai-input 은 여전히 13.104px 이었다.
   이유는 명시도가 '동일'(둘 다 1-0-0)해서 CSS 소스 순서가 승자를 정하기
   때문이다. 원 선언이 L390(#cmdline-input), L673(#ai-input) 로 이 블록보다
   뒤에 있어 나중 선언이 이겼다. 미디어 쿼리는 명시도를 올려주지 않는다.
   → 같은 ID 를 겨냥하는 규칙은 원 선언보다 '뒤'에 두어야 한다.
     파일 맨 끝의 모바일 입력 블록으로 옮겼다(아래 '입력 확대 방어 — 최종' 참조). */
@media (max-width:639.98px){
  input,select,textarea{font-size:16px}
  /* 위 요소 선택자를 이기는 '실측된' 위반자.
     추측으로 선택자를 늘리지 않는다 — 390px 에서 13.104px 로 계산되는 것을
     브라우저에서 직접 확인한 대상만 적는다.
     (같은 명시도 충돌이 있는 #cmdline-input/#ai-input 은 파일 끝에서 처리) */
  .catalog-search input,
  #catalog-search,
  #detail-filter,
  #pgp-search,
  #pgp-mode{font-size:16px}
}
button{cursor:pointer}
a{color:var(--accent-cyan)}
::selection{background:rgba(255,176,32,.28)}
.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)}
.skip-link{position:fixed;top:-50px;left:20px;z-index:200;background:var(--accent);padding:10px 16px;color:#1a1300;border-radius:6px;font-weight:700}
.skip-link:focus{top:10px}
code,kbd{font-family:var(--mono)}

/* ---------------------------------------------------------------- 배경 장식(테마별) */
body::before{content:'';position:fixed;inset:0;z-index:0;pointer-events:none;background:var(--bg-decor)}
#app-shell{position:relative;z-index:1}

/* ---------------------------------------------------------------- 앱 셸 */
/* 100vh 는 모바일에서 주소창 높이를 포함해 실제보다 커진다(스크롤 튐 원인).
   dvh 를 쓰되 미지원 브라우저를 위해 vh 를 먼저 선언한다. */
#app-shell{display:flex;flex-direction:column;min-height:100vh;min-height:100dvh}

/* ---------------------------------------------------------------- 타이틀바 */
.app-titlebar{min-height:calc(var(--h-titlebar) + var(--safe-t));padding:var(--safe-t) calc(var(--gutter-sm) + var(--safe-r)) 0 calc(var(--gutter-sm) + var(--safe-l));background:var(--bg-panel);color:var(--ink);display:flex;align-items:center;gap:var(--sp-3);position:sticky;top:0;z-index:60;border-bottom:1px solid var(--line);box-shadow:var(--shadow-sm)}
.app-id{display:flex;align-items:center;gap:10px;white-space:nowrap}
.app-logo{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:10px;color:var(--accent);background:var(--bg-elevated);box-shadow:var(--shadow-sm)}
.app-id strong{font-family:var(--sans);font-size:var(--fs-md);letter-spacing:.01em;font-weight:700}
.app-sub{font-family:var(--mono);font-size:var(--fs-3xs);letter-spacing:.05em;color:var(--ink-faint);padding-left:4px;border-left:1px solid var(--line);margin-left:2px}
.titlebar-actions{margin-left:auto;display:flex;align-items:center;gap:10px}
/* WCAG 2.2 SC 2.5.5(강화) = 44x44. flex-shrink:0 이 없으면 좁은 폭에서
   축소되어 기준 미만이 되므로 함께 고정한다. */
.icon-button{width:var(--touch);height:var(--touch);flex-shrink:0;border:1px solid var(--line);background:var(--bg-elevated);color:var(--ink-dim);border-radius:12px;display:inline-flex;align-items:center;justify-content:center;font-size:var(--fs-md);transition:.15s}
.icon-button:hover{color:var(--ink);border-color:var(--accent-dim);background:var(--bg-hover)}
.icon-button svg{display:block}
#theme-toggle{gap:0;position:relative}
.theme-swatch{width:16px;height:16px;border-radius:5px;background:conic-gradient(from 180deg,var(--accent),var(--accent-cyan),var(--accent-green),var(--accent-red),var(--accent))}
.ai-button{display:inline-flex;align-items:center;gap:var(--sp-2);flex-shrink:0;border:1px solid var(--accent-dim);background:var(--accent);color:var(--bg);border-radius:999px;padding:0 var(--sp-4);height:var(--touch);font-weight:700;font-size:var(--fs-xs);letter-spacing:.02em;box-shadow:var(--shadow-sm)}
.ai-button:hover{filter:brightness(1.08)}
.spark{display:inline-block}

/* ---------------------------------------------------------------- 세그먼트 탭 내비 (앱스타일 필 탭바) */
/* 탭바는 좁은 화면에서 가로 스크롤된다. scroll-snap 으로 탭이 반쯤 잘린
   상태에서 멈추지 않게 하고, 스크롤바는 숨기되 스크롤 자체는 유지한다. */
.ribbon{padding:var(--sp-2) calc(var(--gutter-sm) + var(--safe-r)) var(--sp-2) calc(var(--gutter-sm) + var(--safe-l));background:var(--bg);border-bottom:1px solid var(--line);display:flex;align-items:center;gap:var(--sp-3);position:sticky;top:calc(var(--h-titlebar) + var(--safe-t));z-index:55;overflow-x:auto;overflow-y:hidden;scroll-snap-type:x proximity;-webkit-overflow-scrolling:touch;scrollbar-width:none}
.ribbon::-webkit-scrollbar{height:0}
.ribbon-tabs>.ribbon-tab{scroll-snap-align:start}
.ribbon-tabs{display:flex;gap:4px;background:var(--bg-panel);border:1px solid var(--line);border-radius:999px;padding:4px}
.ribbon-tab{display:inline-flex;align-items:center;justify-content:center;gap:7px;min-height:38px;padding:var(--sp-2) var(--sp-4);border:0;border-radius:999px;background:none;color:var(--ink-dim);font-size:var(--fs-sm);font-weight:600;white-space:nowrap;transition:.15s}
.ribbon-tab i{font-style:normal;opacity:.85;display:inline-flex}
.ribbon-tab i svg{display:block}
.ribbon-tab:hover{color:var(--ink)}
.ribbon-tab.active{color:var(--bg);background:var(--accent);box-shadow:var(--shadow-sm)}
.ribbon-stats{margin-left:auto;display:flex;gap:var(--sp-3);font-family:var(--mono);font-size:var(--fs-2xs);color:var(--ink-faint);white-space:nowrap}
.ribbon-stats b{color:var(--accent-cyan);font-weight:700;margin-right:4px}

/* ---------------------------------------------------------------- 워크스페이스 레이아웃 */
.workspace{flex:1;padding:var(--sp-6) calc(var(--gutter) + var(--safe-r)) var(--sp-12) calc(var(--gutter) + var(--safe-l));max-width:1360px;margin:0 auto;width:100%;min-width:0}
.view{display:none}
.view.active{display:block}
.eyebrow{display:inline-flex;align-items:center;gap:var(--sp-2);font-family:var(--mono);font-size:var(--fs-2xs);letter-spacing:.12em;text-transform:uppercase;color:var(--accent-cyan);margin:0 0 var(--sp-2)}
.eyebrow span{width:7px;height:7px;background:var(--accent-cyan);display:inline-block;border-radius:2px}

/* ---------------------------------------------------------------- 홈: 뷰포트 패널(=히어로 카드) */
.viewport-grid{margin-bottom:var(--sp-10)}
.viewport-panel{background:var(--bg-panel);border:1px solid var(--line);border-radius:calc(var(--radius) + 6px);overflow:hidden;box-shadow:var(--shadow-lg);backdrop-filter:var(--card-blur)}
.panel-titlebar{display:flex;align-items:center;flex-wrap:wrap;gap:var(--sp-2);padding:var(--sp-2) var(--gutter-sm);background:var(--bg-inset);border-bottom:1px solid var(--line)}
.ucs-icon{display:inline-flex;align-items:center;justify-content:center;opacity:.55}
.panel-titlebar small{margin-left:2px;font-family:var(--mono);font-size:var(--fs-2xs);color:var(--ink-faint);letter-spacing:.03em}
.layer-pill{margin-left:auto;display:inline-flex;align-items:center;gap:6px;font-family:var(--mono);font-size:var(--fs-2xs);color:var(--ink-dim);border:1px solid var(--line);border-radius:999px;padding:2px var(--sp-2);flex-shrink:0}
.layer-swatch{width:9px;height:9px;border-radius:999px;background:var(--accent-cyan);display:inline-block}
.viewport-copy{padding:var(--sp-7) var(--gutter-sm) var(--sp-2)}
.viewport-copy h1{margin:0 0 var(--sp-3);font-size:var(--fs-3xl);line-height:1.25;font-weight:700;letter-spacing:-.01em;overflow-wrap:break-word}
.viewport-copy h1 em{font-style:normal;color:var(--accent)}
.hero-description{margin:0;max-width:640px;color:var(--ink-dim);font-size:var(--fs-base)}
.hero-sim-label{margin:var(--sp-5) var(--gutter-sm) var(--sp-2);display:flex;align-items:center;gap:var(--sp-2);font-family:var(--mono);font-size:var(--fs-2xs);letter-spacing:.03em;color:var(--ink-dim)}
/* 스테이지는 장비 화면을 표현하는 상자다. 고정 260px 는 잎혀놓은 폰에서
   화면의 상당 부분을 잡아먹으므로 짧은 화면 높이에선 줄어들게 한다. */
.hero-sim-stage{margin:0 var(--gutter-sm);border-radius:var(--radius-sm);overflow:hidden;border:1px solid var(--line-soft);min-height:200px;min-height:clamp(180px,30dvh,260px);background:var(--bg-inset)}
.quick-queries{display:flex;flex-wrap:wrap;gap:var(--sp-2);padding:var(--sp-5) var(--gutter-sm) var(--sp-7)}
.quick-queries button{border:1px solid var(--line);background:var(--bg-elevated);color:var(--ink-dim);border-radius:999px;min-height:var(--touch);padding:var(--sp-2) var(--sp-4);font-size:var(--fs-xs);font-family:var(--mono);transition:.15s}
.quick-queries button:hover{border-color:var(--accent-dim);color:var(--accent);background:var(--bg-hover)}
/* [D6 실측 근거 — 넓은 화면에서 히어로가 화면을 독점했다]
   1280×900 에서 측정: 히어로 패널 1122px, 첫 섹션 top **1311px**.
   즉 첫 화면(900px)에 실제 콘텐츠가 **0개**였고, 스크롤 없이는 아무것도 못 봤다.
   [원인은 min-height 가 아니었다] .hero-sim-stage 의 min-height 는
   clamp(180px,30dvh,260px)=260px 인데 실제 높이는 712px 이었다. 시뮬 SVG 가
   viewBox="0 0 820 470"(비율 1.745)이고 width:100% 이므로, 패널이 넓어질수록
   **폭에 비례해 높이가 강제로 늘어난다**(실측 1141×654). 세로 값을 건드려도
   해결되지 않는 구조였다.
   [해결책을 두 개 재서 골랐다]
     [A] .sim-svg{max-height:...;width:auto} → 패널 1122→810, 첫섹션 1311→999
        (900 을 못 넘겨 여전히 첫 화면 콘텐츠 0개 · 미달)
     [B] 960px 이상에서 패널을 2단으로(설명·바로가기 | 시뮬)
        → 패널 1122→**569**, 첫섹션 1311→**758**, 첫 화면 콘텐츠 0→**1개**
   B 를 쓴다. 폭을 반으로 나누면 SVG 높이도 비율대로 함께 줄어(654→427)
   세로 예산이 남는다. 960px 미만은 기존 1단 흐름을 그대로 둔다 —
   좁은 화면에서 2단은 오히려 각 칸을 못 쓰게 만든다. */
/* [D6 후속 — 2단으로 바꾸고 나서 내가 만든 결함을 실측이 잡아냈다]
   위 D6 를 "OK"로 확정했지만, 텍스트 칸을 420px 고정으로 두고 제목 크기는
   전체폭 시절 값(--fs-3xl ≒ 39px)을 그대로 뒀다. 실측하니 제목 30자가
   **행당 9자 · 4줄(196px)** 로 터져 있었다. 나는 이 값을 640px 띠를 기각할 때
   "9자는 못 쓴다"는 근거로 썼는데, 정작 내가 통과시킨 1280px 이 같은 9자였다.
   즉 기각 근거로 쓴 '14자 하한'은 내가 재지 않고 만든 숫자였고, 그 기준으로는
   D6 자신이 탈락한다. 기준을 버리고 실측으로 다시 골랐다:
     현재      1280px — h1 373px/39.3px → 9자 **4줄** · 설명 23자 · 첫섹션 758
     G 칸 확대  1280px — h1 576px/39.3px → 15자 2줄 · 설명 36자 · 첫섹션 641
     H 글자축소 1280px — h1 373px/28px  → 13자 2줄 · 설명 23자 · 첫섹션 758
     I  G+H    1280px — h1 576px/28px  → **21자 2줄** · 설명 36자 · 첫섹션 641
   960px·1600px 에서도 I 가 같은 방향(2줄 · 설명 27~38자)이라 I 를 쓴다.
   칸을 fr 로 바꾼 이유: 420px 고정은 화면이 넓어져도 제목 폭이 그대로여서
   1600px 에서 오히려 9자로 더 나빠졌다(365px). 비율이면 함께 넓어진다. */
@media (min-width:960px){
  /* [D9 — 스크린샷을 눈으로 보고 찾은 빈틈] 설명문과 바로가기 버튼 사이가
     넓은 화면에서 벌어져 있었다. 실측: 1280px **33px**, 1600px **66px**
     (화면이 넓어질수록 커진다 = 시뮬 높이에 맞춰 왼쪽 행이 늘어난 것).
     [원인 진단이 한 번 틀렸다] 처음엔 정렬 문제로 보고 .viewport-copy 에
     align-self:start 를 줘봤는데 빈틈이 33/66px 그대로였다 — 정렬이 아니라
     행 자체가 늘어난 것이었다. 행 크기를 auto auto 1fr 로 고정하면
     1280·1600px 모두 빈틈 **0px**, 패널 높이·첫섹션 위치는 변화 없다. */
  .viewport-panel{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);
    grid-template-rows:auto auto 1fr;
    grid-template-areas:"bar bar" "copy stage" "quick stage";align-items:start}
  .panel-titlebar{grid-area:bar}
  .viewport-copy{grid-area:copy}
  /* 좁아진 칸에 맞춰 제목 크기를 한 단 내린다(3xl→2xl). 크기를 줄여도
     --fs-2xl 은 여전히 clamp 유동 크기이므로 화면폭에 따라 함께 자란다. */
  .viewport-copy h1{font-size:var(--fs-2xl)}
  /* 2단에서는 시뮬 상자가 바로 옆에 붙으므로 "아래는 …" 안내문이 가리키는
     대상이 어긋난다. 라벨을 숨기고 위치로 설명한다. */
  .hero-sim-label{display:none}
  .hero-sim-stage{grid-area:stage;align-self:stretch;margin:var(--sp-5) var(--gutter-sm) var(--sp-5) 0}
  .quick-queries{grid-area:quick;align-self:start}
}

/* [D7 — D6 가 960px 이상만 고쳐서 그 아래 띠가 남아 있었다]
   640~959px 를 재보니 첫 섹션 top 이 950~1132px 로 뷰포트(900) 밖이었고
   첫 화면 콘텐츠가 **0개**였다. 원인이 D6 때문이 아님을 격리해서 확인했다:
   이 띠에는 2단 규칙이 아예 안 걸리며, 시뮬 SVG 를 지우면 959px 첫섹션이
   1132→847px 로 내려온다 — 즉 원래부터 있던 SVG 비율 문제다.
   [두 안을 재서 골랐다]
     [J] 2단 시작점을 640px 로 내림
        640px — 첫섹션 664 · 첫화면 2개 · 설명 **18자**
        959px — 첫섹션 596 · 첫화면 3개 · 설명 27자
     [K] 1단 유지 + 제목 한 단 축소 + 시뮬 높이 상한
        640px — 첫섹션 876 · 첫화면 1개 · 설명 36자 · 제목 23자
        959px — 첫섹션 884 · 첫화면 1개 · 설명 40자 · 제목 33자
   J 가 세로를 더 많이 벌지만 설명문이 18~21자로 좁아진다. 이는 D2 에서
   내가 세운 한글 본문 40~50자 기준과 정면으로 어긋난다. 세로 한 칸을 더 벌려고
   본문 가독 폭을 절반으로 깎는 거래는 하지 않는다. K 를 쓴다 —
   첫 화면에 콘텐츠가 1개는 들어오고(0→1), 본문 폭은 그대로 지킨다.
   폭 640px 은 --bp-md, 959.98 은 --bp-lg − 0.02 다. */
@media (min-width:640px) and (max-width:959.98px){
  .viewport-copy h1{font-size:var(--fs-2xl)}
  /* 폭을 줄이지 않고 높이만 상한을 둔다. width:auto 를 함께 주면 SVG 가
     가운데로 몰려 상자 좌우에 빈 띠가 생기므로 폭은 100% 로 남긴다. */
  .sim-svg{max-height:clamp(180px,30dvh,260px)}
}

/* [D8 — 좁은 폰에서 제목이 4줄로 터졌다]
   실기 해상도로 재서 찾았다(앞서 세로를 900 으로 고정해 잰 값은 실기와 달랐다):
     320×568 — h1 26px → 행당 10자 **4줄**(130px)
     360×640 — h1 26.6px → 11자 **3줄**
     375×667 — h1 26.8px → 12자 2줄  (여기부터는 이미 정상)
   즉 결함 구간은 375px 미만이다. 그런데 375 는 --bp-* 에 없는 값이고,
   경계를 새로 만들면 반응형 규율 검사(모든 @media 폭은 --bp-* 에서 온다)를
   깨뜨린다. 그래서 --bp-sm(480) 경계를 쓰되, 그 대가를 먼저 실측했다:
     제목을 2xl 로 내릴 때
       320px — 12자 **2줄**(위계 1.47배)   · 360px — 14자 2줄
       375px — 14자 2줄                  · 479px — 18자 2줄
     제목을 xl 로 내리면 18px 이 되어 본문 15px 과 위계가 1.2배로 무너진다 → 기각
   2xl 을 쓴다. 480px 미만 전체에서 제목이 조금 작아지지만 전 구간 2줄이 되고
   제목·본문 위계는 1.47배로 유지된다. */
@media (max-width:479.98px){
  .viewport-copy h1{font-size:var(--fs-2xl)}
}

/* ---------------------------------------------------------------- 섹션 블록 */
.section-block{margin-bottom:var(--sp-10)}
.section-heading{display:flex;align-items:flex-end;justify-content:space-between;gap:var(--sp-4);margin-bottom:var(--sp-4);flex-wrap:wrap}
.section-heading h2{margin:2px 0 0;font-size:var(--fs-xl);font-weight:700}
.text-button{border:0;background:none;color:var(--accent-cyan);min-height:var(--touch-min);padding:var(--sp-1) 0;font-size:var(--fs-sm);font-weight:600;white-space:nowrap}
.text-button:hover{text-decoration:underline}

/* auto-fit + minmax 가 이미 미디어쿼리 없이 복장하므로 무리하게
   반을 건드리지 않고, min() 으로 360px 에서도 넘지지 않게만 보장한다. */
.learning-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(230px,100%),1fr));gap:var(--sp-3)}
.learning-card{position:relative;padding:var(--sp-5) var(--sp-5);border:1px solid var(--line);border-radius:var(--radius);background:var(--bg-panel);text-align:left;display:flex;flex-direction:column;gap:6px;transition:.18s;box-shadow:var(--shadow-sm);backdrop-filter:var(--card-blur)}
.learning-card:hover{border-color:var(--accent-dim);box-shadow:var(--shadow-md);transform:translateY(-3px)}
.learning-card .number{position:absolute;top:var(--sp-4);right:var(--sp-4);font-family:var(--mono);font-size:var(--fs-2xs);color:var(--ink-faint)}
.learning-card .course-icon{font-size:var(--fs-2xl);color:var(--accent-cyan);margin-bottom:var(--sp-1)}
.learning-card h3{margin:0;font-size:var(--fs-md);overflow-wrap:break-word}
.learning-card p{margin:0;font-size:var(--fs-xs);color:var(--ink-dim);flex:1}
.learning-card button{margin-top:var(--sp-2);border:0;background:none;color:var(--accent);font-size:var(--fs-xs);font-weight:700;text-align:left;padding:var(--sp-1) 0;min-height:var(--touch-min)}

.command-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(250px,100%),1fr));gap:var(--sp-3)}
.command-card{position:relative;text-align:left;padding:var(--sp-4);border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--bg-panel);display:flex;flex-direction:column;gap:8px;transition:.18s;box-shadow:var(--shadow-sm);backdrop-filter:var(--card-blur)}
.command-card:hover{border-color:var(--accent-dim);box-shadow:var(--shadow-md);transform:translateY(-2px)}
/* 카드 본문 열기 버튼 — 카드 전체 영역을 차지하되 액션 바 아래 레이어
   [D3 실측 근거] flex:1 이 없어서 이 버튼은 '내용 높이'로만 커졌다. 카드 자체는
   그리드가 같은 줄 최대 높이(225px)로 늘려주는데, 안쪽 버튼이 늘어나지 않으니
   그 아래 .card-actions 가 남은 공백 위에 떠 있었다.
   실측: 같은 줄 카드 4장의 액션행 top = 163 / 163 / **143** / 163
        (3번째 카드만 설명이 1줄이라 20px 위로 붙었다)
   .command-card p 에는 이미 flex:1 이 있었지만, 부모가 안 늘어나면 나눠줄
   여백 자체가 없어 아무 일도 하지 않는다. 부모부터 늘린다. */
.command-card>.card-open{all:unset;cursor:pointer;display:flex;flex:1;flex-direction:column;gap:8px;text-align:left;color:inherit;font:inherit}
/* 하단 고정을 flex:1(p) 한 곳에만 의존하지 않는다. p 가 짧아 줄바꿈이 없거나
   -webkit-line-clamp 로 잘리는 경우가 있으므로 footer 자체를 바닥에 붙인다. */
.command-card>.card-open>footer{margin-top:auto}
.command-card>.card-open:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:6px}

/* ------- 즐겨찾기(★) / 학습 완료(✓) 마크 ------- */
.card-actions{display:flex;align-items:center;flex-wrap:wrap;gap:var(--sp-2);margin-top:var(--sp-1);padding-top:var(--sp-2);border-top:1px dashed var(--line-soft)}
/* 카드 전역이 열기 버튼이므로 ★/✓ 는 그 위에 있다. 턱수 오입을 막기 위해
   필토토하게 36px 높이를 보장한다(인접 토글 사이 간겁도 8px 유지). */
.fav-toggle,.done-toggle{display:inline-flex;align-items:center;gap:5px;min-height:36px;border:1px solid var(--line);background:var(--bg-elevated);color:var(--ink-faint);border-radius:999px;padding:var(--sp-1) var(--sp-3);font-size:var(--fs-2xs);font-weight:600;cursor:pointer;transition:.15s}
.fav-toggle:hover,.done-toggle:hover{border-color:var(--accent-dim);color:var(--accent)}
.fav-toggle.on{border-color:color-mix(in srgb,var(--accent-amber,var(--accent)) 50%,transparent);background:color-mix(in srgb,var(--accent-amber,var(--accent)) 14%,transparent);color:var(--accent-amber,var(--accent))}
.done-toggle.on{border-color:color-mix(in srgb,var(--accent-green) 50%,transparent);background:color-mix(in srgb,var(--accent-green) 14%,transparent);color:var(--accent-green)}
.command-card.is-fav{border-color:color-mix(in srgb,var(--accent-amber,var(--accent)) 38%,var(--line))}
.command-card.is-done{border-color:color-mix(in srgb,var(--accent-green) 38%,var(--line))}
.command-card.is-done>.card-open h3::after{content:" ✓";color:var(--accent-green);font-size:var(--fs-xs)}
.detail-marks{display:inline-flex;flex-wrap:wrap;gap:var(--sp-2);margin-left:auto}

/* ------- 홈 학습 진도 패널 ------- */
.progress-panel{border:1px solid var(--line);border-radius:var(--radius);background:var(--bg-panel);box-shadow:var(--shadow-sm);padding:var(--sp-5);display:flex;flex-direction:column;gap:var(--sp-4);backdrop-filter:var(--card-blur);min-width:0}
.progress-summary{display:flex;align-items:center;gap:var(--sp-5);flex-wrap:wrap}
.progress-figure{position:relative;width:88px;height:88px;flex-shrink:0;display:grid;place-items:center}
.progress-figure svg{position:absolute;inset:0}
.progress-figure circle{fill:none;stroke-width:4}
.progress-figure .ring-bg{stroke:var(--line)}
.progress-figure .ring-fg{stroke:var(--accent-green);stroke-linecap:round;transition:stroke-dasharray .4s ease}
.progress-figure strong{font:700 var(--fs-xl)/1.2 var(--mono);color:var(--ink)}
/* min-width:240px 는 360px 화면에서 패널 패딩(2×20px)을 더하면 상자를
   넘을 수 있다. flex-basis 로 바꿔 좁은 폭에선 100% 를 허용한다. */
.progress-meta{flex:1 1 min(240px,100%);min-width:0}
.progress-meta h3{margin:2px 0 var(--sp-1);font-size:var(--fs-lg)}
.progress-meta p:not(.eyebrow){margin:0;color:var(--ink-dim);font-size:var(--fs-xs)}
.progress-actions{display:flex;gap:var(--sp-4);flex-wrap:wrap;margin-top:var(--sp-3)}
.text-button.danger{color:var(--accent-red,#ff5d6c)}
.fav-strip{display:flex;gap:var(--sp-2);flex-wrap:wrap;padding-top:var(--sp-3);border-top:1px dashed var(--line-soft)}
.fav-chip{display:inline-flex;align-items:center;gap:6px;min-height:36px;max-width:100%;border:1px solid var(--line);background:var(--bg-elevated);color:var(--ink-dim);border-radius:999px;padding:var(--sp-1) var(--sp-3);font-family:var(--mono);font-size:var(--fs-2xs);cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fav-chip span{color:var(--accent-amber,var(--accent))}
.fav-chip:hover{border-color:var(--accent-dim);color:var(--accent)}
.fav-chip.more{font-family:var(--sans,inherit)}

/* ------- 코스별 진도 바 ------- */
.course-progress{display:flex;flex-direction:column;gap:5px;margin:var(--sp-2) 0 2px}
.course-progress .bar{height:6px;border-radius:999px;background:var(--bg-inset);border:1px solid var(--line-soft);overflow:hidden}
.course-progress .bar span{display:block;height:100%;width:0;background:linear-gradient(90deg,var(--accent-cyan),var(--accent-green));transition:width .35s ease}
.course-progress .label{font-family:var(--mono);font-size:var(--fs-3xs);color:var(--ink-faint)}
.course-progress.complete .label{color:var(--accent-green)}
.card-top{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-2);flex-wrap:wrap;min-width:0}
.command-badge{font-family:var(--mono);font-weight:700;font-size:var(--fs-xs);color:var(--accent-text);background:color-mix(in srgb,var(--accent) 14%,transparent);border:1px solid var(--accent-dim);border-radius:8px;padding:3px var(--sp-2);letter-spacing:.02em;overflow-wrap:anywhere}
.source-badge{font-size:var(--fs-3xs);font-weight:700;border-radius:999px;padding:3px var(--sp-2);border:1px solid transparent;white-space:nowrap;flex-shrink:0}
.confidence-manual{background:color-mix(in srgb,var(--accent-green) 16%,transparent);color:var(--accent-green);border-color:color-mix(in srgb,var(--accent-green) 40%,transparent)}
.confidence-official{background:color-mix(in srgb,var(--accent-blue) 16%,transparent);color:var(--accent-blue);border-color:color-mix(in srgb,var(--accent-blue) 40%,transparent)}
.confidence-inferred{background:color-mix(in srgb,var(--accent) 16%,transparent);color:var(--accent-text);border-color:color-mix(in srgb,var(--accent) 40%,transparent)}
.command-card h3{margin:0;font-size:var(--fs-md);overflow-wrap:anywhere}
.command-card p{margin:0;font-size:var(--fs-xs);color:var(--ink-dim);flex:1;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.command-card footer{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-2);flex-wrap:wrap;font-size:var(--fs-2xs);color:var(--ink-faint);font-family:var(--mono);min-width:0}
.command-card footer span:last-child{color:var(--accent-cyan)}
.empty{padding:var(--sp-12) var(--sp-5);text-align:center;color:var(--ink-dim);border:1px dashed var(--line);border-radius:var(--radius);grid-column:1/-1;display:flex;flex-direction:column;align-items:center;gap:var(--sp-3)}
.empty strong{display:block;color:var(--ink);margin-bottom:0}
.empty p{margin:0;max-width:420px}
.ask-ai-button{display:inline-flex;align-items:center;gap:var(--sp-2);border:1px solid var(--accent-dim);background:var(--accent);color:var(--bg);border-radius:999px;padding:0 var(--sp-4);min-height:var(--touch);font-weight:700;font-size:var(--fs-sm);letter-spacing:.01em;box-shadow:var(--shadow-sm);cursor:pointer;max-width:min(92vw,480px)}
.ask-ai-button span:not(.spark){overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ask-ai-button:hover{filter:brightness(1.05)}
body[data-theme="mono"] .ask-ai-button{color:#fff}
body[data-theme="neon"] .ask-ai-button{background:linear-gradient(135deg,var(--accent),var(--accent-cyan));color:#0a0b16}
body[data-theme="pastel"] .ask-ai-button{color:#fff}
body[data-theme="warm"] .ask-ai-button{color:#fff}

/* ---------------------------------------------------------------- 페이지 헤더 / 카탈로그 */
/* [D1 — 처음 세운 결함 판정을 실측이 뒤집었다]
   나는 "page-header 760px vs 카드 그리드 1192px 이니 폭 규율이 깨졌다"고 적었다.
   그런데 폭(px)은 그 자체로 좋고 나쁨이 없다. 판정 기준을 '행당 글자수'로
   바꿔 재보니 760px 는 설명문 **51자** / 제목 27자였다 — 한글 적정 범위 안이고,
   설명문을 좁게 두고 카드 나열만 넓게 쓰는 것은 의도된 배치였다.
   **즉 결함이 아니었고, 내 판정 근거가 틀렸다.** 폭 통일을 하지 않는다.
   [다만 실측 중에 진짜 문제가 하나 드러났다] px 고정 상한은 사용자가 글꼴을
   확대하면 글자수가 함께 흔들린다:
     760px 고정 — 기본 51자 → 글꼴 125% 확대 시 **41자**로 좁아진다
     45em 기준 — 기본 **48자**(720px) → 확대 시 **48자**(900px) 그대로 유지
   글꼴 확대는 WCAG 1.4.4 가 보장해야 하는 동작이므로 em 기준으로 바꾼다.
   [주석 정정] 처음 이 자리에 "기본 상태에서 760px 와 같은 51자가 나오도록
   맞췄다"고 적었는데, 적용 후 실측은 **720px / 48자**였다. 재지 않은 숫자를
   적었던 것이므로 실측값으로 바로잡는다. 51자와 48자는 둘 다 한글 권장
   범위(40~50자) 안이라 값을 다시 키우지 않고 48자로 확정한다. */
.page-header{margin-bottom:var(--sp-6);max-width:45em}
.page-header h1{margin:0 0 var(--sp-2);font-size:var(--fs-2xl);overflow-wrap:break-word}
.page-header p{margin:0;color:var(--ink-dim);font-size:var(--fs-md)}

.catalog-toolbar{display:flex;gap:var(--sp-3);flex-wrap:wrap;margin-bottom:var(--sp-4)}
/* min-width:240px 는 360px 화면에서도 들어가지만, 섬렉트와 나란히 놓이면
   서로 밀어낸다. flex-basis:100% 로 좁은 화면에서는 자연하게 줄바꿈한다. */
.catalog-search{display:flex;align-items:center;gap:var(--sp-2);flex:1 1 min(240px,100%);min-width:0;border:1px solid var(--line);background:var(--bg-panel);border-radius:999px;padding:0 var(--sp-4)}
.catalog-search span{color:var(--ink-faint);font-size:var(--fs-md);flex-shrink:0}
.catalog-search input{flex:1;min-width:0;background:none;border:0;padding:var(--sp-3) 0;color:var(--ink);font-size:var(--fs-sm)}
.catalog-search input::placeholder{color:var(--ink-faint)}
.catalog-toolbar select{border:1px solid var(--line);background:var(--bg-panel);color:var(--ink);border-radius:999px;padding:0 var(--sp-3);font-size:var(--fs-sm);height:var(--touch);flex:0 1 auto;max-width:100%}
.category-chips{display:flex;flex-wrap:wrap;gap:var(--sp-2);margin-bottom:var(--sp-4)}
.category-chips button{border:1px solid var(--line);background:var(--bg-panel);color:var(--ink-dim);border-radius:999px;min-height:36px;padding:var(--sp-2) var(--sp-3);font-size:var(--fs-xs);display:inline-flex;align-items:center;gap:6px}
.category-chips button small{color:var(--ink-faint);font-family:var(--mono)}
.category-chips button.active{border-color:var(--accent-dim);color:var(--bg);background:var(--accent)}
.category-chips button.active small{color:var(--bg)}
.result-meta{margin-bottom:var(--sp-4);color:var(--ink-dim);font-size:var(--fs-sm)}
.result-meta strong{color:var(--ink);font-family:var(--mono)}
.catalog-grid{margin-bottom:20px}
.load-more{display:block;margin:0 auto;border:1px solid var(--line);background:var(--bg-panel);color:var(--ink-dim);border-radius:999px;min-height:var(--touch);padding:var(--sp-2) var(--sp-6);font-size:var(--fs-xs);font-weight:600}
.load-more:hover{border-color:var(--accent-dim);color:var(--accent)}

/* ---------------------------------------------------------------- 학습 코스 */
.course-list{display:flex;flex-direction:column;gap:var(--sp-3)}
.course-row{display:flex;align-items:center;gap:var(--sp-4);padding:var(--sp-5);border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--bg-panel);box-shadow:var(--shadow-sm)}
.course-num{font-family:var(--mono);font-size:var(--fs-2xl);font-weight:700;color:var(--ink-faint);width:40px;flex-shrink:0}
.course-row h2{margin:0 0 var(--sp-1);font-size:var(--fs-lg);overflow-wrap:break-word}
.course-row p{margin:0;color:var(--ink-dim);font-size:var(--fs-xs)}
/* [D2 실측 근거 — 한 줄이 너무 길어 눈이 되돌아오지 못했다]
   1280px 에서 측정: .course-row p 폭 993px / font-size 13px → **행당 약 76자**,
   h2 는 55자. 한글 본문 권장은 행당 40~50자다(라틴 45~75자를 한글 1자=1em 로 환산).
   폭 상한을 주면 되지만 '버튼이 텍스트를 따라 왼쪽으로 붙는' 부작용이 생길 수
   있으므로 함께 재고 넣었다:
     적용 전  p 76자 · h2 55자 · 버튼 우측여백 21px · 행높이 132px
     적용 후  p **42자** · h2 30자 · 버튼 우측여백 21px · 행높이 132px (동일)
   → 가독 폭만 줄고 버튼 위치·행 높이는 그대로다. */
.course-row>div{flex:1 1 auto;min-width:0;max-width:34rem}
.course-row button{margin-left:auto}
.course-row button{flex-shrink:0;border:1px solid var(--accent-dim);background:color-mix(in srgb,var(--accent) 10%,transparent);color:var(--accent-text);border-radius:999px;min-height:var(--touch);padding:var(--sp-2) var(--sp-4);font-weight:700;font-size:var(--fs-xs)}

/* ---------------------------------------------------------------- PGP */
/* 5칸 고정은 360px 에서 칸당 실효폭이 50px 이하로 떨어진다(=수치 잠김).
   auto-fit 으로 바꿔 칸 수를 폭이 결정하게 하면 모든 폭에서 자연하게 동작한다. */
.pgp-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(150px,100%),1fr));gap:var(--sp-3);margin:0 0 var(--sp-6)}
.pgp-stats article{display:flex;flex-direction:column;padding:var(--sp-4);border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--bg-panel);box-shadow:var(--shadow-sm);min-width:0}
.pgp-stats strong{font:700 var(--fs-2xl)/1.2 var(--mono);color:var(--accent-cyan)}
.pgp-stats span{font-size:var(--fs-2xs);color:var(--ink-faint);margin-top:2px}
.history-card .command-badge{background:color-mix(in srgb,var(--accent-blue) 12%,transparent);color:var(--accent-blue);border-color:color-mix(in srgb,var(--accent-blue) 40%,transparent)}
.normalization-log,.definition-history{margin-top:24px;border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--bg-panel);overflow:hidden}
.normalization-log>summary,.definition-history>summary{padding:var(--sp-4);font-weight:700;cursor:pointer;background:var(--bg-inset);font-size:var(--fs-sm);min-height:var(--touch);display:flex;align-items:center}
.normalization-log>div,.definition-history>div{display:grid;gap:var(--sp-2);padding:var(--sp-3)}

/* ---- 정교화 로그 ----------------------------------------------------
   실직: 4칸 고정(minmax(160)+24+minmax(110)+2fr, gap 10x3)은 360px 뷰포트에서
   필수폭 344px 를 요구하는데 쓸 수 있는 폭은 328px 뿐이라 가로로 넘치다.
   모바일을 기본으로(한 칸 서로 쌓기) 둘고, 폭이 넘사면 4칸로 올린다. */
.normalization-log article{display:grid;grid-template-columns:minmax(0,1fr);align-items:center;gap:var(--sp-1) var(--sp-2);padding:var(--sp-2) var(--sp-3);border:1px solid var(--line-soft);border-radius:var(--radius-sm);min-width:0}
.normalization-log article>*{min-width:0}
/* 4형제는 code / span(→) / strong / p 순서다. 모바일 단일 칸에서는 화살표를
   아래 방향으로 바꿔야 의미가 맞으므로, 원문 '→' 는 숨기고 CSS 로 '↓' 를 넣는다. */
.normalization-log article>span{color:var(--ink-faint);font-size:var(--fs-2xs);text-align:center}
@media (max-width:639.98px){
  .normalization-log article>span{font-size:0;line-height:1}
  .normalization-log article>span::after{content:'↓';font-size:var(--fs-2xs);display:block}
}
@media (min-width:640px){
  .normalization-log article{grid-template-columns:minmax(120px,1fr) 24px minmax(100px,.7fr) minmax(0,2fr);gap:var(--sp-2)}
}
.normalization-log article>strong{font-family:var(--mono);font-size:var(--fs-xs);color:var(--accent-green);overflow-wrap:anywhere}
.normalization-log code,.definition-history code{font-family:var(--mono);color:var(--accent-cyan);overflow-wrap:anywhere;word-break:break-word}
.normalization-log p{margin:0;color:var(--ink-dim);font-size:var(--fs-xs);overflow-wrap:anywhere}

/* ---- 정의 이력 (상단 동일 사유로 모바일 단일 칸) ---- */
.definition-history article{position:relative;display:grid;grid-template-columns:minmax(0,1fr);align-items:start;gap:var(--sp-1) var(--sp-3);padding:var(--sp-3);border:1px solid var(--line-soft);border-radius:var(--radius-sm);min-width:0}
.definition-history article>*{min-width:0}
@media (min-width:640px){
  .definition-history article{grid-template-columns:74px minmax(120px,.7fr) minmax(0,1fr) auto;align-items:center;gap:var(--sp-3)}
}
.definition-history article.active{border-color:color-mix(in srgb,var(--accent-green) 40%,transparent);background:color-mix(in srgb,var(--accent-green) 6%,transparent)}
.definition-history article>span,.definition-history article>small{font-size:var(--fs-2xs);color:var(--ink-faint)}
.definition-history article>p{margin:0;font-size:var(--fs-xs);color:var(--ink-dim);overflow-wrap:anywhere}
.definition-history article>strong{color:var(--accent-green);font-size:var(--fs-2xs)}

/* ---------------------------------------------------------------- 상태 표시줄 */
/* 상태표시줄은 토글 버튼까지 담기 때문에 공간이 모자란다. 좁은 화면에서는
   숫자가 깔리거나 줄바꿈해 높이가 틀어지는 것보다 가로 스크롤이 안전하다. */
.statusbar{min-height:30px;padding:var(--sp-1) calc(var(--gutter-sm) + var(--safe-r)) var(--sp-1) calc(var(--gutter-sm) + var(--safe-l));background:var(--bg-inset);border-top:1px solid var(--line);display:flex;align-items:center;gap:var(--sp-3);font-family:var(--mono);font-size:var(--fs-2xs);color:var(--ink-faint);overflow-x:auto;scrollbar-width:none}
.statusbar::-webkit-scrollbar{height:0}
.status-item{display:flex;align-items:center;white-space:nowrap;flex-shrink:0}
.status-sep{width:1px;height:14px;background:var(--line)}
/* [실측 근거] GRID/SNAP/ORTHO 는 CAD 분위기용 장식이며 app.js 에
   클릭 핸들러가 하나도 없다(grep "status-toggle" → 0건).
   이전에는 ::after 로 24px 적중 영역을 만들어 뒀는데, 그것은 SC 2.5.8 준수가
   아니라 '눌리는 것처럼 보이지만 아무 일도 없는' 거짓 어포던스였다.
   → 적중 영역을 없애고 장식임을 명시한다(cursor:default, pointer-events:none). */
.status-toggle{position:relative;padding:var(--sp-1) var(--sp-2);border-radius:999px;border:1px solid transparent;letter-spacing:.03em;cursor:default;pointer-events:none}
.status-toggle.on{color:var(--accent-green);background:color-mix(in srgb,var(--accent-green) 10%,transparent);border-color:color-mix(in srgb,var(--accent-green) 30%,transparent)}
.status-right{margin-left:auto}
#status-coord{min-width:110px}
.status-sep{flex-shrink:0}
/* [실측 근거] #status-coord 는 document mousemove 로만 갱신된다(app.js L861).
   터치 기기에는 mousemove 가 없으므로 'X 0.00, Y 0.00' 이 영구 고정된다.
   포인터가 없는 기기에서는 값이 절대 변하지 않으므로 표시하지 않는다. */
@media (hover:none){
  #status-coord{display:none}
}

/* ---------------------------------------------------------------- 명령행 검색바 */
/* 하단 고정바는 iOS 홈바/제스처 인디타 아래로 깔린다.
   safe-area-inset-bottom 을 하단 패딩에 더해 입력창을 안전 영역 위로 올린다. */
.cmdline-bar{position:sticky;bottom:0;z-index:40;display:flex;align-items:center;gap:var(--sp-2);padding:var(--sp-2) calc(var(--gutter-sm) + var(--safe-r)) calc(var(--sp-2) + var(--safe-b)) calc(var(--gutter-sm) + var(--safe-l));background:var(--bg-panel);border-top:1px solid var(--line)}
.cmdline-label{font-family:var(--mono);font-size:var(--fs-xs);color:var(--accent);font-weight:700;flex-shrink:0}
#cmdline-input{flex:1;min-width:0;background:var(--bg-inset);border:1px solid var(--line);border-radius:999px;min-height:var(--touch);padding:var(--sp-2) var(--sp-4);color:var(--ink);font-family:var(--mono);font-size:var(--fs-sm)}
#cmdline-input:focus{outline:none;border-color:var(--accent-dim)}
#cmdline-input::placeholder{color:var(--ink-faint)}
/* 키보드 힌트는 키보드가 없는 환경에선 의미 없이 공간만 먹는다.
   마우스/정밀 하드웨어가 있는 환경에서만 노출한다. */
.cmdline-bar kbd{display:none;flex-shrink:0;border:1px solid var(--line);border-radius:8px;padding:4px 8px;font-size:var(--fs-3xs);color:var(--ink-faint);background:var(--bg-inset)}
@media (hover:hover) and (pointer:fine){ .cmdline-bar kbd{display:inline-block} }

/* ---------------------------------------------------------------- 명령 대화상자 */
/* dvh: 100vh/94vh 는 모바일 주소창 높이를 포함하므로, 주소창이 드러난
   상태에서 닫기 버튼이 화면 밖에 자리잡을 수 있다. dvh 로 실제 높이를 쓴다. */
.command-dialog{width:min(1180px,96vw);max-height:94vh;max-height:94dvh;padding:0;border:1px solid var(--line);border-radius:calc(var(--radius) + 6px);background:var(--bg);color:var(--ink);box-shadow:var(--shadow-lg);overflow-x:hidden}
/* 640px 미만에선 상세를 전면 시트로 다룬다. 좁은 화면에서 모달 테두리는
   읽을 공간을 깎을 뿐 이득이 없으므로 전폭을 본문에 쓴다.
   <dialog> 는 UA 기본 스타일로 margin:auto 를 가지므로 inset:0 + margin:0 을
   함께 지정해야 실제로 화면 전체를 채운다. */
@media (max-width:639.98px){
  .command-dialog{position:fixed;inset:0;width:100vw;max-width:100vw;height:100dvh;max-height:100dvh;border:0;border-radius:0;margin:0}
}
.command-dialog::backdrop{background:rgba(4,7,11,.72);backdrop-filter:blur(2px)}
.dialog-titlebar{display:flex;align-items:center;gap:var(--sp-2);padding:var(--sp-2) var(--sp-3);padding-top:calc(var(--sp-2) + var(--safe-t));background:var(--bg-inset);border-bottom:1px solid var(--line);position:sticky;top:0;z-index:5}
.dialog-titlebar small{margin-left:2px;font-family:var(--mono);font-size:var(--fs-2xs);color:var(--ink-faint);letter-spacing:.03em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 닫기는 모바일에서 가장 중요한 탈출 경로다. 44px 를 확보한다. */
.dialog-close{margin-left:auto;flex-shrink:0;width:var(--touch);height:var(--touch);border:0;background:none;color:var(--ink-faint);font-size:var(--fs-xl);border-radius:999px;display:inline-flex;align-items:center;justify-content:center}
.dialog-close:hover{background:var(--bg-hover);color:var(--ink)}

/* 상세 좌우 여백은 다이얼로그 폭에 종속된다. 30px 고정은 360px 화면에서
   본문 가용폭을 300px 로 줄이므로 유동 값으로 바꾼다. */
.detail-hero{padding:var(--sp-6) var(--gutter-sm) var(--sp-1)}
.detail-badges{display:flex;align-items:center;flex-wrap:wrap;gap:var(--sp-2);margin-bottom:var(--sp-4)}
.evidence-badge{display:inline-flex;align-items:center;border:1px solid transparent;border-radius:999px;padding:var(--sp-1) var(--sp-2);font-size:var(--fs-2xs);font-weight:700}
.detail-hero h2{margin:0 0 var(--sp-2);font-size:var(--fs-2xl);overflow-wrap:anywhere}
.detail-hero p{margin:0 0 var(--sp-2);color:var(--ink-dim);font-size:var(--fs-sm)}
.detail-hero small{color:var(--ink-faint);font-size:var(--fs-2xs);font-family:var(--mono)}
.detail-body{padding:var(--sp-2) var(--gutter-sm) calc(var(--sp-7) + var(--safe-b));display:grid;gap:var(--sp-6);min-width:0}
.detail-visual{margin-bottom:var(--sp-1);min-width:0}
.detail-content{display:grid;gap:var(--sp-5);min-width:0}
.detail-overview{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(200px,100%),1fr));gap:var(--sp-4)}
.fact-group{min-width:0}
.fact-group h3{margin:0 0 var(--sp-2);font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.06em;color:var(--ink-faint)}
.fact-group ul{margin:0;padding-left:18px;font-size:var(--fs-sm);color:var(--ink-dim);overflow-wrap:anywhere}
.fact-group li{margin-bottom:4px}
.process-section{min-width:0}
.process-section h3{margin:0 0 var(--sp-3);font-size:var(--fs-md)}
.process-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--sp-1)}
.process-list li button{width:100%;min-height:var(--touch);display:flex;align-items:flex-start;gap:var(--sp-3);text-align:left;border:1px solid var(--line);background:var(--bg-panel);border-radius:var(--radius-sm);padding:var(--sp-2) var(--sp-3);min-width:0}
.process-list li.active button{border-color:var(--accent-dim);background:color-mix(in srgb,var(--accent) 6%,transparent)}
.process-list li button span{flex-shrink:0;width:22px;height:22px;border-radius:999px;background:var(--bg-inset);color:var(--ink-faint);font-family:var(--mono);font-size:var(--fs-2xs);display:flex;align-items:center;justify-content:center}
.process-list li.active button span{color:var(--accent-text);background:color-mix(in srgb,var(--accent) 14%,transparent)}
.process-list li button>div{min-width:0}
.process-list li button strong{display:block;font-size:var(--fs-xs);margin-bottom:2px;overflow-wrap:anywhere}
.process-list li button p{margin:0;font-size:var(--fs-xs);color:var(--ink-dim);overflow-wrap:anywhere}
.related h3{margin:0 0 var(--sp-2);font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.06em;color:var(--ink-faint)}
.related div{display:flex;flex-wrap:wrap;gap:var(--sp-2)}
.related button{border:1px solid var(--line);background:var(--bg-panel);color:var(--accent-cyan);border-radius:999px;min-height:36px;padding:var(--sp-1) var(--sp-3);font-size:var(--fs-xs);font-family:var(--mono)}
.related button:hover{border-color:var(--accent-cyan)}
.evidence{border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--bg-panel);padding:var(--sp-4);min-width:0}
.evidence p{margin:var(--sp-2) 0 0;color:var(--ink-dim);font-size:var(--fs-xs);overflow-wrap:anywhere}
.evidence ul{margin:var(--sp-2) 0 0;padding-left:18px;font-size:var(--fs-xs);color:var(--ink-faint);overflow-wrap:anywhere}

/* ==========================================================================
   CAD 실행 시뮬레이션 플레이어 (cad-sim.js 호스트 컨테이너)
   ========================================================================== */
.cad-tutorial-player{overflow:hidden;border:1px solid var(--line);border-radius:var(--radius);background:var(--bg-inset);box-shadow:var(--shadow-md)}
/* 헤더는 제목 + 단계라벨 + 등급배지 3종을 담는다. 360px 에서 한 줄로
   가면 배지가 밀려나가므로 줄바꿈을 허용한다. */
.cad-tutorial-player>header{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:var(--sp-2);padding:var(--sp-3);border-bottom:1px solid var(--line);background:var(--bg-panel)}
.cad-tutorial-player>header>div{display:flex;align-items:center;flex-wrap:wrap;gap:var(--sp-2);min-width:0}
.cad-tutorial-player header strong{font-size:var(--fs-sm)}
.cad-tutorial-player header small{color:var(--ink-faint);font-size:var(--fs-2xs);overflow-wrap:anywhere}
.live-dot{width:8px;height:8px;border-radius:50%;background:var(--accent-green);box-shadow:0 0 0 0 rgba(51,209,137,.6);animation:live-pulse 1.6s infinite}
.scene-type{flex-shrink:0;padding:var(--sp-1) var(--sp-2);border:1px solid var(--line);border-radius:999px;color:var(--accent-cyan);font:var(--fs-3xs)/1.4 var(--mono);text-transform:uppercase;letter-spacing:.06em;white-space:nowrap}

/* ---- 매핑 정직성 등급 표기 ----
   전용/별칭/계열 재현은 신뢰 가능(시안), generic은 경고색(앰버)으로 분리해
   사용자가 배지 색만 보고도 "이건 이 명령의 실제 재현이 아니다"를 알 수 있게 한다. */
.scene-type[data-fidelity="exact"],
.scene-type[data-fidelity="alias"],
.scene-type[data-fidelity="family"]{color:var(--accent-cyan);border-color:color-mix(in srgb,var(--accent-cyan) 40%,var(--line))}
.scene-type[data-fidelity="generic"]{color:var(--accent-amber,var(--accent));border-color:color-mix(in srgb,var(--accent-amber,var(--accent)) 48%,var(--line));background:color-mix(in srgb,var(--accent-amber,var(--accent)) 12%,transparent)}
/* 부분 재현: 거짓은 없지만 범위가 좁다 → 중립색으로 구분 */
.scene-type[data-fidelity="alias-only"]{color:var(--ink-dim);border-color:var(--line);background:color-mix(in srgb,var(--ink-faint) 10%,transparent)}
.cad-tutorial-player[data-fidelity="alias-only"] .live-dot{background:var(--ink-faint);animation:none;box-shadow:none}
.cad-tutorial-player[data-fidelity="generic"]{border-color:color-mix(in srgb,var(--accent-amber,var(--accent)) 32%,var(--line))}
.cad-tutorial-player[data-fidelity="generic"] .live-dot{background:var(--accent-amber,var(--accent));animation:none;box-shadow:none}

.fidelity-note{display:flex;flex-direction:column;gap:var(--sp-1);padding:var(--sp-3);border-bottom:1px solid var(--line);background:color-mix(in srgb,var(--accent-amber,var(--accent)) 10%,var(--bg-inset));border-left:3px solid var(--accent-amber,var(--accent))}
.fidelity-note strong{font-size:var(--fs-2xs);letter-spacing:.02em;color:var(--accent-amber,var(--accent))}
.fidelity-note p{margin:0;font-size:var(--fs-xs);line-height:1.6;color:var(--ink-dim);overflow-wrap:anywhere}
.fidelity-note .fidelity-note-guide{color:var(--ink-faint);font-size:var(--fs-2xs)}
.fidelity-note b{color:var(--ink)}
.fidelity-note.is-partial{background:var(--bg-inset);border-left-color:var(--ink-faint)}
.fidelity-note.is-partial strong{color:var(--ink-dim)}

/* ---- 명령행 시퀀스 카드 (steps[].commandPrompt 기반 · 전 항목 정직) ---- */
.cmdseq{margin:0 0 var(--sp-4);border:1px solid var(--line);border-radius:var(--radius);background:var(--bg-inset);overflow:hidden;min-width:0}
.cmdseq.cmdseq-primary{border-color:color-mix(in srgb,var(--accent) 38%,var(--line));box-shadow:var(--shadow-md)}
.cmdseq>header{display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-2);flex-wrap:wrap;padding:var(--sp-3);border-bottom:1px solid var(--line);background:var(--bg-panel)}
.cmdseq>header h3{margin:0;font-size:var(--fs-sm)}
.cmdseq-src{font:var(--fs-3xs)/1.4 var(--mono);color:var(--ink-faint);letter-spacing:.04em;overflow-wrap:anywhere}
.cmdseq-list{list-style:none;margin:0;padding:var(--sp-2) 0;counter-reset:none}
/* 좁은 화면에선 30px 번호 칸을 떼어 본문포도 새로 가진다(번호는 위에 쌓긴다). */
.cmdseq-list li{display:grid;grid-template-columns:26px minmax(0,1fr);gap:var(--sp-1) var(--sp-2);padding:var(--sp-2) var(--sp-3);align-items:baseline}
.cmdseq-list li+li{border-top:1px dashed color-mix(in srgb,var(--line) 70%,transparent)}
.cmdseq-n{font:var(--fs-2xs)/1.5 var(--mono);color:var(--ink-faint)}
.cmdseq-list code{display:block;font:var(--fs-xs)/1.6 var(--mono);color:var(--accent-cyan);background:color-mix(in srgb,var(--accent-cyan) 8%,transparent);border-radius:6px;padding:var(--sp-1) var(--sp-2);word-break:break-word;overflow-wrap:anywhere}
.cmdseq-list small{grid-column:2;color:var(--ink-faint);font-size:var(--fs-2xs);overflow-wrap:anywhere}

/* ---- 목록 카드의 재현 유형 표시 ---- */
.sim-mark{display:inline-flex;align-items:center;gap:var(--sp-1);font:var(--fs-3xs)/1.4 var(--mono);color:var(--ink-faint);letter-spacing:.03em;white-space:nowrap}
.sim-mark.is-dedicated{color:var(--accent-cyan)}
/* 장비 화면. 360px 에서 360px 고정 높이는 세로 모드 뷰포트(≈640px)의
   절반 이상을 잡아먹어 명령행/자리표가 사라진다. 높이도 유동으로 바꿔 쓴다. */
.cad-stage{min-height:240px;min-height:clamp(220px,38dvh,360px)}
@media (min-width:960px){ .cad-stage{min-height:360px} }
.tutorial-caption{display:flex;gap:var(--sp-3);padding:var(--sp-3);border-top:1px solid var(--line);background:var(--bg-panel);min-width:0}
.tutorial-caption>span{flex-shrink:0;font-family:var(--mono);font-size:var(--fs-xs);color:var(--ink-faint);width:22px}
.tutorial-caption>div{min-width:0}
.tutorial-caption strong{display:block;font-size:var(--fs-sm);margin-bottom:2px;overflow-wrap:anywhere}
.tutorial-caption p{margin:0;font-size:var(--fs-xs);color:var(--ink-dim);overflow-wrap:anywhere}
/* 재생 쯤 버튼은 가장 번번하게 눌리는 상태다. 44px 를 확보한다. */
.tutorial-controls{display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:var(--sp-2);padding:var(--sp-2) var(--sp-3);border-top:1px solid var(--line);background:var(--bg-inset)}
.tutorial-controls button{border:1px solid var(--line);background:var(--bg-panel);color:var(--ink-dim);border-radius:999px;min-width:var(--touch);height:var(--touch);font-size:var(--fs-md);display:inline-flex;align-items:center;justify-content:center}
.tutorial-controls .play-button{min-width:0;padding:0 var(--sp-4);color:var(--accent);border-color:var(--accent-dim);font-weight:700;font-size:var(--fs-xs)}
.tutorial-controls button:hover{border-color:var(--accent-dim);color:var(--ink)}
.tutorial-timeline{display:flex;gap:var(--sp-1);padding:var(--sp-2) var(--sp-3);border-top:1px solid var(--line);background:var(--bg-inset);overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch}
.tutorial-timeline::-webkit-scrollbar{height:0}
/* 토글 자체는 작지만 턴치 적중 영역을 24px 이상으로 담보한다(SC 2.5.8). */
.tutorial-timeline button{flex-shrink:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--sp-1);border:0;background:none;padding:var(--sp-1) var(--sp-1);min-width:var(--touch-min);min-height:var(--touch-min)}
.tutorial-timeline button span{width:26px;height:4px;border-radius:2px;background:var(--line)}
.tutorial-timeline button.done span{background:var(--accent-dim)}
.tutorial-timeline button.active span{background:var(--accent)}
.tutorial-timeline button small{font-family:var(--mono);font-size:var(--fs-3xs);color:var(--ink-faint)}
.tutorial-timeline button.active small{color:var(--accent)}
@keyframes live-pulse{0%{box-shadow:0 0 0 0 rgba(51,209,137,.55)}70%{box-shadow:0 0 0 7px rgba(51,209,137,0)}100%{box-shadow:0 0 0 0 rgba(51,209,137,0)}}

/* ---- CadSim 엔진이 동적으로 생성하는 DOM (cad-sim.js) — 다크 고정(가독성 유지) ----
   [유동 토큰 적용 제외 구간]
   아래 .sim-* 의 font-size 는 SVG viewBox 사용자 단위다. SVG 는 부모 상자 폭에
   맞춰 이미 기하학적으로 스케일링되므로(=텍스트도 함께 커짐), 여기에 clamp/vw 를
   넣으면 이중 스케일링이 되어 도면 배치가 깨진다. 의도적으로 px 을 유지한다. */
.cad-sim,.sim-stage{position:relative;background:#0c1218}
.sim-svg{display:block;width:100%;height:auto;background:radial-gradient(circle at 50% 40%,#0e161f 0%,#0c1218 70%)}
.sim-grid .g-min{stroke:#182029;stroke-width:.6}
.sim-grid .g-maj{stroke:#212b37;stroke-width:.9}
.sim-ucs .ux{stroke:#ff5d6c;stroke-width:1.6;fill:none}
.sim-ucs .uy{stroke:#33d189;stroke-width:1.6;fill:none}
.sim-ucs .ul{font-family:var(--mono);font-size:10.5px;fill:#576375}
.sim-cursor .ch{stroke:#c9d4e0;stroke-width:1;opacity:.85}
.sim-cursor .pickbox{fill:none;stroke:#c9d4e0;stroke-width:1.1}
.sim-coord{font-family:var(--mono);font-size:11px;fill:#576375}

.sim-cmdline{border-top:1px solid #232d3a;background:#10161d;font-family:var(--mono);font-size:12.5px}
.sim-cmd-history{padding:6px 14px 0;display:flex;flex-direction:column;gap:2px;min-height:20px}
.cmd-line{color:#576375;font-size:11.5px}
.cmd-line.echo{color:#8695a8}
.cmd-line.sys{color:#4fd1ff}
.cmd-line.ok{color:#33d189}
.sim-cmd-live{display:flex;align-items:center;gap:7px;padding:7px 14px 9px}
.cmd-label{color:var(--accent);font-weight:700;flex-shrink:0}
.cmd-text{color:#e7edf4;flex:1}
.cmd-caret{width:7px;height:14px;background:var(--accent);display:inline-block;animation:caret-blink 1s step-end infinite}
@keyframes caret-blink{0%,50%{opacity:1}50.01%,100%{opacity:0}}

.band-line,.band-circle{stroke:#4fd1ff;stroke-width:1.3;stroke-dasharray:5 4;fill:none}
.band-win{stroke:#4a8cff;stroke-width:1.2;fill:rgba(74,140,255,.08)}
.band-cross{stroke:#33d189;stroke-width:1.2;stroke-dasharray:4 3;fill:rgba(51,209,137,.08)}
.click-ripple{stroke:var(--accent);stroke-width:1.4;fill:none}
.click-mark{fill:var(--accent)}
.grip{fill:#4a8cff;stroke:#0c1218;stroke-width:1}
.grip:hover{fill:#4fd1ff}

.ent{stroke:#c3ccd8;stroke-width:1.7;fill:none;transition:stroke .15s}
.ent.is-sel{stroke:#4fd1ff;stroke-dasharray:4 3}
.ent-strong{stroke:var(--accent);stroke-width:2.1}
.ent-strong.is-sel{stroke:#4fd1ff}
.ent-copy{stroke:#33d189;stroke-width:1.7}
.ent-aux{stroke:#576375;stroke-width:1.1;stroke-dasharray:2 3}
.ent-xref{stroke:#8b7bc4;stroke-width:1.3;stroke-dasharray:8 3 2 3}
.ent-terrain{stroke:#c98b4c;stroke-width:1.5;stroke-dasharray:6 3}
.ent-text{fill:#dbe3ec;font-family:var(--sans)}
.ly-center{stroke:#ff7ab8!important;stroke-dasharray:10 3 2 3}
.ly-dim{stroke:#4fd1ff!important}
.ly-off{opacity:.28}

.dim-ext{stroke:#576375;stroke-width:1}
.dim-line{stroke:#8695a8;stroke-width:1.1}
.dim-arrow{fill:#8695a8}
.dim-text{fill:#e7edf4;font-family:var(--mono);font-size:12px}

.ent-hatch{fill:url(#sim-hatch);stroke:#576375;stroke-width:1}
.hatch-line{stroke:#576375;stroke-width:1}
.pt-dot{fill:var(--accent)}

.sim-note text{font-family:var(--sans);font-size:12px;fill:#e7edf4}
.note-bg{fill:#151d27;stroke:#232d3a;stroke-width:1}
.sim-note circle{fill:var(--accent)}

.sim-panel .panel-bg{fill:#151d27;stroke:#232d3a;stroke-width:1}
.sim-panel .panel-head{fill:#0c1218}
.sim-panel .panel-title{fill:#4fd1ff;font-family:var(--mono);font-size:12px;font-weight:700}
.sim-panel .panel-row{fill:#8695a8;font-family:var(--sans);font-size:11.5px}
.sim-panel .panel-row.on{fill:#33d189}
.sim-panel .panel-val{fill:#e7edf4;font-family:var(--mono);font-size:11px}

/* ==========================================================================
   AI 패널
   ========================================================================== */
/* height:100vh 는 모바일 주소창 때문에 패널이 화면보다 커지고, 그 결과
   하단 입력창(#ai-form)이 화면 밖으로 밀려나가 질문을 아예 못 하게 된다.
   dvh 로 실제 보이는 높이만 사용한다. */
.ai-panel{position:fixed;top:0;right:-420px;width:400px;max-width:92vw;height:100vh;height:100dvh;background:var(--bg-panel);border-left:1px solid var(--line);z-index:120;display:flex;flex-direction:column;transition:right .25s ease;box-shadow:var(--shadow-lg);overscroll-behavior:contain}
.ai-panel.open{right:0}
.ai-panel>header{display:flex;align-items:center;gap:var(--sp-2);padding:var(--sp-4);padding-top:calc(var(--sp-4) + var(--safe-t));border-bottom:1px solid var(--line);background:var(--bg-inset)}
.ai-panel>header>div{display:flex;align-items:center;gap:var(--sp-2);flex:1;min-width:0}
.ai-panel>header .spark{color:var(--accent);font-size:var(--fs-lg);flex-shrink:0}
.ai-panel>header strong{display:block;font-size:var(--fs-md)}
.ai-panel>header small{color:var(--ink-faint);font-size:var(--fs-2xs)}
.ai-panel>header button{border:0;background:none;color:var(--ink-faint);font-size:var(--fs-xl);width:var(--touch);height:var(--touch);flex-shrink:0;border-radius:999px;display:inline-flex;align-items:center;justify-content:center}
.ai-panel>header button:hover{background:var(--bg-hover);color:var(--ink)}
.ai-messages{flex:1;overflow-y:auto;padding:var(--sp-4);display:flex;flex-direction:column;gap:var(--sp-3);overscroll-behavior:contain;-webkit-overflow-scrolling:touch}
.message{display:flex;gap:var(--sp-2);align-items:flex-start;min-width:0}
.message.user{flex-direction:row-reverse}
.message p{margin:0;background:var(--bg-elevated);border:1px solid var(--line);border-radius:var(--radius-sm);padding:var(--sp-3);font-size:var(--fs-sm);color:var(--ink-dim);max-width:100%;min-width:0;overflow-wrap:anywhere}
.message.user p{background:color-mix(in srgb,var(--accent) 10%,transparent);border-color:var(--accent-dim);color:var(--ink)}
.message>span{flex-shrink:0;color:var(--accent);font-size:var(--fs-sm);margin-top:var(--sp-1)}
.message.thinking{opacity:.7}
.message.thinking span{animation:pulse-dot 1.2s infinite}
@keyframes pulse-dot{0%,100%{opacity:.4}50%{opacity:1}}
.ai-mode{display:block;width:max-content;margin-bottom:var(--sp-2);padding:3px var(--sp-2);border-radius:999px;background:color-mix(in srgb,var(--accent-blue) 12%,transparent);color:var(--accent-blue);font-weight:700;font-size:var(--fs-3xs)}
.result-links{display:flex;flex-wrap:wrap;gap:var(--sp-1);margin-top:var(--sp-2)}
.result-links button{border:1px solid var(--line);background:var(--bg-panel);color:var(--accent-cyan);border-radius:999px;min-height:32px;padding:var(--sp-1) var(--sp-3);font-size:var(--fs-2xs)}

/* ==== AI 도움말 카드 =====================================================
   [반례 주의] 위쪽 `.message p` 규칙이 배경·테두리·패딩을 가지므로,
   카드 안의 <p> 가 그 스타일을 그대로 물려받아 '카드 안에 말풍선'이 생긴다.
   따라서 .ai-answer 하위의 p 는 먼저 리셋한 뒤 다시 스타일링한다. */
.ai-answer{min-width:0;flex:1;display:flex;flex-direction:column;gap:var(--sp-2)}
.ai-answer p{margin:0;background:none;border:0;border-radius:0;padding:0;font-size:inherit;color:inherit;max-width:none}
.help-card-stack{display:flex;flex-direction:column;gap:var(--sp-2);min-width:0}
.help-card{background:var(--bg-elevated);border:1px solid var(--line);border-radius:var(--radius-sm);padding:var(--sp-3);min-width:0;overflow-wrap:anywhere}
.help-card.is-summary{background:color-mix(in srgb,var(--accent-blue) 6%,var(--bg-elevated));border-color:var(--accent-dim)}
.hcard-head{display:flex;flex-direction:column;gap:var(--sp-1);margin-bottom:var(--sp-2)}
.hcard-id{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-2);min-width:0}
.hcard-id>code{font-family:var(--mono);font-size:var(--fs-sm);font-weight:700;color:var(--accent-cyan);background:none;padding:0}
.hcard-sum-tag{font-size:var(--fs-3xs);font-weight:700;letter-spacing:.04em;color:var(--accent-blue);text-transform:uppercase}
.hcard-head h4{margin:0;font-size:var(--fs-md);font-weight:600;color:var(--ink);line-height:1.35}
.hcard-badge{flex-shrink:0;padding:2px var(--sp-2);border-radius:999px;font-size:var(--fs-3xs);font-weight:700;border:1px solid var(--line);color:var(--ink-faint)}
/* 등급별 색: 전용/별칭/계열은 안전(초록계), 부분은 주의(노랑), 일반은 경고(주황) */
.hcard-badge[data-fidelity="exact"],
.hcard-badge[data-fidelity="alias"],
.hcard-badge[data-fidelity="family"]{color:var(--accent-green);border-color:color-mix(in srgb,var(--accent-green) 45%,transparent);background:color-mix(in srgb,var(--accent-green) 10%,transparent)}
/* 부분 재현(alias-only)은 '주의' 수준, 전용 재현 없음(generic)은 더 강한 경고 수준.
   두 등급이 시각적으로 같아 보이면 배지의 의미가 사라지므로 채도로 구분한다. */
.hcard-badge[data-fidelity="alias-only"]{color:var(--accent-amber);border-color:color-mix(in srgb,var(--accent-amber) 40%,transparent);background:color-mix(in srgb,var(--accent-amber) 9%,transparent)}
.hcard-badge[data-fidelity="generic"]{color:var(--accent-amber);border-color:var(--accent-amber);background:color-mix(in srgb,var(--accent-amber) 22%,transparent);font-weight:800}
.hcard-caution{margin:0 0 var(--sp-2);padding:var(--sp-2);border-left:3px solid var(--accent-amber);border-radius:var(--radius-sm);background:color-mix(in srgb,var(--accent-amber) 8%,transparent);font-size:var(--fs-2xs);color:var(--ink-dim)}
.hcard-body{display:flex;flex-direction:column;gap:var(--sp-3);min-width:0}
.hcard-sec{min-width:0}
.hcard-sec h5{margin:0 0 var(--sp-1);font-size:var(--fs-3xs);font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-faint)}
.hcard-text{font-size:var(--fs-sm);line-height:1.6;color:var(--ink-dim)}
.hcard-list{margin:0;padding-left:1.1em;display:flex;flex-direction:column;gap:2px}
.hcard-list li{font-size:var(--fs-xs);line-height:1.55;color:var(--ink-dim)}
.hcard-code{display:block;font-family:var(--mono);font-size:var(--fs-xs);color:var(--accent-green);background:var(--bg-panel);border:1px solid var(--line-soft);border-radius:var(--radius-sm);padding:var(--sp-2)}
.hcard-steps{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--sp-2);counter-reset:none}
.hcard-steps li{display:grid;grid-template-columns:auto minmax(0,1fr);gap:var(--sp-2);align-items:start;min-width:0}
.hcard-n{font-family:var(--mono);font-size:var(--fs-3xs);font-weight:700;color:var(--accent-blue);background:color-mix(in srgb,var(--accent-blue) 12%,transparent);border-radius:var(--radius-sm);padding:2px var(--sp-1);line-height:1.5}
.hcard-steps li>div{min-width:0;display:flex;flex-direction:column;gap:3px}
.hcard-steps li p{font-size:var(--fs-xs);line-height:1.55;color:var(--ink-dim)}
.hcard-steps li code{font-family:var(--mono);font-size:var(--fs-2xs);color:var(--accent-cyan);background:var(--bg-panel);border:1px solid var(--line-soft);border-radius:var(--radius-sm);padding:2px var(--sp-2);overflow-wrap:anywhere}
.hcard-foot{margin-top:var(--sp-3);display:flex}
.hcard-foot button{flex:1;min-height:var(--touch);border:1px solid var(--line);background:var(--bg-panel);color:var(--accent-cyan);border-radius:var(--radius-sm);font-size:var(--fs-2xs);font-weight:600;cursor:pointer;transition:background .15s,border-color .15s}
.hcard-foot button:hover{background:var(--bg-hover);border-color:var(--accent-dim)}
/* 480px 미만: 단계 번호를 위로 올려 본문 폭을 최대한 확보한다. */
@media (max-width:479.98px){
  .hcard-steps li{grid-template-columns:minmax(0,1fr)}
  .hcard-n{width:max-content}
}
.ai-suggestions{display:flex;flex-wrap:wrap;gap:var(--sp-2);padding:0 var(--sp-4) var(--sp-3)}
.ai-suggestions button{border:1px solid var(--line);background:var(--bg-elevated);color:var(--ink-dim);border-radius:999px;min-height:34px;padding:var(--sp-1) var(--sp-3);font-size:var(--fs-2xs)}
/* 입력바는 판넬 하단에 고정된다. 제스처 인디타 분만큼 띄우지 않으면
   전속 버튼이 홈바에 가린다. */
#ai-form{display:flex;gap:var(--sp-2);padding:var(--sp-3) var(--sp-4);border-top:1px solid var(--line);background:var(--bg-inset)}
#ai-input{flex:1;min-width:0;resize:none;border:1px solid var(--line);background:var(--bg-panel);border-radius:var(--radius-sm);padding:var(--sp-2) var(--sp-3);color:var(--ink);font-size:var(--fs-sm)}
#ai-input:focus{outline:none;border-color:var(--accent-dim)}
#ai-form button{flex-shrink:0;width:var(--touch);min-height:var(--touch);border:1px solid var(--accent-dim);background:color-mix(in srgb,var(--accent) 10%,transparent);color:var(--accent-text);border-radius:var(--radius-sm);font-size:var(--fs-lg)}
.ai-disclaimer{margin:0;padding:0 var(--sp-4) calc(var(--sp-4) + var(--safe-b));font-size:var(--fs-3xs);color:var(--ink-faint)}

.panel-backdrop{position:fixed;inset:0;background:rgba(4,7,11,.6);opacity:0;pointer-events:none;transition:opacity .2s;z-index:110}
.panel-backdrop.open{opacity:1;pointer-events:auto}

/* 하단 명령행바 + 제스처 인디타 위에 띄워야 토스트가 가리지 않는다. */
#toast{position:fixed;bottom:calc(72px + var(--safe-b));left:50%;transform:translate(-50%,12px);max-width:calc(100vw - 2 * var(--sp-4));background:var(--bg-elevated);border:1px solid var(--line);color:var(--ink);padding:var(--sp-2) var(--sp-4);border-radius:999px;font-size:var(--fs-xs);text-align:center;opacity:0;pointer-events:none;transition:.2s;z-index:200;box-shadow:var(--shadow-md)}
#toast.show{opacity:1;transform:translate(-50%,0)}

/* ---- 테마 전환 안내 배지 ----
   [실측 근거 — 스크린샷으로 발견, 측정으로 확정]
   이전 규칙은 bottom:-18px 로 #theme-toggle 버튼 아래에 라벨을 붙였다.
   그러나 버튼 하단(49.5px)이 이미 타이틀바 높이에 가까워, 라벨이 타이틀바
   경계를 넘어 그 아래 리본(z-index:55) 위에 떠 있었다. 전 폭에서 재현된다:
     vw= 320 label 51.0~66.5 | titlebarBot=56 → 넘침 10.5px
     vw= 390 label 50.9~66.5 | titlebarBot=56 → 넘침 10.5px
     vw= 480 label 50.7~66.5 | titlebarBot=56 → 넘침 10.5px
     vw= 640 label 52.4~68.5 | titlebarBot=60 → 넘침  8.5px
     vw=1280 label 51.5~68.5 | titlebarBot=60 → 넘침  8.5px
   (리본 탭 '텍스트'와 겹치지는 않으므로 가독성 파괴는 아니지만,
    타이틀바 소속 라벨이 다른 영역 위에 떠 있어 소속이 오독된다.)

   반례 검토 — '라벨을 지우면 테마명을 알 수 없다'?
   applyTheme() 은 이미 toast(`테마 변경: ${theme.label}`) 를 띄운다(app.js L693).
   즉 전환 순간의 안내는 토스트가 담당하므로 상시 표시는 중복이다.
   → 평시에는 숨기고, 버튼에 hover/focus 했을 때만 타이틀바 '안쪽'에 보인다.
     터치 기기는 hover 가 없으므로 :focus-visible 과 토스트가 경로를 보장한다. */
#theme-name{
  position:absolute;
  /* 버튼 아래(-18px)가 아니라 버튼 위쪽 안전 지대에 둔다. 타이틀바를 넘지 않는다. */
  top:50%;right:calc(100% + var(--sp-2));transform:translateY(-50%);
  font-family:var(--mono);font-size:var(--fs-3xs);color:var(--ink-faint);
  white-space:nowrap;letter-spacing:.04em;pointer-events:none;
  padding:2px var(--sp-2);border-radius:999px;
  background:var(--bg-elevated);border:1px solid var(--line);
  opacity:0;transition:opacity .15s ease;
}
#theme-toggle:hover #theme-name,
#theme-toggle:focus-visible #theme-name{opacity:1}
/* 좁은 화면에서는 왼쪽에 놓을 자리가 없다(로고·제목과 충돌) → 표시하지 않는다.
   토스트가 테마명을 알려주므로 정보 손실은 없다. */
@media (max-width:479.98px){
  #theme-name{display:none}
}

/* ==========================================================================
   테마 1 — BLUEPRINT (청사진/CAD 다크) — 기본값, :root 그대로 사용
   장식: 옅은 청사진 격자
   ========================================================================== */
body[data-theme="blueprint"],
body:not([data-theme]){
  --bg-decor:
    linear-gradient(rgba(79,209,255,.05) 1px,transparent 1px) 0 0/48px 48px,
    linear-gradient(90deg,rgba(79,209,255,.05) 1px,transparent 1px) 0 0/48px 48px;
}
body[data-theme="blueprint"] .ucs-icon,body:not([data-theme]) .ucs-icon{opacity:.9}

/* ==========================================================================
   테마 2 — MONO (모노크롬 미니멀) — 화이트/블랙, 필 형태, 매우 여유로운 여백
   ========================================================================== */
body[data-theme="mono"]{
  --bg:#ffffff;
  --bg-panel:#ffffff;
  --bg-elevated:#f7f7f8;
  --bg-inset:#f1f1f2;
  --bg-hover:#ececed;
  --line:#e3e3e5;
  --line-soft:#ececed;
  --ink:#0a0a0a;
  --ink-dim:#59595d;
  --ink-faint:#6e6e71;   /* was #9a9a9e — 가장 밝은 배경(--bg-inset #f1f1f2) 기준 2.62:1 → 4.50:1 */
  --accent:#0a0a0a;
  --accent-dim:#3a3a3c;
  --accent-cyan:#40515f;
  --accent-green:#22684a;
  --accent-red:#8c2f39;
  --accent-blue:#31456e;
  --accent-amber:#0a0a0a;
  --radius:22px;
  --radius-sm:14px;
  --shadow-lg:0 24px 55px rgba(10,10,10,.08);
  --shadow-md:0 10px 26px rgba(10,10,10,.07);
  --shadow-sm:0 2px 8px rgba(10,10,10,.05);
  --card-blur:none;
  --bg-decor:none;
}
body[data-theme="mono"] .ai-button{color:#fff}
body[data-theme="mono"] .ribbon-tab.active{color:#fff}
body[data-theme="mono"] .category-chips button.active{color:#fff}
body[data-theme="mono"] .ucs-icon,body[data-theme="mono"] .layer-pill{opacity:.35}
body[data-theme="mono"] ::selection{background:rgba(10,10,10,.12)}

/* ==========================================================================
   테마 3 — NEON (네온 글래스모피즘) — 다크 + 보라/시안 글로우 + 블러 카드
   ========================================================================== */
body[data-theme="neon"]{
  --bg:#0a0b16;
  --bg-panel:#151732;
  --bg-elevated:#1b1e3c;
  --bg-inset:#08091380;
  --bg-hover:#242861;
  --line:#31336a;
  --line-soft:#24274f;
  --ink:#eef0ff;
  --ink-dim:#a5a9dd;
  --ink-faint:#8084b2;   /* was #6f73a8 — #1b1e3c 상에서 3.62:1 → 4.52:1 */
  --accent:#a06bff;
  --accent-text:#ad80ff;   /* 자기합성 배경 위 최악 4.11:1 → 4.55:1.
                              (1차 #a675ff 는 --bg-elevated #1b1e3c 를 빼고 계산해서 부족했다 —
                               서페이스 하나만 빠져도 검사는 통과하고 화면은 안 읽힌다) */
  --accent-dim:#6b46b3;
  --accent-cyan:#22e0f5;
  --accent-green:#39e6ac;
  --accent-red:#ff5f96;
  --accent-blue:#6f99ff;   /* was #5f8dff — 자기합성 배경 최악 4.07:1 → 4.52:1 */
  --accent-amber:#ffd166;
  --radius:24px;
  --radius-sm:15px;
  --shadow-lg:0 0 90px rgba(160,107,255,.28),0 30px 70px rgba(0,0,0,.5);
  --shadow-md:0 0 34px rgba(160,107,255,.22);
  --shadow-sm:0 0 0 1px rgba(160,107,255,.14),0 4px 16px rgba(0,0,0,.3);
  --card-blur:blur(18px);
  --bg-decor:
    radial-gradient(circle at 15% 8%,rgba(160,107,255,.22) 0%,transparent 45%),
    radial-gradient(circle at 88% 22%,rgba(34,224,245,.16) 0%,transparent 42%),
    radial-gradient(circle at 50% 95%,rgba(57,230,172,.12) 0%,transparent 45%);
}
body[data-theme="neon"] .viewport-panel,
body[data-theme="neon"] .learning-card,
body[data-theme="neon"] .command-card,
body[data-theme="neon"] .course-row,
body[data-theme="neon"] .pgp-stats article,
body[data-theme="neon"] .ai-panel,
body[data-theme="neon"] .command-dialog{background:rgba(21,23,50,.62);backdrop-filter:blur(18px);border-color:rgba(160,107,255,.22)}
body[data-theme="neon"] .ribbon-tabs{background:rgba(21,23,50,.55);backdrop-filter:blur(12px)}
body[data-theme="neon"] .ribbon-tab.active{background:linear-gradient(135deg,var(--accent),var(--accent-blue));color:#fff}
body[data-theme="neon"] .ai-button{background:linear-gradient(135deg,var(--accent),var(--accent-cyan));color:#0a0b16}

/* ==========================================================================
   테마 4 — PASTEL (플레이풀 파스텔) — 라벤더/핑크, 큰 라운드, 부드러운 그림자
   ========================================================================== */
body[data-theme="pastel"]{
  --bg:#fdf6fb;
  --bg-panel:#ffffff;
  --bg-elevated:#fff9fc;
  --bg-inset:#f6ecf9;
  --bg-hover:#f1e2f6;
  --line:#efd9ee;
  --line-soft:#f5e6f4;
  --ink:#3d2c4a;
  --ink-dim:#7c6a8c;
  --ink-faint:#76687f;   /* was #ac98ba — 가장 밝은 배경(--bg-inset #f6ecf9) 기준 2.54:1 → 4.51:1 */
  /* [실산 근거] --accent 는 전경 글자(18곳)와 버튼 배경(23곳)을 겸한다.
     이전 #ff8fb3 는 카드 배경(#ffffff) 상 글자로 2.14:1,
     동시에 .ask-ai-button 배경으로 쓰일 때 그 위 글자(var(--bg)=#fdf6fb)가
     2.01:1 로 두 경로 모둘 AA 반이했다(배경 경로는 이전에 검사하지도 않았다).
     두 조건을 동시에 만족하는 명도를 탐색해 상색을 유지한 값을 찾았다:
       글자 on 카드 4.86:1 / 버튼글자 on 배경 4.57:1 */
  --accent:#a35c73;
  --accent-text:#8a4e62;   /* 자기합성 배경(10~14%) 위 최악 3.57:1 → 4.52:1 */
  --accent-dim:#e06f95;
  --accent-cyan:#326e77; /* was #5bc7d6 → #3a7f89 → 현재. '상세 화면 열기' 버튼 글자 1.98:1,
                            다시 .cmdseq-list code(8% 자기합성) 경로에서 3.64:1 → 4.54:1 */
  /* [실재 근거] 이전 #7bd4a3 는 상태표시줄 GRID/SNAP 필, .done-toggle.on,
     .confidence-manual 배지 글자로 쓰이며 자기합성 배경(6~16%) 위에서
     가장 나쁘게 1.44:1 — 사실상 안 보이는 상태여서 초록 필이 '촜진 배경상자'
     쯤럼 보이기만 하고 글자는 읽힐 수 없었다.
     이 토큰은 color: 9곳 / background: 1곳(장식 그라디언트) — 사실상
     전경색 전용이다. --accent 처럼 '버튼 배경 위 글자' 이중 제약이 없으므로
     토큰 자신을 낮추는 것이 안전하다(새 토큰 신설 불필요).
     불통명 배경 4종 × 합성 4종의 최악값이 AA 를 넘는 가장 밝은 해: 1.44:1 → 4.53:1 */
  --accent-green:#236a43;
  --accent-red:#ff8a8a;
  /* was #8ea3ff — 최악 1.85:1(사실상 안 보임). 채도를 유지한 최명해는
     #113dff 로 파스텔 톤과 너무 어긋나서, 채도를 0.45 로 낮춘 해를 썬다. */
  --accent-blue:#4357b1;   /* 최악 1.85:1 → 4.51:1 */
  --accent-amber:#725200;  /* was #ffb703 — .hcard-badge[alias-only](9%)·[generic](22%) 최악 1.37:1 → 4.51:1 */
  --radius:26px;
  --radius-sm:18px;
  --shadow-lg:0 26px 60px rgba(190,140,200,.22);
  --shadow-md:0 12px 30px rgba(190,140,200,.18);
  --shadow-sm:0 3px 12px rgba(190,140,200,.14);
  --card-blur:none;
  --bg-decor:
    radial-gradient(circle at 10% 10%,rgba(255,143,179,.16) 0%,transparent 40%),
    radial-gradient(circle at 92% 18%,rgba(142,163,255,.14) 0%,transparent 40%),
    radial-gradient(circle at 40% 96%,rgba(123,212,163,.13) 0%,transparent 42%);
}
body[data-theme="pastel"] .app-titlebar,body[data-theme="pastel"] .ribbon{background:#fff}
body[data-theme="pastel"] .ai-button{color:#fff}
body[data-theme="pastel"] .ribbon-tab.active{color:#fff}
body[data-theme="pastel"] .category-chips button.active{color:#fff}
body[data-theme="pastel"] .ucs-icon,body[data-theme="pastel"] .layer-pill{opacity:.5}

/* ==========================================================================
   테마 5 — WARM (웜 프로페셔널) — 크림/테라코타, 중간 라운드, 차분한 그림자
   ========================================================================== */
body[data-theme="warm"]{
  --bg:#faf5ee;
  --bg-panel:#ffffff;
  --bg-elevated:#fff9f0;
  --bg-inset:#f2e9db;
  --bg-hover:#ede1cc;
  --line:#e6d7bf;
  --line-soft:#efe3cf;
  --ink:#2f2115;
  --ink-dim:#6f5c44;
  --ink-faint:#766854;   /* was #9c8a6f — 가장 밝은 배경(--bg-inset #f2e9db) 기준 3.20:1 → 4.50:1 */
  --accent:#aa5c29;      /* was #c1682f — 글자on카드 3.97→4.94:1 / 버튼글자on배경 3.66→4.55:1 */
  --accent-text:#8c4b22;   /* 자기합성 배경(10/14/16%) 위 최악 3.45:1 → 4.56:1.
                              16% 합성(.confidence-inferred)을 빠뜨렸을 때는 4.49:1 로
                              경계에서 미달했다 — 합성비율도 전수로 넣어야 한다. */
  --accent-dim:#8f4d22;
  --accent-cyan:#306c68; /* was #3f8f8a → #39817c → 현재. 자기합성 경로 최악 3.47:1 → 4.53:1 */
  --accent-green:#406443;   /* was #5f9463 — 자기합성 배경 최악 2.53:1 → 4.51:1 */
  --accent-red:#bd4a3d;
  --accent-blue:#405b87;   /* was #4d6ea3 — 최악 3.53:1 → 4.56:1 */
  --accent-amber:#7a4914;  /* was #d98324 — 최악 2.02:1 → 4.52:1 */
  --radius:16px;
  --radius-sm:11px;
  --shadow-lg:0 26px 60px rgba(70,45,15,.14);
  --shadow-md:0 12px 30px rgba(70,45,15,.11);
  --shadow-sm:0 3px 12px rgba(70,45,15,.08);
  --card-blur:none;
  --bg-decor:none;
}
body[data-theme="warm"] .ai-button{color:#fff}
body[data-theme="warm"] .ribbon-tab.active{color:#fff}
body[data-theme="warm"] .category-chips button.active{color:#fff}
body[data-theme="warm"] .ucs-icon,body[data-theme="warm"] .layer-pill{opacity:.55}

/* ==========================================================================
   반응형
   ========================================================================== */
/* 브레이크포인트 체계 — :root 의 --bp-* 와 1:1 대응하는 단일 출처.
   xs 360 / sm 480 / md 640 / lg 960 / xl 1280
   레이아웃 기본값(미디어쿼리 밖)은 이미 auto-fit·clamp·flex-wrap 으로
   유동적이므로, 아래 블록은 '유동만으로 못 푸는 구조 변경'만 담는다.
   상향(min-width) 규칙과 하향(max-width) 규칙을 섞지 않고 구간을 나눈다.
   경계 중복을 막기 위해 max 쪽은 .98px 을 뺀다(639.98 / 959.98). */

/* ---- xs: 360px 이하 (아주 좁은 폰) ---- */
@media (max-width:359.98px){
  /* 이 폭에선 통계는 사치다. 명령 검색이라는 본질에 공간을 준다.
     (.app-sub 숨김은 640px 미만 블록으로 통합 — 중복 제거) */
  .layer-pill{display:none}
  .ribbon-tab{padding:var(--sp-2) var(--sp-2)}
  /* 별칭/완료 토글 텍스트를 지우고 아이콘만 남긴다(폭 절약, 타깃 크기는 유지) */
  .fav-toggle,.done-toggle{padding:var(--sp-1) var(--sp-2)}
  /* .status-sep 숨김은 640px 미만 블록으로 통합됨(중복 제거) */
}

/* ---- sm 이하: 480px (일반 폰 세로) ---- */
@media (max-width:479.98px){
  /* .app-sub 숨김은 640px 미만 블록으로 이동(아래 실측 근거 참조) */
  /* 홈 히어로의 코스 행은 이 폭에서 버튼이 텍스트를 밀어낸다 → 세로로 쌓는다 */
  .course-row{flex-wrap:wrap}
  .course-row button{width:100%;justify-content:center}
  .progress-summary{gap:var(--sp-4)}
  .tutorial-controls .play-button{order:-1;flex-basis:100%}
}

/* ---- md 이하: 640px (폰 전반) ---- */
@media (max-width:639.98px){
  /* AI 패널을 전면 시트로. 오른쪽 슬라이드 오프셋도 폭에 맞춰 100vw 로 바꿔야
     닫힌 상태에서 화면에 걸쳐 보이는 잔상이 생기지 않는다. */
  .ai-panel{width:100vw;max-width:100vw;right:-100vw}
  .ai-panel.open{right:0}
  /* ---- 타이틀바 부제 --------------------------------------------------
     [실측 근거] 480px 에서 문서 오버플로우 70px 이 재현됐다. 폭을 1px 단위로
     훑은 결과 임계가 정확히 480px 이었다:
       479px → over=0   docSW=479   (.app-sub 숨김)
       480px → over=70  docSW=550   (.app-sub 표시)
       520px → over=31  docSW=551
       560px → over=0   docSW=560
     docSW 가 480~520 구간에서 550 으로 '고정'된 것이 핵심 단서다. 즉
     "뷰포트가 좁아서 넘친" 것이 아니라 부제가 보이는 타이틀바의 자연 폭이
     550px 이고, 숨김 임계값(480px)이 그보다 작게 잡혀 있던 것이다.
     넘친 주체는 header.app-titlebar > div.titlebar-actions(+37px)였다.

     [반례로 배제한 원인] 그룹 A 의 요소 스캔은 같은 실행에서 '문서를 넓히는
     요소 0개'를 보고했다. 오프캔버스 .ai-panel(fixed) 이 원인이라는 추정은
     따라서 성립하지 않는다 — 440~479px 에서도 maxRight 는 뷰포트의 2배였지만
     오버플로우는 0 이었다.

     임계값을 550px 로 두면 브레이크포인트 체계에 없는 새 경계가 생긴다.
     기존 --bp-md(640px) 로 올려 통합한다(561~639 구간은 넘치지 않지만,
     그 구간에서 부제를 지우는 비용이 임의 경계를 추가하는 비용보다 작다). */
  .app-sub{display:none}
  /* 리본 통계는 탭 스크롤 공간을 잡아먹는다 */
  .ribbon-stats{display:none}
  .detail-overview{grid-template-columns:1fr}
  /* 목록 카드를 항상 1열로: 250px 2열은 이 폭에서 글자가 2~3자씩 잘린다 */
  .command-grid{grid-template-columns:1fr}
  .learning-grid{grid-template-columns:1fr}

  /* ---- 상태표시줄 정리 ------------------------------------------------
     [실측] 320px 에서 statusbar scrollWidth=595 / clientWidth=320 → 275px 초과.
     640px 미만 전 구간에서 넘쳤다(360:261, 390:232, 480:144, 640:0).
     그런데 overflow-x:auto + scrollbar-width:none 이라 스크롤 단서가 없어,
     유일한 실제 정보인 #status-count(항목 로드 수, app.js L802)가
     left=464px 위치로 영구히 보이지 않았다.

     [반례로 확인한 것] "overflow-x:auto 니까 스크롤하면 보인다"는 반론은
     성립하지 않는다. 스크롤바를 숨겼고 터치 힌트도 없어 그 콘텐츠의
     존재 자체가 사용자에게 전달되지 않는다.

     정리 기준은 '정보량'이다:
     - GRID/SNAP/ORTHO(149px): 클릭 핸들러 0건인 순수 장식 → 숨김
     - #status-context(133px): 활성 리본 탭과 중복 → 숨김
     - #status-count(119px): 유일한 실데이터 → 남기고 왼쪽 정렬로 되돌린다
       (형제가 사라지면 margin-left:auto 는 의미가 없다) */
  .status-toggle{display:none}
  #status-context{display:none}
  .status-sep{display:none}
  .status-right{margin-left:0}
  /* 남은 항목이 그래도 넘칠 경우(긴 문구) 잘림 대신 줄임표로 알린다.
     nowrap 을 유지하려면 flex 아이템에 min-width:0 을 줘야 ellipsis 가 작동한다. */
  #status-count{min-width:0;overflow:hidden;text-overflow:ellipsis;display:block}
}

/* ---- lg 이하: 960px (태블릿) ---- */
@media (max-width:959.98px){
  .ribbon-stats{display:none}
  .detail-overview{grid-template-columns:repeat(auto-fit,minmax(min(240px,100%),1fr))}
}

/* ---- xl 이상: 1280px (데스크톱) — 여유 폭을 실제로 쓴다 ---- */
@media (min-width:1280px){
  .detail-body{gap:var(--sp-7)}
  .command-grid{grid-template-columns:repeat(auto-fill,minmax(min(270px,100%),1fr))}
}

/* ---- 가로 모드 폰: 높이가 극단적으로 낮은 경우 ---- */
@media (max-height:520px) and (orientation:landscape){
  /* 세로 공간이 없으면 스테이지가 화면 전부를 먹고 명령행이 안 보인다 */
  .hero-sim-stage{min-height:150px}
  .cad-stage{min-height:180px}
  .viewport-copy{padding-top:var(--sp-4)}
}

/* ---------------------------------------------------------------- 모션 감소 선호 대응 */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important;scroll-behavior:auto!important}
}

/* ---------------------------------------------------------------- 업데이트 배너(PWA 자동 업데이트) */
/* 업데이트 배너는 하단 명령행바/제스처 인디케이터 위에 떠야 누를 수 있다. */
#update-banner{position:fixed;left:50%;bottom:calc(var(--sp-6) + var(--safe-b));transform:translate(-50%,20px);max-width:calc(100vw - 2 * var(--sp-4));opacity:0;pointer-events:none;transition:.25s;z-index:250;background:var(--accent);color:var(--bg);border-radius:999px;padding:var(--sp-2) var(--sp-2) var(--sp-2) var(--sp-4);display:flex;align-items:center;gap:var(--sp-3);font-size:var(--fs-xs);font-weight:600;box-shadow:var(--shadow-lg)}
#update-banner.show{opacity:1;transform:translate(-50%,0);pointer-events:auto}
#update-banner button{border:0;background:rgba(0,0,0,.16);color:inherit;border-radius:999px;min-height:36px;padding:var(--sp-2) var(--sp-3);font-weight:700;font-size:var(--fs-xs);flex-shrink:0}

/* ================================================================
   Phase 9 — 핵심 요약(core) · 스크립트 조합기(script)
   ----------------------------------------------------------------
   [대비 규칙 — Phase 8 에서 확정된 것을 그대로 따른다]
   강조색 위에 강조색 글자를 올리는 '자기합성' 배경
     color:var(--accent) + background:color-mix(… var(--accent) N%, transparent)
   은 pastel 4.09:1 / warm 3.45:1 로 AA 미달이었다. 비율 조정으로는
   해결되지 않아(p→0 에서도 4.22:1) 별도 전경 토큰 --accent-text 를 만들었다.
   → 아래 신규 규칙에서 강조 배경 위 글자는 반드시 var(--accent-text) 를 쓴다.
     var(--accent) 를 글자색으로 쓰면 scripts/test_contrast.mjs 그룹 D 가
     자동으로 잡아낸다(CSS 에서 규칙을 직접 추출하므로 등록 불필요).
   ================================================================ */

/* ---- 공용 버튼(Phase 9 에서 처음 필요해졌다) ---- */
.ghost-button{display:inline-flex;align-items:center;justify-content:center;gap:6px;border:1px solid var(--line);background:var(--bg-panel);color:var(--ink-dim);border-radius:999px;min-height:var(--touch);padding:var(--sp-2) var(--sp-4);font-size:var(--fs-xs);font-weight:600;text-decoration:none;transition:.15s}
.ghost-button:hover{color:var(--ink);border-color:var(--accent-dim)}
.ghost-button.sb-mini{min-height:34px;padding:var(--sp-1) var(--sp-3);font-size:var(--fs-2xs)}
.primary-button{display:inline-flex;align-items:center;justify-content:center;gap:6px;border:1px solid var(--accent-dim);background:var(--accent);color:var(--bg);border-radius:999px;min-height:var(--touch);padding:var(--sp-2) var(--sp-6);font-size:var(--fs-xs);font-weight:700}
body[data-theme="mono"] .primary-button,
body[data-theme="pastel"] .primary-button,
body[data-theme="warm"] .primary-button{color:#fff}
.icon-mini{width:32px;height:32px;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--line);background:var(--bg-panel);color:var(--ink-dim);border-radius:8px;font-size:var(--fs-sm);line-height:1}
.icon-mini:disabled{opacity:.35}
.icon-mini.danger:hover{color:var(--accent-red,#ff5d6c);border-color:currentColor}
.empty-note{color:var(--ink-faint);font-size:var(--fs-xs);padding:var(--sp-6);text-align:center;border:1px dashed var(--line);border-radius:var(--radius-sm)}

/* ---- 정직성 고지 박스 ----
   '이 숫자가 무엇이고 무엇이 아닌지' 를 화면에서 먼저 밝히는 블록.
   장식이 아니라 이 프로젝트의 핵심 약속이므로 눈에 띄게 둔다. */
.honesty-note{border:1px solid var(--accent-dim);border-left-width:3px;background:color-mix(in srgb,var(--accent) 6%,transparent);border-radius:var(--radius-sm);padding:var(--sp-4);margin-bottom:var(--sp-6);font-size:var(--fs-xs);line-height:1.65}
.honesty-note strong{display:block;color:var(--accent-text);margin-bottom:var(--sp-2);font-size:var(--fs-sm)}
.honesty-note p{margin:0 0 var(--sp-2);color:var(--ink-dim)}
.honesty-note ul{margin:0;padding-left:1.1em;color:var(--ink-dim)}
.honesty-note li{margin-bottom:4px}
.honesty-note code{font-family:var(--mono);font-size:.94em;color:var(--ink)}
.honesty-note b{color:var(--ink)}
/* 핵심 요약 범위 고지 — 상단 리본 숫자와 이 화면 숫자가 왜 다른지 밝힌다. */
.core-scope-note{margin:0 0 var(--sp-3);font-size:var(--fs-xs);line-height:1.6;color:var(--ink-dim)}
.core-scope-note b{color:var(--ink)}

/* ---- 9-2 핵심 요약 목록 ---- */
.core-list{display:flex;flex-direction:column;gap:var(--sp-2);margin-bottom:var(--sp-6)}
.core-row{display:grid;grid-template-columns:34px minmax(0,1fr) 150px auto;align-items:center;gap:var(--sp-3);border:1px solid var(--line);background:var(--bg-panel);border-radius:var(--radius-sm);padding:var(--sp-3)}
.core-rank{font-family:var(--mono);font-size:var(--fs-xs);color:var(--ink-faint);text-align:center}
.core-main{display:block;text-align:left;border:0;background:none;padding:0;min-width:0;color:inherit}
.core-head{display:flex;align-items:center;gap:var(--sp-2);flex-wrap:wrap;margin-bottom:4px}
.core-cat{font-size:var(--fs-2xs);color:var(--ink-faint);font-family:var(--mono)}
.core-title{display:block;font-weight:600;font-size:var(--fs-sm);color:var(--ink);margin-bottom:2px}
.core-sum{display:block;font-size:var(--fs-xs);color:var(--ink-dim);overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
/* 절차 복잡도 배지(9-3). 기존 토큰만 쓴다 — 새 색을 만들면 test:contrast 가 깨진다.
   '측정 불가'는 낮은 값처럼 보이면 안 되므로 색을 흐리게 하되 점선 밑줄로
   '값이 없다'는 사실 자체를 눈에 보이게 남긴다. */
.core-proc{display:inline-block;margin-top:4px;font-family:var(--mono);font-size:var(--fs-2xs);color:var(--ink-dim)}
.core-proc.unmeasured{color:var(--ink-faint);border-bottom:1px dotted var(--line)}
.core-metric{display:flex;flex-direction:column;gap:4px;min-width:0}
.core-bar{display:block;height:6px;border-radius:3px;background:color-mix(in srgb,var(--ink) 10%,transparent);overflow:hidden}
.core-bar b{display:block;height:100%;background:var(--accent);border-radius:3px}
.core-metric small{font-family:var(--mono);font-size:var(--fs-2xs);color:var(--ink-faint)}
.core-add{flex-shrink:0}

/* ---- 9-1 스크립트 조합기 ---- */
.sb-block{margin-bottom:var(--sp-8)}
.sb-h{display:flex;align-items:center;gap:var(--sp-3);flex-wrap:wrap;font-size:var(--fs-md);margin:0 0 var(--sp-3);color:var(--ink)}
.sb-h small{font-family:var(--mono);font-size:var(--fs-2xs);color:var(--ink-faint);font-weight:400}

.sb-presets{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:var(--sp-3)}
.sb-preset{text-align:left;border:1px solid var(--line);background:var(--bg-panel);border-radius:var(--radius-sm);padding:var(--sp-3);min-height:var(--touch);color:var(--ink);display:flex;flex-direction:column;gap:6px}
.sb-preset:hover{border-color:var(--accent-dim)}
.sb-preset strong{font-size:var(--fs-sm)}
.sb-preset small{color:var(--ink-dim);font-size:var(--fs-xs)}
.sb-preset-cmds{display:flex;flex-wrap:wrap;gap:4px}
.sb-preset-cmds code{font-family:var(--mono);font-size:var(--fs-2xs);color:var(--accent-text);background:color-mix(in srgb,var(--accent) 12%,transparent);border-radius:6px;padding:2px 6px}

.sb-picker{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:var(--sp-2);max-height:340px;overflow-y:auto;padding:2px}
.sb-pick{text-align:left;border:1px solid var(--line);background:var(--bg-panel);border-radius:var(--radius-sm);padding:var(--sp-2) var(--sp-3);min-height:var(--touch);display:grid;grid-template-columns:auto 1fr;gap:4px var(--sp-2);align-items:center;color:var(--ink)}
.sb-pick:hover{border-color:var(--accent-dim)}
.sb-pick-alias{font-family:var(--mono);font-size:var(--fs-2xs);color:var(--ink-faint);text-align:right}
.sb-pick-sum{grid-column:1/-1;font-size:var(--fs-2xs);color:var(--ink-dim);overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}

.sb-cart{display:flex;flex-direction:column;gap:var(--sp-3)}
.sb-row{border:1px solid var(--line);background:var(--bg-panel);border-radius:var(--radius-sm);padding:var(--sp-3)}
.sb-row-head{display:flex;align-items:center;gap:var(--sp-2);flex-wrap:wrap;margin-bottom:var(--sp-2)}
.sb-step{width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;border-radius:50%;background:var(--accent);color:var(--bg);font-family:var(--mono);font-size:var(--fs-2xs);font-weight:700;flex-shrink:0}
body[data-theme="mono"] .sb-step,
body[data-theme="pastel"] .sb-step,
body[data-theme="warm"] .sb-step{color:#fff}
.sb-row-title{font-size:var(--fs-xs);color:var(--ink-dim);min-width:0;overflow-wrap:anywhere}
.sb-row-tools{margin-left:auto;display:flex;gap:4px;flex-shrink:0}
.sb-usage{font-size:var(--fs-2xs);color:var(--ink-dim);font-family:var(--mono);margin:0 0 var(--sp-3);padding:var(--sp-2);border-radius:6px;background:color-mix(in srgb,var(--ink) 4%,transparent);overflow-wrap:anywhere}
.sb-usage b{color:var(--accent-text);font-family:var(--sans,inherit)}

.sb-slots{display:flex;flex-direction:column;gap:var(--sp-2)}
.sb-slot{display:grid;grid-template-columns:74px minmax(0,1fr);gap:4px var(--sp-2);align-items:center}
.sb-slot-label{font-family:var(--mono);font-size:var(--fs-2xs);color:var(--accent-text);background:color-mix(in srgb,var(--accent) 12%,transparent);border-radius:6px;padding:3px 6px;text-align:center}
.sb-slot-input{display:flex;flex-wrap:wrap;gap:6px;min-width:0}
.sb-slot-input input,.sb-slot-input select{min-height:var(--touch);border:1px solid var(--line);background:var(--bg);color:var(--ink);border-radius:8px;padding:var(--sp-2) var(--sp-3);font-size:16px;font-family:var(--mono);min-width:0;flex:1 1 130px}
.sb-slot-combo{display:flex;gap:6px;flex-wrap:wrap;width:100%}
.sb-slot-seg,.sb-slot-note{grid-column:2;font-size:var(--fs-2xs);color:var(--ink-faint);line-height:1.5}
.sb-slot-note b{color:var(--ink-dim)}
.sb-slot-note code{font-family:var(--mono)}
.sb-slot.is-enter .sb-slot-fixed{font-family:var(--mono);font-size:var(--fs-2xs);color:var(--ink-faint)}
.sb-slot-none{font-size:var(--fs-xs);color:var(--ink-faint);margin:0}

.sb-toggle{display:inline-flex;align-items:center;gap:var(--sp-2);font-size:var(--fs-xs);color:var(--ink-dim);min-height:var(--touch);margin-bottom:var(--sp-3);cursor:pointer}
.sb-toggle em{color:var(--ink-faint);font-style:normal;font-size:var(--fs-2xs)}
.sb-warn{margin:0 0 var(--sp-3);padding:var(--sp-3) var(--sp-3) var(--sp-3) 1.6em;border:1px solid var(--accent-dim);border-radius:var(--radius-sm);background:color-mix(in srgb,var(--accent-amber) 8%,transparent);font-size:var(--fs-2xs);color:var(--ink-dim)}

.sb-out-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--sp-4)}
.sb-out-col{min-width:0}
.sb-output{width:100%;border:1px solid var(--line);background:var(--bg);color:var(--ink);border-radius:var(--radius-sm);padding:var(--sp-3);font-family:var(--mono);font-size:16px;line-height:1.6;resize:vertical}
.sb-out-actions{display:flex;gap:var(--sp-2);margin-top:var(--sp-2);flex-wrap:wrap}
.sb-pv-label{font-size:var(--fs-2xs);color:var(--ink-faint);margin:0 0 var(--sp-2)}
.sb-preview{list-style:none;margin:0;padding:0;max-height:360px;overflow-y:auto;border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--bg-panel)}
.sb-preview li{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.1fr);gap:var(--sp-2);align-items:baseline;padding:6px var(--sp-3);border-bottom:1px solid var(--line);font-size:var(--fs-2xs)}
.sb-preview li:last-child{border-bottom:0}
.sb-preview code{font-family:var(--mono);color:var(--ink);overflow-wrap:anywhere}
.sb-preview code em{color:var(--ink-faint);font-style:normal}
.sb-preview small{color:var(--ink-faint);overflow-wrap:anywhere}
.sb-preview li.pv-cmd{background:color-mix(in srgb,var(--accent) 8%,transparent)}
.sb-preview li.pv-cmd code{color:var(--accent-text);font-weight:700}
.sb-preview li.pv-auto-enter code em{color:var(--ink-faint);opacity:.8}

/* 좁은 화면: 2열 → 1열, 슬롯 라벨을 위로.
   [폭 선택 근거 — 1차 구현이 틀렸던 이유]
   처음에 899.98px 을 썼다가 test:responsive 가 잡아냈다:
     "미디어쿼리 폭 17개가 모두 --bp-* 기반 → 이탈: 899.98"
   :root L92 의 --bp-lg 는 960px 이고 이 저장소의 모든 미디어쿼리는
   --bp-* 에서 1px 뺀 값만 쓴다. 임의 폭을 하나 더 만들면 브레이크포인트가
   6개로 늘어 다른 화면과 접히는 지점이 어긋난다.
   → 959.98px(--bp-lg 직하)로 통합한다. 검사가 옳았고 내 CSS 가 틀렸다. */
@media (max-width:959.98px){
  .sb-out-grid{grid-template-columns:1fr}
  .core-row{grid-template-columns:28px minmax(0,1fr);row-gap:var(--sp-2)}
  .core-metric{grid-column:2}
  .core-add{grid-column:2;justify-self:start}
}
@media (max-width:639.98px){
  .sb-slot{grid-template-columns:1fr}
  .sb-slot-seg,.sb-slot-note{grid-column:1}
  .sb-picker{grid-template-columns:1fr;max-height:280px}
  .sb-preview li{grid-template-columns:1fr;gap:2px}
}

/* ---------------------------------------------------------------- 입력 확대 방어 — 최종 */
/* [이 블록이 파일 맨 끝에 있는 이유 — 실측 근거]
   iOS Safari 는 font-size < 16px 인 입력창에 포커스하면 페이지를 확대한다.
   L108 의 모바일 블록에서 이미 16px 을 지정했지만, #cmdline-input(L390) 과
   #ai-input(L673) 은 그 뒤에서 font-size:var(--fs-sm) 을 다시 선언한다.
   두 규칙의 명시도가 똑같이 1-0-0 이므로 승자는 '소스 순서'로 결정되고,
   L390/L673 이 뒤에 있어 앞선 방어를 덮어썼다(390px 실측 13.104px).
   미디어 쿼리는 명시도에 기여하지 않으므로 위치를 옮기는 것이 유일한 해법이다.
   → 원 선언보다 뒤인 파일 끝에서 다시 선언한다. 여기가 마지막이므로
     이후 어떤 규칙에도 밀리지 않는다.
   이 규칙을 파일 중간으로 되돌리면 회귀한다. e2e_visual.mjs 그룹 B 가 이를 감시한다. */
/* [Phase 9 추가분 — 같은 결함이 신규 화면에서 재발했다]
   시각 검증 실측: 390px 에서 #core-sort=13.104px, #sb-search=13.104px.
   원인도 앞과 같은 계열이지만 명시도 구조가 다르다.
     - #core-sort  ← `.catalog-toolbar select{font-size:var(--fs-sm)}` (0-1-1)
     - #sb-search  ← `.catalog-search input{font-size:var(--fs-sm)}`   (0-1-1)
   L144 의 `input,select,textarea`(0-0-1) 는 명시도에서 지므로 순서를 옮겨도
   이기지 못한다. 위 두 줄과 달리 ID 선택자(1-0-0)로 올려야 확실히 이긴다.
   '규칙이 있다'와 '규칙이 이긴다'는 다른 문제라는 Phase 6 의 결론을 그대로 적용한다. */
@media (max-width:639.98px){
  #cmdline-input,
  #ai-input,
  #core-sort,
  #sb-search{font-size:16px}
}

/* ───────────────────────── 2026-08 · 기능 그룹 + Agent 100 ───────────────────────── */
:root{--agent-cyan:#55d9ff;--agent-blue:#74a7ff;--agent-violet:#b491ff;--agent-amber:#f8c76c;--agent-green:#77dbac;--motion-out:cubic-bezier(.23,1,.32,1);--motion-move:cubic-bezier(.77,0,.175,1)}
.function-groups{margin:var(--sp-5) 0 var(--sp-4);padding:clamp(16px,2vw,24px);border:1px solid color-mix(in srgb,var(--line) 84%,var(--accent));border-radius:16px;background:linear-gradient(135deg,color-mix(in srgb,var(--bg-panel) 94%,var(--accent) 6%),var(--bg-panel));box-shadow:0 16px 35px color-mix(in srgb,var(--bg) 60%,transparent)}
.function-groups-heading{display:flex;align-items:end;justify-content:space-between;gap:var(--sp-4);margin-bottom:var(--sp-4)}.function-groups-heading .eyebrow{margin-bottom:var(--sp-1)}.function-groups-heading h2,.agent-matrix-heading h2{margin:0;font-size:var(--fs-xl);letter-spacing:-.03em}.function-groups-heading>p{margin:0;max-width:28ch;color:var(--ink-dim);font-size:var(--fs-sm);line-height:1.55;text-align:right}.function-groups-list{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:10px}
.function-group-card{position:relative;min-height:106px;display:grid;grid-template-columns:10px minmax(0,1fr) auto;grid-template-rows:1fr auto;align-items:center;column-gap:12px;overflow:hidden;padding:16px;border:1px solid var(--line);border-radius:12px;background:color-mix(in srgb,var(--bg-elevated) 86%,transparent);color:var(--ink);text-align:left;transition:transform 180ms var(--motion-out),border-color 180ms var(--motion-out),box-shadow 180ms var(--motion-out),background 180ms var(--motion-out)}
.function-group-card::after{content:"";position:absolute;inset:auto -18% -62% 40%;height:120px;border-radius:50%;background:radial-gradient(circle,color-mix(in srgb,var(--accent) 22%,transparent),transparent 66%);opacity:0;transform:translate3d(-12px,9px,0);transition:opacity 220ms var(--motion-out),transform 260ms var(--motion-out);pointer-events:none}.function-group-card:hover,.function-group-card:focus-visible,.function-group-card.active{border-color:color-mix(in srgb,var(--accent) 70%,var(--line));box-shadow:0 10px 22px color-mix(in srgb,var(--accent) 12%,transparent);transform:translateY(-2px);outline:none}.function-group-card:hover::after,.function-group-card:focus-visible::after,.function-group-card.active::after{opacity:1;transform:translate3d(0,0,0)}.function-group-card:active{transform:scale(.97)}
.function-group-mark{grid-row:1/-1;width:4px;min-height:62px;border-radius:8px;background:var(--accent);box-shadow:0 0 16px color-mix(in srgb,var(--accent) 60%,transparent)}.function-group-copy{min-width:0;display:flex;flex-direction:column;gap:5px;align-self:center}.function-group-copy strong{font-size:var(--fs-sm);font-weight:700}.function-group-copy small{color:var(--ink-dim);font-size:11px;line-height:1.5}.function-group-count{align-self:start;color:var(--ink);font-family:var(--mono);font-size:var(--fs-md);font-weight:700;line-height:1}.function-group-count em{margin-left:2px;color:var(--ink-faint);font-size:10px;font-style:normal;font-weight:500}.function-group-arrow{grid-column:3;align-self:end;color:var(--accent);font-size:16px;line-height:1;transition:transform 180ms var(--motion-out)}.function-group-card:hover .function-group-arrow,.function-group-card:focus-visible .function-group-arrow{transform:translate3d(3px,-3px,0)}
.function-group-card.base .function-group-mark{background:#66c6ff}.function-group-card.pgp .function-group-mark{background:#b092ff}.function-group-card.provided .function-group-mark{background:#4fe1bd}.function-group-card.group-1 .function-group-mark{background:#5ddcc8}.function-group-card.group-2 .function-group-mark{background:#fbba65}.function-group-card.group-3 .function-group-mark{background:#ff879e}.function-group-card.group-4 .function-group-mark{background:#9aaeff}.function-group-card.group-5 .function-group-mark{background:#b4df71}.function-group-card.group-6 .function-group-mark{background:#dc9aff}.function-group-card.group-7 .function-group-mark{background:#67cae8}

.agent-loop-view{--agent-panel:color-mix(in srgb,var(--bg-panel) 84%,#071d30 16%);--agent-line:color-mix(in srgb,var(--line) 72%,var(--agent-cyan));position:relative;isolation:isolate;padding-bottom:calc(var(--sp-7) + 28px)}.agent-loop-header{position:relative;z-index:1}.agent-loop-header h1{max-width:14ch}.agent-loop-header b{color:var(--agent-cyan)}
.agent-hero{position:relative;z-index:0;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:1px;overflow:hidden;margin:var(--sp-5) 0 12px;border:1px solid var(--agent-line);border-radius:18px;background:var(--agent-line);box-shadow:0 20px 50px color-mix(in srgb,var(--accent) 13%,transparent)}.agent-hero::before{content:"";position:absolute;inset:0;background:linear-gradient(110deg,color-mix(in srgb,var(--accent) 11%,transparent),transparent 36%,color-mix(in srgb,var(--agent-cyan) 9%,transparent));pointer-events:none}.agent-hero-glow{position:absolute;right:10%;top:-70px;width:240px;height:220px;border-radius:50%;background:radial-gradient(circle,color-mix(in srgb,var(--agent-cyan) 28%,transparent),transparent 68%);filter:blur(10px);pointer-events:none;transition:transform 480ms var(--motion-move),opacity 360ms var(--motion-out)}.is-running .agent-hero-glow{transform:translate3d(-20px,24px,0) scale(1.12);opacity:.9}
.agent-stat-block{position:relative;display:flex;min-height:132px;flex-direction:column;justify-content:center;gap:7px;padding:22px clamp(16px,2.4vw,30px);background:var(--agent-panel)}.agent-stat-block>span{color:var(--ink-faint);font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:.12em}.agent-stat-block strong{color:var(--ink);font-family:var(--mono);font-size:clamp(22px,3vw,35px);letter-spacing:-.05em;line-height:1}.agent-stat-block small{color:var(--ink-dim);font-size:11px}.agent-stat-block.primary strong{color:var(--agent-cyan)}
.agent-control-panel{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-4);margin-bottom:var(--sp-4);padding:18px 20px;border:1px solid var(--line);border-radius:14px;background:var(--bg-panel)}.agent-control-panel strong{font-size:var(--fs-md)}.agent-control-panel p{margin:5px 0 0;color:var(--ink-dim);font-size:var(--fs-xs);line-height:1.55}.agent-controls{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px}.agent-controls button{min-height:38px;white-space:nowrap}.agent-controls button:active{transform:scale(.97)}
.agent-group-summary{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:10px;margin-bottom:var(--sp-5)}.agent-group-card{position:relative;display:flex;align-items:center;gap:10px;min-width:0;overflow:hidden;padding:13px;border:1px solid var(--line);border-radius:12px;background:var(--bg-panel)}.agent-group-card::before{content:"";position:absolute;inset:auto -30% -120% 18%;width:160px;height:120px;border-radius:50%;background:radial-gradient(circle,var(--agent-color),transparent 67%);opacity:.14;pointer-events:none}.agent-group-card.cyan{--agent-color:var(--agent-cyan)}.agent-group-card.blue{--agent-color:var(--agent-blue)}.agent-group-card.violet{--agent-color:var(--agent-violet)}.agent-group-card.amber{--agent-color:var(--agent-amber)}.agent-group-card.green{--agent-color:var(--agent-green)}
.agent-group-code{position:relative;z-index:1;display:grid;width:31px;height:31px;flex:0 0 31px;place-items:center;border:1px solid color-mix(in srgb,var(--agent-color) 55%,var(--line));border-radius:9px;background:color-mix(in srgb,var(--agent-color) 14%,transparent);color:var(--agent-color);font-family:var(--mono);font-size:10px;font-weight:700}.agent-group-card>div{position:relative;z-index:1;display:flex;min-width:0;flex-direction:column;gap:2px}.agent-group-card strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px}.agent-group-card small{color:var(--ink-dim);font-size:10px;white-space:nowrap}.agent-group-orb{position:absolute;right:12px;width:7px;height:7px;border-radius:50%;background:var(--agent-color);box-shadow:0 0 11px var(--agent-color)}.is-running .agent-group-orb{animation:agentPulse 1.6s var(--motion-out) infinite}
.agent-matrix-section{padding:clamp(16px,2vw,24px);border:1px solid var(--agent-line);border-radius:16px;background:linear-gradient(145deg,var(--agent-panel),var(--bg-panel));box-shadow:inset 0 1px 0 color-mix(in srgb,#fff 7%,transparent)}.agent-matrix-heading{display:flex;align-items:end;justify-content:space-between;gap:var(--sp-4);margin-bottom:var(--sp-4)}.agent-matrix-heading .eyebrow{margin-bottom:var(--sp-1)}.agent-filter{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px}.agent-filter button{min-height:30px;border:1px solid var(--line);border-radius:999px;background:var(--bg-elevated);color:var(--ink-dim);padding:5px 10px;font-size:10px;transition:color 160ms var(--motion-out),background 160ms var(--motion-out),border-color 160ms var(--motion-out),transform 160ms var(--motion-out)}.agent-filter button.active{border-color:var(--agent-cyan);background:color-mix(in srgb,var(--agent-cyan) 18%,var(--bg-elevated));color:var(--ink)}.agent-filter button:active{transform:scale(.97)}
.agent-grid{display:grid;grid-template-columns:repeat(10,minmax(0,1fr));gap:7px}.agent-node{position:relative;min-height:72px;overflow:hidden;padding:9px;border:1px solid color-mix(in srgb,var(--line) 87%,var(--agent-color));border-radius:9px;background:color-mix(in srgb,var(--bg-elevated) 92%,var(--agent-color) 8%);transition:border-color 180ms var(--motion-out),transform 180ms var(--motion-out),box-shadow 180ms var(--motion-out)}.agent-node.cyan{--agent-color:var(--agent-cyan)}.agent-node.blue{--agent-color:var(--agent-blue)}.agent-node.violet{--agent-color:var(--agent-violet)}.agent-node.amber{--agent-color:var(--agent-amber)}.agent-node.green{--agent-color:var(--agent-green)}.agent-node-id{display:block;color:var(--ink-faint);font-family:var(--mono);font-size:9px;letter-spacing:.04em}.agent-node-score{display:block;margin-top:5px;color:var(--ink);font-family:var(--mono);font-size:16px;font-weight:700;letter-spacing:-.04em}.agent-node-score::after{content:"점";margin-left:1px;color:var(--ink-faint);font-size:9px;font-weight:500}.agent-node-bar{position:absolute;right:9px;bottom:9px;left:9px;height:3px;overflow:hidden;border-radius:999px;background:color-mix(in srgb,var(--agent-color) 14%,var(--line))}.agent-node-bar i{display:block;width:100%;height:100%;border-radius:inherit;background:var(--agent-color);box-shadow:0 0 8px var(--agent-color);transform-origin:left;transition:transform 170ms var(--motion-out)}.agent-node.done{border-color:var(--agent-color);box-shadow:0 0 0 1px color-mix(in srgb,var(--agent-color) 25%,transparent),0 6px 15px color-mix(in srgb,var(--agent-color) 13%,transparent)}.agent-node.done::after{content:"✓";position:absolute;right:9px;top:9px;color:var(--agent-color);font-size:11px;font-weight:700}.is-running .agent-node:not(.done){animation:agentNode 1.8s var(--motion-out) infinite}.is-running .agent-node:nth-child(3n){animation-delay:-.45s}.is-running .agent-node:nth-child(5n){animation-delay:-.9s}.is-complete .agent-matrix-section,.is-complete .agent-hero{border-color:var(--agent-green)}
@keyframes agentPulse{0%,100%{transform:scale(1);opacity:.8}55%{transform:scale(1.8);opacity:.28}}@keyframes agentNode{0%,100%{transform:translateY(0)}50%{transform:translateY(-2px)}}
@media (max-width:959.98px){.agent-group-summary{grid-template-columns:repeat(3,minmax(0,1fr))}.agent-grid{grid-template-columns:repeat(8,minmax(0,1fr))}}
@media (max-width:639.98px){.function-groups-heading,.agent-matrix-heading,.agent-control-panel{align-items:flex-start;flex-direction:column}.function-groups-heading>p{text-align:left}.function-groups-list{grid-template-columns:1fr 1fr}.agent-hero{grid-template-columns:1fr 1fr}.agent-stat-block{min-height:110px}.agent-controls{justify-content:flex-start}.agent-group-summary{grid-template-columns:1fr 1fr}.agent-grid{grid-template-columns:repeat(5,minmax(0,1fr))}.agent-filter{justify-content:flex-start}}
@media (max-width:479.98px){.function-groups-list{grid-template-columns:1fr}.agent-group-summary{grid-template-columns:1fr}.agent-hero{grid-template-columns:1fr 1fr}.agent-stat-block{padding:17px 14px}.agent-stat-block strong{font-size:23px}.agent-grid{gap:5px}.agent-node{min-height:63px;padding:7px}.agent-node-score{font-size:14px}.agent-node-id{font-size:8px}.agent-filter button{padding:5px 8px}}
@media (prefers-reduced-motion:reduce){.function-group-card,.function-group-card::after,.function-group-arrow,.agent-hero-glow,.agent-node-bar i{transition:none!important}.is-running .agent-group-orb,.is-running .agent-node:not(.done){animation:none!important}}

/* 실제 CAD 명령의 단계형 따라하기 범위 고지와 상세 튜토리얼 패널 */
.tutorial-coverage{display:inline-flex;align-items:center;gap:var(--sp-2);margin:var(--sp-3) 0 0;padding:var(--sp-2) var(--sp-3);border:1px solid color-mix(in srgb,var(--accent) 42%,var(--line));border-radius:999px;background:color-mix(in srgb,var(--accent) 8%,var(--bg-panel));color:var(--accent-text);font-family:var(--mono);font-size:var(--fs-2xs);font-weight:700;line-height:1.35;overflow-wrap:anywhere}
.tutorial-coverage::before{content:'▶';color:var(--accent-cyan);font-size:.9em}
.provided-catalog-coverage{display:flex;align-items:flex-start;gap:var(--sp-2);max-width:72ch;margin:var(--sp-2) 0 0;padding:var(--sp-2) var(--sp-3);border:1px solid color-mix(in srgb,var(--accent-cyan) 32%,var(--line));border-radius:var(--radius-sm);background:color-mix(in srgb,var(--accent-cyan) 5%,var(--bg-panel));color:var(--ink-dim);font-family:var(--mono);font-size:var(--fs-2xs);line-height:1.55;overflow-wrap:anywhere}
.provided-catalog-coverage::before{content:'✓';flex:0 0 auto;color:var(--accent-cyan);font-weight:800}
.source-locator{padding-top:var(--sp-2);border-top:1px solid var(--line-soft);font-family:var(--mono);color:var(--ink-faint)!important}
.source-locator strong{color:var(--accent-text)}
.follow-along-process{padding:var(--sp-4);border:1px solid color-mix(in srgb,var(--accent) 30%,var(--line));border-radius:var(--radius);background:linear-gradient(135deg,color-mix(in srgb,var(--accent) 8%,var(--bg-panel)),var(--bg-panel));box-shadow:inset 0 1px 0 color-mix(in srgb,var(--accent) 14%,transparent)}
.follow-along-head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--sp-3);margin-bottom:var(--sp-2)}
.follow-along-head .eyebrow{margin:0 0 var(--sp-1);font-size:var(--fs-3xs)}
.follow-along-head h3{margin:0;font-size:var(--fs-md)}
.follow-along-head>small{flex:0 0 auto;padding:var(--sp-1) var(--sp-2);border:1px solid var(--line);border-radius:999px;color:var(--ink-faint);font-family:var(--mono);font-size:var(--fs-3xs);white-space:nowrap}
.follow-along-intro{margin:0 0 var(--sp-3);max-width:58ch;color:var(--ink-dim);font-size:var(--fs-xs);line-height:1.65}
.follow-along-process .process-list{gap:var(--sp-2)}
.follow-along-process .process-list li button{position:relative;padding:var(--sp-3);transition:border-color .18s ease,transform .18s ease,background .18s ease}
.follow-along-process .process-list li button:hover{border-color:var(--accent-dim);transform:translateX(2px)}
.follow-along-process .process-list li button:focus-visible{outline:3px solid color-mix(in srgb,var(--accent) 65%,transparent);outline-offset:2px}
.follow-state{display:inline-flex;margin:0 0 3px;color:var(--accent-cyan);font-family:var(--mono);font-size:var(--fs-3xs);font-weight:700;letter-spacing:.03em}
.follow-prompt{display:block;margin-top:var(--sp-2);max-width:100%;padding:var(--sp-2);border:1px solid var(--line-soft);border-radius:var(--radius-sm);background:var(--bg-inset);color:var(--accent-text);font-family:var(--mono);font-size:var(--fs-2xs);line-height:1.55;overflow-wrap:anywhere;white-space:pre-wrap}
@media (max-width:479.98px){
  .tutorial-coverage{border-radius:var(--radius-sm)}
  .follow-along-process{padding:var(--sp-3)}
  .follow-along-head{align-items:flex-start;flex-direction:column;gap:var(--sp-2)}
  .follow-along-head>small{white-space:normal}
}
@media (prefers-reduced-motion:reduce){
  .follow-along-process .process-list li button{transition:none}
  .follow-along-process .process-list li button:hover{transform:none}
}

/* 사용자가 제공한 PGP 원문과 현재 유효 별칭 카탈로그의 대조 상태 */
.pgp-source-audit{display:inline-flex;align-items:center;max-width:100%;margin:var(--sp-3) 0 0;padding:var(--sp-2) var(--sp-3);border:1px solid color-mix(in srgb,var(--accent-green) 45%,var(--line));border-radius:var(--radius-sm);background:color-mix(in srgb,var(--accent-green) 8%,var(--bg-panel));color:var(--accent-green);font-family:var(--mono);font-size:var(--fs-2xs);font-weight:700;line-height:1.45;overflow-wrap:anywhere}
.pgp-source-audit::before{content:'✓';display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:16px;height:16px;margin-right:var(--sp-2);border-radius:50%;background:color-mix(in srgb,var(--accent-green) 18%,transparent);font-size:var(--fs-3xs)}
