/* 사본 — 원본: website-clean public/assets/css/design-system.css sha256=96f9845bb0efb3e430516c7ffaea5c5383951f1414496535809b99aac01a93b7
   사본 사유: 콘솔 독립성 헌법(00-plan.md §1) — 원본 참조는 본체 배포에 물린다.
   드리프트 검출: 셀프테스트가 이 헤더의 sha 와 원본을 대조한다(E-14). */
/* ============================================================
 * 웹워크 디자인 시스템 (design-system.css) — 전용 번들
 * 출처: claude.ai/design 프로젝트 "디자인 시스템"(사용자 정의)의 토큰을 이식.
 * 격리 원칙: 모든 토큰·컴포넌트를 .dsx 스코프 아래 정의 → 기존 사이트/output.css 무영향.
 *   적용: 페이지(또는 영역)를 <div class="dsx"> 로 감싸고 이 파일을 link 하면 .ds-* 사용 가능.
 * 테마: 기본 OS(prefers-color-scheme), 토글 시 최상위 요소의 data-theme 로 override.
 * 컴포넌트 클래스는 .ds-* 접두사(기존 .card/.btn 과 충돌 없음).
 * ============================================================ */

/* ---------- 누락 토큰 보강 (2026-08-13) ----------
   `--on-brand` 8곳, `--scrim` 3곳이 **선언 없이 참조**되고 있었다(var() 가 값 없이 해석됨).
   값은 시안 원본(Webi 레이아웃 리뉴얼.dc.html)에서 그대로 가져왔다 — 지어내지 않았다.
   용도: --on-brand = 브랜드·위험 면 위 글자색, --scrim = 미디어 위 반투명 막.
   하드코딩된 흰색/검정을 이 둘로 대체하라는 것이 시안의 지시다. */
:root, .dsx { --on-brand: oklch(1 0 0); --scrim: oklch(0.08 0.01 268 / 0.78); }
:root[data-theme="dark"], [data-theme="dark"] .dsx { --scrim: oklch(0.20 0.01 268 / 0.78); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]), :root:not([data-theme="light"]) .dsx { --scrim: oklch(0.20 0.01 268 / 0.78); }
}

/* ---------- 토큰(라이트) ---------- */
/* :root 에도 노출한다(2026-08-12, 공개 리뉴얼). 토큰 정의는 그 자체로 아무것도 렌더하지 않으므로
   공개 페이지에 무해하고, .dsx 안에서는 기존 정의가 그대로 이긴다. 값 복제 금지 — 셀렉터만 늘린다.
   ⚠️ color-scheme 은 :root 로 올리지 않는다 — 테마를 안 입힌 페이지의 네이티브 컨트롤까지 어두워진다. */
:root,
.dsx {
  --bg:oklch(0.982 0.003 265); --surface:#ffffff; --surface-2:oklch(0.968 0.004 265); --surface-3:oklch(0.945 0.006 265);
  --border:oklch(0.912 0.006 265); --border-strong:oklch(0.845 0.010 265);
  --text:oklch(0.255 0.015 265); --text-2:oklch(0.505 0.014 265); --text-3:oklch(0.635 0.012 265);
  --brand:oklch(0.525 0.155 268); --brand-hover:oklch(0.455 0.155 268); --brand-soft:oklch(0.955 0.028 268); --brand-text:oklch(0.455 0.150 268); --brand-border:oklch(0.885 0.045 268);
  --success:oklch(0.560 0.130 158); --success-soft:oklch(0.955 0.035 158); --success-text:oklch(0.470 0.115 158);
  --warn:oklch(0.700 0.140 78); --warn-soft:oklch(0.965 0.045 78); --warn-text:oklch(0.520 0.110 62);
  --danger:oklch(0.570 0.190 27); --danger-soft:oklch(0.962 0.030 27); --danger-text:oklch(0.500 0.175 27);
  --info:oklch(0.600 0.120 235); --info-soft:oklch(0.958 0.030 235); --info-text:oklch(0.480 0.110 235);
  --overlay:oklch(0.255 0.015 265 / 0.45);
  --sh-1:0 1px 2px oklch(0.255 0.015 265 / 0.06); --sh-2:0 2px 8px oklch(0.255 0.015 265 / 0.08); --sh-3:0 12px 32px oklch(0.255 0.015 265 / 0.14);
  --r-sm:4px; --r-md:6px; --r-lg:10px; --r-xl:14px; --r-full:999px;
  --ctl-h:32px; --ctl-h-sm:26px; --ctl-h-lg:38px;
  --font-sans:'Noto Sans KR',system-ui,-apple-system,'Segoe UI',sans-serif;
  --font-mono:'JetBrains Mono',ui-monospace,Menlo,Consolas,monospace;
}
/* ---------- 토큰(다크) — 토글: [data-theme="dark"], OS: prefers-color-scheme ---------- */
:root[data-theme="dark"],
[data-theme="dark"] .dsx, .dsx[data-theme="dark"] {
  --bg:oklch(0.185 0.012 265); --surface:oklch(0.225 0.014 265); --surface-2:oklch(0.262 0.014 265); --surface-3:oklch(0.300 0.014 265);
  --border:oklch(0.320 0.016 265); --border-strong:oklch(0.400 0.018 265);
  --text:oklch(0.955 0.004 265); --text-2:oklch(0.760 0.012 265); --text-3:oklch(0.640 0.012 265);
  --brand:oklch(0.660 0.145 268); --brand-hover:oklch(0.720 0.140 268); --brand-soft:oklch(0.300 0.055 268); --brand-text:oklch(0.800 0.100 268); --brand-border:oklch(0.380 0.070 268);
  --success-soft:oklch(0.300 0.050 158); --success-text:oklch(0.800 0.090 158);
  --warn-soft:oklch(0.310 0.050 78); --warn-text:oklch(0.840 0.090 78);
  --danger-soft:oklch(0.300 0.060 27); --danger-text:oklch(0.800 0.110 27);
  --info-soft:oklch(0.295 0.050 235); --info-text:oklch(0.810 0.085 235);
  --overlay:oklch(0.130 0.010 265 / 0.62);
  --sh-1:0 1px 2px oklch(0 0 0 / 0.3); --sh-2:0 2px 8px oklch(0 0 0 / 0.35); --sh-3:0 12px 32px oklch(0 0 0 / 0.5);
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]),
  :root:not([data-theme="light"]) .dsx {
    --bg:oklch(0.185 0.012 265); --surface:oklch(0.225 0.014 265); --surface-2:oklch(0.262 0.014 265); --surface-3:oklch(0.300 0.014 265);
    --border:oklch(0.320 0.016 265); --border-strong:oklch(0.400 0.018 265);
    --text:oklch(0.955 0.004 265); --text-2:oklch(0.760 0.012 265); --text-3:oklch(0.640 0.012 265);
    --brand:oklch(0.660 0.145 268); --brand-hover:oklch(0.720 0.140 268); --brand-soft:oklch(0.300 0.055 268); --brand-text:oklch(0.800 0.100 268); --brand-border:oklch(0.380 0.070 268);
    --success-soft:oklch(0.300 0.050 158); --success-text:oklch(0.800 0.090 158);
    --warn-soft:oklch(0.310 0.050 78); --warn-text:oklch(0.840 0.090 78);
    --danger-soft:oklch(0.300 0.060 27); --danger-text:oklch(0.800 0.110 27);
    --info-soft:oklch(0.295 0.050 235); --info-text:oklch(0.810 0.085 235);
    --overlay:oklch(0.130 0.010 265 / 0.62);
    --sh-1:0 1px 2px oklch(0 0 0 / 0.3); --sh-2:0 2px 8px oklch(0 0 0 / 0.35); --sh-3:0 12px 32px oklch(0 0 0 / 0.5);
  }
}

/* ---------- 스코프 기본 ---------- */
.dsx{font-family:var(--font-sans);font-size:13px;line-height:1.5;color:var(--text);-webkit-font-smoothing:antialiased}
.dsx *,.dsx *::before,.dsx *::after{box-sizing:border-box}
.dsx :where(a){color:var(--brand-text);text-decoration:none}
.dsx :where(a:hover){color:var(--brand-hover);text-decoration:underline}
/* 네이티브 위젯(스크롤바·기본 폼·자동완성)도 테마를 따르게 — DS 단일 소스 */
.dsx{color-scheme:light}
[data-theme="dark"] .dsx,.dsx[data-theme="dark"]{color-scheme:dark}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]) .dsx{color-scheme:dark}}

/* ---------- 자립 리셋(Tailwind preflight 대체 — output.css 없이도 정상) ---------- */
.dsx h1,.dsx h2,.dsx h3,.dsx h4,.dsx h5,.dsx h6{margin:0;font-size:inherit;font-weight:inherit;line-height:inherit}
.dsx p,.dsx figure,.dsx blockquote,.dsx dl,.dsx dd{margin:0}
.dsx ul,.dsx ol{margin:0;padding:0;list-style:none}
.dsx button,.dsx input,.dsx select,.dsx textarea,.dsx optgroup{font-family:inherit;font-size:100%;line-height:inherit;color:inherit;margin:0}
.dsx button,.dsx [type=button],.dsx [type=submit],.dsx [type=reset]{background:none;border:0;padding:0;cursor:pointer;-webkit-appearance:button}
.dsx img,.dsx svg,.dsx video,.dsx canvas,.dsx iframe{display:block;max-width:100%}
.dsx img,.dsx video{height:auto}
.dsx table{border-collapse:collapse;border-spacing:0}
.dsx hr{height:0;border:0;border-top:1px solid var(--border);color:inherit}
.dsx b,.dsx strong{font-weight:700}
.dsx small{font-size:80%}
.dsx code,.dsx kbd,.dsx pre,.dsx samp{font-family:var(--font-mono);font-size:1em}
.dsx summary{cursor:pointer}
.dsx [hidden]{display:none}

/* ---------- 타이포 유틸 ---------- */
.dsx .ds-h1{font-size:22px;font-weight:700;letter-spacing:-.02em;margin:0}
.dsx .ds-h2{font-size:17px;font-weight:700;letter-spacing:-.01em;margin:0}
.dsx .ds-h3{font-size:14px;font-weight:700;margin:0}
.dsx .ds-muted{color:var(--text-2)}
.dsx .ds-mono{font-family:var(--font-mono)}
.dsx .ds-num{font-variant-numeric:tabular-nums}

/* ---------- Button ---------- */
.dsx .ds-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;height:var(--ctl-h);padding:0 14px;
  font:inherit;font-size:13px;font-weight:500;border-radius:var(--r-md);border:1px solid var(--border-strong);
  background:var(--surface);color:var(--text);cursor:pointer;white-space:nowrap;transition:background .12s,border-color .12s,color .12s}
.dsx .ds-btn:hover{background:var(--surface-2)}
.dsx .ds-btn:focus-visible{outline:2px solid var(--brand);outline-offset:1px}
.dsx .ds-btn.is-sm{height:var(--ctl-h-sm);padding:0 10px;font-size:12px}
.dsx .ds-btn.is-lg{height:var(--ctl-h-lg);padding:0 18px;font-size:14px}
.dsx .ds-btn--primary{background:var(--brand);border-color:var(--brand);color:var(--on-brand)}
.dsx .ds-btn--primary:hover{background:var(--brand-hover);border-color:var(--brand-hover)}
.dsx .ds-btn--ghost{background:transparent;border-color:transparent;color:var(--text-2)}
.dsx .ds-btn--ghost:hover{background:var(--brand-soft)}
.dsx .ds-btn--danger{background:var(--danger);border-color:var(--danger);color:#fff}
.dsx .ds-btn[disabled],.dsx .ds-btn.is-disabled{opacity:.5;pointer-events:none}
/* 링크로 만든 버튼(a.ds-btn)은 일반 링크 규칙의 색·밑줄을 물려받지 않는다.
   [버그] .dsx a:hover(명시도 0,2,1)가 .dsx .ds-btn--primary(0,2,0)를 이기고,
   --primary:hover 는 배경만 바꾸고 color 를 다시 선언하지 않아 hover 시
   글자색 = 배경색(--brand-hover) 이 되어 텍스트가 사라졌다.
   2026-08-11 결제완료 화면에서 실측 재현(글자색·배경 모두 oklch(0.455 0.155 268)).
   명시도를 (0,3,1)로 올려 버튼이 자기 색을 지키게 한다. */
.dsx a.ds-btn,.dsx a.ds-btn:hover{color:var(--text);text-decoration:none}
.dsx a.ds-btn--primary,.dsx a.ds-btn--primary:hover{color:var(--on-brand)}
.dsx a.ds-btn--danger,.dsx a.ds-btn--danger:hover{color:var(--on-brand)}
.dsx a.ds-btn--ghost,.dsx a.ds-btn--ghost:hover{color:var(--text-2)}

/* ---------- Card ---------- */
.dsx .ds-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);box-shadow:var(--sh-1);padding:16px}
.dsx .ds-card--pad-lg{padding:20px}
.dsx .ds-card__title{font-size:14px;font-weight:700;margin:0 0 4px}
.dsx .ds-card__desc{font-size:12.5px;color:var(--text-2);margin:0}

/* ---------- Badge / Tag / Chip ---------- */
.dsx .ds-badge{display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:600;line-height:1;padding:3px 8px;border-radius:var(--r-full);background:var(--surface-2);color:var(--text-2)}
.dsx .ds-badge--brand{background:var(--brand-soft);color:var(--brand-text)}
.dsx .ds-badge--success{background:var(--success-soft);color:var(--success-text)}
.dsx .ds-badge--warn{background:var(--warn-soft);color:var(--warn-text)}
.dsx .ds-badge--danger{background:var(--danger-soft);color:var(--danger-text)}
.dsx .ds-badge--info{background:var(--info-soft);color:var(--info-text)}
.dsx .ds-badge .ds-dot{width:6px;height:6px;border-radius:50%;background:currentColor}
.dsx .ds-tag{display:inline-flex;align-items:center;gap:5px;font-size:12px;padding:3px 9px;border-radius:var(--r-sm);border:1px solid var(--border);background:var(--surface-2);color:var(--text-2)}
.dsx .ds-chip{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;font-weight:600;padding:5px 12px;border-radius:var(--r-full);border:1px solid var(--border);background:var(--surface);color:var(--text-2);cursor:pointer}
.dsx .ds-chip.is-active{background:var(--brand);border-color:var(--brand);color:#fff}

/* ---------- Form controls ---------- */
.dsx .ds-field{display:flex;flex-direction:column;gap:5px}
.dsx .ds-label{font-size:12px;font-weight:600;color:var(--text-2)}
.dsx .ds-input,.dsx .ds-select,.dsx .ds-textarea{height:var(--ctl-h);padding:0 10px;font:inherit;font-size:13px;color:var(--text);
  background:var(--surface);border:1px solid var(--border-strong);border-radius:var(--r-md);width:100%}
.dsx .ds-textarea{height:auto;min-height:76px;padding:8px 10px;line-height:1.5}
.dsx .ds-input:focus,.dsx .ds-select:focus,.dsx .ds-textarea:focus{outline:none;border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}
.dsx .ds-input::placeholder{color:var(--text-3)}
/* Toggle */
.dsx .ds-toggle{position:relative;display:inline-block;width:38px;height:22px;flex:none}
.dsx .ds-toggle input{position:absolute;opacity:0;width:0;height:0}
.dsx .ds-toggle .ds-track{position:absolute;inset:0;border-radius:var(--r-full);background:var(--surface-3);transition:.15s}
.dsx .ds-toggle .ds-knob{position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:#fff;box-shadow:var(--sh-1);transition:.15s}
.dsx .ds-toggle input:checked + .ds-track{background:var(--brand)}
.dsx .ds-toggle input:checked + .ds-track + .ds-knob,.dsx .ds-toggle input:checked ~ .ds-knob{left:18px}
/* Radio */
.dsx .ds-radio{display:inline-flex;align-items:center;gap:7px;font-size:13px;cursor:pointer}
.dsx .ds-radio input{accent-color:var(--brand);width:15px;height:15px}

/* ---------- Alert ---------- */
.dsx .ds-alert{display:flex;gap:10px;padding:11px 14px;border-radius:var(--r-md);border:1px solid var(--border);
  border-left-width:3px;background:var(--surface-2);color:var(--text);font-size:12.5px}
.dsx .ds-alert--brand{background:var(--brand-soft);border-color:var(--brand-border);border-left-color:var(--brand);color:var(--brand-text)}
.dsx .ds-alert--success{background:var(--success-soft);border-left-color:var(--success);color:var(--success-text)}
.dsx .ds-alert--warn{background:var(--warn-soft);border-left-color:var(--warn);color:var(--warn-text)}
.dsx .ds-alert--danger{background:var(--danger-soft);border-left-color:var(--danger);color:var(--danger-text)}
.dsx .ds-alert--info{background:var(--info-soft);border-left-color:var(--info);color:var(--info-text)}

/* ---------- Table ---------- */
.dsx .ds-table-wrap{overflow-x:auto;border:1px solid var(--border);border-radius:var(--r-lg)}
.dsx .ds-table{border-collapse:collapse;width:100%;font-size:12.5px}
.dsx .ds-table th,.dsx .ds-table td{text-align:left;padding:9px 12px;border-top:1px solid var(--border);white-space:nowrap}
.dsx .ds-table thead th{background:var(--surface-2);border-top:0;color:var(--text-2);font-weight:600;font-size:11px;text-transform:uppercase;letter-spacing:.04em}
.dsx .ds-table tbody tr:hover{background:var(--surface-2)}

/* ---------- Tabs ---------- */
.dsx .ds-tabs{display:flex;gap:2px;border-bottom:1px solid var(--border)}
.dsx .ds-tab{padding:8px 14px;font-size:13px;font-weight:600;color:var(--text-2);border-bottom:2px solid transparent;cursor:pointer;background:none;border-top:0;border-left:0;border-right:0}
.dsx .ds-tab.is-active{color:var(--brand-text);border-bottom-color:var(--brand)}

/* ---------- Skeleton / Empty ---------- */
.dsx .ds-skel{background:linear-gradient(90deg,var(--surface-2) 25%,var(--surface-3) 37%,var(--surface-2) 63%);
  background-size:320px 100%;border-radius:var(--r-sm);animation:ds-shimmer 1.3s infinite linear;height:12px}
@keyframes ds-shimmer{0%{background-position:-160px 0}100%{background-position:160px 0}}
.dsx .ds-empty{display:flex;flex-direction:column;align-items:center;gap:8px;padding:56px 20px;color:var(--text-3);text-align:center}
.dsx .ds-empty i{font-size:28px;color:var(--text-3);margin-bottom:8px;display:block}

/* ---------- Page header (제목 + 부제 + 우측 액션) ---------- */
.dsx .ds-page-head{display:flex;flex-direction:column;gap:14px;margin-bottom:22px}
@media (min-width:768px){.dsx .ds-page-head{flex-direction:row;align-items:center;justify-content:space-between}}
.dsx .ds-page-head__title{font-size:22px;font-weight:700;letter-spacing:-.02em;margin:0;color:var(--text)}
.dsx .ds-page-head__sub{margin:5px 0 0;font-size:13px;color:var(--text-2)}
.dsx .ds-page-head__sub strong{color:var(--warn-text);font-weight:700}

/* ---------- Stat card (지표 카드 그리드) ---------- */
.dsx .ds-stats{display:grid;grid-template-columns:repeat(2,1fr);gap:14px;margin-bottom:22px}
@media (min-width:768px){.dsx .ds-stats{grid-template-columns:repeat(4,1fr)}}
.dsx .ds-stat{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);box-shadow:var(--sh-1);padding:14px 16px}
.dsx .ds-stat__label{margin:0;font-size:12px;color:var(--text-2)}
.dsx .ds-stat__value{margin:6px 0 0;font-size:24px;font-weight:700;letter-spacing:-.02em;color:var(--text);font-variant-numeric:tabular-nums}
.dsx .ds-stat--brand .ds-stat__value{color:var(--brand-text)}
.dsx .ds-stat--success .ds-stat__value{color:var(--success-text)}
.dsx .ds-stat--warn .ds-stat__value{color:var(--warn-text)}
.dsx .ds-stat--info .ds-stat__value{color:var(--info-text)}

/* ---------- Panel (제목 헤더가 있는 섹션 컨테이너) ---------- */
.dsx .ds-panel{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);box-shadow:var(--sh-1);overflow:hidden}
.dsx .ds-panel__head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:15px 18px;border-bottom:1px solid var(--border)}
.dsx .ds-panel__title{font-size:15px;font-weight:700;margin:0;color:var(--text)}
/* 패널 본문(폼·텍스트 등 여백이 필요한 내용). 테이블·목록은 가장자리까지 채우는 게 정상이라
   __body 없이 .ds-panel 바로 아래에 둔다. 이게 없던 동안 페이지마다 인라인 padding(16/18/24px)을
   따로 발명해 값이 갈렸다 — 앞으로 패널 본문 여백은 이 클래스로 통일한다. */
.dsx .ds-panel__body{padding:18px}
.dsx .ds-panel__body--lg{padding:24px}

/* ---------- Modal (오버레이 + 패널) — 토글: .is-open ---------- */
.dsx .ds-modal{position:fixed;inset:0;z-index:50;display:none;align-items:center;justify-content:center;padding:16px;background:var(--overlay)}
.dsx .ds-modal.is-open{display:flex}
.dsx .ds-modal__panel{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-xl);box-shadow:var(--sh-3);width:100%;max-width:512px;max-height:90vh;overflow-y:auto}
.dsx .ds-modal--sm .ds-modal__panel{max-width:400px}
.dsx .ds-modal__head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:16px 20px;border-bottom:1px solid var(--border);position:sticky;top:0;background:var(--surface)}
.dsx .ds-modal__title{font-size:16px;font-weight:700;margin:0;color:var(--text)}
.dsx .ds-modal__close{width:30px;height:30px;border-radius:var(--r-md);border:0;background:none;color:var(--text-3);cursor:pointer;font-size:15px;flex:none}
.dsx .ds-modal__close:hover{background:var(--surface-2);color:var(--text)}
.dsx .ds-modal__body{padding:20px}

/* ---------- Icon button (표 액션) ---------- */
.dsx .ds-iconbtn{width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;border-radius:var(--r-md);border:0;background:none;color:var(--text-2);cursor:pointer;font-size:13px}
.dsx .ds-iconbtn:hover{background:var(--surface-2);color:var(--text)}
.dsx .ds-iconbtn--brand:hover{background:var(--brand-soft);color:var(--brand-text)}
.dsx .ds-iconbtn--success:hover{background:var(--success-soft);color:var(--success-text)}
.dsx .ds-iconbtn--info:hover{background:var(--info-soft);color:var(--info-text)}
.dsx .ds-iconbtn--danger:hover{background:var(--danger-soft);color:var(--danger-text)}

/* ---------- Switch (버튼형 on/off — .is-on 토글) ---------- */
.dsx .ds-switch{position:relative;display:inline-block;width:38px;height:22px;border-radius:var(--r-full);border:0;background:var(--surface-3);cursor:pointer;padding:0;transition:background .15s;vertical-align:middle}
.dsx .ds-switch::after{content:"";position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:#fff;box-shadow:var(--sh-1);transition:left .15s}
.dsx .ds-switch.is-on{background:var(--brand)}
.dsx .ds-switch.is-on::after{left:18px}

/* ---------- Spinner (로딩) ---------- */
.dsx .ds-loading{padding:40px 20px;text-align:center;color:var(--text-3)}
.dsx .ds-loading i{font-size:26px}

/* ---------- 유틸리티(Tailwind 완전 대체용 — JS/마크업이 DS 정의만 참조) ---------- */
/* 표시 토글: 기존 Tailwind .hidden 대체(JS classList 토글 포함). 컴포넌트 display 이겨야 하므로 마지막·important */
.dsx .ds-hidden{display:none !important}
/* 레이아웃 */
.dsx .ds-row{display:flex;align-items:center;gap:8px}
.dsx .ds-row-t{display:flex;align-items:flex-start;gap:8px}
.dsx .ds-between{display:flex;align-items:center;justify-content:space-between;gap:8px}
.dsx .ds-col{display:flex;flex-direction:column;gap:8px}
.dsx .ds-wrap{flex-wrap:wrap}
.dsx .ds-gap-sm{gap:4px}.dsx .ds-gap-lg{gap:16px}
.dsx .ds-grid-2{display:grid;grid-template-columns:repeat(2,1fr);gap:12px}
.dsx .ds-grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
.dsx .ds-grid-auto{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:12px}
/* 텍스트 색(토큰) */
.dsx .ds-t{color:var(--text)}.dsx .ds-t2{color:var(--text-2)}.dsx .ds-t3{color:var(--text-3)}
.dsx .ds-t-brand{color:var(--brand-text)}.dsx .ds-t-success{color:var(--success-text)}
.dsx .ds-t-warn{color:var(--warn-text)}.dsx .ds-t-danger{color:var(--danger-text)}.dsx .ds-t-info{color:var(--info-text)}
/* 배경 색(토큰) */
.dsx .ds-bg-2{background:var(--surface-2)}.dsx .ds-bg-3{background:var(--surface-3)}
.dsx .ds-bg-brand{background:var(--brand-soft)}.dsx .ds-bg-success{background:var(--success-soft)}
.dsx .ds-bg-warn{background:var(--warn-soft)}.dsx .ds-bg-danger{background:var(--danger-soft)}.dsx .ds-bg-info{background:var(--info-soft)}
/* 폰트 무게 */
.dsx .ds-fw-medium{font-weight:500}.dsx .ds-fw-semibold{font-weight:600}.dsx .ds-fw-bold{font-weight:700}
/* 소프트 박스(soft 배경 + 대응 텍스트 + 라운드/패딩) — JS가 만드는 상태 배지·요약칸용 */
.dsx .ds-box{padding:12px 14px;border-radius:var(--r-md);background:var(--surface-2);color:var(--text-2)}
.dsx .ds-box--brand{background:var(--brand-soft);color:var(--brand-text)}
.dsx .ds-box--success{background:var(--success-soft);color:var(--success-text)}
.dsx .ds-box--warn{background:var(--warn-soft);color:var(--warn-text)}
.dsx .ds-box--danger{background:var(--danger-soft);color:var(--danger-text)}
.dsx .ds-box--info{background:var(--info-soft);color:var(--info-text)}

/* ---------- 카탈로그 보조(스와치 등) ---------- */
.dsx .ds-grid{display:grid;gap:12px}
.dsx .ds-swatch{border:1px solid var(--border);border-radius:var(--r-md);overflow:hidden}
.dsx .ds-swatch .sw{height:44px}
.dsx .ds-swatch .lb{padding:6px 8px;font-size:11px;font-family:var(--font-mono);color:var(--text-2);background:var(--surface)}

:root{--chrome-h:0px}  /* 셸 위에 얹힌 크롬 높이. 운영=0, 프로토타입 데모바=44px */
/* ============================================================
 * 앱 셸 증분 (2026-08-12 신설) — 리뉴얼 프로토타입 이식분
 *   .ds-shell / .ds-side / .ds-topbar / .ds-content + 화면 패턴 + 카탈로그(.ds-doc*)
 * ⚠️ 의도적으로 `.dsx` 스코프를 붙이지 않는다.
 *    기존 정의는 `.dsx .ds-x`(0,2,0), 이 블록은 `.ds-x`(0,1,0) 이라
 *    이름이 겹쳐도 **기존 화면에서는 서버 정의가 이긴다**(무영향 보장).
 * ⚠️ 프로토타입의 `.ds-toggle` 은 기존 스위치와 구조가 달라 `.ds-checkrow` 로 개명했다.
 * ============================================================ */
/* ============================================================
   1) 증분: 앱 셸  .ds-shell / .ds-side / .ds-topbar / .ds-content
   ============================================================ */
.ds-shell{display:grid;grid-template-columns:var(--side-w) 1fr;min-height:calc(100vh - var(--chrome-h));background:var(--bg)}
.ds-shell[data-rail="1"]{--side-w:var(--side-w-rail)}
.ds-side{background:var(--surface);border-right:1px solid var(--border);position:sticky;top:var(--chrome-h);height:calc(100vh - var(--chrome-h));display:flex;flex-direction:column;z-index:30}
.ds-side__brand{display:flex;align-items:center;gap:10px;padding:12px 14px;border-bottom:1px solid var(--border);min-height:53px}
.ds-side__mark{width:34px;height:34px;border-radius:var(--r-lg);flex:none;display:grid;place-items:center;color:var(--on-brand);font-weight:800;font-size:14px;background:linear-gradient(145deg,var(--brand),var(--brand-hover))}
.ds-side__mark--admin{background:linear-gradient(145deg,var(--danger),var(--danger-text))}
.ds-side__name{font-weight:800;letter-spacing:-.01em}
.ds-side__host{display:block;color:var(--text-3);font-size:10.5px;font-family:var(--font-mono)}
.ds-side__nav{flex:1;overflow-y:auto;padding:6px 10px;scrollbar-width:thin}
.ds-nav-grp{font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--text-3);padding:12px 11px 5px}
.ds-nav-link{display:flex;align-items:center;gap:10px;padding:8px 11px;border-radius:8px;font-size:13px;color:var(--text-2);width:100%;text-align:left}
.ds-nav-link:hover{background:var(--surface-2);color:var(--text);text-decoration:none}
.ds-nav-link svg{width:16px;height:16px;flex:none;opacity:.85}
.ds-nav-link.is-on{background:var(--brand-soft);color:var(--brand-text);font-weight:700}
.ds-nav-link.is-on svg{opacity:1}
.ds-nav-link .ds-pin{margin-left:auto}
.ds-pin{font-size:10px;font-weight:700;padding:1px 6px;border-radius:var(--r-full);background:var(--surface-3);color:var(--text-3)}
.ds-pin--brand{background:var(--brand-soft);color:var(--brand-text)}
.ds-pin--danger{background:var(--danger-soft);color:var(--danger-text)}
.ds-side__foot{border-top:1px solid var(--border);padding:10px}
.ds-side__sep{font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--text-3);padding:2px 11px 6px}
.ds-topbar{position:sticky;top:var(--chrome-h);z-index:20;padding:10px 22px;background:var(--surface);border-bottom:1px solid var(--border);display:flex;align-items:center;gap:14px;min-height:53px}
.ds-topbar__l{display:flex;align-items:center;gap:12px;min-width:0}
.ds-topbar__r{display:flex;align-items:center;gap:8px;margin-left:auto}
.ds-hamburger{display:none;width:32px;height:32px;border-radius:var(--r-md);align-items:center;justify-content:center;color:var(--text-2)}
.ds-hamburger:hover{background:var(--surface-2)}
.ds-crumb{display:flex;align-items:center;gap:6px;font-size:12.5px;color:var(--text-3);min-width:0;white-space:nowrap;overflow:hidden}
.ds-crumb a{color:var(--text-3);white-space:nowrap;flex:none}
.ds-crumb b{min-width:0}
.ds-crumb b{color:var(--text);font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ds-crumb .sep{opacity:.5}
.ds-search{position:relative;width:230px}
.ds-search svg{position:absolute;left:9px;top:8px;width:15px;height:15px;color:var(--text-3)}
.ds-search input{padding-left:29px;padding-right:44px}
.ds-search kbd{position:absolute;right:6px;top:6px;font-family:var(--font-mono);font-size:10.5px;color:var(--text-3);background:var(--surface-2);border:1px solid var(--border);border-radius:var(--r-sm);padding:1px 5px}
.ds-iconbtn{position:relative;width:34px;height:34px;border-radius:var(--r-md);display:inline-flex;align-items:center;justify-content:center;color:var(--text-2)}
.ds-iconbtn:hover{background:var(--surface-2);color:var(--text)}
.ds-iconbtn svg{width:17px;height:17px}
.ds-count{position:absolute;top:2px;right:1px;min-width:15px;height:15px;padding:0 4px;border-radius:var(--r-full);background:var(--danger);color:var(--on-brand);font-size:9.5px;font-weight:800;display:grid;place-items:center;font-family:var(--font-mono)}
.ds-dot{position:absolute;top:6px;right:6px;width:7px;height:7px;border-radius:var(--r-full);background:var(--danger);box-shadow:0 0 0 2px var(--surface)}
.ds-usage{display:flex;align-items:center;gap:8px;height:var(--ctl-h);padding:0 10px;border:1px solid var(--border);border-radius:var(--r-full);background:var(--surface-2);font-size:11.5px;color:var(--text-2)}
.ds-usage b{font-family:var(--font-mono);color:var(--text)}
.ds-user{display:flex;align-items:center;gap:8px;height:var(--ctl-h);padding:0 8px 0 4px;border-radius:var(--r-full)}
.ds-user:hover{background:var(--surface-2)}
.ds-avatar{width:26px;height:26px;border-radius:var(--r-full);flex:none;display:grid;place-items:center;background:var(--brand-soft);color:var(--brand-text);font-weight:800;font-size:11px}
.ds-menu{position:absolute;top:calc(100% + 6px);right:0;min-width:216px;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);box-shadow:var(--sh-3);padding:6px;z-index:60}
.ds-menu__item{display:flex;align-items:center;gap:9px;padding:7px 9px;border-radius:var(--r-md);font-size:12.5px;color:var(--text-2);width:100%}
.ds-menu__item:hover{background:var(--surface-2);color:var(--text);text-decoration:none}
.ds-menu__sep{height:1px;background:var(--border);margin:5px 2px}
.ds-menu__head{padding:8px 9px 6px;font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--text-3)}
.ds-env-badge{display:inline-flex;align-items:center;gap:5px;height:22px;padding:0 8px;border-radius:var(--r-full);background:var(--danger-soft);color:var(--danger-text);border:1px solid var(--danger);font-size:11px;font-weight:800;letter-spacing:.02em}
.ds-switcher{position:relative}
.ds-switcher__btn{display:inline-flex;align-items:center;gap:7px;height:var(--ctl-h);padding:0 10px;border-radius:var(--r-md);border:1px solid var(--border);background:var(--surface-2);font-size:12px;font-weight:700;color:var(--text-2)}
.ds-switcher__btn:hover{color:var(--text)}
.ds-switcher__dot{width:8px;height:8px;border-radius:var(--r-full);background:var(--brand)}
.ds-content{padding:var(--content-pad);background:var(--bg);min-width:0}
.ds-content__in{max-width:var(--content-max);margin:0 auto;min-width:0}
.ds-content--1440{--content-max:1440px}
.ds-content--read{--content-max:880px}
.ds-backdrop{position:fixed;inset:var(--chrome-h) 0 0;background:var(--overlay);z-index:29}
.ds-main{min-width:0;display:flex;flex-direction:column}

/* 접기 레일 (248px ↔ 64px) */
.ds-shell[data-rail="1"] .ds-side__name,.ds-shell[data-rail="1"] .ds-side__host,.ds-shell[data-rail="1"] .ds-nav-grp,.ds-shell[data-rail="1"] .ds-pin,.ds-shell[data-rail="1"] .ds-side__foot{display:none}
.ds-shell[data-rail="1"] .ds-side__brand{justify-content:center;padding:12px 0}
.ds-shell[data-rail="1"] .ds-nav-link,.ds-shell[data-rail="1"] .ds-acc__btn{justify-content:center;padding:8px 0;gap:0;font-size:0}
.ds-shell[data-rail="1"] .ds-nav-link svg:not(:first-child),.ds-shell[data-rail="1"] .ds-acc__btn .chev{display:none}
.ds-shell[data-rail="1"] .ds-acc__body{margin:0;padding:0 0 4px;border-left:0}
.ds-shell[data-rail="1"] .ds-side__nav{padding:6px 8px}

/* 반응형 (데스크톱 우선) */
@media (max-width:1024px){
  .ds-shell{grid-template-columns:1fr}
  .ds-side{position:fixed;top:var(--chrome-h);left:0;width:var(--side-w-drawer);transform:translateX(-101%);transition:transform .18s ease;box-shadow:var(--sh-3)}
  .ds-shell[data-drawer="1"] .ds-side{transform:none}
  .ds-hamburger{display:inline-flex}
  .ds-search{width:180px}
  .ds-shell[data-rail="1"]{--side-w:var(--side-w-drawer)}
  .ds-shell[data-rail="1"] .ds-side__name,.ds-shell[data-rail="1"] .ds-side__host,.ds-shell[data-rail="1"] .ds-nav-grp,.ds-shell[data-rail="1"] .ds-side__foot{display:revert}
  .ds-shell[data-rail="1"] .ds-nav-link,.ds-shell[data-rail="1"] .ds-acc__btn{justify-content:flex-start;padding:8px 11px;gap:10px;font-size:13px}
  .ds-railbtn{display:none}
}
@media (max-width:860px){ .ds-usage{display:none} .ds-search{width:44px} .ds-search input{padding-right:10px} .ds-search kbd{display:none} }
@media (max-width:640px){ .ds-content{padding:16px 14px} .ds-topbar{padding:9px 14px;gap:10px} }
@media (max-width:520px){ .ds-user span{display:none} .ds-crumb .hide-sm{display:none} }
@media (max-width:400px){ .ds-usage,.ds-search{display:none} }

/* ============================================================
   1b) 증분: 사이드바 아코디언 / 명령 팔레트 / 즐겨찾기
   ============================================================ */
.ds-acc__btn{display:flex;align-items:center;gap:10px;width:100%;padding:8px 11px;border-radius:8px;font-size:12.5px;font-weight:700;color:var(--text-2)}
.ds-acc__btn:hover{background:var(--surface-2);color:var(--text)}
.ds-acc__btn svg.ic{width:16px;height:16px;flex:none;opacity:.8}
.ds-acc__btn svg.chev{width:14px;height:14px;margin-left:auto;transition:transform .15s;opacity:.6}
.ds-acc__btn.is-open{color:var(--text)}
.ds-acc__btn.is-open svg.chev{transform:rotate(90deg)}
.ds-acc__body{padding:2px 0 6px 12px;margin-left:13px;border-left:1px solid var(--border)}
.ds-acc__body .ds-nav-link{font-size:12.5px;padding:6px 10px}
.ds-cmdk{position:fixed;inset:0;z-index:90;background:var(--overlay);display:flex;align-items:flex-start;justify-content:center;padding-top:12vh}
.ds-cmdk__panel{width:min(560px,92vw);background:var(--surface);border:1px solid var(--border);border-radius:var(--r-xl);box-shadow:var(--sh-3);overflow:hidden}
.ds-cmdk__input{display:flex;align-items:center;gap:10px;padding:12px 14px;border-bottom:1px solid var(--border)}
.ds-cmdk__input svg{width:17px;height:17px;color:var(--text-3);flex:none}
.ds-cmdk__input input{border:0;background:none;font:inherit;font-size:14px;color:var(--text);width:100%;outline:none}
.ds-cmdk__list{max-height:46vh;overflow-y:auto;padding:6px}
.ds-cmdk__row{display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:var(--r-md);color:var(--text-2);width:100%;text-align:left}
.ds-cmdk__row:hover{background:var(--brand-soft);color:var(--brand-text);text-decoration:none}
.ds-cmdk__row svg{width:15px;height:15px;flex:none}
.ds-cmdk__row .g{margin-left:auto;font-size:11px;color:var(--text-3)}
.ds-cmdk__foot{display:flex;gap:12px;padding:8px 14px;border-top:1px solid var(--border);background:var(--surface-2);font-size:11px;color:var(--text-3)}
.ds-cmdk__foot kbd{font-family:var(--font-mono);background:var(--surface);border:1px solid var(--border);border-radius:var(--r-sm);padding:0 4px}

/* ============================================================
   1c) 증분: 화면 패턴 (.ds-kpi .ds-filterbar .ds-stickybar .ds-toc …)
   ============================================================ */
.ds-grid{display:grid;gap:14px}
.ds-kpi{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:12px}
.ds-kpi__c{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);padding:14px 16px;box-shadow:var(--sh-1);min-height:96px;display:flex;flex-direction:column;gap:6px}
.ds-kpi__l{font-size:11.5px;font-weight:700;color:var(--text-3);letter-spacing:.02em;display:flex;align-items:center;gap:6px}
.ds-kpi__v{font-family:var(--font-mono);font-size:24px;font-weight:700;letter-spacing:-.02em;line-height:1.1}
.ds-kpi__d{font-size:11.5px;color:var(--text-3);margin-top:auto}
.ds-kpi__d b{font-weight:700}
.ds-up{color:var(--success-text)} .ds-down{color:var(--danger-text)}
.ds-filterbar{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:12px 18px;background:var(--surface-2);border-bottom:1px solid var(--border)}
.ds-filterbar__sp{margin-left:auto;display:flex;align-items:center;gap:8px}
.ds-bulkbar{display:flex;align-items:center;gap:10px;padding:9px 18px;background:var(--brand-soft);border-bottom:1px solid var(--brand-border);color:var(--brand-text);font-weight:700;font-size:12.5px}
.ds-stickybar{position:sticky;bottom:0;z-index:15;display:flex;align-items:center;gap:10px;padding:12px 24px;background:var(--surface);border-top:1px solid var(--border);box-shadow:var(--sh-2);margin:18px -24px -22px}
.ds-pager{display:flex;align-items:center;gap:4px;padding:12px 18px;justify-content:center;border-top:1px solid var(--border)}
.ds-pager a,.ds-pager span{min-width:30px;height:30px;padding:0 8px;display:inline-flex;align-items:center;justify-content:center;border-radius:var(--r-md);font-size:12.5px;color:var(--text-2);font-family:var(--font-mono)}
.ds-pager a:hover{background:var(--surface-2);text-decoration:none}
.ds-pager a[aria-current="page"]{background:var(--brand);color:var(--on-brand);font-weight:700}
.ds-skel{background:linear-gradient(90deg,var(--surface-2),var(--surface-3),var(--surface-2));background-size:200% 100%;animation:dsShimmer 1.2s linear infinite;border-radius:var(--r-sm)}
@keyframes dsShimmer{from{background-position:200% 0}to{background-position:-200% 0}}
.ds-chip{display:inline-flex;align-items:center;gap:5px;height:var(--ctl-h-sm);padding:0 10px;border-radius:var(--r-full);border:1px solid var(--border);background:var(--surface);font-size:12px;font-weight:600;color:var(--text-2)}
.ds-chip:hover{background:var(--surface-2);text-decoration:none;color:var(--text)}
.ds-chip.is-on{background:var(--brand-soft);border-color:var(--brand-border);color:var(--brand-text);font-weight:700}
.ds-hero{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-xl);padding:44px 40px;box-shadow:var(--sh-1);position:relative;overflow:hidden}
.ds-hero::after{content:"";position:absolute;inset:0;background:radial-gradient(680px 260px at 88% -10%,var(--brand-soft),transparent 70%);pointer-events:none}
.ds-hero h1{font-size:34px;font-weight:800;letter-spacing:-.025em;line-height:1.25;max-width:16em}
.ds-hero p{color:var(--text-2);font-size:14px;margin-top:12px;max-width:44em;text-wrap:pretty}
.ds-price{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:14px}
.ds-price__c{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);padding:20px;display:flex;flex-direction:column;gap:12px;box-shadow:var(--sh-1)}
.ds-price__c.is-reco{border-color:var(--brand);box-shadow:0 0 0 1px var(--brand),var(--sh-2)}
.ds-price__v{font-family:var(--font-mono);font-size:26px;font-weight:700;letter-spacing:-.02em}
.ds-price__c ul{display:flex;flex-direction:column;gap:7px;font-size:12.5px;color:var(--text-2)}
.ds-price__c li{display:flex;gap:7px}
.ds-thumb{position:relative;aspect-ratio:16/9;background:repeating-linear-gradient(135deg,var(--surface-2),var(--surface-2) 7px,var(--surface-3) 7px,var(--surface-3) 14px);border-bottom:1px solid var(--border);display:grid;place-items:center}
.ds-thumb span:not([class]){font-family:var(--font-mono);font-size:10.5px;color:var(--text-3);background:var(--surface);padding:2px 6px;border-radius:var(--r-sm);border:1px solid var(--border)}
.ds-thumb__meta{position:absolute;right:7px;bottom:7px;font-family:var(--font-mono);font-size:10.5px;background:var(--scrim);color:var(--on-brand);padding:1px 5px;border-radius:var(--r-sm)}
.ds-cardgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:14px}
.ds-postcard{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);overflow:hidden;display:flex;flex-direction:column;box-shadow:var(--sh-1)}
.ds-postcard:hover{border-color:var(--border-strong);box-shadow:var(--sh-2)}
.ds-postcard__b{padding:12px 14px 14px;display:flex;flex-direction:column;gap:8px;flex:1}
.ds-postcard h3{font-size:13.5px;font-weight:700;line-height:1.45;letter-spacing:-.01em}
.ds-postcard h3 a{color:var(--text)}
.ds-postcard__m{display:flex;align-items:center;gap:10px;font-size:11.5px;color:var(--text-3);margin-top:auto;font-family:var(--font-mono)}
.ds-postcard__m .it{display:inline-flex;align-items:center;gap:3px}
.ds-postcard__m svg{width:13px;height:13px}
.ds-ogcard{aspect-ratio:16/9;border-bottom:1px solid var(--border);display:flex;flex-direction:column;justify-content:center;gap:6px;padding:18px;background:linear-gradient(140deg,var(--brand-soft),var(--surface-2))}
.ds-typebar{display:flex;flex-wrap:wrap;align-items:center;gap:6px;padding:8px 10px;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg)}
.ds-clamp2{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;color:var(--text-2);font-size:12.5px;line-height:1.6}
.ds-serialmeta{display:flex;align-items:center;gap:8px;font-size:11.5px;color:var(--text-2);background:var(--surface-2);border-radius:var(--r-md);padding:6px 9px}
.ds-serialmeta time{margin-left:auto;font-family:var(--font-mono);color:var(--text-3)}
.ds-eplist{max-height:260px;overflow-y:auto}
.ds-eplist .ds-rank.is-read .t{color:var(--text-3)}
.ds-eplist .ds-rank.is-cur{background:var(--brand-soft)}
.ds-player{aspect-ratio:16/9;background:var(--scrim);display:grid;place-items:center;border-radius:var(--r-lg) var(--r-lg) 0 0;position:relative}
.ds-player .play{width:56px;height:56px;border-radius:var(--r-full);background:var(--on-brand);display:grid;place-items:center;color:var(--text)}
.ds-player small{position:absolute;bottom:10px;left:12px;font-family:var(--font-mono);font-size:10.5px;color:var(--surface-3)}
.ds-playlist__row{display:flex;gap:9px;padding:7px 6px;border-radius:var(--r-md);align-items:flex-start}
.ds-playlist__row:hover{background:var(--surface-2);text-decoration:none}
.ds-playlist__row .th{width:56px;height:32px;flex:none;border-radius:var(--r-sm);background:repeating-linear-gradient(135deg,var(--surface-2),var(--surface-2) 5px,var(--surface-3) 5px,var(--surface-3) 10px);border:1px solid var(--border);position:relative}
.ds-playlist__row .th b{position:absolute;right:2px;bottom:1px;font-family:var(--font-mono);font-size:9px;background:var(--scrim);color:var(--on-brand);padding:0 3px;border-radius:2px}
.ds-ogcard b{font-size:14px;font-weight:800;letter-spacing:-.01em;color:var(--brand-text);line-height:1.4}
.ds-ogcard small{font-family:var(--font-mono);font-size:10.5px;color:var(--text-3)}
.ds-read{display:grid;grid-template-columns:minmax(0,var(--read-w)) var(--aside-w);gap:24px;align-items:start}
.ds-article{min-width:0;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);box-shadow:var(--sh-1);overflow:hidden}
.ds-prose{padding:24px;font-size:15px;line-height:1.85;color:var(--text)}
.ds-prose h2{font-size:19px;font-weight:800;letter-spacing:-.015em;margin:30px 0 10px;line-height:1.4}
.ds-prose h3{font-size:15.5px;font-weight:700;margin:22px 0 8px;line-height:1.5}
.ds-prose p{margin:0 0 14px;color:var(--text-2);text-wrap:pretty}
.ds-prose figure{margin:18px 0}
.ds-prose figcaption{font-size:11.5px;color:var(--text-3);margin-top:6px;text-align:center}
.ds-aside{display:flex;flex-direction:column;gap:14px;position:sticky;top:calc(var(--chrome-h) + var(--topbar-h) + 12px)}
.ds-widget{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);box-shadow:var(--sh-1);overflow:hidden}
.ds-widget__h{display:flex;align-items:center;gap:8px;padding:10px 14px;border-bottom:1px solid var(--border);font-weight:700;font-size:12.5px}
.ds-widget__h .tabs{margin-left:auto;display:flex;gap:2px}
.ds-widget__h .tabs button{font-size:11px;font-weight:700;padding:2px 7px;border-radius:var(--r-full);color:var(--text-3)}
.ds-widget__h .tabs button.is-on{background:var(--surface-3);color:var(--text)}
.ds-widget__b{padding:8px 10px}
.ds-rank{display:flex;gap:9px;padding:6px 6px;border-radius:var(--r-md);align-items:flex-start}
.ds-rank:hover{background:var(--surface-2);text-decoration:none}
.ds-rank .n{font-family:var(--font-mono);font-size:11.5px;font-weight:700;color:var(--text-3);width:14px;flex:none;text-align:center;padding-top:1px}
.ds-rank .t{font-size:12.5px;color:var(--text);line-height:1.5;display:block}
.ds-rank .m{font-size:10.5px;color:var(--text-3);font-family:var(--font-mono)}
.ds-rank:first-child .n{color:var(--brand-text)}
.ds-taglist{display:flex;flex-wrap:wrap;gap:6px;padding:10px}
.ds-toc{font-size:12.5px}
.ds-toc a{display:block;padding:4px 10px;border-left:2px solid transparent;color:var(--text-2);line-height:1.5}
.ds-toc a:hover{color:var(--text);text-decoration:none;background:var(--surface-2)}
.ds-toc a.is-on{border-left-color:var(--brand);color:var(--brand-text);font-weight:700;background:var(--brand-soft)}
.ds-toc a.lv3{padding-left:22px;font-size:12px}
.ds-progress{position:sticky;top:calc(var(--chrome-h) + var(--topbar-h));z-index:18;height:3px;background:transparent;margin:calc(var(--topbar-h) * 0)}
.ds-progress__in{height:3px;background:var(--brand);width:38%;border-radius:0 var(--r-full) var(--r-full) 0}
.ds-pairnav{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:18px}
.ds-pairnav__a{display:flex;gap:12px;padding:12px;border:1px solid var(--border);border-radius:var(--r-lg);background:var(--surface);align-items:center;box-shadow:var(--sh-1)}
.ds-pairnav__a:hover{border-color:var(--brand-border);background:var(--brand-soft);text-decoration:none}
.ds-pairnav__a.is-series{border-color:var(--brand-border)}
.ds-pairnav__a.next{flex-direction:row-reverse;text-align:right}
.ds-pairnav__t{width:64px;height:44px;flex:none;border-radius:var(--r-md);background:repeating-linear-gradient(135deg,var(--surface-2),var(--surface-2) 6px,var(--surface-3) 6px,var(--surface-3) 12px);border:1px solid var(--border)}
.ds-pairnav__l{font-size:10.5px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--text-3);display:flex;align-items:center;gap:4px}
.ds-pairnav__a.is-series .ds-pairnav__l{color:var(--brand-text)}
.ds-pairnav__x{font-size:13px;font-weight:700;color:var(--text);line-height:1.45;display:block;margin-top:3px}
.ds-comment{display:flex;gap:10px;padding:14px 0;border-bottom:1px solid var(--border)}
.ds-comment--reply{margin-left:34px;padding-left:14px;border-left:2px solid var(--border)}
.ds-comment__b{min-width:0;flex:1}
.ds-comment__h{display:flex;align-items:center;gap:8px;font-size:12px}
.ds-comment__h b{font-weight:700}
.ds-comment__h time{color:var(--text-3);font-family:var(--font-mono);font-size:11px}
.ds-comment p{color:var(--text-2);margin-top:5px;line-height:1.7}
.ds-comment__act{display:flex;gap:10px;margin-top:7px;font-size:11.5px;color:var(--text-3)}
.ds-totop{position:fixed;right:22px;bottom:22px;width:40px;height:40px;border-radius:var(--r-full);background:var(--surface);border:1px solid var(--border-strong);box-shadow:var(--sh-2);display:grid;place-items:center;color:var(--text-2);z-index:25}
.ds-totop:hover{background:var(--surface-2);color:var(--text)}
.ds-crumbline{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--text-3);margin-bottom:10px;flex-wrap:wrap}
.ds-crumbline a{color:var(--text-3)}
.ds-metanote{font-family:var(--font-mono);font-size:11px;color:var(--text-3);background:var(--surface-2);border:1px dashed var(--border-strong);border-radius:var(--r-md);padding:10px 12px;white-space:pre-wrap;overflow-x:auto;line-height:1.7}
.ds-bars{display:flex;align-items:flex-end;gap:8px;height:150px;padding-top:10px}
.ds-bars .b{flex:1;background:var(--brand);border-radius:var(--r-sm) var(--r-sm) 0 0;min-height:3px;opacity:.85}
.ds-bars .b:hover{opacity:1}
.ds-barx{display:flex;gap:8px;margin-top:6px;font-family:var(--font-mono);font-size:10.5px;color:var(--text-3)}
.ds-barx span{flex:1;text-align:center}
.ds-act{display:flex;gap:10px;padding:10px 0;border-bottom:1px solid var(--border);align-items:flex-start}
.ds-act:last-child{border-bottom:0}
.ds-act .tm{font-family:var(--font-mono);font-size:11px;color:var(--text-3);flex:none;padding-top:2px}
@media (max-width:1180px){ .ds-read{grid-template-columns:minmax(0,1fr)} .ds-aside{position:static;flex-direction:row;flex-wrap:wrap} .ds-aside>*{flex:1 1 300px} }
@media (max-width:760px){ .ds-pairnav{grid-template-columns:1fr} .ds-stickybar{padding:10px 14px;margin:16px -14px -16px} }

/* ============================================================
   1e) 증분: 사이트 정체성 (--site) · 관리자 사이트 전환기 · 범위 표시
   ============================================================ */
/* 사이트 정체성은 색이 아니라 형태·질감이다 (색상환은 의미색·브랜드가 선점) */
.ds-side__mark--site{background:linear-gradient(145deg,var(--brand),var(--brand-hover));color:var(--on-brand)}
.ds-side__mark--site[data-site="main"]{border-radius:var(--r-lg)}
.ds-side__mark--site[data-site="biztool"]{border-radius:var(--r-full)}
.ds-side__mark--site[data-site="lab"]{border-radius:var(--r-lg) var(--r-sm) var(--r-lg) var(--r-sm)}
.ds-side__mark--site[data-site="tool"]{border-radius:var(--r-sm)}
.ds-side[data-site="main"]{box-shadow:inset 3px 0 0 var(--border-strong)}
.ds-side[data-site="biztool"]{box-shadow:inset 3px 0 0 var(--border-strong),inset 6px 0 0 var(--surface),inset 9px 0 0 var(--border-strong)}
.ds-side[data-site="lab"]{background-image:linear-gradient(var(--border-strong) 50%,transparent 0);background-size:3px 10px;background-repeat:repeat-y;background-position:left top}
.ds-side[data-site="tool"]{background-image:linear-gradient(var(--border-strong) 70%,transparent 0);background-size:3px 22px;background-repeat:repeat-y;background-position:left top}
.ds-sitemark{width:22px;height:22px;flex:none;display:grid;place-items:center;color:var(--on-brand);font-size:10px;font-weight:800;background:linear-gradient(145deg,var(--brand),var(--brand-hover))}
.ds-sitemark[data-site="main"]{border-radius:var(--r-md)}
.ds-sitemark[data-site="biztool"]{border-radius:var(--r-full)}
.ds-sitemark[data-site="lab"]{border-radius:var(--r-md) 2px var(--r-md) 2px}
.ds-sitemark[data-site="tool"]{border-radius:2px}
.ds-sitesw{position:relative}
.ds-env-badge,.ds-tier,.ds-scope{white-space:nowrap;flex:none}
.ds-topbar__l{flex:1 1 auto;overflow:hidden}
.ds-topbar__r{flex:none}
.ds-sitesw__btn{white-space:nowrap;flex:none;display:inline-flex;align-items:center;gap:8px;height:var(--ctl-h);padding:0 10px;border-radius:var(--r-md);border:1px dashed var(--border-strong);background:var(--surface-2);font-size:12px;font-weight:700;color:var(--text-2)}
.ds-sitesw__btn:hover{color:var(--text);border-style:solid}
.ds-sitesw__dot{width:9px;height:9px;background:var(--text-2);flex:none}
.ds-sitesw__dot[data-site="main"]{border-radius:var(--r-sm)}
.ds-sitesw__dot[data-site="biztool"]{border-radius:var(--r-full)}
.ds-sitesw__dot[data-site="lab"]{border-radius:var(--r-sm) 0 var(--r-sm) 0}
.ds-sitesw__dot[data-site="tool"]{border-radius:0}
.ds-sitesw__menu{position:absolute;top:calc(100% + 6px);right:0;min-width:290px;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);box-shadow:var(--sh-3);padding:6px;z-index:70}
.ds-siterow{display:flex;align-items:center;gap:10px;padding:8px 9px;border-radius:var(--r-md);width:100%;text-align:left;color:var(--text-2)}
.ds-siterow:hover{background:var(--surface-2);color:var(--text);text-decoration:none}
.ds-siterow.is-on{background:var(--brand-soft);color:var(--brand-text)}
.ds-siterow.is-locked{opacity:.55;cursor:not-allowed}
.ds-siterow .nm{font-weight:700;font-size:12.5px;display:block}
.ds-siterow .hs{font-family:var(--font-mono);font-size:10.5px;color:var(--text-3)}
.ds-scope{display:inline-flex;align-items:center;gap:6px;height:22px;padding:0 9px;border-radius:var(--r-full);background:var(--surface-2);border:1px solid var(--border-strong);font-size:11px;font-weight:700;color:var(--text-2)}
.ds-scope b{color:var(--text)}
.ds-scope--all{background:var(--info-soft);border-color:var(--info);color:var(--info-text)}
.ds-tier{display:inline-flex;align-items:center;gap:5px;height:22px;padding:0 8px;border-radius:var(--r-full);font-size:11px;font-weight:800;background:var(--surface-3);color:var(--text-2)}
.ds-tier--global{background:var(--info-soft);color:var(--info-text)}
@media (max-width:1180px){ .ds-topbar .ds-tier{display:none} }
@media (max-width:640px){ .ds-topbar .ds-sitesw,.ds-topbar .ds-switcher{display:none} }
@media (max-width:520px){ .ds-topbar .ds-btn--ghost{display:none} }
.ds-login{min-height:calc(100vh - var(--chrome-h));display:grid;place-items:center;padding:40px 16px;background:var(--bg)}
.ds-login__c{width:min(380px,100%);background:var(--surface);border:1px solid var(--border);border-radius:var(--r-xl);box-shadow:var(--sh-2);overflow:hidden}
.ds-login__top{padding:22px 24px 18px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:11px}
.ds-pwwrap{position:relative}
.ds-pwwrap .ds-input{padding-right:38px}
.ds-pwwrap button{position:absolute;right:4px;top:3px;width:26px;height:26px;border-radius:var(--r-sm);display:grid;place-items:center;color:var(--text-3)}
.ds-pwwrap button:hover{background:var(--surface-2);color:var(--text)}
.ds-checkrow{display:flex;align-items:center;gap:10px;position:relative;cursor:pointer}
.ds-checkrow__in{position:absolute;left:0;top:0;width:38px;height:22px;margin:0;opacity:0;cursor:pointer}
.ds-checkrow__ui{position:relative;width:38px;height:22px;border-radius:var(--r-full);background:var(--surface-3);border:1px solid var(--border-strong);flex:none;transition:background .15s}
.ds-checkrow__ui::after{content:"";position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:var(--r-full);background:var(--surface);box-shadow:var(--sh-1);transition:left .15s}
.ds-checkrow__in:checked+.ds-checkrow__ui{background:var(--brand);border-color:var(--brand)}
.ds-checkrow__in:checked+.ds-checkrow__ui::after{left:18px}
.ds-checkrow__in:focus-visible+.ds-checkrow__ui{outline:2px solid var(--brand);outline-offset:2px}
.ds-cmdk__row.is-active{background:var(--brand-soft);color:var(--brand-text)}
.ds-srlive{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
@media (prefers-reduced-motion: reduce){
  .ds-side{transition:none}
  .ds-checkrow__ui,.ds-checkrow__ui::after,.ds-switch::after{transition:none}
  .ds-skel{animation:none;background:var(--surface-2)}
  .ds-spin{animation:none;border-top-color:var(--border-strong)}
}

/* ============================================================
   1d) 증분: 디자인 시스템 카탈로그 (.ds-doc*)
   ============================================================ */
.ds-doc{display:grid;grid-template-columns:212px minmax(0,1fr);gap:20px;align-items:start}
.ds-doc__nav{position:sticky;top:calc(var(--chrome-h) + var(--topbar-h) + 12px);display:flex;flex-direction:column;gap:2px;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);padding:8px;box-shadow:var(--sh-1)}
.ds-doc__nav a{display:block;padding:6px 10px;border-radius:var(--r-md);font-size:12.5px;color:var(--text-2)}
.ds-doc__nav a:hover{background:var(--surface-2);color:var(--text);text-decoration:none}
.ds-doc__nav a.is-on{background:var(--brand-soft);color:var(--brand-text);font-weight:700}
.ds-doc__nav .grp{font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--text-3);padding:10px 10px 4px}
.ds-doc__body{display:flex;flex-direction:column;gap:16px;min-width:0}
.ds-doc__h2{font-size:16px;font-weight:800;letter-spacing:-.01em;display:flex;align-items:center;gap:8px;margin-top:6px}
.ds-sw{display:grid;grid-template-columns:repeat(auto-fill,minmax(146px,1fr));gap:10px}
.ds-sw__c{border:1px solid var(--border);border-radius:var(--r-md);overflow:hidden;background:var(--surface);text-align:left;width:100%}
.ds-sw__c:hover{border-color:var(--border-strong)}
.ds-sw__chip{height:46px;border-bottom:1px solid var(--border)}
.ds-sw__m{padding:6px 8px;display:flex;flex-direction:column;gap:1px}
.ds-sw__m b{font-family:var(--font-mono);font-size:11px;font-weight:700}
.ds-sw__m span{font-family:var(--font-mono);font-size:10px;color:var(--text-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ds-themepair{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.ds-themepair>div{border:1px solid var(--border);border-radius:var(--r-lg);padding:12px;background:var(--bg)}
.ds-themepair h4{font-size:11px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--text-3);margin-bottom:8px}
.ds-ex{border:1px solid var(--border);border-radius:var(--r-md);overflow:hidden;background:var(--surface)}
.ds-ex__demo{padding:16px;display:flex;flex-wrap:wrap;gap:10px;align-items:center;background:var(--bg)}
.ds-ex__code{display:flex;align-items:flex-start;gap:8px;padding:8px 10px;border-top:1px solid var(--border);background:var(--surface-2)}
.ds-ex__code pre{flex:1;font-family:var(--font-mono);font-size:11px;color:var(--text-2);white-space:pre-wrap;line-height:1.6;margin:0;overflow-x:auto}
.ds-matrix{display:grid;grid-template-columns:repeat(auto-fit,minmax(104px,1fr));gap:10px;padding:14px 16px;background:var(--bg)}
.ds-matrix__c{display:flex;flex-direction:column;gap:6px;align-items:flex-start}
.ds-matrix__c small{font-size:10.5px;color:var(--text-3);font-weight:700;letter-spacing:.03em}
.ds-btn.is-hover{background:var(--surface-2)}
.ds-btn--primary.is-hover{background:var(--brand-hover);border-color:var(--brand-hover)}
.ds-btn.is-focus{outline:2px solid var(--brand);outline-offset:2px}
.ds-btn.is-disabled{opacity:.45;pointer-events:none}
.ds-btn.is-error{border-color:var(--danger);color:var(--danger-text)}
.ds-spin{width:13px;height:13px;border-radius:var(--r-full);border:2px solid var(--border-strong);border-top-color:var(--brand);animation:dsSpin .7s linear infinite;flex:none}
@keyframes dsSpin{to{transform:rotate(360deg)}}
.ds-warncard{border:1px solid var(--danger);border-left-width:3px;border-radius:var(--r-lg);background:var(--surface);overflow:hidden}
.ds-warncard__h{display:flex;align-items:center;gap:8px;padding:11px 16px;background:var(--danger-soft);color:var(--danger-text);font-weight:800;border-bottom:1px solid var(--border)}
.ds-vs{display:grid;grid-template-columns:1fr 1fr;gap:12px;padding:16px}
.ds-vs__l{font-size:11px;font-weight:800;letter-spacing:.03em;margin-bottom:6px;display:flex;align-items:center;gap:5px}
.ds-vs__bad{color:var(--danger-text)} .ds-vs__good{color:var(--success-text)}
.ds-diagram{border:1px solid var(--border-strong);border-radius:var(--r-md);overflow:hidden;background:var(--surface-2);font-family:var(--font-mono);font-size:11px}
.ds-diagram__row{display:flex;min-height:112px}
.ds-diagram__side{width:150px;flex:none;border-right:1px solid var(--border-strong);background:var(--surface);padding:8px;display:flex;flex-direction:column;gap:4px;color:var(--text-2)}
.ds-diagram__rail{width:56px;flex:none;border-right:1px solid var(--border-strong);background:var(--surface);display:grid;place-items:center;color:var(--text-3);text-align:center;padding:4px}
.ds-diagram__col{flex:1;display:flex;flex-direction:column;min-width:0}
.ds-diagram__top{border-bottom:1px solid var(--border-strong);background:var(--surface);padding:8px;color:var(--text-2)}
.ds-diagram__cnt{flex:1;padding:10px 8px;color:var(--text-3);background:var(--bg)}
.ds-doc__search{position:sticky;top:calc(var(--chrome-h) + var(--topbar-h));z-index:12;background:var(--bg);padding:0 0 12px}
.ds-tabs{display:flex;gap:2px;border-bottom:1px solid var(--border)}
.ds-tabs button{padding:7px 12px;font-size:12.5px;font-weight:700;color:var(--text-3);border-bottom:2px solid transparent;margin-bottom:-1px}
.ds-tabs button.is-on{color:var(--brand-text);border-bottom-color:var(--brand)}
.ds-toast{display:flex;align-items:center;gap:10px;padding:10px 14px;border-radius:var(--r-lg);background:var(--surface);border:1px solid var(--border);box-shadow:var(--sh-3);font-size:12.5px}
.ds-toast--success{border-color:var(--success);background:var(--success-soft);color:var(--success-text)}
.ds-toast--danger{border-color:var(--danger);background:var(--danger-soft);color:var(--danger-text)}
.ds-field-error{color:var(--danger-text);font-size:11.5px;margin-top:5px;display:flex;align-items:center;gap:4px}
.ds-input.is-error{border-color:var(--danger)}
@media (max-width:900px){ .ds-doc{grid-template-columns:1fr} .ds-doc__nav{position:static;flex-direction:row;flex-wrap:wrap} .ds-themepair,.ds-vs{grid-template-columns:1fr} }


/* ---------- Font Awesome 호환 보정 (2026-08-12) ----------
 * 셸 증분은 아이콘을 <svg> 기준으로 썼는데, 관리자 메뉴 정본(admin-menu-items.php)은
 * Font Awesome 클래스(fas fa-*)를 담고 있다. 정본을 고치지 않고 <i> 를 같은 규격으로 맞춘다. */
.ds-nav-link i{width:16px;text-align:center;flex:none;opacity:.85;font-size:13px}
.ds-nav-link.is-on i{opacity:1}
.ds-acc__btn i.ic{width:16px;text-align:center;flex:none;opacity:.8;font-size:13px}
.ds-acc__btn i.chev{margin-left:auto;font-size:11px;opacity:.6;transition:transform .15s}
.ds-acc__btn.is-open i.chev{transform:rotate(90deg)}
.ds-cmdk__input i{width:17px;text-align:center;color:var(--text-3);flex:none}
.ds-cmdk__row i{width:15px;text-align:center;flex:none}

/* ---------- 앱 셸 토큰 (2026-08-12 보강) ----------
 * 셸 증분(.ds-shell 등)이 참조하는 치수 토큰. 프로토타입에서는 재현 블록의 :root 에 있었는데
 * 증분만 옮기며 누락됐다 → 사이드바가 var(--side-w) 미해결로 전체폭이 되던 버그. .dsx 스코프에 정의.
 * --chrome-h 는 위에서 이미 0 으로 정의됨(프로토타입 데모바 높이, 운영=0). */
.dsx{
  --side-w:248px; --side-w-rail:64px; --side-w-drawer:min(84vw,300px);
  --topbar-h:55px; --content-max:none; --content-pad:22px 24px; --aside-w:300px; --read-w:800px;
}

/* ---------- 디자인 시스템 카탈로그 레이아웃 헬퍼 (2026-08-12) ----------
 * design-system.php(관리자 카탈로그 화면)가 쓰는 배치 전용 클래스. .ds-doc* 계열과 짝. */
.ds-swatchgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px}
.ds-swatch{display:flex;flex-direction:column;border:1px solid var(--border);border-radius:var(--r-md);overflow:hidden;background:var(--surface);cursor:pointer;text-align:left;transition:box-shadow .12s}
.ds-swatch:hover{box-shadow:var(--sh-2)}
.ds-swatch.is-copied{box-shadow:0 0 0 2px var(--success)}
.ds-swatch .sw{height:44px;border-bottom:1px solid var(--border)}
.ds-swatch .lb{display:flex;flex-direction:column;gap:1px;padding:6px 9px}
.ds-swatch .lb .nm{font-family:var(--font-mono);font-size:11.5px;color:var(--text)}
.ds-swatch .lb .val{font-family:var(--font-mono);font-size:10px;color:var(--text-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ds-doc__row2{display:grid;grid-template-columns:1fr 1fr;gap:24px}
@media(max-width:760px){.ds-doc__row2{grid-template-columns:1fr}}
.ds-doc__subh{font-size:11px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;color:var(--text-3);margin-bottom:10px}
.ds-doc__specrow{display:flex;align-items:center;gap:12px;padding:6px 0;font-size:12.5px}
.ds-doc__specrow .box{width:34px;height:34px;flex:none;background:var(--brand-soft);border:1px solid var(--brand-border)}
.ds-doc__specrow .bar{width:80px;flex:none;background:var(--brand);border-radius:var(--r-sm)}
.ds-doc__specrow .v{font-family:var(--font-mono);color:var(--text-2)}
.ds-doc__specrow .u{color:var(--text-3);margin-left:auto}
.ds-doc__formgrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}
@media(max-width:640px){.ds-doc__formgrid{grid-template-columns:1fr}}
.ds-doc__formgrid label.ds-label{display:block;margin-bottom:5px}
.ds-ex__row{display:flex;flex-wrap:wrap;gap:9px;align-items:center}

/* ---------- 셸 토큰 :root 노출 (2026-08-12, A안 래퍼 스왑) ----------
 * `.ds-shell`/`.ds-content`/`.ds-backdrop` 는 콘텐츠를 `.dsx` 밖에 두기 위해 `.dsx` 조상 없이 렌더된다.
 * 그래서 이들이 참조하는 소수 토큰을 :root 에도 노출한다. 리셋(.dsx h1 등)은 .dsx 스코프 그대로라
 * 콘텐츠(Tailwind)에 새지 않는다. 사이드바·상단바는 여전히 .dsx 로 전체 토큰·컴포넌트를 얻는다.
 * ⚠️ 이유 실증: DS 리셋 `.dsx h1`(0,1,1)이 Tailwind `.text-2xl`(0,1,0)을 이겨, 콘텐츠를 .dsx 안에
 *    넣으면 제목·목록·버튼이 깨진다. 따라서 콘텐츠는 반드시 .dsx 밖. */
:root{
  --side-w:248px; --side-w-rail:64px; --side-w-drawer:min(84vw,300px);
  --content-pad:22px 24px;
  --bg:oklch(0.982 0.003 265);
  --overlay:oklch(0.255 0.015 265 / 0.45);
  --sh-3:0 12px 32px oklch(0.255 0.015 265 / 0.14);
}
@media (prefers-color-scheme:dark){ :root:not([data-theme="light"]){
  --bg:oklch(0.185 0.012 265);
  --overlay:oklch(0.130 0.010 265 / 0.62);
  --sh-3:0 12px 32px oklch(0 0 0 / 0.5);
}}
:root[data-theme="dark"]{
  --bg:oklch(0.185 0.012 265);
  --overlay:oklch(0.130 0.010 265 / 0.62);
  --sh-3:0 12px 32px oklch(0 0 0 / 0.5);
}

/* ---------- `hidden` 속성 보증 (2026-08-12) ----------
 * A안 래퍼 스왑으로 `.dsx` 가 사이드바·상단바로 좁혀지면서 `.ds-cmdk`(body 직속)와
 * `.ds-backdrop` 이 `.dsx` 조상을 잃었다. 그래서 `.dsx [hidden]{display:none}`(0,2,0)이
 * 안 걸리고, `.ds-cmdk{display:flex}`(0,1,0)가 뒤에 와서 `[hidden]{display:none}`(0,1,0)을
 * 순서로 이겨 **팔레트가 항상 떠 있었다**(JS 는 닫힘으로 알아 Esc 도 안 먹고 전 화면이 막혔다).
 * 속성 선택자를 붙여 (0,2,0)으로 올려 `hidden` 이 항상 이기게 한다. */
.ds-cmdk[hidden],
.ds-backdrop[hidden] { display: none; }

/* 92개 Tailwind 화면은 자체 `p-4 md:p-6` 래퍼로 여백을 만든다(레거시 래퍼 시절부터의 계약).
   거기에 `.ds-content` 패딩까지 더하면 좌우 14px 이 이중으로 붙는다. 콘텐츠가 DS 화면이
   아닐 때는 셸이 패딩을 내지 않는다 — 여백의 주인을 한쪽으로 못박는다. */
.ds-content--bare { padding: 0; }

/* ---------- 공개 셸 상단바 검색 (2026-08-12) ----------
   시안의 "제품·콘텐츠·도움말 검색". 상단바 가운데를 채우고 좁은 화면에서는 숨긴다. */
.ds-topbar__search{
  display:flex; align-items:center; gap:8px; flex:1 1 auto;
  max-width:420px; margin-left:16px;
  height:var(--ctl-h); padding:0 12px;
  background:var(--surface-2); border:1px solid var(--border);
  border-radius:var(--r-md); color:var(--text-3);
}
.ds-topbar__search i{ font-size:12px; flex:none }
.ds-topbar__search input{
  border:0; background:none; outline:none; width:100%;
  font:inherit; font-size:13px; color:var(--text);
}
.ds-topbar__search:focus-within{ border-color:var(--brand); box-shadow:0 0 0 3px var(--brand-soft) }
@media (max-width:900px){ .ds-topbar__search{ display:none } }

/* ---------- 사이드바 3단(하위 항목) + 배지 (2026-08-12, 공개 셸 시안 대응) ----------
   시안의 콘텐츠 그룹은 "콘텐츠 > AI 콘텐츠 > 전체/블로그/기술/영상/만화/소설" 3단이다.
   기존 DS 에는 2단까지만 있어 신설한다. 왼쪽 가는 선으로 계층을 표시한다. */
.ds-nav-sub{
  margin:2px 0 4px 0;
  padding-left:12px;
  border-left:1px solid var(--border);
  margin-left:14px;
  display:flex; flex-direction:column; gap:1px;
}
.ds-nav-link--sub{
  font-size:12.5px;
  padding-top:5px; padding-bottom:5px;
  color:var(--text-2);
}
.ds-nav-link--sub i{ font-size:11px; opacity:.85 }

/* 항목 옆 작은 표식(NEW·연재). em 을 쓰되 기울임은 끈다. */
.ds-nav-badge{
  margin-left:auto;
  font-style:normal; font-weight:600; font-size:9.5px; line-height:1;
  padding:3px 5px; border-radius:var(--r-sm);
  background:var(--brand-soft); color:var(--brand-text);
  letter-spacing:.02em; flex:none;
}
.ds-nav-link.is-on .ds-nav-badge{ background:var(--surface); }

/* ============================================================
 * 시안 컴포넌트 이식 — 홈 화면분 (2026-08-13)
 *
 * 홈을 시안 구조로 짰는데 **내가 쓴 27종 중 18종이 CSS 에 없어** 화면이
 * 통째로 스타일을 못 받았다(마크업은 정상 렌더, CSS 만 부재).
 * 어제 이식한 증분이 일부만 담고 있었다.
 *
 * 아래 규칙은 시안 원본(Webi 화면 시안.dc.html)의 <style> 에서 **그대로 추출**했다.
 * 내가 새로 짜지 않았다 — 짜면 또 어긋난다.
 * 다만 시안은 .dsx 스코프 없이 쓰므로, 우리 관례에 맞춰 .dsx 를 붙였다
 * (기존 화면에 새 클래스가 새는 것을 막는다).
 * ============================================================ */

.dsx .ds-btn--sm{height:var(--ctl-h-sm);padding:0 9px;font-size:12px}
.dsx .ds-btn--lg{height:var(--ctl-h-lg);padding:0 18px;font-size:13.5px}
.dsx .ds-hero__cta{display:flex;gap:10px;margin-top:22px;flex-wrap:wrap}
.dsx .ds-secthead{display:flex;align-items:flex-end;justify-content:space-between;gap:16px;margin:28px 0 12px}
.dsx .ds-secthead h2{font-size:17px;font-weight:800;letter-spacing:-.012em}
.dsx .ds-secthead p{color:var(--text-2);margin-top:3px}
.dsx .ds-grid--3{grid-template-columns:repeat(auto-fill,minmax(280px,1fr))}
.dsx .ds-grid--4{grid-template-columns:repeat(auto-fill,minmax(240px,1fr))}
.dsx .ds-prodcard{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);box-shadow:var(--sh-1);overflow:hidden;display:flex;flex-direction:column}
.dsx .ds-prodcard:hover{border-color:var(--border-strong);box-shadow:var(--sh-2)}
.dsx .ds-prodcard__b{padding:14px 16px 16px;display:flex;flex-direction:column;gap:9px;flex:1}
.dsx .ds-prodcard__t{display:flex;align-items:center;gap:10px}
.dsx .ds-prodcard h3{font-size:14px;font-weight:700;letter-spacing:-.01em}
.dsx .ds-prodcard h3 a{color:var(--text)}
.dsx .ds-prodcard__d{color:var(--text-2);font-size:12.5px;line-height:1.6;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.dsx .ds-prodcard__f{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:auto;padding-top:4px}
.dsx .ds-prodmark{width:30px;height:30px;border-radius:var(--r-md);flex:none;display:grid;place-items:center;background:var(--brand-soft);color:var(--brand-text);font-weight:800;font-size:12px}
.dsx .ds-feat{display:flex;gap:12px;align-items:flex-start}
.dsx .ds-feat__i{width:34px;height:34px;border-radius:var(--r-md);flex:none;display:grid;place-items:center;background:var(--brand-soft);color:var(--brand-text)}
.dsx .ds-feat h3{font-size:13.5px;font-weight:700;margin-bottom:4px}
.dsx .ds-feat p{color:var(--text-2);font-size:12.5px;line-height:1.65}
.dsx .ds-statstrip{display:flex;flex-wrap:wrap;gap:34px;margin-top:30px;padding-top:22px;border-top:1px solid var(--border)}
.dsx .ds-statstrip dt{font-size:11.5px;color:var(--text-3);font-weight:700}
.dsx .ds-statstrip dd{margin:2px 0 0;font-family:var(--font-mono);font-size:19px;font-weight:700;letter-spacing:-.02em}
.dsx .ds-statstrip .ds-mono{color:var(--text-3);font-size:10.5px}
.dsx .ds-plan{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);box-shadow:var(--sh-1);padding:20px;display:flex;flex-direction:column;gap:14px}
.dsx .ds-plan.is-reco{border-color:var(--brand);box-shadow:0 0 0 1px var(--brand),var(--sh-2)}
.dsx .ds-plan__h{display:flex;align-items:flex-start;justify-content:space-between;gap:8px}
.dsx .ds-plan__h h3{font-size:13.5px;font-weight:800}
.dsx .ds-plan__h p{color:var(--text-3);font-size:12px;margin-top:3px}
.dsx .ds-plan__v{font-family:var(--font-mono);font-size:26px;font-weight:700;letter-spacing:-.025em;display:flex;align-items:baseline;gap:4px}
.dsx .ds-plan__v small{font-family:var(--font-sans);font-size:12px;font-weight:400;color:var(--text-3)}
.dsx .ds-statstrip{gap:20px}

/* ============================================================
 * 홈 색·가격 표기 교정 (2026-08-13) — 값은 전부 시안 원본에서 가져왔다
 *
 * 1) .ds-price 이름 충돌
 *    레이아웃 시안: .ds-price = 요금제 카드 **그리드**(display:grid) + __c/__v
 *    화면   시안: .ds-price = 가격 **텍스트**(mono)
 *    같은 이름이 다른 구조다. 그리드 정의를 정본으로 두고(이미 그렇게 되어 있다),
 *    카드 **안에서** 쓰는 가격 텍스트만 스코프로 되돌린다. 전역 이름을 새로 만들지 않는다.
 *    → 이걸 안 하면 <small>월</small>60,000원 이 grid item 둘로 쪼개져 세로로 나온다.
 *
 * 2) 제품 카드 제목 링크 색
 *    시안: .ds-prodcard h3 a { color: var(--text) }
 *    우리 .dsx a{color:var(--brand-text)} 가 이겨 제품명이 파랗게 나왔다.
 *
 * 3) 채운 버튼 글자색
 *    시안은 var(--on-brand). 우리는 #fff 하드코딩이었고 링크 규칙과 명시도 싸움이 있었다.
 *    --on-brand 는 선언돼 있으므로(oklch(1 0 0)) 토큰으로 통일한다.
 * ============================================================ */

.dsx .ds-prodcard .ds-price,
.dsx .ds-plan .ds-price {
  display: inline;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: -.02em;
}
.dsx .ds-prodcard .ds-price small,
.dsx .ds-plan .ds-price small {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 400;
  color: var(--text-3);
  margin-right: 3px;
}

.dsx .ds-prodcard h3 a,
.dsx .ds-prodcard h3 a:hover { color: var(--text); text-decoration: none; }

.dsx a.ds-btn--primary,
.dsx a.ds-btn--primary:hover,
.dsx button.ds-btn--primary { color: var(--on-brand); }


/* ============================================================
 * 장바구니(cart) — 시안 이식 (v2 시안 원문 값 그대로)
 *   없는 클래스를 쓰면 화면이 스타일 없이 나온다. 아래는 전부 시안에서 옮겨온 것이다.
 *   .ds-page-head 는 컨테이너를 바꾸지 않는다 — 관리자 92화면이 __title/__sub 를 쓴다.
 *   시안 형태(h1 + .sub)는 자식 규칙으로 나란히 둔다.
 * ============================================================ */
.dsx .ds-page-head h1:not(.ds-page-head__title){font-size:19px;font-weight:800;letter-spacing:-.01em}
.dsx .ds-page-head .sub{color:var(--text-2);margin-top:3px}

.dsx .ds-summary{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);box-shadow:var(--sh-1);position:sticky;top:calc(44px + var(--topbar-h) + 12px);overflow:hidden}
.dsx .ds-summary__row{display:flex;justify-content:space-between;gap:12px;padding:8px 0;font-size:12.5px;color:var(--text-2)}
.dsx .ds-summary__row b{font-family:var(--font-mono);color:var(--text)}
.dsx .ds-summary__total{display:flex;justify-content:space-between;align-items:baseline;gap:12px;padding-top:12px;margin-top:4px;border-top:1px solid var(--border)}
.dsx .ds-summary__total b{font-family:var(--font-mono);font-size:22px;font-weight:700;letter-spacing:-.02em}

.dsx .ds-qty{display:inline-flex;align-items:center;border:1px solid var(--border-strong);border-radius:var(--r-md);overflow:hidden;height:var(--ctl-h-sm)}
.dsx .ds-qty button{width:26px;height:100%;color:var(--text-2)}
.dsx .ds-qty button:hover{background:var(--surface-2);color:var(--text)}
.dsx .ds-qty span{min-width:30px;text-align:center;font-family:var(--font-mono);font-size:12px;border-left:1px solid var(--border);border-right:1px solid var(--border);line-height:24px}

.dsx .ds-toggle__in{position:absolute;left:0;top:0;width:16px;height:16px;margin:0;opacity:0;cursor:pointer}
.dsx .ds-toggle__ui{width:16px;height:16px;border-radius:var(--r-sm);border:1px solid var(--border-strong);background:var(--surface);flex:none;display:grid;place-items:center;color:var(--on-brand)}
.dsx .ds-toggle__ui svg{opacity:0;width:11px;height:11px;stroke-width:3}
.dsx .ds-toggle__in:checked+.ds-toggle__ui{background:var(--brand);border-color:var(--brand)}
.dsx .ds-toggle__in:checked+.ds-toggle__ui svg{opacity:1}
.dsx .ds-toggle__in:focus-visible+.ds-toggle__ui{outline:2px solid var(--brand);outline-offset:2px}

.dsx .ds-hint{color:var(--text-3);font-size:11.5px;margin-top:5px}
.dsx .ds-btn--block{width:100%}

/* 본문+요약 2단. 시안은 base 를 인라인 style 로 두고 1024px 이하에서 1단으로 접는다. */
.dsx .ds-detail{display:grid;grid-template-columns:minmax(0,1fr) 320px;gap:16px;align-items:start}
@media (max-width:1024px){.dsx .ds-detail{grid-template-columns:minmax(0,1fr)}}

/* .dsx .ds-input(0,2,0) 이 .ds-search input(0,1,1) 을 이겨 아이콘 자리가 사라진다. */
.dsx .ds-search .ds-input{padding-left:30px}


/* ============================================================
 * 요금제(pricing) — 시안 이식 (v3 시안 원문 값 그대로)
 *   ds-acc__* 는 의도적으로 제외 — 우리 .ds-acc__btn/__body 는 사이드바용이라 충돌한다.
 * ============================================================ */
.dsx .ds-plans{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:14px;align-items:start}

.dsx .ds-plan__h h2{font-size:13.5px;font-weight:800}

.dsx .ds-plan__f{display:flex;flex-direction:column;gap:8px;font-size:12.5px;color:var(--text-2)}
.dsx .ds-plan__f li{display:flex;gap:8px;align-items:flex-start}
.dsx .ds-plan__f li.is-off{color:var(--text-3)}
.dsx .ds-plan__f svg{width:15px;height:15px;margin-top:2px;color:var(--success);flex:none}
.dsx .ds-plan__f li.is-off svg{color:var(--text-3);opacity:.6}

.dsx .ds-billing{display:inline-flex;padding:3px;gap:2px;background:var(--surface-2);border:1px solid var(--border);border-radius:var(--r-full)}
.dsx .ds-billing button{height:var(--ctl-h-sm);padding:0 14px;border-radius:var(--r-full);font-size:12px;font-weight:700;color:var(--text-2)}
.dsx .ds-billing button.is-on{background:var(--surface);color:var(--text);box-shadow:var(--sh-1)}

.dsx .ds-cmp td:not(:first-child),.dsx .ds-cmp th:not(:first-child){text-align:center}
.dsx .ds-cmp .reco{background:var(--brand-soft)}
.dsx .ds-cmp .off{color:var(--text-3)}

/* [hidden] 은 display:none 을 노리지만 .dsx .ds-plan__v{display:flex} 가 이긴다.
   (같은 함정을 .ds-cmdk 에서도 겪었다 — 컴포넌트마다 hidden 을 되살려야 한다.) */
.dsx .ds-plan__v[hidden]{display:none}


/* ============================================================
 * 제품 상세(detail) — 시안 이식 + 구매위젯 JS 계약 다리
 * ============================================================ */
.dsx .ds-buybox{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);box-shadow:var(--sh-1);position:sticky;top:calc(44px + var(--topbar-h) + 12px);overflow:hidden}
.dsx .ds-buybox__price{padding:18px;border-bottom:1px solid var(--border)}
.dsx .ds-buybox__price .v{font-family:var(--font-mono);font-size:26px;font-weight:700;letter-spacing:-.025em;display:block}
.dsx .ds-buybox__body{padding:18px;display:flex;flex-direction:column;gap:10px}

.dsx .ds-gallery{display:flex;flex-direction:column;gap:8px}
.dsx .ds-gallery__main{border-radius:var(--r-lg);overflow:hidden;border:1px solid var(--border);position:relative}
.dsx .ds-gallery__main img{display:block;width:100%;height:100%;object-fit:cover}
.dsx .ds-gallery__strip{display:flex;gap:8px;overflow-x:auto;padding-bottom:2px}
.dsx .ds-gallery__strip button{flex:0 0 72px;border:0;background:none;padding:0;cursor:pointer;line-height:0}
.dsx .ds-gallery__strip img{display:block;width:100%;aspect-ratio:16/10;object-fit:cover;border-radius:var(--r-md)}
/* 썸네일 선택 표시도 JS 가 **img 에** border-blue-600/border-gray-300 을 토글해서 만든다.
   (updateMainImage: thumb.querySelector('img').classList …) 그 이름 그대로 DS 색으로 읽는다. */
.dsx .ds-gallery__strip img.border-gray-300{border-color:var(--border)}
.dsx .ds-gallery__strip img.border-blue-600{border-color:var(--brand)}
.dsx .ds-gallery__nav{position:absolute;top:50%;transform:translateY(-50%);width:30px;height:30px;border-radius:var(--r-full);display:grid;place-items:center;background:var(--scrim,rgba(0,0,0,.45));color:#fff;border:0;cursor:pointer}
.dsx .ds-gallery__nav:hover{background:rgba(0,0,0,.65)}

.dsx .ds-speclist{display:flex;flex-direction:column;gap:0}
.dsx .ds-speclist>div{display:flex;gap:12px;padding:8px 0;border-bottom:1px solid var(--border);font-size:12.5px}
.dsx .ds-speclist>div:last-child{border-bottom:0}
.dsx .ds-speclist dt{width:112px;flex:none;color:var(--text-3);font-weight:700}
.dsx .ds-speclist dd{margin:0;color:var(--text-2);min-width:0}

/* ---- 다리: 구매 위젯 JS 가 토글하는 Tailwind 클래스에 DS 모양을 입힌다 ----
   JS(views/products/product_detail.php 인라인)는 아래 클래스를 add/remove 한다.
   JS 를 바꾸지 않는 대신 여기서 모양만 DS 로 맞춘다. 클래스 이름을 바꾸면 결제 진입이 깨진다. */
.dsx .license-option-card,
.dsx .purchase-type-option{
  display:block;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-md);
  padding:12px 14px;cursor:pointer;transition:border-color .15s,background .15s;
}
.dsx .license-option-card:hover,
.dsx .purchase-type-option:hover{border-color:var(--border-strong)}
/* JS 가 붙이는 선택 표시 */
.dsx .license-option-card.border-blue-600,
.dsx .purchase-type-option.border-blue-600{border-color:var(--brand);box-shadow:0 0 0 1px var(--brand)}
.dsx .license-option-card.bg-blue-50,
.dsx .purchase-type-option.bg-blue-50{background:var(--brand-soft)}
/* JS 가 되돌릴 때 쓰는 회색 테두리 — DS 기본 테두리로 읽는다 */
.dsx .license-option-card.border-gray-200,
.dsx .purchase-type-option.border-gray-200{border-color:var(--border)}
/* 라디오 점 */
.dsx .purchase-type-radio{width:16px;height:16px;border:1px solid var(--border-strong);border-radius:var(--r-full);display:grid;place-items:center;flex:none}
.dsx .purchase-type-radio>div{width:8px;height:8px;border-radius:var(--r-full);background:var(--brand)}

/* JS 는 .hidden(0,1,0) 으로 숨긴다. 컴포넌트 규칙(0,2,0)이 이기면 안 숨겨진다.
   display 를 지정하는 곳마다 .hidden 을 되살려야 한다(ds-cmdk·ds-plan__v 와 같은 함정). */
.dsx .price-options{display:flex;flex-direction:column;gap:8px}
.dsx .price-options.hidden{display:none}
.dsx .ds-alert.hidden{display:none}

/* 레거시 리치텍스트(제품 상세 설명 등).
   시안의 70ch 는 산문 기준인데 여기 본체는 스크린샷이다 — 폭을 가두면 화면이 안 읽힌다.
   글줄은 길어지지만 이미지가 제 크기로 보이는 쪽이 이 콘텐츠에 맞다. */
.dsx .ds-richtext{color:var(--text-2);line-height:1.8}
.dsx .ds-richtext img{max-width:100%;height:auto;border-radius:var(--r-md)}
.dsx .ds-richtext table{max-width:100%;border-collapse:collapse}
.dsx .ds-richtext a{color:var(--brand-text)}

/* 라디오 점의 초기 숨김이 Tailwind .hidden 하나에만 기대고 있었다.
   output.css 재빌드에서 그 클래스가 사라지면 두 점이 모두 켜진다(코덱스 지적).
   DS 쪽에도 같은 의미를 박아 둔다. */
.dsx .purchase-type-radio > div.hidden{display:none}


/* ============================================================
 * 콘텐츠 목록(clist) — 시안 이식
 * ============================================================ */
.dsx .ds-postcard__d{color:var(--text-2);font-size:12.5px;line-height:1.6;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}

/* 시안 썸네일은 자리표시 상자다. 우리는 실제 이미지를 넣으므로 채운다. */
.dsx .ds-thumb img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
/* aspect-ratio 는 **선호** 크기다. 이미지가 흐름 안에 있으면 자연 높이가 더 클 때 상자가 늘어난다
   (실측: 288px 폭 카드가 162 대신 240). 절대배치로 빼서 상자 높이를 비율이 정하게 한다. */
.dsx .ds-thumb{overflow:hidden}
.dsx a.ds-thumb{text-decoration:none}
.dsx a.ds-ogcard{text-decoration:none}
.dsx .ds-thumb__meta{display:inline-flex;align-items:center;gap:3px}
/* 배지가 두 개(재생시간+화수) 붙는 경우 겹치지 않게 위로 쌓는다. */
.dsx .ds-thumb__meta + .ds-thumb__meta{bottom:calc(7px + 20px)}


/* ---- 콘텐츠 카드 보강(NEW·재생·포커스) ---- */
.dsx .ds-thumb__new{position:absolute;left:7px;top:7px;font-size:10px;font-weight:800;letter-spacing:.02em;background:var(--danger);color:var(--on-brand);padding:2px 6px;border-radius:var(--r-sm)}
.dsx .ds-thumb__play{position:absolute;inset:0;display:grid;place-items:center;color:var(--on-brand)}
.dsx .ds-thumb__play i,.dsx .ds-thumb__play svg{background:var(--scrim);border-radius:var(--r-full);padding:7px;width:30px;height:30px}
.dsx a.ds-thumb:focus-visible,.dsx a.ds-ogcard:focus-visible{outline:2px solid var(--brand);outline-offset:2px}

/* .hidden 은 "감춘다" 는 뜻이다. 컴포넌트의 display 규칙(0,2,0)이 유틸리티(0,1,0)를
   이기면 감춰지지 않는다. 이 함정을 다섯 번 만났다 —
   ds-cmdk · ds-plan__v · price-options · ds-alert · ds-badge. 여기서 끝낸다.
   유틸리티가 이기는 것이 원래 의도이므로 !important 가 맞다. */
.dsx .hidden{display:none!important}


/* ============================================================
 * 커뮤니티(community) — 시안 이식 + 출석 버튼 다리
 * ============================================================ */
.dsx .ds-list__row{display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid var(--border);font-size:12.5px}
.dsx .ds-list__row:last-child{border-bottom:0}
.dsx .ds-list__row .ti{min-width:0;flex:1;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dsx .ds-list__row .mt{font-family:var(--font-mono);font-size:11px;color:var(--text-3);flex:none}
.dsx .ds-cnt{font-family:var(--font-mono);font-size:11px;color:var(--brand-text);font-weight:700;margin-left:4px}

/* ---- 다리: 출석체크 JS 가 토글하는 클래스에 DS 의미를 입힌다 ----
   JS(views/community/board_list.php)는 포인트를 지급하는 흐름이라 손대지 않는다.
   완료 상태는 초록 그라데이션 대신 DS 성공색으로 읽는다. */
.dsx #attendanceBtn.from-green-500{
  background:var(--success)!important;border-color:var(--success)!important;color:var(--on-brand)!important;
}
.dsx #attendanceBtn:disabled{cursor:default}
.dsx #attendanceBtn.cursor-not-allowed{cursor:not-allowed}


/* ============================================================
 * 게시판 목록(board) — 표 대신 그리드
 *   시안은 <table> 이지만 목록/카드 전환을 유지하려면 같은 컨테이너를 갈아 끼워야 한다.
 *   열 배치를 그리드로 맞춰 보이는 결과를 같게 한다.
 * ============================================================ */
.dsx .ds-boardhead,
.dsx .ds-postrow{
  display:grid;
  grid-template-columns:56px minmax(0,1fr) 110px 64px 64px 88px;
  gap:10px;align-items:center;
}
.dsx .ds-boardhead{
  padding:9px 18px;background:var(--surface-2);border-top:1px solid var(--border);
  border-bottom:1px solid var(--border);font-size:11.5px;font-weight:700;color:var(--text-3);
}
.dsx .ds-postrow{padding:10px 18px;border-bottom:1px solid var(--border);font-size:12.5px}
.dsx .ds-postrow:last-child{border-bottom:0}
.dsx .ds-postrow:hover{background:var(--surface-2)}
.dsx .ds-postrow.is-notice{background:var(--brand-soft)}
.dsx .ds-postrow__no{text-align:center;color:var(--text-3);font-size:11.5px}
.dsx .ds-postrow__t{display:flex;align-items:center;gap:6px;min-width:0}
.dsx .ds-postrow__t a{color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dsx .ds-postrow__m{text-align:center;color:var(--text-3);font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* 보기 전환 — renderPosts 가 .post-grid(카드) / .post-table(목록) 로 감싼다(실측). */
.dsx .post-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:14px;padding:16px 18px}

/* ---- 다리: 필터 JS 가 토글하는 Tailwind 클래스에 DS 의미를 입힌다 ----
   filterByCategory·toggleView 는 필터 동작의 일부라 JS 를 손대지 않는다. */
.dsx .category-btn.bg-blue-600,
.dsx .view-btn.bg-white{
  background:var(--brand)!important;border-color:var(--brand)!important;color:var(--on-brand)!important;
}
.dsx .category-btn.bg-gray-100,
.dsx .view-btn.text-gray-500{
  background:var(--surface)!important;border-color:var(--border-strong)!important;color:var(--text-2)!important;
}

/* 좁은 화면에서는 제목만 남긴다(표를 옆으로 밀지 않는다). */
@media (max-width:860px){
  .dsx .ds-boardhead{display:none}
  .dsx .ds-postrow{grid-template-columns:44px minmax(0,1fr);row-gap:4px}
  .dsx .ds-postrow__m{display:none}
  .dsx .ds-postrow__m:last-child{display:block;grid-column:2;text-align:left}
}

/* 모바일 전용 글쓰기 버튼(기존 기능 유지) */
.dsx .ds-fab{
  position:fixed;right:18px;bottom:18px;z-index:40;width:48px;height:48px;border-radius:var(--r-full);
  display:none;place-items:center;background:var(--brand);color:var(--on-brand);box-shadow:var(--sh-3);
}
.dsx .ds-fab:hover{background:var(--brand-hover);text-decoration:none}
@media (max-width:860px){ .dsx .ds-fab{display:grid} }


/* ============================================================
 * 서브 네비(sub-nav) — 커뮤니티 상단 줄
 *   시안의 ds-typebar 와 같은 생김새. 안쪽 항목은 ds-chip / ds-menu 를 그대로 쓴다.
 *   .ds-menu 계열은 셸 레벨(.dsx 미접두)이라 여기서도 그대로 먹는다.
 * ============================================================ */
.ds-subnav{margin:0 0 14px}
.ds-subnav__in{min-width:0}
.ds-subnav__row{display:flex;flex-wrap:wrap;align-items:center;gap:6px;padding:8px 10px;
  background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg)}
.ds-subnav__end{margin-left:auto}

/* 드롭다운 — .ds-menu 는 기본이 오른쪽 정렬이라 왼쪽으로 돌린다. */
.ds-menu--left{left:0;right:auto}
.ds-menu--scroll{max-height:400px;overflow-y:auto}
.ds-menu__item.is-on{background:var(--brand-soft);color:var(--brand-text);font-weight:700}
.ds-menu__ic{width:20px;text-align:center;color:var(--text-3);flex:none}
.ds-menu__item.is-on .ds-menu__ic{color:var(--brand-text)}
.ds-menu__desc{font-size:11px;color:var(--text-3);font-weight:400}


/* ============================================================
 * 위젯 행(ds-rank) 보조 — 커뮤니티 홈의 공지·인기·최신·랭킹
 * ============================================================ */
.ds-rank__b{min-width:0;flex:1}
.ds-rank__t{display:block;font-size:12.5px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ds-rank:hover .ds-rank__t{color:var(--brand-text)}
.ds-rank__m{display:flex;align-items:center;gap:9px;margin-top:2px;font-family:var(--font-mono);font-size:11px;color:var(--text-3)}
.ds-rank .n.is-top{color:var(--brand-text)}
.ds-rank__hd{display:flex;align-items:center;gap:6px;min-width:0}
.ds-widget-empty{padding:26px 16px;text-align:center;font-size:12.5px;color:var(--text-3)}
.ds-widget-empty.is-err{color:var(--danger-text)}


/* ============================================================
 * 콘텐츠 상세 — 아이콘·세로 영상
 *   ds-ico 는 아이콘을 공급하지 않는다. 크기·정렬만 맡는다.
 *   아이콘 자체는 lucide(<i data-lucide="...">)가 그린다 — 이 사이트의 정본이다.
 * ============================================================ */
.ds-ico{width:16px;height:16px;flex:none;stroke:currentColor;fill:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round}
.ds-postcard__m .ds-ico{width:13px;height:13px}

/* 영상 플레이어 — 기본은 16:9, 세로 영상은 9:16.
   video/detail.php 가 video_orientation 으로 가른다. 인라인 padding-top 을 대신한다. */
.ds-player-frame{position:relative;width:100%;aspect-ratio:16/9;border-radius:var(--r-lg);overflow:hidden;background:var(--surface-3)}
.ds-player-frame--portrait{aspect-ratio:9/16;max-width:420px;margin-left:auto;margin-right:auto}
.ds-player-frame iframe,.ds-player-frame video{position:absolute;inset:0;width:100%;height:100%;border:0}


/* ============================================================
 * 본문 타이포그래피(.content-body)를 DS 테마에 맞춘다
 *   input.css 의 .content-body 는 색을 라이트로 고정한다(text-gray-900 등).
 *   DS 화면은 배경이 어두워서 그대로 두면 제목·표가 배경에 묻힌다(실측).
 *   여기서는 **색과 경계선만** 덮는다 — 크기·여백·굵기는 그대로 둔다.
 *   .dsx 스코프라 아직 전환되지 않은 화면에는 영향이 없다.
 * ============================================================ */
.dsx .content-body{color:var(--text-2)}
.dsx .content-body h1,
.dsx .content-body h2,
.dsx .content-body h3,
.dsx .content-body h4,
.dsx .content-body h5,
.dsx .content-body h6{color:var(--text)}
.dsx .content-body h2{border-bottom-color:var(--border)}
.dsx .content-body strong,
.dsx .content-body b{color:var(--text)}
.dsx .content-body a{color:var(--brand-text)}
.dsx .content-body a:hover{color:var(--brand-text)}
.dsx .content-body hr{border-color:var(--border)}
.dsx .content-body blockquote{background:var(--surface-2);border-left-color:var(--brand-border);color:var(--text-3)}
.dsx .content-body code{background:var(--surface-2);color:var(--brand-text)}
.dsx .content-body pre{background:var(--surface-2);color:var(--text)}
.dsx .content-body pre code{background:transparent;color:inherit}

/* 표 — thead 의 밝은 띠가 다크 위에 얹히던 것을 없앤다 */
.dsx .content-table-wrap{border-color:var(--border)}
.dsx .content-body thead th,
.dsx .content-table thead th{background:var(--surface-2);color:var(--text);border-bottom-color:var(--border)}
.dsx .content-body tbody td,
.dsx .content-table tbody td{color:var(--text-2);border-bottom-color:var(--border)}
.dsx .content-body tbody tr:hover{background:var(--surface-2)}


/* ============================================================
 * 다리: 좋아요·댓글 파셜(interactions.php) — PHP·JS 를 건드리지 않는다
 *   이 파셜은 상세 5종이 공유한다. 지금 마크업을 갈아엎으면 아직 전환되지 않은
 *   화면에서 DS 댓글 + 옛 본문이 섞인다. 그래서 .dsx 스코프에서 색만 입힌다
 *   (.dsx 는 $content_dsx 를 켠 화면에만 붙는다 = 사실상 opt-in).
 *   JS 가 나중에 그리는 댓글 노드도 같은 조상 아래라 함께 덮인다.
 *   ⚠️ interactions.php 를 진짜 DS 마크업으로 옮기면 이 블록을 **지워라**.
 * ============================================================ */
.dsx #content-interactions{color:var(--text-2)}
.dsx #content-interactions .text-gray-900{color:var(--text)}
.dsx #content-interactions .text-gray-600,
.dsx #content-interactions .text-gray-500,
.dsx #content-interactions .text-gray-400{color:var(--text-3)}
.dsx #content-interactions .text-indigo-600{color:var(--brand-text)}
.dsx #content-interactions .bg-gray-50{background:var(--surface-2)}
/* JS 가 만드는 댓글 노드 — commentNode() 가 카드에 bg-white, 본문에 text-gray-700 을 준다.
   초기 HTML 에는 없어서 처음엔 놓쳤다(댓글 0건 표본만 봤다). */
.dsx #content-interactions .bg-white{background:var(--surface-2);border-color:var(--border)}
.dsx #content-interactions .text-gray-700{color:var(--text-2)}
.dsx #content-interactions .border-gray-100,
.dsx #content-interactions .border-gray-200{border-color:var(--border)}

/* 댓글 입력 */
.dsx #content-interactions textarea{background:var(--surface);color:var(--text);border-color:var(--border-strong)}
.dsx #content-interactions textarea::placeholder{color:var(--text-3)}
.dsx #content-interactions textarea:focus{border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}

/* 등록 버튼 */
.dsx #content-interactions .bg-indigo-600{background:var(--brand);color:var(--on-brand)}
.dsx #content-interactions .bg-indigo-600:hover{background:var(--brand-hover)}

/* 좋아요 — 누른 상태는 danger, 안 누른 상태는 기본 표면 */
.dsx #content-interactions #ci-like-btn{background:var(--surface);border-color:var(--border);color:var(--text-2)}
.dsx #content-interactions #ci-like-btn:hover{border-color:var(--danger);color:var(--danger-text)}
.dsx #content-interactions #ci-like-btn.bg-rose-50{background:var(--danger-soft);border-color:var(--danger);color:var(--danger-text)}


/* ============================================================
 * 웹툰 뷰어 · 미리보기 배너
 *   시안에 없는 화면이라 현재 동작을 DS 어휘로 옮긴 것이다(새 디자인 아님).
 *   이미지 사이 여백은 0 이어야 컷이 끊겨 보이지 않는다.
 * ============================================================ */
.dsx .ds-webtoon{max-width:720px;margin-inline:auto;background:var(--surface-2);border-top:1px solid var(--border);border-bottom:1px solid var(--border)}
.dsx .ds-webtoon__img{display:block;width:100%;margin:0;padding:0}
.dsx .ds-webtoon__narr{background:var(--scrim);color:var(--on-brand);font-size:13.5px;line-height:1.7;padding:12px 20px;text-align:center}
.dsx .ds-webtoon__lines{display:flex;flex-direction:column;gap:8px;padding:14px 20px;background:var(--surface)}
.dsx .ds-webtoon__line{display:flex;gap:8px;align-items:flex-start}
.dsx .ds-webtoon__who{flex:none;font-size:12.5px;font-weight:700;color:var(--brand-text)}
.dsx .ds-webtoon__say{font-size:12.5px;color:var(--text);background:var(--surface-2);border:1px solid var(--border);border-radius:var(--r-lg);padding:5px 11px}

/* 미리보기(draft) 배너 — 공개 전 콘텐츠임을 알린다 */
.dsx .ds-note{display:flex;align-items:center;gap:8px;padding:10px 14px;border-radius:var(--r-md);font-size:12.5px;margin-bottom:14px;border:1px solid var(--border);background:var(--surface-2);color:var(--text-2)}
.dsx .ds-note--warn{background:var(--warn-soft);border-color:var(--warn);color:var(--warn-text)}
.dsx .ds-note code{font-family:var(--font-mono);font-size:11.5px}


/* ============================================================
 * 다리: 소설 상세 안쪽 위젯 — 낭독·목차·이전다음·작품소개
 *   novel-audio.js 가 잡는 DOM 이라 마크업을 건드리지 않는다. 색만 입힌다.
 *   ⚠️ novel/detail.php 를 진짜 DS 마크업으로 옮기면 이 블록을 지워라.
 * ============================================================ */
.dsx .ds-novel{min-width:0}

/* 낭독 플레이어 */
.dsx .ds-novel #novelAudioPlayer{background:var(--success-soft);border-color:var(--success)}
.dsx .ds-novel #novelAudioPlayer .text-emerald-800,
.dsx .ds-novel #novelAudioPlayer .text-emerald-700,
.dsx .ds-novel #novelAudioPlayer .text-emerald-600{color:var(--success-text)}
.dsx .ds-novel #novelAudioPlayer button,
.dsx .ds-novel #novelAudioPlayer select{background:var(--surface);border-color:var(--border-strong);color:var(--text-2)}
.dsx .ds-novel #novelAudioPlayer button:hover{background:var(--surface-2);color:var(--text)}
.dsx .ds-novel #novelAudioPlayer .border-emerald-100{border-color:var(--border)}
.dsx .ds-novel #novelAudioPlayer audio{width:100%}

/* 작품 소개 */
.dsx .ds-novel .bg-sky-50\/50{background:var(--brand-soft)}
.dsx .ds-novel .border-sky-100{border-color:var(--brand-border)}
.dsx .ds-novel .text-sky-700,
.dsx .ds-novel .text-sky-600{color:var(--brand-text)}

/* 목차 아코디언 */
.dsx .ds-novel details{background:var(--surface-2);border-color:var(--border)}
.dsx .ds-novel details summary{color:var(--text-2)}
.dsx .ds-novel details ul{border-color:var(--border)}
.dsx .ds-novel details li + li{border-top-color:var(--border)}
.dsx .ds-novel details a{color:var(--text-2)}
.dsx .ds-novel details a:hover{background:var(--surface);color:var(--text)}
.dsx .ds-novel details a.bg-sky-50{background:var(--brand-soft);color:var(--brand-text)}
.dsx .ds-novel details .text-gray-400{color:var(--text-3)}

/* 이전화/다음화 */
.dsx .ds-novel .border-gray-200{border-color:var(--border)}
.dsx .ds-novel a.bg-white{background:var(--surface);color:var(--text-2)}
.dsx .ds-novel a.bg-white:hover{background:var(--surface-2);color:var(--text)}
.dsx .ds-novel a.bg-sky-50{background:var(--brand-soft);border-color:var(--brand-border);color:var(--brand-text)}
.dsx .ds-novel .bg-gray-50{background:var(--surface-2);border-color:var(--border);color:var(--text-3)}

/* 제목·메타·배지 */
.dsx .ds-novel h1{color:var(--text)}
.dsx .ds-novel .text-gray-900{color:var(--text)}
.dsx .ds-novel .text-gray-700,
.dsx .ds-novel .text-gray-600,
.dsx .ds-novel .text-gray-500{color:var(--text-3)}
.dsx .ds-novel .bg-sky-100{background:var(--brand-soft);color:var(--brand-text)}
.dsx .ds-novel .bg-emerald-100{background:var(--success-soft);color:var(--success-text)}
.dsx .ds-novel .bg-gray-700{background:var(--surface-3);color:var(--text)}
.dsx .ds-novel .bg-indigo-50{background:var(--surface-2);color:var(--text-2);border:1px solid var(--border)}
.dsx .ds-novel .h-px{background:var(--border)}

/* 카테고리 배지는 content_category_style() 이 Tailwind 문자열을 반환한다(카테고리마다 색이 다르다).
   그 헬퍼는 목록 화면도 쓰므로 건드리지 않는다 — 링크 자체를 잡아 어떤 색이 와도 DS 로 덮는다. */
.dsx .ds-novel a[href*="?category="]{background:var(--brand-soft)!important;color:var(--brand-text)!important}


/* ============================================================
 * 아코디언(ds-acc) — 고객지원 FAQ
 *   시안 정의를 그대로 옮겼다. 회전 아이콘 규칙은 빼 두었다 —
 *   기존 JS(setupFaqToggles)가 .faq-icon 의 style.transform 을 직접 건드리기 때문이다.
 * ============================================================ */
.dsx .ds-acc__item{border-bottom:1px solid var(--border)}
.dsx .ds-acc__item:last-child{border-bottom:0}
.dsx .ds-acc__btn{display:flex;align-items:center;gap:10px;width:100%;padding:13px 18px;font-size:13px;font-weight:700;text-align:left;color:var(--text);cursor:pointer}
.dsx .ds-acc__btn:hover{background:var(--surface-2)}
.dsx .ds-acc__body{padding:0 18px 15px;color:var(--text-2);font-size:12.5px;line-height:1.75;max-width:70ch}
.dsx .ds-acc__cat{font-size:15px;font-weight:800;color:var(--text);padding:14px 18px 10px;border-bottom:1px solid var(--border)}


/* ============================================================
 * 공지 목록 행 · 모달 열림 판정 다리
 *   ⚠️ DS 모달은 `.is-open` 으로 열도록 설계돼 있는데, 이 화면들의 JS 는
 *      `hidden` 클래스를 빼는 방식이다(`modal.classList.remove('hidden')`).
 *      JS 를 안 건드리는 것이 이 작업의 원칙이라 **CSS 로 다리를 놓는다.**
 *      `.hidden` 이 없으면 열린 것으로 본다 — 명시도가 높아 기본 display:none 을 이긴다.
 *   ⚠️ 반드시 ID로 스코프한다 — 예전에 `.ds-modal:not(.hidden)` 로 전체를 잡았다가
 *      `.is-open` 방식을 쓰는 다른 모든 모달(예: biztool 대시보드 빠른 순위 체크)이
 *      항상 열린 채로 고정되고 닫히지 않는 사고가 있었다(2026-08-16).
 *   ⚠️ 나중에 이 화면들의 JS 를 `.is-open` 방식으로 바꾸면 해당 ID를 지워라.
 * ============================================================ */
.dsx #noticeDetailModal:not(.hidden),
.dsx #orderModal:not(.hidden),
.dsx #ticketModal:not(.hidden){display:flex}

.dsx .ds-noticerow{cursor:pointer;transition:border-color .12s}
.dsx .ds-noticerow:hover{border-color:var(--brand-border)}
.dsx .ds-noticerow.is-important{border-left:3px solid var(--danger)}
.dsx .ds-noticerow__t{font-size:15px;font-weight:700;color:var(--text);line-height:1.5}
.dsx .ds-noticerow:hover .ds-noticerow__t{color:var(--brand-text)}
.dsx .ds-noticerow__period{font-size:11.5px;color:var(--text-3);margin-top:8px;font-family:var(--font-mono)}
.dsx .ds-noticerow__m{display:flex;align-items:center;gap:12px;margin-top:12px;padding-top:12px;border-top:1px solid var(--border);font-size:11.5px;color:var(--text-3)}


/* ── 사용자 영역(마이페이지) 껍데기 ── 2026-08-15 ──────────────────────
   `/user/*` 14화면이 공유한다. 바깥 셸(.ds-shell) **안에** 다시 얹히는 2층 구조라
   바깥 셸의 높이·색을 그대로 물려받아야 한다.

   옛 판은 `sticky top-16`(64px)에 붙어 있었는데 셸 상단바는 그보다 낮다 — 스크롤하면
   흰 틈이 벌어졌다(브라우저 실측). 높이 정본은 `.dsx{--topbar-h}` 하나뿐이다.

   덤으로 `.ds-topbar` 에 53px 이 하드코딩돼 토큰(55px)과 2px 어긋나 있던 것도 합친다.
   그 토큰은 `.ds-aside`·`.ds-doc__nav`·`.ds-doc__search`·`.ds-progress`·`.ds-summary`·
   `.ds-buybox` 의 sticky 계산이 전부 쓰는 값이라, 어긋난 채로는 그 6곳이 다 어긋난다. */
.ds-topbar{ min-height:var(--topbar-h) }   /* 53px 하드코딩을 정본 토큰으로. 55px 로 통일 */

.ds-userwrap{ display:flex; flex-direction:column; min-height:100%; background:var(--bg) }
.ds-userwrap__main{ flex:1 1 auto; width:100%; min-width:0 }
.ds-userwrap__in{ padding:16px }
@media (min-width:768px){ .ds-userwrap__in{ padding:22px } }

/* 상단 서브메뉴 — 셸 상단바 **바로 아래**에 붙는다(틈 없이). */
.ds-usernav{ position:sticky; top:calc(var(--chrome-h) + var(--topbar-h)); z-index:19;
  background:var(--bg); padding:14px 16px 0 }
@media (min-width:768px){ .ds-usernav{ padding:18px 22px 0 } }
.ds-usernav__row{ display:flex; flex-wrap:wrap; align-items:center; gap:6px; padding:8px 10px;
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg) }
.ds-usernav__sep{ width:1px; height:18px; background:var(--border); flex:none; align-self:center; margin:0 3px }
.ds-usernav__cta{ display:inline-flex; align-items:center; gap:6px; height:var(--ctl-h-sm);
  padding:0 10px; border-radius:var(--r-md); font-size:12.5px; font-weight:600; color:var(--text-2) }
.ds-usernav__cta:hover{ background:var(--surface-2); color:var(--text); text-decoration:none }
.ds-usernav__cta--danger:hover{ background:var(--danger-soft); color:var(--danger-text) }
.ds-usernav__cta i{ font-size:11.5px }
.ds-usernav__cta svg{ width:14px; height:14px; transition:transform .15s }
/* 2행 탭 — 지금 그룹의 하위 메뉴. 좁은 화면에서는 1행 드롭다운으로 충분해 감춘다. */
.ds-usernav__tabs{ display:flex; flex-wrap:wrap; align-items:center; gap:6px;
  margin-top:8px; padding-top:8px; border-top:1px solid var(--border) }
@media (max-width:767px){ .ds-usernav__tabs{ display:none } }

/* 페이지 머리 — `.ds-page-head` 는 `.dsx` 스코프라 쓸 수 없다. 뷰가 넣는
   `$user_page_actions` 조각이 아직 Tailwind 라, DS 리셋이 닿지 않는 무스코프로 둔다. */
.ds-userhead,
.ds-pagehead{ margin:0 0 18px }
.ds-userhead__row,
.ds-pagehead__row{ display:flex; flex-direction:column; gap:12px }
@media (min-width:768px){ .ds-userhead__row,
.ds-pagehead__row{ flex-direction:row; align-items:center; justify-content:space-between } }
.ds-userhead h1,
.ds-pagehead h1{ display:flex; align-items:center; gap:8px; margin:0;
  font-size:21px; font-weight:700; letter-spacing:-.02em; color:var(--text) }
.ds-userhead h1 i,
.ds-pagehead h1 i{ color:var(--brand); font-size:18px }
.ds-userhead__sub,
.ds-pagehead__sub{ margin:6px 0 0; font-size:13px; color:var(--text-2) }
.ds-userhead__sub strong,
.ds-pagehead__sub strong{ color:var(--text); font-weight:700 }
.ds-userhead__act,
.ds-pagehead__act{ display:flex; flex-wrap:wrap; gap:8px }


/* ── 구독 자동갱신 토글 ── 2026-08-15 ────────────────────────
   `/user/subscriptions` 의 자동갱신 스위치는 **JS 가 표현을 소유한다** —
   toggleAutoRenew() 가 bg-blue-600 / bg-gray-300 / translate-x-6 / translate-x-1 을
   직접 붙였다 뗐다 한다. 결제에 직결된 흐름이라 그 JS 는 손대지 않기로 했다.

   그래서 클래스 이름은 그대로 두고 **의미만** DS 로 준다. id 가
   autoRenewToggle{idx} 처럼 동적이라 앞자리로 잡는다.
   translate-x-* 는 Tailwind 것이 그대로 동작하므로 건드리지 않는다. */
.dsx [id^="autoRenewToggle"]{ background:var(--surface-3); border:1px solid var(--border-strong) }
.dsx [id^="autoRenewToggle"].bg-blue-600{ background:var(--brand); border-color:var(--brand) }
.dsx [id^="autoRenewThumb"]{ background:#fff; box-shadow:var(--sh-1) }


/* ── 콘텐츠 목록 필터 — 좁은 화면 ── 2026-08-16 ────────────────────
   375px 에서 첫 카드가 y=482 에서 시작했다 — 뷰포트 900 의 **54%** 가 카드 위 영역이었다.
   그중 태그 줄이 122px 로 가장 컸다(태그 12개가 여러 줄로 접힌다).

   접는 대신 **한 줄 가로 스크롤**로 바꾼다. 칩을 지우지 않으니 고를 수 있는 것이 줄지 않고,
   `<details>` 처럼 「닫혀 있어도 넓은 화면에서는 펼침」을 CSS 로 강제하지 않아도 된다
   (그건 브라우저마다 달라서 운영에서 확인할 수 없는 차이를 만든다).

   넓은 화면은 지금 그대로다. */
.ds-chiprow{ display:flex; flex-wrap:wrap; gap:6px; align-items:center; margin:0 0 14px }
@media (max-width:767px){
  .ds-chiprow{ flex-wrap:nowrap; overflow-x:auto; scrollbar-width:thin;
    -webkit-overflow-scrolling:touch; padding-bottom:4px }
  /* 스크롤 안에서 칩이 찌그러지지 않게 */
  .ds-chiprow > *{ flex:none }
}
@media (max-width:767px){
  /* 「오른쪽에 더 있다」를 알린다. iOS·macOS 의 overlay 스크롤바는 평소 안 보여서
     스크롤바만으로는 숨은 칩이 있다는 걸 알 수 없다. 끝까지 밀면 페이드가 사라진다. */
  .ds-chiprow{ -webkit-mask-image:linear-gradient(to right, #000 calc(100% - 22px), transparent);
               mask-image:linear-gradient(to right, #000 calc(100% - 22px), transparent) }
}
