/* Path: T2Editor/css/t2-visual-system.css */
/* ═══════════════════════════════════════════════════════════════════════════
 * T2Editor Visual System — Precision Rail (rev 2026-08-09)
 *
 * 레이어 3/3. 코어·관리자·모듈·플러그인 표면의 공통 외형.
 * 로드 순서: t2-foundation.css → core/dark → 모듈 → 플러그인 → 이 파일.
 *
 * 이전 개정 대비 변경
 *  - 전체를 @layer t2.product 로 감싸 !important 559개를 0개로 줄였다.
 *    표준 작업면의 컨트롤만 제품 레이어가 소유하고 custom 작업면의 컴포넌트
 *    외형은 플러그인 레이어가 소유한다.
 *  - 대상 셀렉터의 :where() 를 제거했다. v11 은 앵커와 대상을 모두
 *    :where() 로 싸서 특이도가 0,0,0 이었고 클래스 하나에도 패배했다.
 *  - 상태의 주 단서는 하단 rail 이다. 세로 rail 은 관리자 설정 표면
 *    (내비 현재 위치, 목록 현재 항목)에만 쓴다. 한 요소에 상하단 rail 과
 *    좌우측 rail 을 겹치지 않고, 에디터 셸·모달·플러그인에는 쓰지 않는다.
 *  - 하단 상태 바를 원본 감성으로 복원했다. 실제 클래스는 .t2-editor-status
 *    이며 이 개정의 초안이 .t2-editor-statusbar 로 잘못 지정해 구세대 스타일로
 *    렌더되고 있었다.
 *  - 사용자 콘텐츠(css/content.css)는 건드리지 않는다. authored content.
 * ═══════════════════════════════════════════════════════════════════════════ */

@layer t2.product {

/* ═══ 1. 타이포그래피 ═══════════════════════════════════════════════════ */
:where(.t2-editor-container, .t2-modal-overlay, .t2-plugin-modal-overlay,
  [data-t2-plugin-surface], [data-t2-modal-companion], .t2-plugin-surface-modal,
  .t2-color-picker-container, .t2-dropdown-layer, .t2-font-size-list,
  .t2-privacy-surface, .t2-nsfw-dialog, .t2-safeai-legal-modal,
  .admin-wrap, .auth-screen, .key-screen, .login-screen) {
  font-family: var(--t2-font-ui);
  font-size: 14px;
  line-height: 1.55;
  letter-spacing: var(--t2-track-body);
  color: var(--t2-ink);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* 아이콘 폰트가 없을 때 ligature 텍스트가 레이아웃을 밀지 않게 한다 */
:where(.material-icons, .material-icons-outlined, .mi) {
  font-weight: normal; font-style: normal; line-height: 1;
  letter-spacing: normal; text-transform: none; white-space: nowrap;
  overflow: hidden; max-width: 1.25em; flex: 0 0 auto;
  font-feature-settings: "liga";
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
/* 아이콘 서체를 붙이는 자리는 그것을 선언한 파일 하나다(css/fonts.css).
   여기에 두면 t2.product 가 t2.token 을 이겨 그 파일의 선언이 죽고, fonts.css
   만 불러오는 화면(endpoints/install-check.php)은 아이콘을 글자로 보여 준다. */

/* DSc 계보 표식 — 제품색과 역할이 겹치지 않는다 (law 705 §7-9).
   .t2-logo 계열은 §5b 의 lockup 정의에서 단독으로 다룬다. 여기서 중복
   정의하면 gap 이 두 칸을 벌려 일체감을 깨뜨린다. */
.t2-dsc-service-brand, [data-t2-brand="dsc"] {
  color: var(--t2-dsc-ink); font-weight: 700;
  background: transparent; border: 0; padding: 0;
}
/* 순수 텍스트 워드마크 — 해설 705 §8 의 정본 서명(#ff6600 + bold)이다.
   "T2" 는 계보색을 그대로 쓰고, "Editor" 는 테마의 잉크를 따른다(라이트 짙은
   그레이 / 다크 화이트). Editor 에 제품색(남색)을 쓰지 않는다 — 제품색은 조작
   신호의 자리이고, 워드마크는 조작이 아니다. 로고 lockup(면을 가진 .t2-logo)은
   이 규칙이 아니라 §5b 가 따로 소유한다.
   이 두 조각은 15개 플러그인 CSS 가 각자 다시 적고 있었고(t2.plugin 이 뒤에
   오므로 전부 이 선언을 이겼다), 그 값이 Editor=제품색이었다. 여기 한 곳이
   소유한다. */
.t2-product-brand { color: currentColor; font-weight: 600; letter-spacing: var(--t2-track-title); }
.t2-product-brand .t2-brand-t2 { color: var(--t2-dsc); font-weight: 700; }
.t2-product-brand .t2-brand-editor { color: var(--t2-ink); font-weight: 700; }
.t2-version-line { font-family: var(--t2-font-mono); font-size: 11px; color: var(--t2-ink-muted); font-variant-numeric: tabular-nums; }

/* ═══ 2. 상호작용 기준선 · 포커스 ═══════════════════════════════════════ */
:where(.t2-editor-container, .t2-modal-overlay, .t2-plugin-modal-overlay,
  [data-t2-plugin-surface], [data-t2-modal-companion], .t2-plugin-surface-modal,
  .t2-color-picker-container, .t2-dropdown-layer, .t2-font-size-list,
  .t2-privacy-surface, .t2-nsfw-dialog, .t2-safeai-legal-modal,
  .admin-wrap, .auth-screen, .key-screen, .login-screen)
  :is(button, input, textarea, select, a[href], summary,
      [role="button"], [role="option"], [role="tab"], [tabindex]) {
  font: inherit;
  letter-spacing: inherit;
  -webkit-tap-highlight-color: transparent;
}

/* Focus = 1px outline @2px offset. 두께·레이아웃 불변 (law 707 H) */
:where(.t2-editor-container, .t2-modal-overlay, .t2-plugin-modal-overlay,
  [data-t2-plugin-surface], [data-t2-modal-companion], .t2-plugin-surface-modal,
  .t2-color-picker-container, .t2-dropdown-layer, .t2-font-size-list,
  .t2-privacy-surface, .t2-nsfw-dialog, .t2-safeai-legal-modal,
  .admin-wrap, .auth-screen, .key-screen, .login-screen)
  :is(button, input, textarea, select, a[href], summary,
      [role="button"], [role="option"], [role="tab"], [tabindex]):focus {
  outline: none;
}
:where(.t2-editor-container, .t2-modal-overlay, .t2-plugin-modal-overlay,
  [data-t2-plugin-surface], [data-t2-modal-companion], .t2-plugin-surface-modal,
  .t2-color-picker-container, .t2-dropdown-layer, .t2-font-size-list,
  .t2-privacy-surface, .t2-nsfw-dialog, .t2-safeai-legal-modal,
  .admin-wrap, .auth-screen, .key-screen, .login-screen)
  :is(button, input, textarea, select, a[href], summary,
      [role="button"], [role="option"], [role="tab"], [tabindex]):focus-visible {
  outline: 1px solid var(--t2-product);
  outline-offset: 2px;
  border-radius: inherit;
}

/* 마우스·터치 누름은 키보드 포커스를 흉내내지 않는다 */
:where(.t2-editor-container, .t2-modal-overlay, .t2-plugin-modal-overlay,
  [data-t2-plugin-surface], .admin-wrap) button:active:not(:disabled) { opacity: .78; }

/* ═══ 3. 폼 컨트롤 ══════════════════════════════════════════════════════
   컨트롤은 칠해진 상자여서 컨트롤인 게 아니다. hit area + 상태 (law 707 G).
   모달 overlay 전체를 겨냥하면 custom 플러그인의 상자·탭까지 제품 레이어가
   이긴다. 표준 작업면만 명시적으로 포함한다. */
:where(.t2-editor-container, .t2-modal-surface[data-t2-modal-surface="standard"],
  [data-t2-plugin-surface], [data-t2-modal-companion], .t2-plugin-surface-modal,
  .t2-dropdown-layer, .t2-privacy-surface, .t2-nsfw-dialog,
  .admin-wrap, .auth-screen, .key-screen, .login-screen)
  :is(input:not([type]), input[type="text"], input[type="search"], input[type="email"],
      input[type="url"], input[type="tel"], input[type="number"], input[type="password"],
      input[type="date"], input[type="time"], input[type="datetime-local"],
      textarea, select) {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  min-height: 38px;
  padding: var(--t2-s2) 0;
  color: var(--t2-ink);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--t2-line-strong);
  border-radius: var(--t2-radius-field) var(--t2-radius-field) 0 0;
  box-shadow: none;
  font-size: 15px;
  transition: border-color var(--t2-motion-fast);
}
/* [iOS] coarse 환경 입력 텍스트는 16px 이상 — 자동 확대 방지. 상자는 44px 이 바닥이다.
   이 규칙은 오랫동안 닿지 않고 있었다. 위의 기본 규칙이 :is(input[type="text"], ...) 로
   속성 선택자를 품어 특정도 (0,1,1) 인데, 여기서는 :is(input, textarea, select) 로
   적어 (0,0,1) 이었다 — 미디어 질의는 특정도를 올려 주지 않으므로 타입을 가진 입력칸은
   coarse 에서도 38px · 15px 로 남았다. 재 보면 실제로 40.25px · 15px 이었다.
   같은 목록을 그대로 반복해 특정도를 맞춘다. */
@media (pointer: coarse) {
  :where(.t2-editor-container, .t2-modal-surface[data-t2-modal-surface="standard"],
    [data-t2-plugin-surface], [data-t2-modal-companion], .t2-plugin-surface-modal,
    .t2-dropdown-layer, .t2-privacy-surface, .t2-nsfw-dialog,
    .admin-wrap, .auth-screen, .key-screen, .login-screen)
    :is(input:not([type]), input[type="text"], input[type="search"], input[type="email"],
        input[type="url"], input[type="tel"], input[type="number"], input[type="password"],
        input[type="date"], input[type="time"], input[type="datetime-local"],
        textarea, select) { font-size: 16px; min-height: var(--t2-hit); }
}

:where(.t2-editor-container, .t2-modal-surface[data-t2-modal-surface="standard"],
  [data-t2-plugin-surface], .t2-privacy-surface, .admin-wrap)
  :is(input, textarea, select):hover:not(:disabled) { border-bottom-color: var(--t2-ink-faint); }

/* 포커스 시 두께가 아니라 색만 승격한다 */
:where(.t2-editor-container, .t2-modal-surface[data-t2-modal-surface="standard"],
  [data-t2-plugin-surface], .t2-privacy-surface, .admin-wrap)
  :is(input, textarea, select):focus-visible {
  outline: none;
  border-bottom-color: var(--t2-product);
  box-shadow: 0 1px 0 0 var(--t2-product);
}
:where(.t2-editor-container, .t2-modal-surface[data-t2-modal-surface="standard"],
  [data-t2-plugin-surface], .admin-wrap)
  :is(input, textarea, select):disabled {
  color: var(--t2-ink-faint);
  border-bottom-color: var(--t2-line);
  cursor: not-allowed;
}
:where(.t2-editor-container, .t2-modal-surface[data-t2-modal-surface="standard"],
  [data-t2-plugin-surface], .admin-wrap)
  :is(input, textarea)::placeholder { color: var(--t2-ink-muted); }
:where(.t2-editor-container, .t2-modal-surface[data-t2-modal-surface="standard"],
  [data-t2-plugin-surface], .admin-wrap)
  :is(input[aria-invalid="true"], input.is-invalid, .form-control.is-invalid) {
  border-bottom-color: var(--t2-danger);
}
:where(.t2-editor-container, .t2-modal-surface[data-t2-modal-surface="standard"], .admin-wrap) textarea {
  min-height: 76px; resize: vertical; line-height: 1.6;
}

/* select 화살표는 구조다. 별도 라운드 컨트롤을 만들지 않는다. */
:where(.t2-editor-container, .t2-modal-surface[data-t2-modal-surface="standard"],
  [data-t2-plugin-surface], .admin-wrap) select:not([multiple]):not([size]) {
  padding-inline-end: var(--t2-s5);
  cursor: pointer;
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 10px) 50%, calc(100% - 5px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

/* checkbox / radio — 제품색 accent 만, glow 없음.
   :not(.t2-toggle-input) — js/utils/toggle.js 가 올린 체크박스는 화면에서
   빠지는 값 운반용(clip-path 1×1)이다. 이 규칙은 t2.product 층이라 그
   1px 선언(t2.module)보다 계층상 위라서, 제외하지 않으면 이 15px 이 이겨
   업그레이드된 토글의 숨은 input 이 coarse 포인터에서 15×15 히트 상자로
   실측된다(link 플러그인 "Open in new tab" 토글, 390px 폰). */
:where(.t2-editor-container, .t2-modal-surface[data-t2-modal-surface="standard"],
  [data-t2-plugin-surface], .t2-privacy-surface, .admin-wrap)
  :is(input[type="checkbox"], input[type="radio"]):not(.t2-toggle-input) {
  accent-color: var(--t2-product);
  width: 15px; height: 15px;
  margin: 0;
  flex: 0 0 auto;
}
:where(.t2-editor-container, .admin-wrap) input[type="range"] { accent-color: var(--t2-product); }

/* 라벨 · 힌트 */
:where(.admin-wrap, .t2-modal-surface[data-t2-modal-surface="standard"], [data-t2-plugin-surface]) .form-label {
  font-size: 12px; font-weight: 500; color: var(--t2-ink-muted);
  /* 한국어는 어절 안에서 끊지 않는다. 좁은 칸에서 "적용 대상 버튼 검색" 이
     "적용 대상 버 / 튼 검색" 으로 갈라졌다 — 라틴 문자에는 영향이 없고, 경로·URL
     같은 긴 토큰만 overflow-wrap 이 받는다. admin-surface.css 의 .save-hint 가
     쓰던 짝을 라벨 어휘로 넓힌다. */
  word-break: keep-all;
  overflow-wrap: anywhere;
}
:where(.admin-wrap, .t2-modal-surface[data-t2-modal-surface="standard"]) :is(.form-hint, .empty-hint) {
  font-size: 11.5px; color: var(--t2-ink-muted); line-height: 1.6;
}
:where(.admin-wrap, .t2-modal-surface[data-t2-modal-surface="standard"]) .form-group { display: grid; gap: var(--t2-s1); margin-bottom: var(--t2-s4); }
:where(.admin-wrap) .form-row { display: grid; gap: var(--t2-s4); }
@media (min-width: 720px) { :where(.admin-wrap) .form-row { grid-template-columns: 1fr 1fr; } }

/* ═══ 4. 버튼 — 결정적 실행만 rail 을 얻는다 (law 705 §6) ═══════════════ */
:where(.t2-editor-container, .t2-modal-surface[data-t2-modal-surface="standard"],
  [data-t2-plugin-surface], [data-t2-modal-companion], .t2-plugin-surface-modal,
  .t2-privacy-surface, .t2-nsfw-dialog) button,
:where(.admin-wrap, .auth-screen, .key-screen, .login-screen) :is(.btn, .btn-sm, .btn-icon) {
  -webkit-appearance: none;
  appearance: none;
  /* Layout belongs to each component; keep plugin absolute/flex declarations. */
  position: revert-layer;
  display: revert-layer;
  align-items: center;
  justify-content: center;
  gap: var(--t2-s1);
  min-height: 36px;
  padding: var(--t2-s2) var(--t2-s3);
  border: 0;
  border-radius: var(--t2-radius-control);
  background: transparent;
  color: var(--t2-ink-muted);
  font-size: 13px;
  font-weight: 500;
  box-shadow: none;
  cursor: pointer;
  transition: color var(--t2-motion-fast), background-color var(--t2-motion-fast);
}
@media (pointer: coarse) {
  :where(.t2-editor-container, .t2-modal-surface[data-t2-modal-surface="standard"],
    [data-t2-plugin-surface], [data-t2-modal-companion], .t2-plugin-surface-modal,
    .t2-privacy-surface, .t2-nsfw-dialog) button { min-height: 44px; }
}
:where(.t2-editor-container, .t2-modal-surface[data-t2-modal-surface="standard"],
  [data-t2-plugin-surface], .t2-plugin-surface-modal, .t2-privacy-surface) button:hover:not(:disabled),
:where(.admin-wrap, .auth-screen, .key-screen, .login-screen) .btn:hover:not(:disabled) {
  color: var(--t2-ink);
  background: var(--t2-product-wash);
}
:where(.t2-editor-container, .t2-modal-surface[data-t2-modal-surface="standard"], [data-t2-plugin-surface]) button:disabled,
:where(.admin-wrap) .btn:disabled {
  color: var(--t2-ink-faint); cursor: not-allowed; background: transparent;
}

/* 주요 실행 — 라벨 밑 하단 rail. 채움·굵은 테두리를 쓰지 않는다. */
:where(.admin-wrap, .auth-screen, .key-screen, .login-screen) :is(.btn-primary, .btn[data-primary="true"]),
:where(.t2-modal-surface[data-t2-modal-surface="standard"], [data-t2-modal-companion],
  .t2-plugin-surface-modal, [data-t2-plugin-surface])
  :is(.t2-btn-primary, .t2-btn[data-action="insert"], .t2-btn[data-action="upload"],
      .t2-btn[data-action="apply"], .t2-btn[data-action="save"], .t2-btn[data-action="confirm"]) {
  color: var(--t2-product-ink);
  background: transparent;
  border: 0;
  font-weight: 600;
}
:where(.admin-wrap, .auth-screen, .key-screen, .login-screen) :is(.btn-primary, .btn[data-primary="true"])::after,
:where(.t2-modal-surface[data-t2-modal-surface="standard"], [data-t2-modal-companion],
  .t2-plugin-surface-modal, [data-t2-plugin-surface])
  :is(.t2-btn-primary, .t2-btn[data-action="insert"], .t2-btn[data-action="upload"],
      .t2-btn[data-action="apply"], .t2-btn[data-action="save"], .t2-btn[data-action="confirm"])::after {
  content: "";
  position: absolute;
  left: var(--t2-rail-inset); right: var(--t2-rail-inset); bottom: 0;
  height: var(--t2-rail-w);
  background: var(--t2-product);
}
:where(.admin-wrap) .btn-primary:hover:not(:disabled) { background: var(--t2-product-wash); color: var(--t2-product-ink); }
:where(.admin-wrap) .btn-primary:disabled::after { background: var(--t2-line-strong); }

/* 파괴적 실행 — 의미색 rail. 제품색을 대용하지 않는다 (law 705 §11). */
:where(.admin-wrap, .auth-screen) :is(.btn-danger, .btn[data-danger="true"]),
:where(.t2-modal-surface[data-t2-modal-surface="standard"], [data-t2-plugin-surface])
  :is(.t2-btn-danger, .t2-btn[data-action="delete"], .t2-btn[data-action="remove"]) {
  color: var(--t2-danger); background: transparent; border: 0; font-weight: 600;
}
:where(.admin-wrap, .auth-screen) :is(.btn-danger, .btn[data-danger="true"])::after,
:where(.t2-modal-surface[data-t2-modal-surface="standard"], [data-t2-plugin-surface])
  :is(.t2-btn-danger, .t2-btn[data-action="delete"], .t2-btn[data-action="remove"])::after {
  content: "";
  position: absolute;
  left: var(--t2-rail-inset); right: var(--t2-rail-inset); bottom: 0;
  height: var(--t2-rail-w);
  background: var(--t2-danger);
}
:where(.admin-wrap) .btn-danger:hover:not(:disabled) { background: var(--t2-danger-soft); }

/* 보조 · 아이콘 전용 */
:where(.admin-wrap) .btn-outline { border: 0; background: transparent; color: var(--t2-ink-muted); }
:where(.admin-wrap) .btn-icon {
  width: var(--t2-face); height: var(--t2-face); min-height: var(--t2-face); padding: 0;
}
:where(.admin-wrap) .btn-sm { min-height: 30px; padding: var(--t2-s1) var(--t2-s2); font-size: 12.5px; }

/* ═══ 5. 에디터 셸 ══════════════════════════════════════════════════════ */
/* 710 A-2: 편집기의 기준 비례는 가로 15 : 세로 10 이다. u 자체와 그 파생값은
   토큰 레이어가 소유하고(t2-foundation.css), 여기서는 그 u 가 읽을 컨테이너를
   연다.

   ── 컨테이너를 여는 순간 셸은 자기 폭을 스스로 정할 수 없게 된다 ──────────
   container-type: inline-size 는 contain: inline-size 를 건다. 그 뜻은 "이
   요소의 가로 크기는 내용에 의존해서는 안 된다" 이다. 그래서 셸이 자기 폭을
   내용에서 얻어야 하는 자리 — flex 항목·grid 항목·inline-block·table·float,
   즉 shrink-to-fit 이 되는 모든 자리 — 에 놓이면 폭이 0 으로 무너진다.
   재 보면 flex/grid 부모 안에서 셸은 테두리 2px 만 남고, 안의 도구는 격자가
   아니라 최소 히트 영역(44px)으로 서고, 밑줄의 버전 칩은 왼쪽 끝으로 간다.

   편집기는 자기가 어디에 심길지 모른다. 게시판 글쓰기 폼은 대부분 flex 나
   grid 다 — 즉 이 무너짐은 예외가 아니라 기본값에 가깝다. 스크래치 프로브
   (tools/ux/_probe_editor.php)는 셸을 body 바로 아래 블록으로 두므로 이
   결함이 보이지 않는다. Chromium/WebKit 을 가리지 않는다: 명세가 그렇다.

   폭을 부모에게서 받도록 못 박아 순환을 끊는다. 블록 부모에서는 width:auto 와
   같은 값이라 아무것도 바뀌지 않고, shrink-to-fit 부모에서만 효과가 있다.

   width:100% 로 낫지 않는 부모가 둘 남는다 — inline-block 과 table 이다. 그
   부모들은 자기 폭을 자식의 max-content 기여분에서 얻는데, 담긴 셸이 이미
   contain 되어 있어 기여분이 없다. 즉 부모도 폭이 없고 100% 도 순환이다.
   contain-intrinsic-inline-size 가 바로 그 자리를 위한 값이다: contain 된
   요소가 부모의 내재 크기 계산에 내놓을 대체 폭을 지정한다. 폭이 이미 확정된
   부모(block·flex·grid)에서는 아무 효과가 없으므로, 지금 무너지는 경우에만
   닿는다. 값은 격자가 정한 셸의 폭 그대로다 — 스펙의 15u 는 100vw 다. 2px 을
   빼는 것은 아래 border 가 좌우 1px 씩이고 이 속성은 content-box 를 가리키기
   때문이며, 빼지 않으면 문서에 가로 스크롤 2px 이 생긴다. */
.t2-editor-container {
  container-type: inline-size;
  box-sizing: border-box;
  width: 100%;
  contain-intrinsic-inline-size: calc(100vw - 2px);
}
/* 셸은 밴드 세 개를 담는 하나의 표면이다. 각(12px)과 그림자는 셸에만 있고 안쪽
   밴드는 각을 갖지 않는다 — overflow:clip 이 셸의 각으로 밴드를 잘라 주므로,
   밴드가 자기 각을 가지면 두 각이 어긋나 모서리에 빈 삼각형이 남는다. */
.t2-editor-container {
  /* 셸 안에 떠 있는 것들(검색 묶음)의 기준 상자다. static 으로 두면 그것들이
     문서 좌표로 도망가 툴바 아래라는 자리를 잃는다. position:fixed 자손은
     이 값에 영향받지 않는다 — 기준을 바꾸는 것은 transform 쪽이다. */
  position: relative;
  /* 가운데 정렬은 간격 토큰과 같은 선언에 실리지 않는다.
     `margin: var(--t2-s4) auto` 한 줄이면 --t2-s4 하나가 해석되지 않는 순간
     shorthand 전체가 무효가 되고, 그때 같이 사라지는 것은 여백이 아니라
     `auto` 다 — 셸이 가운데를 잃고 왼쪽 끝에 붙는다. 축을 나눠 적으면 여백은
     흔들려도 정렬은 남는다. */
  margin-block: var(--t2-s4, 16px);
  margin-inline: auto;
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-shell);
  background: var(--t2-canvas-fill);
  color: var(--t2-ink);
  box-shadow: var(--t2-shadow-overlay);
  overflow: clip;
  min-width: 0;
  max-width: 100%;
}
.t2-editor-container :where(*, *::before, *::after) { min-width: 0; }
/* 메타 줄이 뒤따르면 프레임은 아래 여백을 넘긴다 — 프레임과 메타 줄 사이의 거리를
   메타 줄 혼자 정하게 해야 폭에 따라 컨테이너 여백이 바뀌어도 값이 흔들리지 않는다. */
.t2-editor-container:has(+ .t2-editor-meta-links) { margin-bottom: 0; }

/* ═══ toolbar = 하나의 instrument rail (law 704 §16) ═══════════════════════
   배치 모드는 관리자가 고른다. data-t2-toolbar-mode 하나가 모드의 유일한 훅이며
   JS 는 측정이 필요한 값(--t2-toolbar-btn, --t2-toolbar-columns)만 넘긴다.

     comfortable  버튼 크기 고정 · 높이가 늘어난다(줄바꿈).
                  도구를 숨기지도 줄이지도 않으므로 일반 목적 기본값이다.
     compact      높이 고정 · 버튼 크기가 반응한다. 가운데 정렬.
                  세로 여백과 버튼 사이 간격은 고정하고 가로 바깥 여백만 흡수한다.
     rail         한 줄 고정 크기. 툴바가 자기 가로 넘침을 소유한다(해설 706).
     grouped      한 줄 고정 크기. 넘치는 도구는 그룹 버튼으로 접힌다.

   네 모드 모두 공통으로 지키는 것(해설 708 C-1):
   위·아래 여백이 같고, 좌·우 여백이 같고, 버튼 사이 간격은 사다리에서 온다. */
/* 710 B-3: 밴드 1¼u = 인셋 3/16u + 도구 7/8u + 인셋 3/16u. 하단 바(1u = 1/4u +
   1/2u + 1/4u)와 다른 기하다 — 두 밴드는 위계가 다르고, 그 차이가 세로 예산을
   10u 로 닫는다. */
.t2-toolbar {
  --t2-toolbar-btn: var(--t2-chrome-hit);
  --t2-toolbar-pad: var(--t2-chrome-inset);
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: flex-start;
  /* 710 C-4·B-3: 툴바 안쪽 간격도 격자에서 온다. 자식 사이는 밑단 1/8u 이고
     이는 밴드의 인셋과 같은 값이다 — 도구가 프레임에서 떨어진 만큼 서로에게서도
     떨어져, 첫 도구의 왼쪽과 도구 사이가 한 리듬으로 읽힌다.
     관리자가 간격을 직접 정하면 --t2-toolbar-gap 이 그 값을 들고 오고, 가로
     인셋이 함께 따라가 리듬이 유지된다. 세로 인셋은 --t2-toolbar-pad 로 남아
     밴드 높이가 흔들리지 않는다. */
  gap: var(--t2-toolbar-gap);
  padding: var(--t2-toolbar-pad)
           max(var(--t2-toolbar-gap), env(safe-area-inset-left))
           var(--t2-toolbar-pad)
           max(var(--t2-toolbar-gap), env(safe-area-inset-right));
  box-sizing: border-box;
  min-height: var(--t2-chrome-band);
  background: var(--t2-chrome-fill);
  border: 0;
  border-radius: var(--t2-radius-structural);
  /* 밴드의 높이는 1u 다. 아래 경계는 경계선이지 간격이 아니므로 그 예산을 먹지
     않는다 — border 로 그리면 1px 이 밴드 높이에 더해진다(710 B-3). */
  box-shadow: inset 0 -1px 0 var(--t2-line);
  overflow-x: auto;
  overflow-y: visible;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  /* 가장자리 페이드의 폭. 레이아웃 간격이 아니라 그리는 폭이라 사다리 밖
     값이다 — 히트 면적의 절반 정도가 "잘렸다"고 읽히는 최소 폭이다. */
  --t2-toolbar-fade: 24px;
}
.t2-toolbar::-webkit-scrollbar { height: 0; }

/* 넘침 암시. rail(기본)과 coarse 의 compact 만 가로로 굴르며, 스크롤바는
   위에서 숨겼으므로 이 페이드가 굴림의 유일한 암시다. 상태는 toolbar.js 가
   data-t2-overflow-x 로 알려 준다 — 276px 임베드에서 넘침 메뉴("…")가 굴림
   영역 안쪽에 가려 발견되지 않았다(2026-08-25 실측). 양쪽이 잘린 both 는
   단일 그라디언트로 두 끝을 함께 투명하게 만든다(mask-composite 호환 회피). */
.t2-toolbar[data-t2-overflow-x="right"] {
  mask-image: linear-gradient(90deg, #000 calc(100% - var(--t2-toolbar-fade)), transparent);
}
.t2-toolbar[data-t2-overflow-x="left"] {
  mask-image: linear-gradient(90deg, transparent var(--t2-toolbar-fade), #000);
}
.t2-toolbar[data-t2-overflow-x="both"] {
  mask-image: linear-gradient(90deg, transparent var(--t2-toolbar-fade), #000 calc(var(--t2-toolbar-fade) * 2), #000 calc(100% - var(--t2-toolbar-fade) * 2), transparent);
}

/* 첫 수용량 판정 전에는 툴바를 숨긴다. 위에서 숨긴 실패와 달리 이것은
   "어차피 옳은 모양으로 그려질 수 없는 중간 상태"를 걷어내는 것이다.
   이 애니메이션이 실패 안전망 — JS 가 죽어도 툴바는 스스로 돌아온다.

   agent-context/bugs 의 이전 기록은 저속 기기에서 이 안전망이 1.5s 뒤에도
   JS 판정 전에 먼저 터져 날것이 노출된다며 1.5s → 0.8s 단축을 제안했었다.
   Chromium CDP CPU 스로틀로 다시 재보니 반대 방향이었다: 390px 뷰포트에서
   판정까지 걸리는 실측 시간이 1x(스로틀 없음) 469ms, 2x 612ms, 4x 1104ms,
   6x 1737ms — 6x 에서 이미 기존 1.5s 를 넘는다. 그 보고가 재현한 Moto G4 +
   3G 환경은 이 6x 보다도 느릴 가능성이 크다. 델레이를 줄이면 안전망이 더
   일찍 터져 판정 전 날것이 노출되는 창이 넓어질 뿐이다(0.8s 로는 6x 기준
   0.8~1.7s 구간, 약 900ms 노출 — 기존보다 더 나쁘다). 그래서 반대로 3s 로
   늘려 정상 동작(수백 ms)에는 체감 차이가 없이 저속 기기의 오탐 창만 줄인다. */
.t2-toolbar.t2-toolbar-pending {
  visibility: hidden;
  animation: t2-toolbar-reveal 0s linear 3s forwards;
}
@keyframes t2-toolbar-reveal {
  to { visibility: visible; }
}

/* comfortable — 줄바꿈. 높이만 변하고 버튼과 간격은 변하지 않는다.
   row-gap 은 column-gap 과 같은 값이라 격자가 정사각 리듬을 유지한다. */
.t2-toolbar[data-t2-toolbar-mode="comfortable"] {
  flex-wrap: wrap;
  row-gap: var(--t2-toolbar-gap);
  overflow-x: hidden;
}

/* compact — 높이 고정, 버튼 크기 반응, 가운데 정렬.
   버튼 크기는 JS 가 측정해 --t2-toolbar-btn 으로 넘긴다. */
.t2-toolbar[data-t2-toolbar-mode="compact"] {
  flex-wrap: nowrap;
  justify-content: center;
  height: var(--t2-chrome-band);
  min-height: var(--t2-chrome-band);
  overflow-x: hidden;
}
.t2-toolbar[data-t2-toolbar-mode="compact"] :is(.t2-btn, .t2-toolbar-group-btn) {
  width: var(--t2-toolbar-btn);
  min-width: var(--t2-toolbar-btn);
  height: var(--t2-toolbar-btn);
  min-height: var(--t2-toolbar-btn);
}
@media (pointer: coarse) {
  .t2-toolbar[data-t2-toolbar-mode="compact"] { justify-content: flex-start; overflow-x: auto; }
  .t2-toolbar[data-t2-toolbar-mode="compact"] :is(.t2-btn, .t2-toolbar-group-btn) {
    width: var(--t2-chrome-hit); min-width: var(--t2-chrome-hit);
    height: var(--t2-chrome-hit); min-height: var(--t2-chrome-hit);
  }
}

/* rail — 한 줄. 넘침은 툴바가 소유하고 문서 루트로 새지 않는다(해설 706). */
.t2-toolbar[data-t2-toolbar-mode="rail"] {
  flex-wrap: nowrap;
  overflow-x: auto;
  scroll-snap-type: x proximity;
}
.t2-toolbar[data-t2-toolbar-mode="rail"] .t2-toolbar-group { scroll-snap-align: start; }

/* grouped — 한 줄. 넘치는 도구는 JS 가 그룹 버튼으로 접는다. */
.t2-toolbar[data-t2-toolbar-mode="grouped"] {
  flex-wrap: nowrap;
  overflow-x: hidden;
}

/* 서브툴바 — 그룹 버튼이 여는 임시 레이어. 예전에는 toolbar.js 가 레이어 밖
   <style> 로 주입했다. 소유권을 여기로 옮긴다.

   밴드가 아니라 섬이다. 툴바와 같은 폭을 채우는 네 번째 띠로 그리면 세 밴드의
   위계가 무너지고, 열고 닫을 때마다 화면이 통째로 밀린다. 대신 툴바 아래
   3/16u — 툴바가 자기 도구 사이에 쓰는 것과 같은 간격 — 에 각을 가진 섬으로
   뜬다. 좌우 인셋도 같은 3/16u 라 툴바의 첫 도구와 왼쪽 끝이 맞는다.
   높이 1u 는 섬 자신의 것이고 툴바의 1¼u 와 다르다: 임시 레이어는 밴드보다
   낮게 서서 자기가 영구 구조가 아님을 형상으로 말한다. */
.t2-subtoolbar-container {
  --t2-subtoolbar-h: var(--t2-u);
  /* 닫기 칩의 한 변. 상자 크기와 열린 높이 계산이 이 하나를 함께 읽는다 —
     상수를 두 곳에 적으면 한쪽만 바뀐다(708 G-4). */
  --t2-subtoolbar-close-size: var(--t2-status-hit);
  position: relative; width: 100%;
  /* 닫기는 섬의 오른쪽 아래 모서리에 붙는다 — 아래 규칙 참고. */
  display: flex; flex-direction: column; align-items: stretch;
  overflow: hidden;
  max-height: 0; opacity: 0;
  background: transparent;
  /* 닫혀 있을 때 높이가 0 이어야 한다 — border 로 아래 경계를 그리면 max-height:0
     에서도 1px 이 남아 세로 예산에 네 번째 밴드가 생긴다(710 B-1). */
  border-bottom: 0;
  /* 닫힌 서브툴바는 탭 순서에서도 빠져야 한다. overflow:hidden 은 보이지 않게만
     할 뿐 초점은 그대로 들어간다 — 재 보면 닫힌 상태의 닫기 버튼이 44x44 상자로
     초점을 받았고, 컨테이너에는 이미 aria-hidden 이 걸려 있어 "aria-hidden 안의
     초점 가능한 요소" 라는 접근성 위반이기도 했다. visibility 가 그 둘을 함께
     해결한다. 전환 목록에 넣어 열릴 때는 즉시, 닫힐 때는 페이드가 끝난 뒤 걸린다. */
  visibility: hidden;
  transition: max-height var(--t2-motion), opacity var(--t2-motion-fast),
              visibility 0s linear var(--t2-motion-ms, 130ms);
}
.t2-subtoolbar-container.active {
  /* 섬 높이 + 위아래 3/16u 인셋 + 모서리에 붙은 칩. 칩이 섬의 아래 인셋을
     자기 음수 여백으로 흡수하므로 간격 한 칸이 빠진다 — 별도의 아래 줄로
     띄우면 여기에 3/16u 가 더 붙는다. 열린 만큼만 본문이 자리를 내준다
     (710 A-4). 값은 전부 격자 토큰에서 온다. */
  max-height: calc(var(--t2-subtoolbar-h) + 2 * var(--t2-chrome-inset)
                   + var(--t2-subtoolbar-close-size));
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}
.t2-subtoolbar {
  display: flex; align-items: center;
  gap: var(--t2-toolbar-gap);
  box-sizing: border-box;
  height: var(--t2-subtoolbar-h);
  /* 섬은 네 변 모두 3/16u 인셋으로 자기 폭을 온전히 갖는다. 닫기가 옆
     칸이면 섬이 그 폭만큼 줄어든다(실측 1280px: 섬 우측 1263 → 1203px).
     세로로는 자기 높이만 차지한다 — column flex 에서 flex:1 은 늘어난다. */
  flex: 0 0 auto;
  margin: var(--t2-chrome-inset);
  /* 예전에는 오른쪽에 닫기 상자만큼(--t2-status-hit + gap) 큰 여백을 미리
     비워 그 위에 닫기를 얹었다. 칩이 섬 밖 모서리로 나갔으니 왼쪽과 같은
     기본 여백이면 된다 — 도구 묶음이 넘쳐도(coarse 44px 바닥 × 도구 7개,
     390px 폰) 도구가 칩 밑을 지나갈 자리가 없다. */
  padding: 0 var(--t2-toolbar-gap);
  background: var(--t2-chrome-fill);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-island);
  box-shadow: var(--t2-shadow-overlay);
  overflow-x: auto; overflow-y: hidden;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.t2-subtoolbar::-webkit-scrollbar { display: none; }
.t2-subtoolbar .t2-btn { flex-shrink: 0; }
/* 닫기는 섬의 오른쪽 아래 모서리에 붙는다 — 모달의 표면 밖 ✕ 와 같은 관용이다.
   세 배치를 재 보고 고른 자리다.
     섬 "안"(옛 배치: top:50% 절대 배치) — 도구 행 위를 덮는다. coarse 44px
       바닥에서 도구가 7개를 넘으면 390px 폰의 정지 스크롤 위치에서 마지막
       도구가 이 자리 밑을 지나가 "×"와 도구 아이콘이 실측 39px 겹쳐 뭉갰다.
     섬 "옆" — 겹침은 없지만 섬이 칩 폭만큼 줄어든다(1280px: 우측 1263→1203px).
     섬 "아래 줄"에 3/16u 띄움 — 안전하지만 칩이 섬에서 떨어져 떠 보이고
       세로를 간격 한 칸 + 칩 한 변만큼 먹는다.
   섬을 물고 들어가지는 않는다: 도구가 섬의 높이를 거의 다 쓴다(실측 아래
   여유 1280px 5.3px, 390px −2.6px — 폰에서는 도구가 섬 밖으로 나온다).
   그래서 칩은 섬의 아래 테두리에 닿되 넘지 않고, 대신 섬의 아래 인셋을
   자기 음수 여백으로 흡수해 간격 한 칸을 돌려준다.
   채움은 옅게 — 콘텐츠를 가리는 무게로 보이지 않게 반투명 + 블러로 남긴다. */
.t2-subtoolbar-close {
  flex: 0 0 auto;
  align-self: flex-end;
  /* 위 음수 여백이 섬의 아래 인셋과 같아 칩의 위 끝이 섬의 아래 테두리에
     정확히 닿는다 — 흐름 배치라 컨테이너 높이가 칩만큼 저절로 자란다.
     절대 배치로 두면 부모 높이에 안 잡혀 overflow:hidden 에 잘린다(실측). */
  margin: calc(-1 * var(--t2-chrome-inset)) var(--t2-chrome-inset)
          var(--t2-chrome-inset) 0;
  width: var(--t2-subtoolbar-close-size); height: var(--t2-subtoolbar-close-size);
  display: flex; align-items: center; justify-content: center;
  border: 0;
  background: color-mix(in srgb, var(--t2-chrome-fill) 55%, transparent);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: var(--t2-ink-muted);
  border-radius: var(--t2-radius-pill);
  cursor: pointer;
}
/* backdrop-filter 미지원 엔진은 반투명 채움만 남는다 — 완전 투명보다는
   또렷해야 하므로 채움 비율을 올려 대비를 보충한다. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .t2-subtoolbar-close { background: color-mix(in srgb, var(--t2-chrome-fill) 82%, transparent); }
}
/* 708 H-3: 최소 히트 영역은 밀도 조정 대상이 아니다. --t2-status-hit 이 이미
   44px 을 바닥으로 갖지만, 격자가 그보다 커진 화면에서도 coarse 상자는 44px
   아래로 내려가지 않는다는 것을 이 규칙이 명시적으로 못박는다. 크기 토큰
   하나만 바꾸면 열린 높이 계산(.active max-height)도 함께 따라온다. */
@media (pointer: coarse) {
  .t2-subtoolbar-container { --t2-subtoolbar-close-size: var(--t2-hit); }
}
.t2-subtoolbar-close:hover {
  color: var(--t2-ink);
  background: color-mix(in srgb, var(--t2-tool-tile-hover) 85%, transparent);
}
.t2-subtoolbar-close .material-icons { font-size: 18px; }

/* 기능군은 spacing 과 divider 로 읽힌다(704 §16). 같은 그룹 안의 도구는 밑단
   1/8u 로 벌려 인접 타일이 한 덩어리로 읽히지 않게 하고, 그룹과 그룹 사이는 그
   두 배인 1/4u 로 벌려 경계가 선 없이도 보이게 한다(708 D-1).
   divider 는 그 경계를 대신 그리는 것이지 새 간격을 만드는 것이 아니다 — 툴바의
   gap 이 divider 양옆에 이미 1/8u 씩 주므로 자기 마진을 갖지 않는다. 그러면
   divider 가 있든 없든 그룹 경계는 똑같이 1/4u 다. */
.t2-toolbar .t2-toolbar-group { display: flex; align-items: center; gap: var(--t2-toolbar-gap); flex: 0 0 auto; }
.t2-toolbar .t2-toolbar-group + .t2-toolbar-group { margin-inline-start: var(--t2-toolbar-gap); }
.t2-toolbar .t2-toolbar-divider,
.t2-toolbar .t2-divider {
  /* 선은 자리를 차지하지 않는다. 폭 1px 을 흐름에 두면 그 1px 이 경계에 더해져
     divider 가 있는 경계만 1px 넓어진다 — 재 보면 실제로 그랬다. 상자를 0 으로
     두고 선은 pseudo 로 그리면, 양옆의 1/8u 만 남아 divider 유무와 무관하게
     경계가 정확히 1/4u 다. 선은 그 경계의 한가운데에 선다. */
  position: relative;
  flex: 0 0 auto; width: 0; height: var(--t2-u2);
  background: transparent;
  margin-inline: 0;
  border: 0;
}
.t2-toolbar .t2-toolbar-divider::after,
.t2-toolbar .t2-divider::after {
  content: "";
  position: absolute;
  inset-block: 0;
  left: 0;
  width: 1px;
  background: var(--t2-line);
}

/* 도구는 정사각 타일이고 한 변은 격자가 정한다 — 3/4u, 44px 이 바닥(710 A-6).
   시각면과 터치 타깃을 하나로 합쳐, 예전처럼 ::before 로 히트 영역을 시각면 밖으로
   늘리지 않는다. 휴식 상태에서도 중성 그레이 면(--t2-tool-tile)을 깔아 "만질 수
   있는 타일"로 읽히게 한다.
   면 명도는 재료일 뿐 상태 단서가 아니다 — 활성 여부는 아래 하단 rail 이 알린다. */
.t2-toolbar .t2-btn,
.t2-toolbar .t2-toolbar-group-btn {
  position: relative;
  width: var(--t2-chrome-hit);
  min-width: var(--t2-chrome-hit);
  height: var(--t2-chrome-hit);
  min-height: var(--t2-chrome-hit);
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--t2-tool-ink);
  background: var(--t2-tool-tile);
  border: 0;
  border-radius: var(--t2-radius-tile);
  box-shadow: none;
  flex: 0 0 auto;
  transition: color var(--t2-motion-fast), background-color var(--t2-motion-fast);
}
/* 그룹 도구는 자기 일을 하지 않고 다른 도구를 연다. 그 차이를 형상으로 말하지
   않으면 눌러 보기 전에는 알 수 없다 — 오른쪽 아래의 작은 점 하나가 "여기 더
   있다"를 뜻한다(스펙 01 의 .tb-btn.is-group). 상태가 아니라 종류를 말하는
   표식이므로 활성 여부와 무관하게 늘 있다. */
.t2-toolbar .t2-toolbar-group-btn::before {
  content: "";
  position: absolute;
  /* 스펙은 타일 모서리에서 2px 이다(u=52 기준 1/26u). 격자로 옮기면 1/32u —
     3/32u 짜리 밴드 인셋의 1/3 이다. 인셋의 절반을 쓰면 점이 타일 한가운데로
     끌려와 "모서리의 표식" 으로 읽히지 않는다. */
  right: calc(var(--t2-u) / 32);
  bottom: calc(var(--t2-u) / 32);
  width: 3px;
  height: 3px;
  border-radius: var(--t2-radius-pill);
  /* 종류를 말하는 표식이므로 상태와 무관하게 늘 같은 색이다. 잉크에서 뽑으면
     옆의 글리프와 섞여 "점이 있다" 는 사실 자체가 읽히지 않는다. */
  background: var(--t2-product);
  pointer-events: none;
}
.t2-toolbar .t2-btn:hover:not(:disabled),
.t2-toolbar .t2-toolbar-group-btn:hover:not(:disabled) {
  color: var(--t2-ink);
  background: var(--t2-tool-tile-hover);
}
/* active — 주 단서 하단 rail, 보조 단서 ink·weight. 타일 명도는 한 단계 또렷해질
   뿐 채움색으로 활성을 대신하지 않는다. */
.t2-toolbar .t2-btn.active,
.t2-toolbar .t2-toolbar-group-btn.active,
.t2-toolbar .t2-btn[aria-pressed="true"] {
  color: var(--t2-product-ink);
  background: var(--t2-tool-tile-active);
  font-weight: 600;
}
.t2-toolbar .t2-btn.active::after,
.t2-toolbar .t2-toolbar-group-btn.active::after,
.t2-toolbar .t2-btn[aria-pressed="true"]::after {
  content: "";
  position: absolute;
  left: var(--t2-rail-inset); right: var(--t2-rail-inset); bottom: 0;
  height: var(--t2-rail-w);
  background: var(--t2-product);
  /* rail 은 가운데에서 양끝으로 살짝 넘어갔다 돌아오며 펴진다. */
  animation: t2-rail-in var(--t2-motion-spring) both;
}
@keyframes t2-rail-in { from { transform: scaleX(0); } to { transform: scaleX(1); } }
/* 그룹으로 접힌 버튼은 자리를 차지하지 않는다. 위 .t2-toolbar .t2-btn 규칙이
   display 를 선언하므로, 같은 레이어에서 그보다 뒤에 오면서 더 구체적이어야
   이긴다. 예전 런타임 CSS 는 !important 로 이겼고, 그 파일을 걷어내면서 잠시
   숨김이 풀려 접힌 버튼이 그대로 폭을 먹고 툴바가 넘쳤다. */
.t2-toolbar .t2-btn.t2-hidden,
.t2-subtoolbar .t2-btn.t2-hidden { display: none; }

/* ── 성격 부여 ────────────────────────────────────────────────────────
   형상은 sharp 그대로 두고 거동만 바꾼다. 도구는 눌리면 살짝 움츠러들고,
   가리키면 아이콘이 1px 올라온다. 색·radius·테두리는 전혀 건드리지 않으므로
   법의 형상 규칙과 충돌하지 않는다. */
.t2-toolbar .t2-btn :is(svg, .material-icons, .material-icons-outlined),
.t2-toolbar .t2-toolbar-group-btn :is(svg, .material-icons, .material-icons-outlined) {
  transition: transform var(--t2-motion-spring);
}
.t2-toolbar .t2-btn:hover:not(:disabled) :is(svg, .material-icons, .material-icons-outlined),
.t2-toolbar .t2-toolbar-group-btn:hover:not(:disabled) :is(svg, .material-icons, .material-icons-outlined) {
  transform: translateY(-1px);
}
.t2-toolbar .t2-btn { transition: color var(--t2-motion-fast), background-color var(--t2-motion-fast),
                                  transform var(--t2-motion-spring); }
.t2-toolbar .t2-btn:active:not(:disabled),
.t2-toolbar .t2-toolbar-group-btn:active:not(:disabled) { transform: scale(.92); opacity: 1; }

/* 콘텐츠 블록 코너 tick 도 같은 거동으로 등장한다 */
.t2-editor .t2-content-block[data-t2-selected="true"]::before,
.t2-editor .t2-content-block[data-t2-selected="true"]::after {
  animation: t2-tick-in var(--t2-motion-spring) both;
}
@keyframes t2-tick-in { from { opacity: 0; transform: scale(.4); } to { opacity: 1; transform: scale(1); } }

/* 비활성 도구는 잉크만 물러난다. 면까지 걷으면 타일을 켠 설치에서 도구 줄에
   구멍이 뚫려, "지금 못 쓴다"가 아니라 "여기 도구가 없다"로 읽힌다 — 면은
   재료이고 사용 가능 여부는 잉크가 말한다(707 상태 단서 예산). 타일을 끈
   기본 설치에서는 이 값이 transparent 라 예전과 같은 화면이다. */
.t2-toolbar .t2-btn:disabled { opacity: 1; color: var(--t2-ink-faint); background: var(--t2-tool-tile); }
/* 플러그인 준비 상태 — disabled 처럼 보이지 않게 약하게만 (기존 계약 유지) */
.t2-toolbar .t2-btn[data-plugin-state="pending"]:not(:disabled),
.t2-toolbar .t2-btn[data-plugin-state="loading"]:not(:disabled) { opacity: .55; }
.t2-toolbar .t2-btn[data-plugin-state="loading"]:not(:disabled) { cursor: progress; }
.t2-toolbar .t2-btn[data-plugin-state="failed"]:not(:disabled) { color: var(--t2-danger); }

.t2-btn-loading-overlay { background: transparent; }
.t2-btn-loading-spinner {
  width: 14px; height: 14px;
  border: 1px solid var(--t2-line-strong);
  border-top-color: var(--t2-product);
  border-radius: 50%;
}

/* subtoolbar — 컨테이너·레이아웃은 위 §toolbar 블록이 소유한다.
   여기는 그 안의 도구 외형만 정한다. */
/* 서브툴바의 도구도 툴바의 도구와 같은 타일이다 — 같은 종류의 조작이 두 밴드에
   나뉘어 있을 뿐이다(스펙 02 의 .mob-editor-stage .tb-btn 은 0.875U 정사각).
   34px 상수는 손가락 표적으로 모자랐다 — 재 보니 48×34 였다. 라벨이 붙는 도구는
   가로로 자랄 수 있게 두되 세로와 최소 가로는 밴드의 조작부 치수를 따른다. */
.t2-subtoolbar .t2-btn {
  position: relative;
  width: auto;
  /* t2-foundation.css 의 호스트 침입 바닥(#90, t2.contract 레이어)이
     .t2-btn 전체에 min-width: var(--t2-hit)(고정 44px) 를 !important 로
     건다. 레이어 순서(token→legacy→module→product→plugin→contract)에서
     important 는 순서가 뒤집혀 "먼저 선언된 레이어가 이긴다" — 그래서 t2.product
     안의 이 규칙이 그냥 min-width 였을 때는 늦게 선언된 t2.contract 의 44px
     고정폭에 져서, 아이콘만 있는 서브툴바 타일이 44 × chrome-hit(1280px에서
     74.5px)로 찌그러졌다(가로만 좁고 세로는 안 줄어든 것은 height 쪽엔 경쟁하는
     important 선언이 없어서다). chrome-hit 자체가 이미 max(..., 44px) 로 44px
     바닥을 내장하므로 바닥을 낮추는 게 아니라, 같은 important 층위에서 더
     구체적인(그리고 더 정확한) 값이 이기게 할 뿐이다. */
  min-width: var(--t2-chrome-hit) !important;
  height: var(--t2-chrome-hit);
  min-height: var(--t2-chrome-hit);
  padding: 0 var(--t2-s2);
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: var(--t2-radius-tile);
  background: var(--t2-tool-tile); color: var(--t2-ink-muted); font-size: 12.5px;
  transition: color var(--t2-motion-fast), background-color var(--t2-motion-fast);
}
.t2-subtoolbar .t2-btn:hover:not(:disabled) { color: var(--t2-ink); background: var(--t2-tool-tile-hover); }
.t2-subtoolbar .t2-btn.active { color: var(--t2-product-ink); font-weight: 600; background: var(--t2-tool-tile-active); }
.t2-subtoolbar .t2-btn.active::after {
  content: ""; position: absolute;
  left: var(--t2-rail-inset); right: var(--t2-rail-inset); bottom: -2px;
  height: var(--t2-rail-w); background: var(--t2-product);
}

/* canvas — 인셋 20px. 자식 블록은 14px 로 한 단계 작다.
   710 B-2: 상단 크롬 1¼u 는 툴바 밴드 그 자체다. 예전에는 밴드 1u 와 그 아래
   간격 1/4u 로 나뉘어 있었는데, 그러면 밴드와 콘텐츠 사이에 아무것도 없는 띠가
   생겨 툴바가 화면에서 떠 보인다. 게다가 그 간격은 세로 예산 밖에 있어서 합이
   10¼u 가 됐다 — 재 보면 실제로 그랬다(1280px 폭에서 873.3px, 10u = 852). 밴드가
   1¼u 를 통째로 가지면 예산은 다시 10u 로 닫히고 콘텐츠는 툴바에 붙어 선다. */
/* 본문의 인셋도 격자에서 온다. 앞 판본은 20px 상수였고, 그래서 화면이 넓어져도
   글줄이 프레임에 붙은 채로 남았다. 스펙의 .content-area 는 u=52 에서 24px×30px —
   세로 0.46u · 가로 0.58u 이며, 사다리로 옮기면 둘 다 1/2u 다.

   가로 1/2u 는 그 위에 뜨는 것들(폭 13u 모달, 좌우 1u 인셋의 검색 묶음)보다
   안쪽이다. 글줄이 떠 있는 표면보다 바깥으로 나가면 두 층의 왼쪽 끝이 어긋나 보인다. */
.t2-editor {
  padding: var(--t2-u2);
  background: var(--t2-canvas-fill);
  color: var(--t2-ink);
  border-radius: var(--t2-radius-structural);
  border: 0;
  outline: none;
  font-size: 16px;
  line-height: 1.7;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--t2-line-strong) transparent;
}
.t2-editor ::selection { background: var(--t2-product-select); text-shadow: none; }
.t2-editor > p[data-t2-boundary] { min-height: 1.5em; cursor: text; }

/* 폰의 세로 예산은 10u 가 아니라 16u 다(710 A-3·B-14).

   데스크톱의 7¾u 를 폰의 u 로 그대로 적으면 본문이 303px 로 줄고 화면 아래
   절반이 빈 채로 남는다 — 7¾ 은 가로 15u 인 프레임의 값이고, 폰의 프레임은
   가로 10u 이기 때문이다. 스펙 02 의 본문에는 heightUnits 가 아예 없고
   scrollable: true 뿐이며, 데모 기기(340×550)에서 실제로 차지하는 높이는
   550 − 1¼u − 1.1u = 468px = 13.8u 다.

   그 값을 사다리에 올리면 13¾u 이고, 폰의 예산이 그 자리에서 닫힌다:
     툴바 1¼ + 본문 13¾ + 하단 바 1 = 16u.
   데스크톱과 같은 구조이고 숫자만 프레임의 것으로 바뀐다. 393px 폰에서
   본문 537.6px — 고치기 전(507px)과 거의 같으면서 크롬만 스펙의 치수로 선다.

   실측은 16u 가 아니라 16.6u 다. 폰의 7/8u(34.2px)가 최소 히트 영역 44px 보다
   작아 두 밴드가 그만큼 자랐기 때문이며, 이것은 예외가 아니라 A-6 이 정한
   순서다 — 히트 영역은 기하 수치이므로 격자보다 우선한다. 데스크톱에서는
   7/8u 가 44px 을 넘으므로 같은 식이 정확히 10u 로 닫힌다.

   관리자가 고른 고정 높이(--t2-editor-content-height)는 여기서도 그대로
   이긴다. 그 값은 운영자가 명시적으로 고른 것이므로 격자보다 우선한다. */
@media (max-width: 760px) {
  .t2-editor {
    height: var(--t2-editor-content-height, calc(var(--t2-u) * 13.75));
    min-height: var(--t2-editor-content-height, calc(var(--t2-u) * 13.75));
  }
}

/* ═══ 5b. 하단 상태 바 — T2Editor 고유 감성 ═══════════════════════════
   ⚠ 실제 클래스는 .t2-editor-status 다 (.t2-editor-statusbar 아님).
   이 셸은 원본의 얼굴이다. 로고 lockup · 자동 저장 토글 · 글자 수 ·
   테마/번역 스위치를 원형 유틸리티로 유지한다. */
.t2-editor-status {
  display: flex;
  align-items: center;
  gap: var(--t2-u4);
  /* 710 C-5: 프레임과 요소 사이는 좌·우·상·하 모두 1/4 로 통일한다. 세로는
     조작 상자가 44px 바닥에 걸리면 그만큼 줄어든다(--t2-status-pad). */
  padding: var(--t2-status-pad) max(var(--t2-status-inset), env(safe-area-inset-right))
           var(--t2-status-pad) max(var(--t2-status-inset), env(safe-area-inset-left));
  box-sizing: border-box;
  /* 하단 바의 밴드는 1u 다 — 툴바의 1¼u 와 다른 값이고, 그 차이가 위계다. */
  min-height: var(--t2-status-band);
  border-top: 0;
  /* 위 경계도 같은 이유로 밴드 높이 밖에 있다(710 B-9). */
  box-shadow: inset 0 1px 0 var(--t2-line);
  border-radius: var(--t2-radius-structural);
  /* 710 C-6: 폭이 모자라도 요소를 지우지 않는다. 수단은 셋이고 순서가 있다 —
     간격을 한 단계 내리고(아래 컨테이너 질의), 자동 저장 라벨을 접고, 그래도
     모자라면 환경 스위치를 ⚙ 넘침 메뉴 안으로 수납한다. 어느 단계에서도 기능은
     사라지지 않는다(해설 706 제1항·제7항).

     바가 자기 가로 넘침을 소유하던 규칙은 걷어냈다. overflow-x:auto 는
     overflow-y:visible 을 auto 로 승격시키므로, 바에서 위로 열리는 층 — 언어
     메뉴와 아래의 넘침 메뉴 — 이 자기 바에 잘렸다. 재 보면 실제로 그랬다:
     1280px 폭에서 27px 짜리 언어 메뉴가 10.4px 만 보였다. 수납 단계가 있어
     바는 더 이상 넘칠 이유가 없으므로, 넘침 소유권 대신 층의 도달 가능성을
     택한다. */
  flex-wrap: nowrap;
  overflow: visible;
  background: var(--t2-chrome-fill);
  font-family: var(--t2-font-ui);
  font-size: 13px;
  color: var(--t2-ink-muted);
}
/* 좁아질 때 첫 수단은 간격을 사다리의 한 단계 아래로 내리는 것이다(710 C-6). */
/* 710 C-2: 로고 · 1/2u · 자동저장 토글. 그 1/2u 는 묶음의 gap 이 아니라 로고가
   자기 오른쪽 여백으로 소유한다 — 관리자가 로고를 끄면 여백도 함께 사라지고,
   토글은 프레임의 1/4u 인셋에 그대로 선다(708 G-4). */
.t2-status-left { display: flex; align-items: center; gap: 0; min-width: 0; flex: 0 0 auto; }
.t2-status-left > .t2-logo-link,
.t2-status-left > .t2-logo { margin-inline-end: var(--t2-u38); }

/* ── 로고 lockup — 고정 브랜드 자산 ──────────────────────────────────
   ⚠ 이것은 일반 표면이 아니다. radius·색·구조를 표면 토큰으로 정규화하지
     않는다. 이 개정의 초안이 radius 일괄 치환에 이 요소를 휩쓸어 넣어 일체감을
     깨뜨렸다.

   정의: 실선 테두리를 두른 조금 둥근 직사각형 하나. 그 안에서
     T2      #ff6600 면에 흰 글자. 두 값 모두 테마와 무관하게 고정이다.
     Editor  면은 투명이고 글자만 테마를 따른다 — 라이트에서 진회색~검정,
             다크에서 밝은 회색~흰색.
   테두리와 radius 는 lockup 전체에만 걸고 각 칸에 걸지 않는다. 칸에 걸면
   두 조각으로 읽힌다. 두 칸의 경계는 색이 바뀌는 것으로 충분하다.

   비율이 불변이고 글꼴이 거기에 맞춘다. 반대가 아니다. 높이 H 하나로 다섯 개가
   전부 결정된다.
     ① lockup   세로 5 : 가로 12                     -> 폭 = H / 5 * 12
     ② 두 칸    T2 : Editor = 1 : 2.25               -> 칸폭 = 안쪽폭 / 3.25, * 2.25
     ③ 상하 여백 글자 위아래 여백 = H / 4.5           -> 글꼴 크기가 여기서 나온다
     ④ 좌우 여백 = 상하 여백 / 1.75
     ⑤ 그 좌우 여백이 두 칸에서 같은 크기
   ③ 이 글꼴 크기를 정한다. 안쪽 높이에서 위아래 여백을 빼면 남는 것이 글자 크기다.

       F = H - 2 x (H / 26) - 2 x (H / 4.5) = H x .478632

   그러면 ④⑤ 는 글자 폭까지 못박는다. 가운데 정렬에서 좌우 여백은
   (칸폭 - 글자폭) / 2 이므로, 여백이 정해졌다는 것은 글자 폭이 정해졌다는 뜻이다.

       그려야 할 T2 폭     = 칸폭 - 2 x 여백 = H x .460825
       그려야 할 Editor 폭 = 칸폭 - 2 x 여백 = H x 1.354316

   이 글꼴·굵기가 F 로 그리는 자연 폭은 T2 가 H x .566747, Editor 가 H x 1.358359 다.
   Editor 는 맞지만 T2 는 23% 넓다 — 두 글자짜리 낱말에 칸의 1/3.25 밖에 없으니
   당연하다. 여기서 비율을 물릴 수는 없으므로 글꼴이 칸에 맞춘다. 아래 fit 계수는
   그 맞춤량이고, 자연 폭 나누기 그려야 할 폭이다. 글꼴이나 굵기를 바꾸면 자연 폭이
   달라지므로 계수를 다시 풀어야 하며, ①~⑤ 쪽을 손대서는 안 된다.
   letter-spacing 으로 줄이는 길은 막혀 있다 — 필요한 양(-.10em)에서 T 가로획이
   2 위로 올라탄다. 자간이 아니라 자폭을 줄여야 한다.

   테두리를 border 가 아니라 inset box-shadow 로 그리고 그 자리를 padding 으로
   비워 두는 이유: Chromium 은 border-width 를 정수 px 로 내림한다. 높이에 비례하는
   테두리(H/26, 기준 크기에서 1px)를 border 로 주면 40px 에서 1.54px 가 1px 로
   깎여 안쪽 상자가 비례에서 벗어나고, 그 순간 ⑤ 가 0.8px 어긋난다. padding 과
   box-shadow 는 소수를 그대로 쓰므로 어떤 높이에서도 도형이 닮은꼴로 유지된다.

   칸에 padding 을 주지 않는 이유는 또 다르다 — 여백은 칸폭과 가운데 정렬이 만들고,
   padding 을 더하면 여백이 두 번 들어가 좌우가 다시 어긋난다.

   글꼴은 끝이 곧게 뻗은 그로테스크 산세리프로 고정한다. 주변 문맥의 본문 글꼴이
   세리프면 로고까지 세리프로 그려지기 때문에 상속에 맡기지 않는다.
   크기를 바꿔야 하면 --t2-logo-height 하나만 바꾼다 — 폭·여백·글자 크기가 함께
   따라온다. */
.t2-logo {
  --t2-logo-height: var(--t2-status-face);
  --t2-logo-line: calc(var(--t2-logo-height) / 26);
  --t2-logo-text: calc(var(--t2-logo-height) * .478632);
  --t2-logo-fit-prefix: .81311;
  --t2-logo-fit-suffix: .99702;
  display: inline-flex;
  align-items: stretch;
  /* 테두리는 비율 안쪽에 들어간다. content-box 로 두면 테두리가 밖으로 붙어
     실제로 그려지는 상자가 12:5 가 아니게 된다. */
  box-sizing: border-box;
  height: var(--t2-logo-height);
  width: calc(var(--t2-logo-height) / 5 * 12);
  aspect-ratio: 12 / 5;
  padding: var(--t2-logo-line);
  box-shadow: inset 0 0 0 var(--t2-logo-line) var(--t2-line);
  border-radius: var(--t2-radius-brand);
  /* T2 면은 칸이 아니라 lockup 이 깐다. 칸에 깔면 글꼴을 칸에 맞추는 scaleX 가
     면까지 같이 줄여 ② 가 무너진다. content-box 기준이라 100% / 3.25 는 T2 칸폭과
     정확히 같다. */
  background: linear-gradient(90deg, #ff6600 0 calc(100% / 3.25), transparent 0);
  background-origin: content-box;
  background-clip: content-box;
  overflow: hidden;
  text-decoration: none;
  flex: 0 0 auto;
  font-family: "IBM Plex Sans", "Helvetica Neue", Arial, "Noto Sans KR", system-ui, sans-serif;
  line-height: 1;
  white-space: nowrap;
  transition: transform var(--t2-motion-spring), box-shadow var(--t2-motion-fast);
}
/* 708 H-3: 로고가 링크일 때만 조작 대상이다(에디터·관리자 대부분은 span/div).
   lockup 은 12:5 고정 비율이라 자기 상자를 키울 수 없다. 그래서 링크가
   lockup 을 감싸고 히트 영역은 링크가 갖는다 — 보이는 칩의 기하는 그대로다. */
.t2-logo-link {
  display: inline-flex; align-items: center;
  min-height: var(--t2-hit);
  text-decoration: none;
}
.t2-logo-prefix,
.t2-logo-suffix {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  min-width: 0;
  padding: 0;
  margin: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  font-size: var(--t2-logo-text);
}
.t2-logo-prefix {
  flex: 0 0 calc(100% / 3.25);
  color: #fff;
  font-weight: 700;
  letter-spacing: normal;
  transform: scaleX(var(--t2-logo-fit-prefix));
}
.t2-logo-suffix {
  flex: 0 0 calc(100% * 2.25 / 3.25);
  color: var(--t2-ink);
  font-weight: 700;
  letter-spacing: normal;
  transform: scaleX(var(--t2-logo-fit-suffix));
}
.t2-logo { gap: 0; }
a:hover > .t2-logo, .t2-logo:hover {
  box-shadow: inset 0 0 0 var(--t2-logo-line) var(--t2-line-strong);
  transform: translateY(-1px);
}

/* 자동 저장 토글 — 진짜 원형 컨트롤 예외. 썸에 스프링 거동을 준다.
   710 C-5: 조작부와 그 이름 사이는 하단 바에서 가장 가까운 관계이므로 사다리의
   밑단 1/8u 다. 4px 사다리의 --t2-s2 를 그대로 두면 이 자리만 화면이 커져도
   8px 에 멈춰, 옆의 모든 간격이 자랄 때 라벨만 토글에 붙는다. */
.t2-autosave-toggle {
  display: flex; align-items: center; gap: var(--t2-u8);
  margin-left: 0;
  min-height: var(--t2-hit);
  height: auto;
  cursor: pointer;
  -webkit-user-select: none; user-select: none;
}
/* 토글은 하단 바에서 가장 작은 조각이다 — 스펙의 .switch 는 0.44U × 0.22U 이고
   그 둘은 2:1 이다. 앞 판본은 1u × 1/2u 였고, 그래서 로고와 맞먹는 폭으로 서서
   "환경 스위치" 가 아니라 하단 바의 주인공처럼 보였다.

   높이 하나만 정하고 폭은 거기서 2배로 유도한다 — 따로 적으면 한쪽만 고쳐져
   비율이 깨진다(708 G-4). 썸·여백·이동 거리는 토글 모듈이 다시 높이에서
   파생하므로 그림을 여기서 다시 그리지 않는다: 같은 조작부가 관리 화면에도
   있고, 두 벌이 되면 한쪽만 고쳐진다.

   14px 은 좁은 화면에서 트랙이 선으로 보이지 않게 막는 바닥이다. 조작 상자는
   어차피 44px 이므로(토글 모듈의 ::before) 이 값은 보이는 크기만 정한다. */
.t2-autosave-toggle .t2-toggle {
  --t2-toggle-h: max(var(--t2-status-mark), 14px);
  --t2-toggle-w: calc(var(--t2-toggle-h) * 2);
}
/* §4 의 버튼 기준선은 라벨이 들어가는 버튼을 위한 것이라 36px 바닥을 깐다.
   토글의 조작 상자에는 라벨이 없고 그 상자가 곧 트랙이므로, 그 바닥이 걸리면
   0.22u 짜리 트랙이 36px 로 부풀어 오른다 — 재 보니 실제로 그랬다(18.8 → 36).
   토글 모듈이 정한 치수를 제품 층에서 한 번 더 확정해 기준선을 넘어선다.
   손가락이 닿는 44px 은 모듈의 ::before 가 따로 소유한다. */
.t2-toggle-control {
  width: var(--t2-toggle-w);
  height: var(--t2-toggle-h);
  min-width: var(--t2-toggle-w);
  min-height: var(--t2-toggle-h);
  padding: 0;
}
/* 710 C-5: 라벨도 읽는 값이므로 세로 높이는 1/4u 다.
   710 C-9 의 두 번째 수단 — 좌·우 묶음이 닿을 만큼 좁아지면 이 라벨이 먼저
   접힌다. 판정은 실제 여백을 재는 런타임이 하고(js/runtime/autosave.js), 여기서는
   무엇이 접히는지만 정한다. <label for> 이므로 화면에서 빠져도 접근 이름은
   토글에 그대로 붙어 있다 — 기능도 상태도 사라지지 않는다. */
.t2-autosave-text {
  /* 편집기 컨테이너가 모든 자손에 min-width:0 을 걸어 두어, 이것이 없으면 라벨이
     좁아질 때 스스로 0 까지 눌린다. 그러면 아래 밀도 판정이 볼 압력이 사라지고
     라벨은 "접힌" 것이 아니라 "찌그러진" 상태로 남는다 — 접는 결정은 런타임이
     내려야 하고 flex 가 대신 내려서는 안 된다. */
  flex: 0 0 auto;
  /* 읽는 값의 바닥. 비례 격자가 텍스트까지 비례시켜 276px 임베드에서 6.85px,
     246px 에서 6.1px 까지 내려갔다(2026-08-25 실측, docs/ux-patch-plan-2026-08-25).
     바닥이 있으므로 접힘(dense) 문턱에도 절대 최소분이 필요하다 — js/runtime/
     autosave.js 의 foldFloor 와 짝이다. */
  font-size: max(var(--t2-u4), 11px);
  line-height: 1;
  color: var(--t2-ink-muted);
  cursor: pointer;
  white-space: nowrap;
}
.t2-editor-status[data-t2-status-dense="true"] .t2-autosave-text {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* 글자 수 — 수치는 mono, tabular. 값이 바뀌어도 폭이 흔들리지 않는다.
   710 C-3: 글자의 세로 높이는 1/4u 다. 조작부가 아니라 읽는 수치이므로 옆의
   원형(1/2u)보다 한 단계 낮다. line-height 1 이라 글자 상자가 곧 그 높이다.
   다음 조작과의 사이만 1/4u 로 벌린다 — 묶음의 1/8u 에 자기 오른쪽 여백
   1/8u 를 더해 만든다. 그 여백의 소유자는 글자 수다(708 G-4). */
.t2-char-count {
  /* 같은 이유로 눌리지 않는다. 숫자가 잘리면 값을 읽을 수 없다. */
  flex: 0 0 auto;
  font-family: var(--t2-font-mono);
  /* 스펙의 .text-count 와 .switch 는 같은 수치(0.22U)를 쓴다 — 하단 바에서
     "작은 것" 한 종류가 글자와 트랙 양쪽에 걸쳐 있다는 뜻이다. 읽는 수치이므로
     바닥 11px — 276px 임베드에서 4px 까지 내려갔었다(2026-08-25 실측). */
  font-size: max(var(--t2-status-mark), 11px);
  line-height: 1;
  margin-inline-end: var(--t2-u8);
  font-variant-numeric: tabular-nums;
  color: var(--t2-ink-muted);
  letter-spacing: 0;
}
.t2-char-count-value { color: var(--t2-ink-muted); font-weight: 500; }
/* #84: 콜론은 소유자 있는 요소로 간격을 토큰에 묶는다(708 G-4) — 공백 텍스트
   노드에 맡기던 시절에는 값의 자간과 공백이 합쳐 "29 : txt" 로 비대칭으로 읽혔다. */
.t2-char-count-sep { margin-inline: var(--t2-u8); }

/* 오른쪽 묶음 — 글자 수 → 번역 → 테마 토글. margin-left:auto 로 하단 바 우측에
   정렬한다. 환경 버튼 자체가 44px hit box를 소유하므로 별도 간격 없이 맞댄다. */
/* 710 C-4·C-7: 글자 수 → 툴바 숨기기 → 번역 → 라이트/다크, 요소 간 1/8u.
   읽기 전용 정보가 먼저, 표시 상태를 바꾸는 조작이 뒤다(709 A). */
.t2-status-right { display: flex; align-items: center; gap: var(--t2-status-inset); margin-left: auto; flex: 0 0 auto; }

/* 710 C-10: 좁은 규모에서는 하단 바의 모든 간격이 사다리의 밑단인 1/8u 로 모인다.
   요소마다 다른 값을 남겨 두면 좁은 폭에서 그 차이가 리듬이 아니라 잡음이 된다.
   프레임 인셋도 같은 값을 쓰고, 자기 여백을 소유한 요소들은 그 여백을 반납한다 —
   묶음의 간격 하나만 남는다. 안전 영역은 그보다 작아질 수 없다. */
@container (max-width: 760px) {
  .t2-editor-status {
    gap: var(--t2-u8);
    padding-inline-start: max(var(--t2-u8), env(safe-area-inset-left));
    padding-inline-end: max(var(--t2-u8), env(safe-area-inset-right));
  }
  .t2-status-left, .t2-status-right { gap: var(--t2-u8); }
  .t2-status-left > .t2-logo-link,
  .t2-status-left > .t2-logo { margin-inline-end: 0; }
  .t2-char-count { margin-inline-end: 0; }
}
/* 환경 스위치 묶음 — 테마 토글 버튼만 감싼다. 우측 정렬은 .t2-status-right 소유. */
/* 이 래퍼는 이제 테마 버튼 하나만 감싼다(툴바 숨기기는 묶음의 형제로 나갔다).
   간격을 남겨 두면 자식이 다시 늘었을 때 격자 밖의 값이 조용히 살아난다. */
.t2-dark-mode-toggle { display: flex; align-items: center; gap: var(--t2-u8); padding-left: 0; }
.t2-translate-toggle { position: relative; display: flex; align-items: center; padding-left: 0; }

/* ── 넘침 수납(⚙) — 710 C-6 의 세 번째 수단 ────────────────────────────
   좁아지면 환경 스위치가 이 메뉴 안으로 들어가고, 라이트/다크 전환만 우측
   끝에 남는다. 테마는 화면을 읽는 조건 자체를 바꾸는 스위치라 다른 스위치와
   같은 층에 수납되면 "안 보여서 못 바꾸는" 상태가 생긴다.
   판정은 폭이 아니라 실제로 남은 자리가 한다(js/runtime/autosave.js). */
.t2-status-overflow { position: relative; display: none; align-items: center; flex: 0 0 auto; }
.t2-editor-status[data-t2-status-overflow="true"] .t2-status-overflow { display: flex; }
.t2-status-overflow-menu {
  position: absolute;
  bottom: calc(100% + var(--t2-status-inset));
  right: 0;
  z-index: 60;
  min-width: calc(var(--t2-u) * 3);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--t2-u8);
  padding: var(--t2-u8);
  background: var(--t2-floating);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-island);
  box-shadow: var(--t2-shadow-overlay);
}
.t2-status-overflow-menu[hidden] { display: none; }
/* 수납된 항목은 번호 · 조작 · 이름의 한 줄이다(스펙 01 의 팝업 메뉴). 아이콘만
   쌓아 두면 메뉴 안에서 무엇이 무엇인지 읽히지 않는다. */
.t2-status-overflow-item {
  display: flex;
  align-items: center;
  gap: var(--t2-u8);
  width: 100%;
  white-space: nowrap;
}
.t2-status-overflow-index {
  flex: 0 0 auto;
  display: inline-grid;
  place-items: center;
  width: 1.5em; height: 1.5em;
  border-radius: var(--t2-radius-edge);
  background: var(--t2-inset);
  color: var(--t2-product-ink);
  font-family: var(--t2-font-mono);
  font-size: 11px;
  font-weight: 700;
}
.t2-status-overflow-label {
  flex: 1 1 auto;
  color: var(--t2-ink);
  font-size: 13px;
}
/* 수납된 스위치의 자기 메뉴는 옆으로 연다 — 위로 열면 수납 메뉴 자신을 덮는다. */
.t2-status-overflow-menu .t2-translate-menu { bottom: auto; top: 0; right: calc(100% + var(--t2-u8)); }

/* 원형 유틸리티 — 문서 도구가 아니라 환경 스위치다. 형상으로 역할을 가른다.
   원형은 이 용도에만 허용한다 (law 704 §3 의 기능적 예외). */
.t2-editor-status .t2-dark-mode-btn,
.t2-editor-status .t2-translate-btn,
.t2-editor-status .t2-toolbar-toggle-btn {
  position: relative;
  width: var(--t2-status-hit); height: var(--t2-status-hit);
  min-height: var(--t2-status-hit); min-width: var(--t2-status-hit);
  padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--t2-product-ink);
  box-shadow: none;
  box-sizing: border-box;
  flex: 0 0 var(--t2-status-hit);
  transition: color var(--t2-motion-fast);
}
/* 710 C-3: 보이는 원의 지름은 1/2u 다. 조작 상자는 44px 을 바닥으로 갖는 별개
   수치이므로(710 A-6) 원이 상자보다 작아도 히트 영역은 줄지 않는다. */
.t2-editor-status .t2-dark-mode-btn::before,
.t2-editor-status .t2-translate-btn::before,
.t2-editor-status .t2-toolbar-toggle-btn::before {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: var(--t2-status-face); height: var(--t2-status-face);
  transform: translate(-50%, -50%);
  border: 1px solid var(--t2-line);
  border-radius: 50%;
  background: var(--t2-surface);
  box-sizing: border-box;
  pointer-events: none;
  transition: border-color var(--t2-motion-fast), background-color var(--t2-motion-fast),
              transform var(--t2-motion-spring);
}
.t2-editor-status .t2-dark-mode-btn:hover::before,
.t2-editor-status .t2-translate-btn:hover::before,
.t2-editor-status .t2-toolbar-toggle-btn:hover::before {
  border-color: var(--t2-product);
  background: var(--t2-product-wash);
  transform: translate(-50%, calc(-50% - 1px));
}
.t2-editor-status .t2-dark-mode-btn:active::before,
.t2-editor-status .t2-translate-btn:active::before,
.t2-editor-status .t2-toolbar-toggle-btn:active::before {
  transform: translate(-50%, -50%) scale(.94);
}
.t2-editor-status .t2-translate-btn.t2-active {
  color: var(--t2-product-ink);
}
.t2-editor-status .t2-translate-btn.t2-active::before {
  border-color: var(--t2-product);
  background: var(--t2-product-wash);
}
.t2-editor-status :is(.t2-dark-mode-btn, .t2-translate-btn, .t2-toolbar-toggle-btn) .material-icons,
.t2-editor-status :is(.t2-dark-mode-btn, .t2-translate-btn, .t2-toolbar-toggle-btn) .material-icons-outlined {
  position: relative;
  z-index: 1;
  font-size: 17px;
}
.t2-toolbar-toggle-btn .material-icons { transition: transform var(--t2-motion-spring); }

/* 번역 메뉴 */
.t2-translate-menu {
  min-width: 168px;
}
.t2-translate-menu-title {
  padding: var(--t2-s2) var(--t2-s3) var(--t2-s1);
  border-bottom: 1px solid var(--t2-line);
  font-family: var(--t2-font-mono);
  font-size: 10px; font-weight: 500;
  letter-spacing: var(--t2-track-label); text-transform: uppercase;
  color: var(--t2-ink-muted);
}
.t2-translate-menu-list { margin: 0; padding: var(--t2-s1) 0 0; list-style: none; }
.t2-translate-menu-item {
  position: relative;
  display: flex;
  align-items: center;
  min-height: 36px;
  cursor: pointer;
}
.t2-translate-menu-item:hover { color: var(--t2-ink); }
@media (pointer: coarse) { .t2-translate-menu-item { min-height: 44px; } }
.t2-translate-menu-item.t2-active {
  color: var(--t2-product-ink); font-weight: 600; background: transparent;
}
.t2-translate-menu-item.t2-active::after {
  content: ""; position: absolute;
  left: var(--t2-rail-inset); right: var(--t2-rail-inset); bottom: 0;
  height: var(--t2-rail-w); background: var(--t2-product);
}

/* 버전 · 메타 링크 — 프레임 밖의 부차 정보.
   프레임 안에 있을 때는 툴바·콘텐츠·하단 바 셋만 들어가야 할 세로 예산에
   네 번째 밴드로 끼어 있었다(해설 710 B-1). 프레임의 형제로 내보내고, 프레임과의
   거리는 프레임의 아래 여백을 메타 줄이 넘겨받아 형제 간격 하나로 고정한다(708 D).
   흐름 안에 두는 것은 그대로다 — 예전의 position:absolute 는 자기 자리를
   예약하지 않아 아래 콘텐츠 위에 얹혀 있었다(708 F-2). */
/* 이 줄은 셸 밖에 서므로 바탕이 편집기의 것이 아니라 호스트 페이지의 것이다.
   편집기 테마의 잉크를 그대로 쓰면 다크 편집기를 밝은 페이지에 얹었을 때 글자가
   바탕과 붙는다 — 재 보니 2.26:1 이었다. 스펙의 .version-label 도 편집기의
   chrome 색이 아니라 페이지의 --text-muted 를 쓴다.

   그래서 자기 바탕을 갖는다. 셸의 작업면과 같은 면을 얇게 깔면 어떤 페이지 위에
   얹혀도 잉크가 설 자리를 안다. 셸 아래에 붙는 작은 칩이므로 위 두 모서리는 각을
   갖지 않는다 — 셸의 아래 모서리와 이어져 보이기 위해서다. */
.t2-editor-meta-links {
  display: flex; align-items: center; justify-content: flex-end;
  gap: var(--t2-gap-related);
  /* 오른쪽 끝에 붙는다 — 셸과 같은 상자 안에 있으므로 그 오른쪽 모서리와 나란하다.
     auto 를 양쪽에 주면 칩이 가운데로 간다.
     이 자리를 만드는 것은 margin-left:auto 하나인데, 그것을 간격 토큰과 같은
     shorthand 에 적으면 토큰 하나가 해석되지 않을 때 함께 무효가 된다 — 그러면
     칩은 오른쪽이 아니라 화면 왼쪽 끝으로 간다(버전·tLite 표식이 왼쪽 아래로
     떨어지던 자리다). 정렬을 자기 선언으로 떼어 둔다. */
  margin-top: var(--t2-gap-related, 8px);
  margin-bottom: var(--t2-s4, 16px);
  margin-right: 0;
  margin-left: auto;
  width: fit-content;
  max-width: 100%;
  padding: var(--t2-s1) var(--t2-s3);
  background: var(--t2-workspace);
  border-radius: var(--t2-radius-control);
  font-family: var(--t2-font-mono);
  font-size: 11px;
  color: var(--t2-ink-muted);
}
/* 버전 문구는 읽는 것이지 누르는 것이 아니다. 디버그 표면의 방아쇠를 겸하던
   동안만 손가락 커서를 달고 있었고, 그 방아쇠는 이제 하단 바의 버튼이다. */
.t2-version-info { display: inline-flex; align-items: center; gap: var(--t2-s1); }
.t2-version-info .material-icons-outlined { font-size: 14px; }
.t2-admin-link { color: var(--t2-ink-muted); text-decoration: none; }
.t2-admin-link:hover { color: var(--t2-product-ink); }
.t2-admin-link .t2-version-mark { font-size: 14px; margin-inline-end: var(--t2-s1); vertical-align: -2px; }

/* 배포 프로필(Max / Lite). 버전과 한 덩어리로 읽히되 버전보다 앞서지 않는다 —
   읽는 값이므로 meta 계열의 글자와 잉크(mono 11px · ink-muted)를 그대로 쓴다
   (704 §14). --t2-ink-faint 로 더 물리지 않는다: 그 잉크는 비활성 컨트롤의
   것이고, 읽으라고 적은 값을 그 명도로 칠하면 꺼진 것처럼 보인다.
   대문자로 바꾸지 않는다: 'Max' 와 'Lite' 는 배포본의 이름이지 분류 라벨이
   아니다. eyebrow 의 대문자·자간은 라벨의 문법이므로 여기서는 쓰지 않는다. */
.t2-profile-label {
  font-family: var(--t2-font-mono);
  font-size: 11px;
  letter-spacing: 0;
  color: var(--t2-ink-muted);
}

/* 최초 설치가 덜 끝났다는 표식. 버전 줄의 (i) 하나가 그것을 말한다 — 편집을
   막지 않으므로 단서는 이 한 글자면 충분하고, 그 이상은 글 쓰러 온 사람의
   화면에서 자리를 뺏는 일이 된다. 굵기는 707 D 가 허용한 보조 단서 하나다. */
.t2-version-mark.t2-setup-pending {
  color: var(--t2-danger);
  font-weight: 700;
  font-variation-settings: "wght" 700;
}

/* CMS 관리자에게만 나가는 한 줄. 위험을 말하는 것이 아니라 갈 곳을 말하므로
   의미색 면을 깔지 않고 잉크만 쓴다(705 제13항 — 넓은 면일수록 중립으로). */
.t2-setup-notice {
  display: inline-flex;
  align-items: center;
  gap: var(--t2-s1);
  margin-inline-start: var(--t2-s2);
  color: var(--t2-ink-muted);
}
.t2-setup-notice > span { color: var(--t2-danger); font-weight: 600; }
.t2-setup-notice > a { color: var(--t2-product-ink); text-decoration: none; }
.t2-setup-notice > a:hover { text-decoration-line: underline; }
@media (max-width: 640px) {
  /* 좁아지면 갈 곳만 남긴다 — 표식(i)이 이미 사실을 말하고 있다. */
  .t2-setup-notice > span { display: none; }
}
/* 메타 줄에서 누를 수 있는 것은 이 링크뿐이므로 coarse 포인터의 조작부 바닥을
   지킨다. 예전에는 같은 자리의 버전 문구가 디버그 방아쇠를 겸하면서 그 바닥을
   갖고 있었고, 방아쇠가 하단 바로 옮겨 가면서 이 줄에는 바닥이 남지 않았다. */
@media (hover: none), (pointer: coarse) {
  .t2-admin-link { display: inline-flex; align-items: center; min-height: 44px; }
}

/* 리사이즈 핸들 — 기능적 signature */
.t2-resize-handle {
  background: transparent;
  border: 0;
  border-top: 1px solid var(--t2-line);
  height: 12px;
  cursor: ns-resize;
  display: grid;
  place-items: center;
}
.t2-resize-handle::after {
  content: ""; width: 28px; height: 2px;
  background: var(--t2-line-strong);
}
.t2-resize-handle:hover::after { background: var(--t2-product); }

/* ═══ 6. 콘텐츠 블록 · 미디어 조작 ══════════════════════════════════════
   선택 상태의 주 단서는 1px 제품색 hairline + tone. 보조는 코너 tick. */
.t2-editor .t2-content-block { position: relative; margin-block: var(--t2-s4); }

/* ── 밖 배치 자리 예약 ────────────────────────────────────────────────────
   슬롯 배치가 'outside' 면 컨트롤 섬은 블록 경계 밖 --t2-block-slot-offset 지점에
   섬 높이만큼 떠 있다. 그 자리를 블록 여백으로 확보하지 않으면 섬이 이웃
   문단 글자와 옆 블록의 섬 위에 겹친다(기본 margin 16px < 필요 46px).

   배치를 inside 로 바꿔 회피하지 않는다. inside 는 콘텐츠 픽셀을 덮으므로
   발행 결과 예견 가능성을 해치고, 그건 블록에 배경을 칠하는 것과 같은 손실이다.

   섬 높이는 아래 __group 규칙(min-height + padding + border)이 정하므로 여기가
   그 값의 소유자다. 예약 규칙도 같은 레이어에 둔다 — 모듈 레이어에 두면
   레이어 순서상 이 파일의 margin-block 에 무조건 진다.

   data-t2-content-block-outside 는 모듈이 실제 슬롯 배치를 보고 부여한다.
   :has() 에 의존하지 않고, 밖 슬롯이 없는 블록은 여백이 늘지 않는다. */
.t2-editor {
  /* __group 의 실제 높이 = 조작부 + padding var(--t2-s1) ×2 + border 1×2.
     조작부는 시각면 26px 로 그려지지만 t2-foundation.css 의 계약 바닥
     `.t2-btn{min-height:var(--t2-hit)!important}` (t2.contract) 가 이겨서
     fine 포인터에서도 44px 이다 — 아래 __control--button 의 "부모 높이를 늘리지
     않는다" 는 의도가 그 !important 에 깨진 뒤로 상수만 40px 에 남아 있었다.
     그동안 예약이 14px 모자라 컨트롤 섬이 바로 위 문단의 글자 아랫부분을
     10px 덮었다(1440x900 실측: 문단 [283,257,859,27] · 섬 [300,275,347,54]).
     상수를 다시 손으로 적지 않는다 — 조작부 바닥에서 유도해 같이 움직인다. */
  --t2-block-island-h: calc(var(--t2-hit) + var(--t2-s1) * 2 + 2px);
  --t2-block-island-reserve: calc(var(--t2-block-slot-offset, 6px) + var(--t2-block-island-h) + var(--t2-s1));
}
/* 예약 규칙 자체는 t2.contract 로 올라갔다 — css/t2-foundation.css 를 보라.
   이 레이어에 두면 플러그인 CSS 의 블록 루트 margin 하나가 예약을 통째로 지운다.
   여기가 소유하는 것은 위의 두 값(섬 높이와 그로부터 나오는 예약치)뿐이다. */

/* ── 콘텐츠 블록은 콘텐츠다 ────────────────────────────────────────────
   ⚠ 배경색을 넣지 않는다. 블록에 면을 칠하면 사용자가 최종 발행 결과를
     예견할 수 없다. 편집 화면과 발행 결과가 달라지는 순간 WYSIWYG 계약이
     깨지고, 그건 미학 문제가 아니라 UX 손실이다.
   초판이 --outlined 에 workspace 면을, 선택 상태에 product-wash 면을
   넣었다. 둘 다 제거했다.

   선택·포커스 표시는 콘텐츠 픽셀을 건드리지 않는 수단만 쓴다:
     outline      박스 밖에 그려지고 레이아웃에 영향이 없다
     코너 tick    블록 경계 밖 모서리
   테두리 두께나 배경으로 상태를 말하지 않는다. */
.t2-editor .t2-content-block,
.t2-editor .t2-content-block.t2-content-block--outlined {
  background: transparent;
}
.t2-editor .t2-content-block.t2-content-block--outlined {
  padding: 0;
  border: 0;
  border-radius: 0;
}
.t2-editor .t2-content-block:focus-within,
.t2-editor .t2-content-block[data-t2-selected="true"] {
  background: transparent;
  border-color: transparent;
  outline: 1px solid var(--t2-product);
  outline-offset: 2px;
}
.t2-editor .t2-content-block[data-t2-selected="true"]::before,
.t2-editor .t2-content-block[data-t2-selected="true"]::after {
  content: ""; position: absolute;
  width: var(--t2-tick); height: var(--t2-tick);
  border-color: var(--t2-product); border-style: solid; pointer-events: none;
  animation: t2-tick-in var(--t2-motion-spring) both;
}
.t2-editor .t2-content-block[data-t2-selected="true"]::before { top: -3px; left: -3px; border-width: 2px 0 0 2px; }
.t2-editor .t2-content-block[data-t2-selected="true"]::after { bottom: -3px; right: -3px; border-width: 0 2px 2px 0; }

/* --bare / --outlined 는 둘 다 면을 갖지 않는다. 위 규칙이 --outlined 를
   0 으로 되돌리므로 --bare 는 같은 값을 명시만 한다 (모듈 레이어에 남아 있던
   중복 선언을 여기로 합쳤다). */
.t2-editor .t2-content-block.t2-content-block--bare { padding: 0; border: 0; background: transparent; }

/* ── 컨테이너 표시 계약 ───────────────────────────────────────────────────
   플러그인이 자기 블록 경계를 보일지 선언하고, 관리자의 "컨테이너 항상 표시"가
   그것을 덮는다. 모듈이 결과를 data-t2-content-block-container 하나로 정규화하므로
   CSS 는 선언 출처를 몰라도 된다.
   경계는 1px hairline 하나뿐이다 — 면을 칠하면 편집 화면과 발행 결과가 달라진다. */
.t2-editor .t2-content-block[data-t2-content-block-container="visible"] {
  padding: var(--t2-inset-control);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-edge);
  background: transparent;
}
.t2-editor .t2-content-block[data-t2-content-block-container="hidden"] {
  padding: 0;
  border: 0;
  background: transparent;
}

/* 기능 그룹 = 유일하게 면을 갖는 요소. 인셋 리듬: 캔버스 > 블록 > 그룹 > 컨트롤
   순으로 한 단계씩 작아진다. 그룹은 공식 spacing 사다리의 최하단인 --t2-s1(4px)을
   쓴다. 이 값의 소유자는 이 규칙 하나다. */
.t2-editor .t2-content-block__group {
  display: inline-flex; align-items: center; gap: var(--t2-s1);
  min-height: 30px;
  padding: var(--t2-s1);
  color: var(--t2-ink-muted);
  background: var(--t2-floating);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-control);
  box-shadow: var(--t2-shadow-overlay);
}
/* 파괴적 그룹 — 의미색 ink + 의미색 hairline. 면은 칠하지 않는다 (law 707 D:
   보조 단서는 ink 승격 + weight 하나까지). */
.t2-editor .t2-content-block__group[data-t2-content-block-group="destructive"] {
  color: var(--t2-danger);
  border-color: color-mix(in srgb, var(--t2-danger) 34%, var(--t2-line));
}
.t2-editor .t2-content-block__group-label {
  padding-inline: var(--t2-s1) var(--t2-s1);
  font-family: var(--t2-font-mono);
  font-size: 10px; font-weight: 500;
  letter-spacing: var(--t2-track-label);
  text-transform: uppercase;
  color: var(--t2-ink-muted);
  white-space: nowrap;
}
/* ── 끌어 옮기기의 외형 ────────────────────────────────────────────────
   손잡이는 이동 섬 안에서 조작부가 아니라 "잡는 곳" 으로 읽혀야 한다 — 면도
   테두리도 없고, 섬이 이미 보일 때만 같이 보인다. 그래서 새 시각 요소가 늘지
   않고 블록 위에 떠 있는 것도 없다.
   놓일 자리는 가는 선 하나로만 말한다. 유령 블록이나 자리 벌리기를 쓰면 문서가
   손 아래에서 재배치되어, 놓기 전에 결과를 읽을 수 없다(704 제20항). */
/* 손잡이는 같은 섬에 사는 이동 단추와 같은 상자를 쓴다. 실측에서 그 단추들은
   .t2-btn 을 함께 달고 있어 어느 포인터에서나 44×44 로 그려진다 — 손잡이만
   26px 로 두면 아이콘은 나란한데 올렸을 때 칠해지는 면적이 서로 달라진다.
   섬 높이(54px)는 --t2-hit 에서 유도되므로 이 값으로 커지지 않는다. */
.t2-editor .t2-block-drag-handle {
  display: inline-grid;
  place-items: center;
  width: var(--t2-hit); height: var(--t2-hit);
  border-radius: var(--t2-radius-control);
  /* 활성 조작부다. --t2-ink-faint 는 비활성 표식 전용이라 여기 쓰지 않는다. */
  color: var(--t2-ink-muted);
  background: transparent;
}
.t2-editor .t2-block-drag-handle .material-icons,
.t2-editor .t2-block-drag-handle .mi { font-size: 18px; }
.t2-editor .t2-block-drag-handle:hover { color: var(--t2-product-ink); background: var(--t2-product-wash); }
.t2-editor .t2-content-block[data-t2-block-dragging="true"] { opacity: .45; }

/* 나란히 놓기 — 계산은 js/utils/content-block.js 가 한다. 여기서는 표시된
   두 속성만 그린다. inline-block + vertical-align:top 이면 한 줄이 찼을 때
   브라우저가 알아서 다음 줄로 넘긴다 — 줄 수를 세는 코드가 필요 없다.
   실측 1440(캔버스 920): 300px 블록 넷 → 2×2, 꼬리 문단은 그 아래. */
.t2-editor > .t2-content-block[data-t2-content-block-flow="side"] {
  display: inline-block;
  vertical-align: top;
  width: auto;
  margin-right: var(--t2-s2);
}
/* 두 원자 블록 사이의 빈 문단. 지우지 않고 접기만 한다 — 캐럿이 들어오면
   모듈이 표시를 떼고 다시 제 줄을 갖는다. */
.t2-editor > p[data-t2-content-block-flow-gap] {
  display: inline;
  margin: 0;
  padding: 0;
}
.t2-editor > p[data-t2-content-block-flow-gap] br { display: none; }

.t2-block-drop-indicator {
  position: fixed;
  height: 2px;
  margin-top: -1px;
  border-radius: 1px;
  background: var(--t2-product);
  opacity: 0;
  pointer-events: none;
  z-index: 30;
  transition: top var(--t2-motion-fast), opacity var(--t2-motion-fast);
}
.t2-block-drop-indicator[data-t2-visible="true"] { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .t2-block-drop-indicator { transition: none; }
}

/* ── §6.5 묶음 (block group) ─────────────────────────────────────────────
   나란히 선 블록 둘 이상은 하나의 묶음이다. 구조·좌표는
   js/utils/content-block-group.css 가 갖고, 여기서는 그 외형만 그린다.

   테두리를 실선 상자로 그리지 않는다. 묶음은 블록을 담는 새 상자가 아니라
   "이 셋이 한 줄이다" 라는 사실이고, 실선 상자는 발행 결과에 없는 경계를
   편집 화면에만 그려 최종 모습을 예견할 수 없게 만든다(704 제20항). 제품색
   hairline 과 옅은 tone 으로만 말한다 — 블록 선택 표시와 같은 어법이다. */
/* 면을 채우지 않는다. 이 테두리는 콘텐츠 *위에* 떠 있으므로(z-index 28),
   옅은 색이라도 그 아래 블록의 대비를 떨어뜨리고 발행 결과에 없는 색을 편집
   화면에만 입힌다(704 제20항). 실측 스크린샷에서 wash 채움이 회색 블록을
   눈에 띄게 밝혔다. 경계는 hairline 하나로 충분하다. */
.t2-block-group-chrome__frame {
  border: 1px solid var(--t2-product);
  border-radius: var(--t2-radius-island);
}

.t2-block-group-chrome__bar {
  gap: var(--t2-s1);
  padding: var(--t2-s1);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-island);
  background: var(--t2-floating);
  box-shadow: var(--t2-shadow-overlay);
  color: var(--t2-ink-muted);
}
/* 떠 있는 막대의 단추는 끌기 손잡이와 같은 상자를 쓴다 — 둘 다 .t2-btn 을
   함께 달고 있어 어느 포인터에서나 44×44 로 그려진다. 블록 안 컨트롤 섬의
   26px 상자를 여기에 가져오면 같은 줄에 선 두 조작부의 크기가 서로 달라진다. */
.t2-block-group-chrome__align,
.t2-block-group-chrome__remove {
  display: inline-grid;
  place-items: center;
  width: var(--t2-hit); height: var(--t2-hit);
  padding: 0;
  border: 0; background: transparent;
  color: inherit;
  border-radius: var(--t2-radius-control);
}
.t2-block-group-chrome__align .material-icons,
.t2-block-group-chrome__remove .material-icons { font-size: 18px; }
.t2-block-group-chrome__align:hover { color: var(--t2-product-ink); background: var(--t2-product-wash); }
/* 고른 정렬은 색만으로 말하지 않는다 — 글자색과 면을 함께 바꾼다 (705 제12항). */
.t2-block-group-chrome__align[aria-pressed="true"] {
  color: var(--t2-product-ink);
  background: var(--t2-product-tone);
}
.t2-block-group-chrome__remove:hover { color: var(--t2-danger); background: var(--t2-danger-soft); }
.t2-block-group-chrome__align:focus-visible,
.t2-block-group-chrome__remove:focus-visible { outline: 1px solid var(--t2-product); outline-offset: 1px; }
.t2-block-group-chrome__size { width: 96px; margin-inline: var(--t2-s1); }
.t2-block-group-chrome__size:disabled { opacity: .4; }

/* 지우기 확인 창의 미리보기. 무엇을 지우는지 글이 아니라 그림으로 말한다. */
.t2-block-group-preview {
  /* auto 와 var() 를 한 shorthand 에 섞지 않는다 — 토큰이 죽으면 선언 전체가
     무효가 되어 가운데 정렬까지 사라진다(webkit-compatibility 시험). */
  margin-block: var(--t2-s3);
  margin-inline: auto;
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-island);
  background: var(--t2-workspace);
}
.t2-block-group-dialog__caption,
.t2-block-group-dialog__note {
  margin: 0;
  color: var(--t2-ink-muted);
  font-size: 13px;
}
.t2-block-group-dialog__body { margin: var(--t2-s3) 0 var(--t2-s2); }
.t2-block-group-dialog__actions { margin-top: var(--t2-s4); }

/* coarse 포인터에서는 옆 단추의 글리프도 20px 로 커진다. 같이 간다. */
@media (hover: none), (pointer: coarse), (max-width: 640px) {
  .t2-editor .t2-block-drag-handle .material-icons,
  .t2-editor .t2-block-drag-handle .mi { font-size: 20px; }
}

.t2-editor .t2-content-block__control--button {
  position: relative;
  width: 26px; height: 26px; padding: 0;
  border: 0; background: transparent;
  color: inherit;
  border-radius: var(--t2-radius-control);
}
/* 시각면 26px, 히트 영역 44px. 부모 높이를 늘리지 않는다 (law 704 §5). */
.t2-editor .t2-content-block__control--button::before { content: ""; position: absolute; inset: -9px; }
.t2-editor .t2-content-block__control--button:hover { color: var(--t2-product-ink); background: var(--t2-product-wash); }
.t2-editor .t2-content-block__group[data-t2-content-block-group="destructive"] .t2-content-block__control--button:hover {
  color: var(--t2-danger); background: var(--t2-danger-soft);
}
.t2-editor .t2-content-block__control--button:active { background: var(--t2-product-tone); }
.t2-editor .t2-content-block__control--button:focus-visible { outline: 1px solid var(--t2-product); outline-offset: 1px; }
.t2-editor .t2-content-block__control--button .material-icons { font-size: 18px; }
@media (hover: none), (pointer: coarse), (max-width: 640px) {
  /* [iOS⚠️] Keep touch controls at or above the 44×44px target size.
     coarse 포인터에서는 히트 영역 확장 대신 시각면 자체를 키운다.
     섬이 커지면 밖 배치 예약도 같이 커져야 한다. */
  /* 섬 높이는 위 --t2-block-island-h 가 조작부 바닥에서 유도하므로 coarse 에서
     따로 적지 않는다 — 시각면이 44px 로 커져도 같은 식이 같은 값을 낸다. */
  .t2-editor .t2-content-block__group { min-height: 48px; }
  .t2-editor .t2-content-block__control--button { width: 44px; height: 44px; }
  .t2-editor .t2-content-block__control--button::before { inset: 0; }
  .t2-editor .t2-content-block__control--button .material-icons { font-size: 20px; }
}

/* ⚠ 슬롯은 레일이다 — 절대 칠하지 않는다.
   t2-media-controls / t2-move-controls / t2-block-controls / t2-table-controls 는
   두 가지 역할로 쓰인다:
     (구) 레거시 플러그인이 직접 만든 "컨트롤 섬" 컨테이너      → 칠한다
     (신) content-block 모듈이 슬롯 요소에 부여하는 위치 레일    → 칠하지 않는다
   슬롯을 칠하면 자동 숨김이 깨진다. 숨김은 자식 __group 의 opacity/visibility 로
   동작하므로, 슬롯에 면·테두리·그림자가 있으면 아이콘만 사라지고 배경 상자가
   그대로 남는다. (증상: "삭제·이동 버튼이 숨겨졌는데 배경만 떠 있다")
   그래서 신 역할일 때는 페인트에서 제외하고, 면은 __group 만 갖는다. */
.t2-media-controls:not(.t2-content-block__slot),
.t2-move-controls:not(.t2-content-block__slot),
.t2-block-controls:not(.t2-content-block__slot),
.t2-table-controls:not(.t2-content-block__slot) {
  background: var(--t2-floating);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-control);
  box-shadow: var(--t2-shadow-overlay);
  padding: var(--t2-s1);
  gap: var(--t2-s1);
}
/* 신 역할(슬롯)일 때는 레일로 되돌린다. 모듈 레이어가 이미 이렇게 선언하지만
   레이어 순서상 product 가 이기므로 여기서 명시적으로 되돌려야 한다. */
.t2-content-block__slot:is(.t2-media-controls, .t2-move-controls, .t2-block-controls, .t2-table-controls) {
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: 0;
  gap: var(--t2-s1);
}
.t2-media-controls button, .t2-block-controls button, .t2-table-controls button {
  width: 26px; height: 26px; min-height: 26px; padding: 0;
  color: var(--t2-ink-muted);
}

/* color-mix 미지원 브라우저 폴백. 구 모듈 파일이 갖고 있던 것을 외형 소유권을
   product 로 옮길 때 빠뜨렸다. 없으면 파괴적 그룹의 테두리가 기본값으로 돌아간다. */
@supports not (color: color-mix(in srgb, black 50%, white)) {
  .t2-editor .t2-content-block__group[data-t2-content-block-group="destructive"] {
    border-color: rgba(180, 35, 45, .35);
  }
}

/* ═══ 7. 오버레이 · 드롭다운 ════════════════════════════════════════════ */
.t2-font-size-list, .t2-dropdown-layer, .t2-translate-menu {
  background: var(--t2-floating);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-overlay);
  box-shadow: var(--t2-shadow-overlay);
  padding: var(--t2-s1) 0;
}
/* ─── 색 선택 팝오버 — UI 스펙 05 ──────────────────────────────────────────
   스펙의 .color-popover 를 그대로 옮긴다. 기하·면·잉크를 한 층에서 소유한다.

   폭도 안쪽 조각과 같은 부류다 — 손가락과 눈의 치수이지 화면 폭을 따라 커지는
   치수가 아니다. 20px 색 칩, 26px 조작부, 110px 패드가 그렇듯이.

   한동안 이 폭을 프레임 단위의 3.6U 로 읽었다. 스펙의 .color-popover 가
   width: calc(var(--u) * 3.6) 이라고 적기 때문인데, 그 규칙은 바로 윗줄에서
   --u: 56px 을 자기 안에 다시 선언한다 — 프레임 단위가 아니라 스펙이 고정해 둔
   픽셀이고, 3.6 × 56 = 201.6 에 min-width 204px 이 바닥으로 붙는다.
   프레임 단위로 읽으면 1280 에서 307px, 1920 에서 461px 이 된다. 재 보니 그때
   HS 패드가 255×104 에서 409×104 로 납작해졌다 — 세로는 110px 상수인데 가로만
   따라 늘기 때문이며, 채도를 고르는 정밀도가 모니터 폭에 따라 달라진다.

   색 모형은 HSB 다. 2D 패드의 가로가 Hue, 세로가 Saturation 이고 명도는 오른쪽
   세로 트랙이 갖는다. 세 축이 각자의 자리에 있으므로 어느 하나를 움직여도 나머지
   둘의 손잡이가 제자리에 남는다.

   세로 순서가 곧 작업 순서다: 손잡이 → 최근 → 팔레트 → 패드/명도 → 채도 →
   미리보기·Hex·복사·적용. 위에서 아래로 "고르는 일"이 "확인하는 일"로 좁혀진다. */
.t2-cp-popover {
  /* 스펙의 chrome 3단(850 표면 / 800 조각 / 700 눌린 면)을 제품 토큰으로 만든다. */
  --t2-cp-face:       var(--t2-floating);
  --t2-cp-chip:       color-mix(in srgb, var(--t2-ink) 7%, var(--t2-floating));
  --t2-cp-chip-hover: color-mix(in srgb, var(--t2-ink) 14%, var(--t2-floating));
  --t2-cp-well:       var(--t2-paper-soft);
  --t2-cp-ring:       color-mix(in srgb, var(--t2-ink) 22%, transparent);

  position: fixed;
  z-index: 10000;
  width: min(204px, calc(100vw - var(--t2-u2)));
  display: flex;
  flex-direction: column;
  /* 위 인셋이 0 인 것은 비대칭이 아니라 손잡이 띠가 그 자리를 갖기 때문이다 —
     0.42U 짜리 잡는 띠가 위 여백 노릇을 한다. 여기에 padding 을 또 주면 손잡이가
     제 몫보다 아래로 내려앉는다. */
  padding: 0 0 12px;
  box-sizing: border-box;
  background: var(--t2-cp-face);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-popover);
  box-shadow: var(--t2-shadow-modal);
  -webkit-user-select: none;
  user-select: none;
  animation: t2cpIn 140ms cubic-bezier(.16, 1, .3, 1);
}
.t2-cp-popover *, .t2-cp-popover *::before, .t2-cp-popover *::after { box-sizing: border-box; }
@keyframes t2cpIn {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* 닫기는 표면 밖 우측 위에 반쯤 걸쳐 앉는다. 안에 두면 팔레트 한 줄을 잡아먹고,
   완전히 밖에 두면 어느 표면의 닫기인지 읽히지 않는다. */
.t2-cp-close-btn {
  position: absolute;
  top: -10px; right: -10px;
  width: 22px; height: 22px;
  min-width: 0; min-height: 0;
  padding: 0;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--t2-radius-pill);
  background: var(--t2-cp-chip);
  border: 1px solid var(--t2-line);
  color: var(--t2-ink-muted);
  font-size: 11px; font-weight: 700; line-height: 1;
  cursor: pointer;
  box-shadow: var(--t2-shadow-overlay);
  z-index: 1;
  transition: transform 150ms var(--t2-ease-spring), background-color 150ms, color 150ms, border-color 150ms;
}
.t2-cp-close-btn:hover {
  background: var(--t2-cp-chip-hover);
  border-color: var(--t2-product);
  color: var(--t2-ink);
  transform: scale(1.12);
}
.t2-cp-close-btn:active { transform: scale(.95); }

/* 끌어 옮기는 손잡이. 잡는 곳은 0.42U 높이의 띠 전체이고 보이는 것은 그 안의
   32×4 막대다 — 보이는 것보다 넓게 잡히는 것이 손가락에 맞는다. */
.t2-cp-handle-zone {
  height: calc(var(--t2-u) * .42);
  display: flex; align-items: center; justify-content: center;
  cursor: grab;
  touch-action: none;
  flex-shrink: 0;
}
.t2-cp-handle-zone:active { cursor: grabbing; }
.t2-cp-handle {
  width: 32px; height: 4px;
  border-radius: var(--t2-radius-pill);
  background: var(--t2-line-strong);
  transition: width 200ms var(--t2-ease-spring), background-color 200ms;
}
.t2-cp-handle-zone:hover .t2-cp-handle { width: 38px; background: var(--t2-product); }

.t2-cp-section-label {
  padding: 0 12px;
  margin-bottom: 6px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: -.02em;
  color: var(--t2-ink-muted);
  flex-shrink: 0;
}

/* 최근 — 이 도구(글자색/형광펜)로 마지막에 고른 색 하나. 기록이 없을 때는 빈
   칸이라는 사실을 형상으로 말한다. 회색으로 칠하면 "회색을 골랐다"로 읽힌다. */
.t2-cp-recent {
  display: flex; align-items: center; gap: 8px;
  padding: 0 12px;
  height: 26px;
  margin-bottom: 8px;
  flex-shrink: 0;
}
.t2-cp-recent-swatch {
  width: 20px; height: 20px;
  min-width: 0; min-height: 0;
  padding: 0;
  flex-shrink: 0;
  border-radius: var(--t2-radius-pill);
  border: 1.5px solid var(--t2-cp-ring);
  box-shadow: 0 2px 4px rgba(16,20,26,.28);
  cursor: pointer;
  transition: transform 200ms var(--t2-ease-spring), border-color 150ms;
}
.t2-cp-recent-swatch:not(.empty):hover { transform: scale(1.2); border-color: var(--t2-product); }
.t2-cp-recent-swatch.empty {
  background-color: transparent !important;
  border: 1.5px dashed var(--t2-cp-ring);
  box-shadow: none;
  cursor: default;
}
.t2-cp-recent-info { display: flex; align-items: center; gap: 6px; flex: 1; min-width: 0; }
.t2-cp-recent-hex {
  font-family: var(--t2-font-mono);
  font-size: 11px; font-weight: 700;
  color: var(--t2-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.t2-cp-recent-clear-btn {
  margin-left: auto;
  min-width: 0; min-height: 0;
  padding: 3px 7px;
  display: inline-flex; align-items: center; gap: 3.5px;
  background: var(--t2-cp-chip);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-control);
  color: var(--t2-ink-muted);
  font-size: 10px; font-weight: 600;
  cursor: pointer;
  flex-shrink: 0;
  transition: transform 150ms var(--t2-ease-spring), background-color 150ms, color 150ms, border-color 150ms;
}
.t2-cp-recent-clear-btn:hover:not(:disabled) {
  background: var(--t2-danger-soft);
  border-color: var(--t2-danger);
  color: var(--t2-danger);
  transform: scale(1.04);
}
.t2-cp-recent-clear-btn:active:not(:disabled) { transform: scale(.95); }
.t2-cp-recent-clear-btn:disabled { opacity: .4; cursor: not-allowed; }

/* 팔레트는 한 줄이다. 줄바꿈을 허용하면 색의 수에 따라 팝오버 높이가 달라져,
   같은 표면이 열 때마다 다른 크기로 선다. 넘치면 가로로 민다. */
.t2-cp-palette {
  display: flex; align-items: center; gap: 6.5px;
  padding: 0 12px;
  height: 28px;
  margin-bottom: 10px;
  overflow-x: auto;
  /* overflow-x 를 auto 로 두면 overflow-y 의 visible 이 auto 로 계산된다 —
     그러면 34px 줄이 세로로도 스크롤되어 두 축이 함께 열린다. 세로는 닫는다. */
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  flex-shrink: 0;
}
.t2-cp-palette::-webkit-scrollbar { display: none; }
.t2-cp-swatch {
  width: 20px; height: 20px;
  min-width: 0; min-height: 0;
  padding: 0;
  flex-shrink: 0;
  border-radius: var(--t2-radius-pill);
  border: 1.5px solid var(--t2-cp-ring);
  box-shadow: 0 2px 4px rgba(16,20,26,.28), inset 0 1px 1px rgba(255,255,255,.25);
  cursor: pointer;
  transition: transform 200ms var(--t2-ease-spring), box-shadow 200ms;
}
.t2-cp-swatch:hover { transform: scale(1.24); box-shadow: 0 3px 8px rgba(16,20,26,.4); }
/* 고른 칩은 흰 테두리 + 제품색 고리 두 겹으로 선다. 밝은 색과 어두운 색 어느
   쪽 위에서도 테두리 하나만으로는 보이지 않는다. */
.t2-cp-swatch.active {
  border: 2px solid var(--t2-floating);
  box-shadow: 0 0 0 2px var(--t2-product), 0 2px 6px rgba(16,20,26,.35);
  transform: scale(1.12);
}

/* 2D 패드 + 명도 트랙. 패드는 남는 폭을 전부 갖고 트랙은 16px 로 고정이다. */
.t2-cp-mid-row {
  display: flex; gap: 10px;
  padding: 3px 12px;
  height: 110px;
  align-items: stretch;
  flex-shrink: 0;
}
.t2-cp-2d {
  flex: 1;
  position: relative;
  border-radius: var(--t2-radius-island);
  background:
    linear-gradient(to top, #ffffff 0%, rgba(255,255,255,0) 100%),
    linear-gradient(to right, #ff0000 0%, #ffff00 16.66%, #00ff00 33.33%,
                    #00ffff 50%, #0000ff 66.66%, #ff00ff 83.33%, #ff0000 100%);
  background-clip: padding-box;
  border: 1px solid var(--t2-line);
  box-shadow: inset 0 1px 3px rgba(16,20,26,.22);
  overflow: hidden;
  cursor: crosshair;
  touch-action: none;
}
.t2-cp-2d-cursor {
  position: absolute;
  width: 14px; height: 14px;
  border-radius: var(--t2-radius-pill);
  border: 2px solid #ffffff;
  box-shadow: 0 0 0 1px rgba(16,20,26,.5), 0 2px 5px rgba(16,20,26,.55);
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 1;
}
.t2-cp-b-track {
  position: relative;
  width: 16px;
  flex-shrink: 0;
  cursor: pointer;
  touch-action: none;
}
.t2-cp-sat-row { padding: 8px 12px 6px; flex-shrink: 0; }
.t2-cp-sat-track {
  position: relative;
  height: 13px;
  cursor: pointer;
  touch-action: none;
}
/* 두 트랙의 막대는 같은 조각이다 — 방향만 다르다. */
.t2-cp-track-bar {
  position: absolute;
  inset: 0;
  border-radius: var(--t2-radius-pill);
  box-shadow: inset 0 0 0 1px rgba(16,20,26,.32), inset 0 1px 3px rgba(16,20,26,.4);
  pointer-events: none;
}
.t2-cp-b-cursor {
  position: absolute;
  left: 50%;
  width: 20px; height: 9px;
  border-radius: var(--t2-radius-pill);
  background: #ffffff;
  border: 1px solid rgba(16,20,26,.5);
  box-shadow: 0 1px 4px rgba(16,20,26,.55);
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 1;
}
.t2-cp-sat-handle {
  position: absolute;
  top: 50%;
  width: 15px; height: 15px;
  border-radius: var(--t2-radius-pill);
  background: #ffffff;
  border: 1.5px solid rgba(16,20,26,.45);
  box-shadow: 0 1px 4px rgba(16,20,26,.5);
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 1;
}

/* 발치 한 줄이 결과를 확인하고 확정한다: 미리보기 · Hex · 복사 · 적용. */
.t2-cp-readout {
  display: flex; align-items: center; gap: 5px;
  padding: 6px 12px 0;
  height: 34px;
  width: 100%;
  font-size: 11px;
  flex-shrink: 0;
}
.t2-cp-preview {
  width: 26px; height: 26px;
  flex-shrink: 0;
  border-radius: var(--t2-radius-inner);
  border: 1.5px solid var(--t2-cp-ring);
  box-shadow: inset 0 1px 2px rgba(255,255,255,.28), 0 1px 4px rgba(16,20,26,.3);
  transition: transform 150ms var(--t2-ease-spring);
}
.t2-cp-preview:hover { transform: scale(1.14); }
.t2-cp-hex-input {
  flex: 1;
  min-width: 58px;
  height: 26px;
  min-height: 0;
  padding: 0 2px;
  background: var(--t2-cp-well);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-inner);
  color: var(--t2-ink);
  font-family: var(--t2-font-mono);
  font-size: 10.5px; font-weight: 700;
  letter-spacing: -.01em;
  text-align: center;
  text-transform: uppercase;
  outline: none;
  transition: border-color 150ms, box-shadow 150ms;
}
.t2-cp-hex-input:focus {
  border-color: var(--t2-product);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--t2-product) 30%, transparent);
}
.t2-cp-copy-btn, .t2-cp-apply-btn {
  height: 26px;
  min-width: 0; min-height: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--t2-radius-inner);
  white-space: nowrap;
  cursor: pointer;
  flex-shrink: 0;
  transition: transform 150ms var(--t2-ease-spring), background-color 150ms, box-shadow 150ms;
}
.t2-cp-copy-btn {
  width: 26px;
  padding: 0;
  font-size: 12px;
  background: var(--t2-cp-chip);
  border: 1px solid var(--t2-line);
  color: var(--t2-ink);
}
.t2-cp-copy-btn:hover { background: var(--t2-cp-chip-hover); transform: scale(1.08); }
.t2-cp-copy-btn:active { transform: scale(.92); }
.t2-cp-copy-btn.is-copied { color: var(--t2-success); border-color: var(--t2-success); }
/* 이 표면의 유일한 실행이다. 스펙의 .cp-apply-btn 처럼 제품색을 통째로 칠하고
   잉크를 뒤집는다 — 옆의 복사(중립 면)와 역할이 눈으로 갈린다. */
.t2-cp-apply-btn {
  padding: 0 9px;
  font-size: 11px;
  background: var(--t2-product);
  border: 1px solid var(--t2-product);
  color: var(--t2-on-product);
  font-weight: 700;
  box-shadow: 0 2px 6px color-mix(in srgb, var(--t2-product) 35%, transparent);
}
.t2-cp-apply-btn:hover {
  background: var(--t2-product-strong);
  border-color: var(--t2-product-strong);
  transform: scale(1.05);
  box-shadow: 0 3px 8px color-mix(in srgb, var(--t2-product) 50%, transparent);
}
.t2-cp-apply-btn:active { transform: scale(.94); }

/* 손가락으로 쓸 때 — 색 칩은 스펙의 시각 치수를 지키고 잡는 곳만 넓힌다.
   칩을 44px 로 키우면 한 줄에 들어가던 열두 색이 세 개로 줄고 팝오버가 스펙과
   전혀 다른 물건이 된다. 대신 투명한 ::after 가 44px 을 덮는다 — 보이는 것과
   잡히는 것을 나누는 것이 이 경우의 옳은 해법이다.
   반대로 발치의 조작부(미리보기·Hex·복사·적용)는 한 줄에 넷뿐이라 그냥 키운다. */
@media (pointer: coarse) {
  .t2-cp-close-btn { width: 32px; height: 32px; top: -16px; right: -16px; font-size: 13px; }
  .t2-cp-recent { height: 30px; }
  .t2-cp-recent-swatch, .t2-cp-swatch { width: 26px; height: 26px; }
  .t2-cp-palette { height: 34px; gap: 8px; }
  .t2-cp-close-btn, .t2-cp-recent-swatch, .t2-cp-swatch, .t2-cp-preview { position: relative; }
  .t2-cp-close-btn::after, .t2-cp-recent-swatch::after,
  .t2-cp-swatch::after, .t2-cp-preview::after {
    content: "";
    position: absolute;
    top: 50%; left: 50%;
    width: var(--t2-hit); height: var(--t2-hit);
    transform: translate(-50%, -50%);
  }
  .t2-cp-recent-clear-btn { min-height: 32px; position: relative; }
  .t2-cp-recent-clear-btn::after {
    content: "";
    position: absolute;
    top: 50%; left: 50%;
    width: 100%; height: var(--t2-hit);
    min-width: var(--t2-hit);
    transform: translate(-50%, -50%);
  }
  .t2-cp-mid-row { height: 132px; }
  .t2-cp-b-track { width: 24px; }
  .t2-cp-sat-row { padding: 10px 12px 8px; }
  .t2-cp-sat-track { height: 20px; }
  .t2-cp-sat-handle { width: 22px; height: 22px; }
  /* 네 조각이 204px 안에 든다. 카드 폭 204 − 좌우 인셋 24 = 180 이고,
     미리보기 32 + Hex 44 + 복사 44 + 적용 46 = 166 에 간격 셋 4px 12 를 더해
     178 이다. 앞 값(간격 6 · Hex 최소 58)으로는 198 이 되어 재 보니 8px 이
     넘쳤다 — 넘친 쪽은 오른쪽 끝의 [적용] 이다. */
  .t2-cp-readout { height: var(--t2-hit); gap: 4px; }
  .t2-cp-preview, .t2-cp-copy-btn, .t2-cp-hex-input, .t2-cp-apply-btn {
    height: var(--t2-hit);
    box-sizing: border-box;
  }
  .t2-cp-copy-btn { width: var(--t2-hit); }
  .t2-cp-preview { width: 32px; }
  /* [iOS⚠️] 16px 아래면 초점이 갈 때 화면이 확대된다. */
  .t2-cp-hex-input { font-size: 16px; min-width: var(--t2-hit); }
  .t2-cp-recent-clear-btn { padding: 0 10px; }
}
@media (prefers-reduced-motion: reduce) {
  .t2-cp-popover { animation: none; }
  .t2-cp-close-btn, .t2-cp-handle, .t2-cp-recent-swatch, .t2-cp-swatch,
  .t2-cp-preview, .t2-cp-copy-btn, .t2-cp-apply-btn, .t2-cp-recent-clear-btn {
    transition-duration: .01ms;
  }
  .t2-cp-close-btn:hover, .t2-cp-recent-swatch:not(.empty):hover, .t2-cp-swatch:hover,
  .t2-cp-swatch.active, .t2-cp-preview:hover, .t2-cp-copy-btn:hover,
  .t2-cp-apply-btn:hover, .t2-cp-recent-clear-btn:hover:not(:disabled) { transform: none; }
}

.t2-font-size-option, .t2-translate-menu-item {
  position: relative;
  padding: var(--t2-s2) var(--t2-s3);
  font-size: 13px;
  color: var(--t2-ink);
  background: transparent;
  cursor: pointer;
}
.t2-font-size-option:hover, .t2-translate-menu-item:hover { background: var(--t2-product-wash); }
.t2-dropdown-layer [role="option"] {
  padding: var(--t2-s2) var(--t2-s3); font-size: 13px; color: var(--t2-ink); background: transparent;
}
.t2-dropdown-layer [role="option"]:hover { background: var(--t2-product-wash); }
.t2-dropdown-layer [role="option"][aria-selected="true"] {
  color: var(--t2-product-ink); font-weight: 600; background: var(--t2-product-wash);
}

/* 알림(toast) — 짧게 머무는 오버레이.
   core.css 는 .t2-notification 에 color:#fff 와 그림자만 주고, 면은
   .t2-notification-<type> 이 칠하도록 되어 있었다. 그 네 규칙이 어느 CSS 에도
   없다. 클래스 이름을 js/utils.js 가 런타임에 조립하므로 소스에 리터럴로
   나타나지 않고, 유령 선택자로 오인되기 쉬운 형태다. 결과는 배경 없는 흰 글자가
   툴바 위에 그대로 겹쳐 뜨는 것이었다 — 다크에서도 라이트에서도 읽을 수 없다.
   여기서 면을 정의한다. 의미는 상단 rail + 아이콘 잉크가 전달하고(law 707),
   면 자체는 중립 재료를 쓴다. 의미색으로 전체를 칠하지 않는다. */
.t2-notification {
  position: relative;
  overflow: hidden;
  color: var(--t2-ink);
  background: var(--t2-floating);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-overlay);
  box-shadow: var(--t2-shadow-overlay);
  font-size: 13px;
}
.t2-notification::before {
  content: "";
  position: absolute;
  top: 0;
  left: var(--t2-rail-inset); right: var(--t2-rail-inset);
  height: var(--t2-rail-w);
  background: var(--t2-ink-faint);
  border-radius: 0 0 var(--t2-radius-edge) var(--t2-radius-edge);
}
.t2-notification-info::before    { background: var(--t2-info); }
.t2-notification-success::before { background: var(--t2-success); }
.t2-notification-warning::before { background: var(--t2-warning); }
.t2-notification-error::before   { background: var(--t2-danger); }
/* action 은 무엇이 잘못됐다는 알림이 아니라 방금 한 일을 되돌릴 기회다 —
   의미색이 아니라 제품색을 쓴다. */
.t2-notification-action::before  { background: var(--t2-product); }

/* 유형 아이콘 — 형태가 종류를 말한다. 면은 그 의미색의 옅은 톤이고 잉크가
   의미색이다. 넓은 면을 의미색으로 칠하지 않는다(705·707 D). */
.t2-notification-icon { background: var(--t2-inset); color: var(--t2-ink-muted); }
.t2-notification-info    .t2-notification-icon { background: var(--t2-info-soft);    color: var(--t2-info); }
.t2-notification-success .t2-notification-icon { background: var(--t2-success-soft); color: var(--t2-success); }
.t2-notification-warning .t2-notification-icon { background: var(--t2-warning-soft); color: var(--t2-warning); }
.t2-notification-error   .t2-notification-icon { background: var(--t2-danger-soft);  color: var(--t2-danger); }
.t2-notification-action  .t2-notification-icon { background: var(--t2-product-wash); color: var(--t2-product-ink); }

.t2-notification-title { color: var(--t2-ink); }
.t2-notification-desc  { color: var(--t2-ink-muted); }

/* 되돌리기 — 알림 안의 유일한 실행이고, 되돌릴 수 있는 시간이 알림이 떠 있는
   동안뿐이다. 스펙 07 의 .toast-action-btn 처럼 자기 면을 갖는 작은 칩이다.
   앞 판본은 라벨 밑 rail 이었고, 그래서 제목·설명과 같은 재료로 보여 "누를 수
   있는 것" 이라는 사실이 형상에서 읽히지 않았다. rail 은 상태의 문법이다. */
.t2-notification-action-btn {
  flex: 0 0 auto;
  min-height: 0;
  height: calc(var(--t2-status-mark) + var(--t2-s3));
  padding: 0 var(--t2-s3);
  border-radius: var(--t2-radius-edge);
  background: var(--t2-product);
  color: var(--t2-on-product);
  font-size: 12px;
  font-weight: 700;
  white-space: nowrap;
}
.t2-notification-action-btn:hover { background: var(--t2-product-strong); }
/* 손가락으로 쓸 때는 칩 자체를 44px 로 키운다 — 알림 높이가 0.85~1.15u 라
   그만한 여유가 있고, 되돌리기는 놓치면 되돌릴 수 없는 조작이다. */
@media (pointer: coarse) {
  .t2-notification-action-btn { height: var(--t2-hit); }
}
.t2-notification-close {
  color: var(--t2-ink-muted);
  background: transparent;
  border: 0;
  border-radius: var(--t2-radius-control);
}
.t2-notification-close:hover { color: var(--t2-ink); background: var(--t2-product-wash); }

/* 남은 시간 표시줄 — 의미 rail 과 같은 자리를 쓰지 않도록 위쪽일 때는 rail 아래에
   붙는다. transform 으로만 줄어들어 레이아웃을 건드리지 않는다. */
.t2-notification-progress {
  position: absolute;
  left: 0; right: 0;
  height: var(--t2-rail-w);
  /* 스펙의 .toast-progress-bar 는 옅은 바탕 위에서 줄어든다 — 바탕이 없으면
     "얼마나 남았는가" 만 보이고 "얼마였는가" 가 보이지 않아 속도를 읽을 수 없다. */
  background: var(--t2-inset);
  pointer-events: none;
}
.t2-notification-progress[data-position="top"] { top: var(--t2-rail-w); }
.t2-notification-progress[data-position="bottom"] { bottom: 0; }
.t2-notification-progress-fill {
  height: 100%;
  transform-origin: left center;
  transform: scaleX(1);
  background: var(--t2-ink-faint);
}
.t2-notification-info .t2-notification-progress-fill    { background: var(--t2-info); }
.t2-notification-success .t2-notification-progress-fill { background: var(--t2-success); }
.t2-notification-warning .t2-notification-progress-fill { background: var(--t2-warning); }
.t2-notification-error .t2-notification-progress-fill   { background: var(--t2-danger); }
.t2-notification-action .t2-notification-progress-fill  { background: var(--t2-product); }
@media (prefers-reduced-motion: reduce) {
  /* 남은 시간은 정보이므로 없애지 않는다. 연속 애니메이션만 끊는다. */
  .t2-notification-progress-fill { transition: none !important; }
}

/* ═══ 8. 작업 레이어 (modal) ═════════════════════════════════════════════ */
.t2-modal-overlay, .t2-plugin-modal-overlay {
  background: var(--md-overlay);
}
.t2-modal-surface, .t2-plugin-surface-shell, .t2-plugin-surface-dialog,
.t2-nsfw-dialog, .t2-safeai-legal-modal, .plugin-modal, .tp-modal-box,
.icon-picker-modal, .module-picker-modal, .ai-budget-plugin-modal-box {
  background: var(--t2-floating);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-modal);
  box-shadow: var(--t2-shadow-modal);
}
/* 플러그인의 오래된 root 클래스와 무관하게 표준 작업면의 최종 기하와 재료는
   공통 modal 런타임이 표시한 안정된 속성 하나로 결정한다.

   710 D: 최대 13u : 6u, 뷰포트와의 마진 1/2u, 프레임 인셋 1/2u. 크기는 내용의
   양이 아니라 격자가 정하고, 넘치는 내용은 안쪽 스크롤이 담당한다(710 D-1·E-8).
   상한이 15u 에서 13u 로 내려간 것은 UI 스펙 03 의 6대 규격 중 최대가 13:6 이기
   때문이다 — 15u 는 편집기 셸의 폭이지 그 안에 뜨는 표면의 폭이 아니다. */
.t2-modal-surface[data-t2-modal-surface="standard"]:not(.t2-modal-surface-fullscreen) {
  width: fit-content;
  min-width: min(calc(var(--t2-u) * 5), calc(100vw - var(--t2-u)));
  max-width: min(calc(var(--t2-u) * 13), calc(100vw - var(--t2-u)));
  /* 하단 고정 고지 바가 떠 있는 동안에는 그 띠만큼 작업면이 줄어든다(해설 708 F-2). */
  max-height: min(calc(var(--t2-u) * 6),
                  calc(100dvh - var(--t2-u) - var(--t2-privacy-notice-inset, 0px)));
  margin: auto;
  padding: var(--t2-u2);
  overflow-x: hidden;
  overflow-y: auto;
  color: var(--t2-ink);
  background: var(--t2-floating);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-modal);
  box-shadow: var(--t2-shadow-modal);
  font-family: var(--t2-font-ui);
}

/* ── 6대 공식 비례 (UI 스펙 03) ────────────────────────────────────────
   폭 {5,7,13}u × 높이 {4,6}u. 어느 규격인지는 modal.js 가 정하고(선언 → 유형
   → 내용 휴리스틱의 3단계), --t2-modal-w/h 는 js/utils/modal.css 가 규격마다
   심는다. 최종 기하를 여기서 읽는 것은, 표준 작업면의 치수를 이미 이 규칙이
   소유하고 있기 때문이다 — 모듈 쪽에 적으면 레이어 순서에 져서 닿지 않는다.
   실제로 그 실수를 한 번 했다: 모듈에만 적었더니 여섯 규격이 전부 6u 폭으로
   렌더됐다. */
@media (min-width: 761px) {
  .t2-modal-surface[data-t2-modal-surface="standard"][data-t2-modal-ratio]:not(.t2-modal-surface-fullscreen) {
    width: min(var(--t2-modal-w), calc(100vw - var(--t2-u)));
    min-width: 0;
    height: min(var(--t2-modal-h), calc(100dvh - var(--t2-u) - var(--t2-privacy-notice-inset, 0px)));
    max-width: min(var(--t2-modal-w), calc(100vw - var(--t2-u)));
    max-height: min(var(--t2-modal-h), calc(100dvh - var(--t2-u) - var(--t2-privacy-notice-inset, 0px)));
  }
  /* 6계열은 위 3/4u·아래 1u, 4계열은 위 7/4u·아래 2u. 두 경우 모두 모달의
     중심이 영역 중심보다 1/8u 위다 — 규격마다 다른 여백을 적는 대신 아래
     패딩을 1/4u 늘려 가운데 정렬을 그만큼 끌어올린다. 표면에 transform 이나
     translate 을 걸지 않는 이유는 둘 다 여닫는 동작이 이미 쓰기 때문이고,
     바깥 닫기 버튼이 표면과 같은 칸에 겹쳐 있어 함께 움직여야 하기 때문이다. */
  .t2-plugin-modal-overlay:has(> .t2-modal-surface[data-t2-modal-ratio]) {
    padding-bottom: calc(var(--t2-s4) + var(--t2-u4) + var(--t2-privacy-notice-inset, 0px));
  }
  /* 정렬 — 가로 여백 1u 안에서 좌·우로 옮긴다. 세로 배치는 그대로다. */
  .t2-plugin-modal-overlay[data-t2-modal-align="left"]  { justify-content: flex-start; padding-inline: var(--t2-u); }
  .t2-plugin-modal-overlay[data-t2-modal-align="right"] { justify-content: flex-end;   padding-inline: var(--t2-u); }
  .t2-plugin-modal-overlay[data-t2-modal-align="left"]  > .t2-modal-surface,
  .t2-plugin-modal-overlay[data-t2-modal-align="right"] > .t2-modal-surface { margin-inline: 0; }
}
/* 710 D-4·D-5: 헤드 타이틀은 굵은 한 줄이고 넘치면 잘라 말줄임으로 표시한다.
   행 끝의 여유 공간(1u)은 행이 소유한 조작과 말줄임의 자리이며 타이틀이 먹지
   않는다. 타이틀과 콘텐츠 사이 마진은 1/2u 다(710 D-2).

   UI 스펙 03 의 모달은 그 타이틀 행을 밴드로 그린다 — 높이 1u, 자기 면, 아래
   헤어라인. 글자만 얹어 두면 표면 안에서 "제목이 어디까지인지"가 여백으로만
   읽히고, 본문이 스크롤될 때 제목이 함께 밀려 올라가 무엇을 보고 있는지 잃는다.
   밴드는 표면의 인셋을 음수 마진으로 되돌려 좌우 끝까지 닿고, sticky 로 스크롤
   포트 위에 머문다. */
.t2-modal-surface[data-t2-modal-surface="standard"]:not(.t2-modal-surface-fullscreen) > :is(h1, h2, h3, [data-modal-title]):first-child,
.t2-modal-surface[data-t2-modal-surface="standard"]:not(.t2-modal-surface-fullscreen) > .t2-modal-sheet-handle + :is(h1, h2, h3, [data-modal-title]) {
  position: sticky;
  /* 표면의 세로 인셋(1/2u)만큼 음수로 잡는다. Chromium 은 sticky 의 기준 사각형에서
     스크롤 컨테이너의 패딩을 빼므로 top:0 이면 밴드가 인셋 아래에 멈춘다 — 재 보면
     표면 위 끝에서 43.7px(=1/2u) 떨어진 자리였다. 음수 인셋이 그 차이를 되돌린다. */
  top: calc(-1 * var(--t2-u2));
  z-index: 1;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  /* 높이가 아니라 바닥이다. 좁은 화면에서 제목이 두 줄로 접히면 고정 1u 는 그
     둘째 줄을 잘라 본문 위로 흘려보낸다 — 390px 폰의 개인정보 동의 제목이
     실제로 그랬다. 한 줄이면 여전히 정확히 1u 로 선다. */
  min-height: var(--t2-u);
  margin: calc(-1 * var(--t2-u2)) calc(-1 * var(--t2-u2)) var(--t2-u2);
  padding-block: var(--t2-u316);
  padding-inline: var(--t2-u2) var(--t2-u);
  /* 밴드가 불투명해야 하는 이유는 sticky 다 — 본문이 그 아래로 지나가야 하기
     때문이지, 다른 재료로 보여야 해서가 아니다. --t2-chrome-fill 은 툴바·하단
     바의 재료라서, 다이얼로그 안에 칠하면 480px 카드 위에 8 단계 밝은 띠가 얹혀
     "표면이 세 장" 으로 읽힌다. 경계는 아래 헤어라인 하나가 이미 말한다.
     inherit 로 두면 카드가 무엇이든(제품 표면·플러그인 표면·시트) 늘 같은 면이 된다. */
  background-color: inherit;
  /* 아래 경계는 밴드 높이 밖에 있다 — border 로 그리면 1u 에 1px 이 더해진다. */
  box-shadow: inset 0 -1px 0 var(--t2-line);
  /* 표면의 각 안쪽에 들어가는 각이므로 테두리 1px 만큼 작다(스펙 03 의 m-head). */
  border-radius: calc(var(--t2-radius-modal) - 1px) calc(var(--t2-radius-modal) - 1px) 0 0;
  color: var(--t2-ink);
  font-size: 18px;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: var(--t2-track-title);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* 정렬은 밴드가 가진 값이다(스펙 03·04·05 의 titleAlign). 가운데 정렬에서 타이틀
   양옆의 구간이 먼저 줄고 그다음에 말줄임이 일어난다 — 행 끝의 1u 여유는 끝까지
   남는다(710 D-7). */
.t2-modal-surface[data-t2-modal-title-align="center"] > :is(h1, h2, h3, [data-modal-title]):first-child,
.t2-modal-surface[data-t2-modal-title-align="center"] > .t2-modal-sheet-handle + :is(h1, h2, h3, [data-modal-title]) { justify-content: center; }
.t2-modal-surface[data-t2-modal-title-align="right"] > :is(h1, h2, h3, [data-modal-title]):first-child,
.t2-modal-surface[data-t2-modal-title-align="right"] > .t2-modal-sheet-handle + :is(h1, h2, h3, [data-modal-title]) { justify-content: flex-end; }
.t2-modal-surface[data-t2-modal-surface="standard"]:not(.t2-modal-surface-fullscreen) > :last-child { margin-bottom: 0; }

/* 실행 행이 고정될 때도 밴드다. 모듈(js/utils/modal.css)은 위치만 소유하고
   재질은 기능이 갖는다는 규정 때문에 재질이 아무에게도 없었고, 그래서 고정된
   행은 투명한 채로 본문 위에 떠 있었다 — 1440px 개인정보 동의에서 "이 기능이
   다루는 정보" 목록의 항목들이 버튼 뒤로 그대로 비쳤다(scrollHeight 545 대
   clientHeight 426). 머리 밴드가 불투명해야 하는 이유가 여기에도 그대로
   적용된다: sticky 이기 때문이다. 표면이 무엇이든 같은 면이 되도록 inherit 를
   쓰고, 경계는 위쪽 헤어라인 하나가 말한다. */
.t2-modal-surface[data-t2-modal-surface="standard"]:not(.t2-modal-surface-fullscreen) > .t2-modal-pinned-actions {
  box-sizing: border-box;
  /* Chromium 은 sticky 기준 사각형에서 스크롤 컨테이너의 패딩을 뺀다. 머리
     밴드의 음수 top 과 같은 이유로 바닥도 인셋만큼 되돌린다. */
  bottom: calc(-1 * var(--t2-u2));
  margin-inline: calc(-1 * var(--t2-u2));
  margin-bottom: calc(-1 * var(--t2-u2));
  padding: var(--t2-u316) var(--t2-u2);
  background-color: inherit;
  box-shadow: inset 0 1px 0 var(--t2-line);
  border-radius: 0 0 calc(var(--t2-radius-modal) - 1px) calc(var(--t2-radius-modal) - 1px);
}


/* 푸터 밴드 — UI 스펙 05 의 실행 행. 헤더와 같은 이유로 밴드다: 본문이 길어져
   스크롤되면 확인·취소가 화면 밖으로 밀려나고, 그러면 답을 요구하는 모달에서
   답할 자리가 사라진다. 마지막 자식이 실행 행일 때만 밴드가 된다 — 본문 안의
   버튼 묶음까지 바닥에 붙이면 문단 사이에 띠가 생긴다. */
.t2-modal-surface[data-t2-modal-surface="standard"]:not(.t2-modal-surface-fullscreen)
  > :is(.t2-btn-group, .t2-modal-actions, [data-modal-actions]):last-child {
  position: sticky;
  bottom: calc(-1 * var(--t2-u2));
  z-index: 1;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  /* 발치의 두 버튼 사이는 3/16u 다 — 툴바의 도구 사이와 같은 리듬이고, 스펙의
     .bm-footer-bar 가 쓰는 6px(u=32 기준 3/16U)이 그 값이다. */
  gap: var(--t2-u316);
  height: var(--t2-u);
  margin: var(--t2-u2) calc(-1 * var(--t2-u2)) calc(-1 * var(--t2-u2));
  padding-inline: var(--t2-u2);
  /* 헤드와 같은 이유로 카드의 면을 그대로 쓴다(위 밴드 주석). */
  background-color: inherit;
  box-shadow: inset 0 1px 0 var(--t2-line);
  border-radius: 0 0 calc(var(--t2-radius-modal) - 1px) calc(var(--t2-radius-modal) - 1px);
}
/* 바텀시트의 아래 두 모서리는 화면 끝에 닿으므로 둥글지 않다. */
@media (max-width: 760px) {
  .t2-modal-overlay-adaptive > .t2-modal-surface[data-t2-modal-surface="standard"]
    > :is(.t2-btn-group, .t2-modal-actions, [data-modal-actions]):last-child {
    /* 시트의 아래 두 모서리는 화면 끝에 닿으므로 각이 없다. */
    border-radius: 0;
    padding-bottom: env(safe-area-inset-bottom, 0px);
    height: calc(var(--t2-u) + env(safe-area-inset-bottom, 0px));
  }
}
/* 710 E · UI 스펙 04·05: 모바일의 공식 모달은 바텀시트와 베이직 둘뿐이다.
   베이직은 폭 7u(좌우 마진 1.5u)에 헤더 1u + 본문 3.5/4.5/5.5u + 푸터 1u.
   바텀시트는 폭 92%, 높이는 세로 단위(화면 높이/10)로 8uv = 화면의 80% 다.

   시트의 세로를 가로에서 뽑은 u 로 적으면 기준 비례(6:10)가 아닌 실제 단말에서
   어긋난다 — 390x780 에서 8u 는 66.7% 로, 정한 80% 보다 13 포인트 낮다.
   실제로 그랬다: 이 규칙이 모듈 레이어의 92%/8uv 를 이기고 있었고, 재 보니
   66.7% x 83.3% 였다. 최종 기하는 이 규칙이 소유하므로 여기에 적는다. */
@media (max-width: 760px) {
  .t2-modal-overlay-basic > .t2-modal-surface[data-t2-modal-surface="standard"]:not(.t2-modal-surface-fullscreen) {
    /* 스펙 05 의 폭은 7u 다. 앞 판본이 5u 로 적은 것은 폰 프레임을 6u 로 보던
       때의 환산값이었다 — 5u + 좌우 1/2u = 6u 로 화면이 닫혔다. 프레임이 10u 로
       바로잡히면서(710 A-3) 스펙의 숫자가 그대로 맞는다: 7u + 좌우 1.5u = 10u,
       스펙 데모의 340px 기기에서 224px(66%)과 같은 비례다. 5u 를 그대로 두면
       393px 폰에서 196px, 화면의 절반짜리 카드가 된다. */
    width: calc(var(--t2-u) * 7);
    min-width: 0;
    max-width: calc(100vw - var(--t2-u));
    padding: var(--t2-u2);
    /* 스마트폰의 베이직 모달은 밴드에 붙은 표면이 아니라 화면 한가운데에 뜨는
       카드다 — 스펙의 .basic-modal 이 14px 을 쓰는 이유다(데스크톱의
       .canvas-modal-card 는 12px). 안쪽 밴드는 그 안에서 13px 로 맞물린다. */
    border-radius: var(--t2-radius-popover);
  }
  /* 헤더·푸터 밴드의 안쪽 각도 카드의 각에서 다시 유도한다. */
  .t2-modal-overlay-basic > .t2-modal-surface[data-t2-modal-surface="standard"]:not(.t2-modal-surface-fullscreen)
    > :is(h1, h2, h3, [data-modal-title]):first-child,
  .t2-modal-overlay-basic > .t2-modal-surface[data-t2-modal-surface="standard"]:not(.t2-modal-surface-fullscreen)
    > .t2-modal-sheet-handle + :is(h1, h2, h3, [data-modal-title]) {
    border-radius: calc(var(--t2-radius-popover) - 1px) calc(var(--t2-radius-popover) - 1px) 0 0;
  }
  .t2-modal-overlay-basic > .t2-modal-surface[data-t2-modal-surface="standard"]:not(.t2-modal-surface-fullscreen)
    > :is(.t2-btn-group, .t2-modal-actions, [data-modal-actions]):last-child {
    border-radius: 0 0 calc(var(--t2-radius-popover) - 1px) calc(var(--t2-radius-popover) - 1px);
  }
  .t2-modal-overlay-adaptive > .t2-modal-surface[data-t2-modal-surface="standard"]:not(.t2-modal-surface-fullscreen) {
    --t2-uv: calc(100dvh / 10);
    width: 92vw;
    min-width: 0;
    max-width: min(92vw, calc(var(--t2-u) * 10));
    padding: var(--t2-u2);
    height: calc(var(--t2-uv) * 8);
    max-height: calc(var(--t2-modal-vv-height, 100dvh) - var(--t2-uv)
                     - var(--t2-privacy-notice-inset, 0px));
    margin: 0 auto;
    border-radius: var(--t2-radius-sheet) var(--t2-radius-sheet) 0 0;
  }
  .t2-modal-overlay-basic > .t2-modal-surface[data-t2-modal-surface="standard"]:not(.t2-modal-surface-fullscreen) {
    height: calc(var(--t2-u) * 5.5);
    max-height: min(calc(var(--t2-u) * 7.5),
                    calc(var(--t2-modal-vv-height, 100dvh) - var(--t2-u)
                         - var(--t2-privacy-notice-inset, 0px)));
  }
  /* 단계 규칙은 위의 기본 규칙과 같은 한정자를 달아야 이긴다 — 속성 셋을 단 기본
     규칙이 속성 하나짜리 단계 규칙보다 특정도가 높다. 미디어 질의는 특정도를
     올려 주지 않는다. */
  .t2-modal-overlay-basic > .t2-modal-surface[data-t2-modal-surface="standard"][data-t2-modal-level="1"]:not(.t2-modal-surface-fullscreen) { height: calc(var(--t2-u) * 6.5); }
  .t2-modal-overlay-basic > .t2-modal-surface[data-t2-modal-surface="standard"][data-t2-modal-level="2"]:not(.t2-modal-surface-fullscreen) { height: calc(var(--t2-u) * 7.5); }

  /* 시트의 크롬은 시트의 단위에서 나온다 — 손잡이 영역 0.8uv, 손잡이 폭 1uv,
     타이틀 행 0.75uv. 타이틀의 좌우 인셋은 시트가 넓어질수록 0.5uv 에서 2.5uv 로
     비례해 늘어난다(스펙 04). 스펙의 식에서 곱셈을 풀면 0.5uv + (W - 6uv)/2 이고,
     양끝을 clamp 로 막으면 길이끼리 나누는 계산 없이 CSS 안에서 떨어진다. */
  .t2-modal-overlay-adaptive > .t2-modal-surface .t2-modal-sheet-handle { height: calc(var(--t2-uv) * 0.8); }
  .t2-modal-overlay-adaptive > .t2-modal-surface .t2-modal-sheet-handle > span { width: var(--t2-uv); }
  .t2-modal-overlay-adaptive > .t2-modal-surface[data-t2-modal-surface="standard"] > :is(h1, h2, h3, [data-modal-title]):first-child,
  .t2-modal-overlay-adaptive > .t2-modal-surface[data-t2-modal-surface="standard"] > .t2-modal-sheet-handle + :is(h1, h2, h3, [data-modal-title]) {
    top: 0;
    height: calc(var(--t2-uv) * 0.75);
    margin-bottom: calc(var(--t2-uv) * 0.25);
    padding-inline: clamp(
      calc(var(--t2-uv) * 0.5),
      calc(var(--t2-uv) * 0.5 + (92vw - var(--t2-uv) * 6) / 2),
      calc(var(--t2-uv) * 2.5));
    /* 시트의 위 두 모서리는 시트 단계(16px)이고, 헤더 밴드는 그 안쪽이다. */
    border-radius: calc(var(--t2-radius-sheet) - 1px) calc(var(--t2-radius-sheet) - 1px) 0 0;
  }
}
/* 절삭 모서리는 critical surface 전용이다 — 날카로움이 "위험한 작업"이라는
   의미를 가질 때만 쓴다. clip-path 는 border-radius 렌더를 덮으므로 겹칠 수
   없고 겹칠 이유도 없다. 일반 modal 은 둥근 모서리를 쓴다. */
.t2-nsfw-dialog-blocked,
[data-t2-surface="critical"] {
  border-radius: 0;
  clip-path: polygon(var(--t2-chamfer) 0, 100% 0, 100% 100%, 0 100%, 0 var(--t2-chamfer));
}
/* 모달은 이미 작업 경계다. 안에서 header/body/footer 를 박스로 반복 분리하지 않는다. */
.t2-plugin-surface-header, .t2-modal-header, .t2-nsfw-dialog-header, .t2-safeai-legal-header {
  padding: var(--t2-s4) var(--t2-s4) var(--t2-s2) var(--t2-s5);
  background: transparent;
  border: 0;
  display: flex; align-items: center; gap: var(--t2-s2);
}
.t2-plugin-surface-title, .t2-modal-title, .t2-nsfw-dialog-title, .t2-safeai-legal-title {
  font-size: 14px; font-weight: 600; letter-spacing: var(--t2-track-title); margin: 0;
}
.t2-plugin-surface-body, .t2-modal-body, .plugin-modal-body,
.t2-nsfw-dialog-body, .t2-safeai-legal-body {
  padding: 0 var(--t2-s4) var(--t2-s4);
  background: transparent;
  border: 0;
  overflow-y: auto;
  overscroll-behavior-y: contain;
}
.t2-plugin-surface-footer, .t2-modal-footer, .t2-nsfw-dialog-footer, .t2-safeai-legal-footer {
  display: flex; align-items: center; gap: var(--t2-s1);
  padding: var(--t2-s2) var(--t2-s4) var(--t2-s4);
  background: transparent;
  border: 0;
}
/* 710 E-3: 손잡이 영역 1/4u, 손잡이 폭 1u. */
.t2-modal-sheet-handle > span { background: var(--t2-line-strong); border-radius: 999px; }

/* ═══ 9. 의미 상태 — rail + 아이콘. 배경 채움을 키우지 않는다. ══════════ */
:where(.admin-wrap, .t2-modal-overlay, [data-t2-plugin-surface], .t2-editor-container)
  :is(.alert, .warning-banner, .up-status-banner) {
  position: relative;
  display: flex; gap: var(--t2-s2);
  padding: var(--t2-s2) var(--t2-s3);
  background: var(--t2-workspace);
  border: 0;
  border-radius: var(--t2-radius-structural);
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--t2-ink);
}
:where(.admin-wrap, .t2-modal-overlay, [data-t2-plugin-surface], .t2-editor-container)
  :is(.alert, .warning-banner, .up-status-banner)::before {
  content: ""; position: absolute;
  left: var(--t2-rail-inset); right: var(--t2-rail-inset); top: 0;
  height: var(--t2-rail-w);
  background: var(--t2-ink-faint);
}
:where(.admin-wrap, .t2-modal-overlay, [data-t2-plugin-surface]) .alert-error::before { background: var(--t2-danger); }
:where(.admin-wrap, .t2-modal-overlay, [data-t2-plugin-surface]) .alert-warn::before { background: var(--t2-warning); }
:where(.admin-wrap) .warning-banner::before { background: var(--t2-warning); }
:where(.admin-wrap, .t2-modal-overlay, [data-t2-plugin-surface]) .alert-success::before { background: var(--t2-success); }
:where(.admin-wrap, .t2-modal-overlay, [data-t2-plugin-surface]) .alert-info::before { background: var(--t2-product); }

/* badge / tag / chip — 채움 pill 이 아니라 hairline + ink */
:where(.admin-wrap, [data-t2-plugin-surface])
  :is(.badge, .tag, .ext-chip, .tp-status-badge, .plugin-manifest-badge, .license-badge, .ai-cap-chip) {
  display: inline-flex; align-items: center; gap: var(--t2-s1);
  padding: var(--t2-s1) var(--t2-s2);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-control);
  background: transparent;
  font-family: var(--t2-font-mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: .02em;
  color: var(--t2-ink-muted);
}
:where(.admin-wrap) .badge-primary { color: var(--t2-product-ink); border-color: var(--t2-product); }
:where(.admin-wrap) .badge-success { color: var(--t2-success); border-color: var(--t2-success); }
:where(.admin-wrap) .badge-warn    { color: var(--t2-warning); border-color: var(--t2-warning); }
:where(.admin-wrap) .badge-error   { color: var(--t2-danger);  border-color: var(--t2-danger); }
:where(.admin-wrap) .badge-muted   { color: var(--t2-ink-muted); }

/* 상태 dot — 진짜 원형 예외 */
:where(.admin-wrap) :is(.admin-status-dot, .status-dot) {
  width: 5px; height: 5px; border-radius: 50%; background: var(--t2-success); flex: 0 0 auto;
}

/* ═══ 10. 관리자 셸 ═════════════════════════════════════════════════════ */
.admin-wrap, .main-wrap { background: var(--t2-base); color: var(--t2-ink); }

/* 상단 헤더 = topbar(제목·검색·액션) + 그 아래 위치표시 strip 두 줄이 이루는
   하나의 고정 밴드다. 둘 다 스크롤러(.admin-wrap main) 바깥의 flex 자식이라
   화면 최상단에 붙박여 있다. 분리선은 밴드 맨 아래 하나만 두고, 좌우 인셋은
   본문(.content-area)과 같은 --t2-inset-shell 로 맞춰 세 요소의 왼쪽 모서리가
   한 세로줄에 선다. 예전에는 topbar 만 흰 면+하단 경계였고 좌우 인셋도 s4 로
   달라서, 위치표시 strip 이 회색 바탕 위 다른 들여쓰기로 홀로 떠 보였다. */
.topbar {
  display: flex; align-items: center; gap: var(--t2-s3);
  height: 48px;
  padding: 0 var(--t2-inset-shell);
  background: var(--t2-surface);
  border: 0;
  box-shadow: none;
}
.topbar-title { font-size: 13px; font-weight: 600; letter-spacing: var(--t2-track-title); }
.topbar-actions { margin-left: auto; display: flex; align-items: center; gap: var(--t2-s1); }

/* 사이드바 열기 버튼은 사이드바가 오버레이로 접히는 폭에서만 존재한다.
   §4 의 관리자 버튼 블록이 display 를 선언하므로 admin-navigation.css 의
   .topbar-menu-btn{display:none} 은 레이어 순서에서 무조건 진다. 실제로
   데스크톱 관리자 상단에 쓸모없는 햄버거 버튼이 늘 떠 있었다.
   존재 조건도 같은 레이어에서 함께 소유한다. 폭 기준은 admin-navigation.css
   의 사이드바 접힘 기준과 같아야 한다. */
.topbar-menu-btn { display: none; }
@media (max-width: 860px) { .topbar-menu-btn { display: inline-flex; } }

.sidebar {
  background: var(--t2-surface);
  border: 0;
  border-right: 1px solid var(--t2-line);
  box-shadow: none;
  padding-block: var(--t2-s2);
  overflow-y: auto;
  overscroll-behavior-y: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--t2-line-strong) transparent;
}
.sidebar-logo { padding: var(--t2-s3) var(--t2-s4); border: 0; }
.sidebar-footer { padding: var(--t2-s3) var(--t2-s4); border-top: 1px solid var(--t2-line); }
.sidebar-overlay { background: var(--md-overlay); }

/* 내비게이션 — 밀도 유지. 현재 위치는 행 tone + ink·weight, 큰 pill 없음. */
.nav-section { margin-block: var(--t2-s1); }
.nav-section-label, .nav-tech-label {
  display: block;
  padding: var(--t2-s3) var(--t2-s4) var(--t2-s1);
  font-family: var(--t2-font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: var(--t2-track-label);
  text-transform: uppercase;
  color: var(--t2-ink-muted);
}
.nav-item {
  display: flex; align-items: center; gap: var(--t2-s2);
  /* 좁아지면 줄바꿈으로 내려간다. 한 줄을 고집하면 이름과 보조 표기가 서로를
     밀어내고, 한국어에서는 flex 항목의 자동 최소 크기가 글자 하나 폭이라
     이름이 세로로 무너진다. */
  flex-wrap: wrap;
  /* 화면 이름이 어절 안에서 끊기지 않는다 — "에디터 기 / 반 기능" 이 아니라
     "에디터 / 기반 기능" 으로 접힌다. */
  word-break: keep-all;
  padding: var(--t2-s2) var(--t2-s4);
  min-height: 36px;
  border: 0;
  border-radius: var(--t2-radius-structural);
  background: transparent;
  color: var(--t2-ink-muted);
  font-size: 13px;
  text-decoration: none;
  transition: color var(--t2-motion-fast), background-color var(--t2-motion-fast);
}
/* 708 H-3: 사이드바 항목도 조작 대상이다. 데스크톱의 시각 밀도(36px)는 두고
   coarse 환경에서만 최소 히트 영역을 보장한다. */
@media (pointer: coarse) { .nav-item, .nav-purpose-toggle { min-height: var(--t2-hit); } }
.nav-item:hover { color: var(--t2-ink); background: var(--t2-product-wash); }
/* 관리자 설정 표면에 한해 세로 rail 을 쓴다.
   ⚠ 여기서 배경 tone 을 함께 쓰면 단서가 3개가 된다 (rail + tone + weight).
     rail 을 주 단서로 삼았으므로 tone 을 걷어내고 weight 만 보조로 남긴다.
   ⚠ 같은 요소에 하단 rail 을 겹치지 않는다. */
.nav-item.active, .nav-item[aria-current] {
  position: relative;
  color: var(--t2-product-ink);
  font-weight: 600;
  background: transparent;
}
.nav-item.active::after, .nav-item[aria-current]::after {
  content: ""; position: absolute;
  inset-inline-start: 0;
  top: var(--t2-rail-inset); bottom: var(--t2-rail-inset);
  width: var(--t2-rail-w);
  background: var(--t2-product);
  animation: t2-rail-in-y var(--t2-motion-spring) both;
}
@keyframes t2-rail-in-y { from { transform: scaleY(0); } to { transform: scaleY(1); } }
.nav-item.active:hover, .nav-item[aria-current]:hover { background: var(--t2-product-wash); }
/* 행 안의 기술 표기는 행의 보조 표식이다. 위 묶음의 값은 홀로 서는 구획
   라벨(.nav-section-label)의 것이라 padding 이 12/16/4 로 크고 위아래가
   비대칭이다 — 그대로 행에 넣으면 94x38 칩이 되어 화면 이름을 한 글자 칸까지
   밀어냈고, 행 높이도 36px 에서 55px 로 늘었다. 행 안에서는 칩 리듬을 쓴다. */
.nav-item > .nav-tech-label {
  /* 잘리지 않는다. 화면 이름과 함께 한 줄에 못 서면 아래 줄 오른쪽으로 내려간다
     — 보조 표기를 "Editor Modu…" 로 자르면 그것대로 읽을 수 없고, 이름을 밀어내면
     주·보조가 뒤집힌다(해설 709 D-2). 260px 사이드바의 들여쓴 하위 항목에서
     이름 105px + 칩 106px 은 남은 155px 에 물리적으로 들어가지 않는다. */
  flex: 0 0 auto;
  padding: var(--t2-inset-chip) var(--t2-inset-control);
  white-space: nowrap;
}
.nav-item :is(.material-icons, .material-icons-outlined, .mi) { font-size: 18px; opacity: .9; }
.nav-item .count {
  margin-left: auto;
  font-family: var(--t2-font-mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--t2-ink-muted);
}
.nav-item.active .count { color: var(--t2-product-ink); }
.nav-purpose-items .nav-item { padding-left: calc(var(--t2-s4) + var(--t2-s6)); font-size: 12.5px; }
.nav-purpose-toggle {
  display: flex; align-items: center; width: 100%;
  padding: var(--t2-s2) var(--t2-s4); min-height: 36px;
  background: transparent; border: 0; color: var(--t2-ink-muted); font-size: 13px;
}
.nav-purpose-chevron { margin-left: auto; color: var(--t2-ink-muted); transition: transform var(--t2-motion-spring); }
[aria-expanded="true"] > .nav-purpose-chevron { transform: rotate(90deg); }

.content-area, .admin-flat-page {
  padding: var(--t2-inset-shell);
  background: var(--t2-base);
}

/* ── 스크롤 소유자와 sticky 스택 — 해설 708 G ───────────────────────────
   관리자 셸은 문서가 아니라 flex 셸이다. 스크롤 소유자는 .admin-wrap main 이고
   .topbar 는 그 바깥에 있다. 그래서 스크롤러 안의 sticky 표면이 헤더 높이를
   오프셋으로 쓰면, 이미 화면에 없는 것을 위해 자리를 비워 둔 채 콘텐츠 한가운데
   떠 있게 된다. 실제로 .plugin-section-nav 와 .ai-purpose-nav 가
   top:calc(--header-h + 6px) 로 그렇게 떠 있었다.
   오프셋의 소유자는 높이를 정하는 쪽 하나뿐이다. 스크롤러가 자기 안쪽 기준을
   선언하고, sticky 표면은 그 이름만 읽는다. */
:where(.admin-wrap) main {
  --t2-sticky-top: 0px;
  scroll-padding-top: var(--t2-sticky-top);
}
:where(.admin-wrap) :is(.plugin-section-nav, .ai-purpose-nav, .module-admin-list-pane) {
  top: var(--t2-sticky-top);
}
/* sticky 표면은 아래를 스쳐 지나가는 콘텐츠를 가린다. 반투명하면 두 층이 겹쳐
   읽히므로 불투명해야 한다 (708 F-1). */
:where(.admin-wrap) :is(.plugin-section-nav, .ai-purpose-nav) {
  background: var(--t2-base);
  border: 0;
  border-bottom: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-structural);
  padding: var(--t2-inset-chip) 0;
  margin-block: 0 var(--t2-gap-group);
  box-shadow: none;
}

/* ── 제목의 소속 — 해설 708 C-3·E ───────────────────────────────────────
   제목 위 간격 > 제목 아래 간격. 그래야 제목이 아래 내용에 속해 보인다.
   section 제목과 subsection 제목은 서로 다른 값을 갖는다 (708 E-2). */
.section-header {
  display: flex; align-items: baseline; gap: var(--t2-gap-related);
  /* 좁아지면 줄바꿈으로 내려간다. 한 줄을 고집하면 flex 항목의 자동 최소
     크기(min-content)가 한국어에서 글자 하나 폭이라, 화면 이름이 세로 한 줄로
     무너진다 — 390px 에서 "에디터 기반 기능" 이 18px 칸에 7줄로 쌓였다. */
  flex-wrap: wrap;
  margin: 0 0 var(--t2-trail-title);
  padding: 0;
  background: transparent;
  border: 0;
}
.section-header :is(h2, h3) {
  margin: 0; font-size: 13px; font-weight: 600; letter-spacing: var(--t2-track-title);
  /* 화면 이름은 줄어들지 않는다. 설명문이 남은 폭을 갖고, 모자라면 아래로 내려간다. */
  flex: 0 0 auto;
  /* 한국어는 어절 안에서 끊지 않는다 — admin-diagnostics.css 의 .card-title-wrap,
     admin-surface.css 의 .save-hint 와 같은 어법이다. */
  word-break: keep-all;
}
.section-header p { margin: 0; flex: 1 1 16rem; min-width: 0; word-break: keep-all; }
/* 구현 용어는 보조 표기다 — 화면 이름을 대체하지 않는다 (해설 709 D-2).
   사이드바의 .nav-tech-label 과 같은 문법을 쓴다. */
.section-tech-label {
  flex: 0 0 auto;
  padding: var(--t2-s1) var(--t2-inset-control);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-control);
  font-family: var(--t2-font-mono);
  font-size: 10px; font-weight: 500;
  letter-spacing: var(--t2-track-label);
  text-transform: uppercase;
  color: var(--t2-ink-muted);
  white-space: nowrap;
}
:where(.admin-wrap) .section-panel > :is(h3, h4, .subsection-title, .form-section-title) {
  margin: var(--t2-lead-subtitle) 0 var(--t2-trail-subtitle);
}
:where(.admin-wrap) .section-panel > :is(h3, h4, .subsection-title, .form-section-title):first-child {
  margin-top: 0;
}
.admin-flat-section, .section-panel {
  background: transparent;
  border: 0;
  padding: 0;
  margin-bottom: var(--t2-gap-section);
}

/* ── 블록 대칭 — 해설 708 C-2 ───────────────────────────────────────────
   면이나 테두리를 가진 블록에서 첫 자식의 위 여백과 마지막 자식의 아래 여백이
   남으면 위아래가 비대칭이 된다. 테두리 있는 사각 블록의 글자 아래 여백만
   유독 넓어 보이던 원인이 이것이다. 여백은 블록의 padding 이 소유하고,
   자식의 바깥 여백은 경계에서 상쇄한다. */
:where(.admin-wrap, .t2-modal-overlay, [data-t2-plugin-surface])
  :is(.card, .stat-card, .api-card, .env-feature-card, .module-policy-card,
      .plugin-manager-card, .tp-card, .up-release-card, .bp-card, .group-card,
      .alert, .warning-banner, .up-status-banner, .save-bar, .auth-card, .key-card,
      .admin-flat-section, .t2-modal-surface, .plugin-modal, .tp-modal-box,
      .ai-budget-metric, .ai-budget-mode-card, .ai-cap-tier-card, .ai-purpose-section,
      .tp-empty, .env-guide, .env-status-panel, .pkg-sec-report, .system-extension-card, .up-file)
  > :first-child { margin-block-start: 0; }
:where(.admin-wrap, .t2-modal-overlay, [data-t2-plugin-surface])
  :is(.card, .stat-card, .api-card, .env-feature-card, .module-policy-card,
      .plugin-manager-card, .tp-card, .up-release-card, .bp-card, .group-card,
      .alert, .warning-banner, .up-status-banner, .save-bar, .auth-card, .key-card,
      .admin-flat-section, .t2-modal-surface, .plugin-modal, .tp-modal-box,
      .ai-budget-metric, .ai-budget-mode-card, .ai-cap-tier-card, .ai-purpose-section,
      .tp-empty, .env-guide, .env-status-panel, .pkg-sec-report, .system-extension-card, .up-file)
  > :last-child { margin-block-end: 0; }
/* 컨테이너를 이름으로 열거하는 것만으로는 새로 생기는 표면을 놓친다. 흐름
   요소 쪽에서도 경계 여백을 상쇄해, 어떤 컨테이너 안에 들어가든 위아래가
   대칭이 되게 한다. 안쪽 여백의 소유자는 컨테이너의 padding 하나다. */
:where(.admin-wrap, .t2-modal-overlay, [data-t2-plugin-surface])
  :is(p, ul, ol, dl, pre, blockquote, table, h1, h2, h3, h4, h5, h6, .form-group, .alert):first-child {
  margin-block-start: 0;
}
:where(.admin-wrap, .t2-modal-overlay, [data-t2-plugin-surface])
  :is(p, ul, ol, dl, pre, blockquote, table, h1, h2, h3, h4, h5, h6, .form-group, .alert):last-child {
  margin-block-end: 0;
}

/* 사다리 밖 값과 의도 없는 비대칭을 이 레이어에서 되돌린다 (708 A·C-1).
   .env-guide 는 margin-top:11px/padding-top:2px, .ai-purpose-section 은
   padding:14px 0 4px 이었다. 둘 다 관리자 CSS 가 자기 값을 발명한 경우다. */
/* Rationale (708 C-1 예외): 위쪽 점선 경계와 본문 사이만 여백을 갖는다.
   아래 여백은 마지막 자식(.env-issue)이 이미 같은 값으로 갖고 있어
   블록 전체로 보면 대칭이다. */
:where(.admin-wrap) .env-guide { margin-top: var(--t2-gap-related); padding-top: var(--t2-gap-related); }
:where(.admin-wrap) .ai-purpose-section { padding-block: var(--t2-inset-section); }
:where(.admin-wrap) :is(.ai-budget-metric, .ai-cap-tier-card, .tp-empty) { padding: var(--t2-inset-island); }
.card, .stat-card, .api-card, .env-feature-card, .module-policy-card,
.plugin-manager-card, .tp-card, .up-release-card, .bp-card, .group-card,
/* 카드 안에 카드를 기본 구분 수단으로 쓰지 않는다 (law 704 §6) */
.card .card, .stat-card .card, .api-card .card, .tp-card .card {
  background: transparent; border: 0; box-shadow: none; padding: 0;
}
.card-header, .api-card-head, .env-feature-head, .up-release-head, .bp-head {
  display: flex; align-items: center; gap: var(--t2-s2);
  /* 708 C-2: 이 블록의 첫 요소다. 아래 .card-body 의 아래 여백과 같은 값을
     위에 두어야 카드가 위아래로 같은 리듬을 갖는다. */
  padding: var(--t2-s3) 0;
  margin: 0;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--t2-line);
}
/* .tp-toolbar 는 카드의 머리가 아니라 네 개의 조작부를 담은 격자다. 위 무리에
   묶여 있는 동안 여기의 display:flex 가 admin/css/admin-diagnostics.css 의
   display:grid 를 이겼고(제품 레이어가 레거시를 이긴다), 그래서 4열 배치도
   840px·460px 의 두 분기(2열·1열)도 전부 사문이었다 — 재 보니 1280 과 폰
   양쪽에서 display 가 flex 였고 grid-template-columns 는 계산만 남아 있었다.
   머리에게 주는 아래 경계선도 함께 얻어서 도구 줄 밑에 선이 그어졌다.
   격자와 분기는 그 화면이 소유하고, 여기서는 면과 리듬만 중립으로 둔다. */
.tp-toolbar { gap: var(--t2-s2); padding: var(--t2-s3) 0; margin: 0; background: transparent; }
.card-title, .card-title-wrap { font-size: 13px; font-weight: 600; letter-spacing: var(--t2-track-title); margin: 0; }
.card-body, .api-card-main, .bp-body { padding: var(--t2-s3) 0; background: transparent; border: 0; }
.divider { height: 1px; background: var(--t2-line); border: 0; margin-block: var(--t2-s4); }

/* stat — 숫자가 주인공. 아이콘 컨테이너를 장식으로 쓰지 않는다. */
/* 통계 격자 — 면을 갖지 않는다. admin CSS 는 "격자를 경계색으로 칠하고 셀은
   surface 로 덮어 2px 틈이 hairline 으로 보이게" 하는 기법을 쓰고 있었다.
   이 파일이 .stat-card 의 배경을 transparent 로 평탄화하면서 덮을 셀이 사라져,
   격자 전체가 --md-border 색 판때기로 남고 그 위에 글자가 얹혔다.
   기법의 나머지 절반도 여기서 함께 걷어낸다. 구분은 간격이 담당한다. */
.stat-grid {
  display: grid; gap: var(--t2-s3);
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: var(--t2-radius-structural);
  box-shadow: none;
  overflow: visible;
}
.stat-label {
  font-family: var(--t2-font-mono);
  font-size: 10px; font-weight: 500;
  letter-spacing: var(--t2-track-label); text-transform: uppercase;
  color: var(--t2-ink-muted);
}
.stat-value {
  font-size: 26px; font-weight: 600;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
  color: var(--t2-ink);
  line-height: 1.15;
}
.stat-icon { background: transparent; border: 0; color: var(--t2-ink-muted); padding: 0; }
/* 칠해진 컨테이너 안의 흰 글리프 — admin CSS 는 색 칩(파랑·틸·보라·주황) 위에
   글리프를 흰색으로 얹는 방식이었다. 이 파일이 칩의 면을 걷어내면서 흰 글리프만
   남아 밝은 배경 위에서 보이지 않게 됐다. 면을 잃은 컨테이너는 글리프도 잉크를
   따라야 한다. 스피너 색은 아래 §업로드의 .spinner 규칙이 소유한다. */
.stat-icon :is(.mi, .material-icons, .material-icons-outlined) { color: inherit; }
.market-hero-icon { color: var(--t2-ink-muted); }
.market-hero-icon :is(.mi, .material-icons, .material-icons-outlined) { color: inherit; }
.dashboard-stat-heading { font-size: 13px; font-weight: 600; letter-spacing: var(--t2-track-title); }

/* 테이블 — hairline 만. 그림자·라운딩 없음. */
:where(.admin-wrap) table { width: 100%; border-collapse: collapse; background: transparent; }
:where(.admin-wrap) :is(th, td) {
  padding: var(--t2-s2) var(--t2-s3);
  border: 0;
  border-bottom: 1px solid var(--t2-line);
  text-align: left;
  vertical-align: top;
  font-size: 13px;
}
:where(.admin-wrap) th {
  font-family: var(--t2-font-mono);
  font-size: 10px; font-weight: 500;
  letter-spacing: var(--t2-track-label); text-transform: uppercase;
  color: var(--t2-ink-muted);
  background: transparent;
}
:where(.admin-wrap) tbody tr:hover { background: var(--t2-product-wash); }

/* 목록 행 — 면은 여기가, 열 배치는 그 목록을 만든 화면이 소유한다.
   한동안 이 한 무리가 display:flex 와 align-items:center 까지 들고 있었다.
   그런데 여섯 행은 관리 화면에서 자기 격자를 갖는다: .plugin-item 은
   minmax(260px,2fr) … auto 다섯 열이고 머리줄 .plugin-list-columns 와 같은
   템플릿으로 맞물려 있으며, .admin-setting-row · .cert-row · .official-row ·
   .up-store-row · .ai-budget-plugin-row 도 각자 라벨/조작부 열을 갖는다.
   제품 레이어가 레거시를 이기므로 그 격자도, 좁은 화면에서 열을 접는 분기도,
   위쪽 정렬(align-items:start)도 전부 사문이었다 — 목록 머리줄과 본문 열이
   어긋나 보이는 원인이 이것이다.
   면(패딩·hairline·모서리)만 여기서 소유하고 배치는 돌려준다. .plugin-item 은
   패딩 값이 나머지 여섯과 달라(아래 참고) 이 무리에서 뗐지만 같은 소유 규칙을
   따른다. */
.module-list-item, .up-store-row,
.cert-row, .official-row, .admin-setting-row, .ai-budget-plugin-row {
  gap: var(--t2-s3);
  padding: var(--t2-s2) var(--t2-s3);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-structural);
  box-shadow: none;
}
/* 플러그인 목록은 위 여섯 행 무리에서 따로 뗐다 — 마크다운 뱃지·점유 디렉티브
   표시가 더해지며 같은 패딩으로는 행이 빽빽해졌다(markdown-ai-plan §2.2-4).
   면(hairline·모서리)은 그대로 물려받되, 패딩만 콘텐츠가 아니라 관리 화면
   크롬이라 --t2-s* 를 --t2-u* 와 섞지 않고 두 축 모두 크롬 사다리로 적는다
   (CLAUDE.md "사다리를 섞지 않는다"). 같은 파일에 .plugin-item 의 padding 을
   두 번 적으면 파일 내 중복 선언 검사(C8)에 걸리므로 위 무리에서 아예 빼고
   여기서 전부를 진술한다. */
.plugin-item {
  gap: var(--t2-s3);
  padding: var(--t2-u8) var(--t2-u4);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-structural);
  box-shadow: none;
}
/* 자기 배치를 선언하지 않는 행들. 이쪽은 배치도 여기서 준다. */
.file-item, .api-list-row, .license-row {
  display: flex; align-items: center; gap: var(--t2-s3);
  padding: var(--t2-s2) var(--t2-s3);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-structural);
  box-shadow: none;
}
/* 마지막 행의 hairline 은 목록의 아래 경계가 아니라 "다음 항목이 있다" 는
   신호다. 남겨 두면 카드 안에 끊긴 선이 하나 떠 있는 것으로 읽힌다.
   관리자 CSS 에 :last-child 규칙이 있었지만 이 파일이 같은 요소에
   border-bottom 을 선언하므로 레이어 순서에서 전부 죽어 있었다. */
.plugin-item:last-child, .module-list-item:last-child, .file-item:last-child,
.api-list-row:last-child, .up-store-row:last-child, .license-row:last-child,
.cert-row:last-child, .official-row:last-child, .admin-setting-row:last-child,
.ai-budget-plugin-row:last-child { border-bottom: 0; }
:where(.admin-wrap) tbody tr:last-child :is(th, td) { border-bottom: 0; }

.plugin-item:hover, .module-list-item:hover, .api-list-row:hover { background: var(--t2-product-wash); }
.module-list-item.active, .plugin-item.active {
  position: relative;
  color: var(--t2-product-ink); font-weight: 600; background: transparent;
}
.module-list-item.active::after, .plugin-item.active::after {
  content: ""; position: absolute;
  inset-inline-start: 0; top: var(--t2-rail-inset); bottom: var(--t2-rail-inset);
  width: var(--t2-rail-w); background: var(--t2-product);
}

/* 탭 / 세그먼트 — 하단 rail */
.module-viewport-tabs, .plugin-section-nav, .ai-purpose-nav, .ai-cap-popover-seg, .plugin-sort-group {
  display: flex; align-items: center; gap: var(--t2-s1);
  border-bottom: 1px solid var(--t2-line);
  padding: 0;
  background: transparent;
}
.module-viewport-tab {
  position: relative;
  padding: var(--t2-s2) var(--t2-s3);
  min-height: 34px;
  background: transparent;
  border: 0;
  border-radius: var(--t2-radius-structural);
  color: var(--t2-ink-muted);
  font-size: 12.5px;
}
.module-viewport-tab.active, .module-viewport-tab[aria-selected="true"] {
  color: var(--t2-product-ink); font-weight: 600; background: transparent;
}
.module-viewport-tab.active::after, .module-viewport-tab[aria-selected="true"]::after {
  content: ""; position: absolute;
  left: var(--t2-rail-inset); right: var(--t2-rail-inset); bottom: -1px;
  height: var(--t2-rail-w); background: var(--t2-product);
}

/* 필터 · chip 버튼 */
.module-filter-btn, .icon-category-chip, .ai-budget-period-chip, .btn-chip, .plugin-sort-btn {
  position: relative;
  padding: var(--t2-s1) var(--t2-s2); min-height: 30px;
  background: transparent;
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-control);
  color: var(--t2-ink-muted);
  font-size: 12.5px;
}
.module-filter-btn:hover, .icon-category-chip:hover, .btn-chip:hover {
  border-color: var(--t2-line-strong); color: var(--t2-ink);
}
/* 선택 상태의 칩은 제품색을 채움이 아니라 글자·테두리로 쓴다 (705 §6·§13).
   `.is-active` 를 쓰는 세 칩이 이 목록에서 빠져 있어 다크에서 밝은 파랑 채움
   위의 흰 글자(2.35:1)로 남아 있었다 — 같은 역할군은 같은 판단 공식을 쓴다. */
.module-filter-btn.active, .icon-category-chip.active, .ai-budget-period-chip.active,
.ai-budget-period-chip.is-active, .dev-filter.is-active, .ext-chip.active,
.btn-chip.active, .plugin-sort-btn.active {
  color: var(--t2-product-ink); border-color: var(--t2-product); font-weight: 600;
  background: var(--t2-product-wash);
}

/* 검색 — 하나의 경계는 하나의 요소가 그린다 (해설 707 C).
   밑줄의 소유자는 사용자가 실제로 입력하는 상자다. 그 상자를 감싸는 래퍼도,
   그 안의 input 도 같은 경계를 다시 긋지 않는다. */
.admin-search-box, .dev-search-wrap, .module-admin-search,
.module-picker-search, .icon-search, .tag-input-wrap, .ext-add-wrap {
  display: flex; align-items: center; gap: var(--t2-s2);
  padding: 0;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--t2-line-strong);
  border-radius: 0;
  box-shadow: none;
}
/* 결과 드롭다운의 위치 기준일 뿐이다. 경계를 갖지 않으므로 상자가 폭을 채운다. */
.admin-search-shell {
  display: block;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
/* 상자가 이미 밑줄을 그린다 — 그 안의 컨트롤은 자기 밑줄을 없앤다. */
:is(.admin-search-box, .dev-search-wrap, .module-admin-search,
    .module-picker-search, .icon-search, .tag-input-wrap, .ext-add-wrap)
  :is(input, textarea, select) {
  border-bottom: 0;
}
.admin-search-box:focus-within, .dev-search-wrap:focus-within,
.tag-input-wrap:focus-within, .ext-add-wrap:focus-within {
  border-bottom-color: var(--t2-product);
  box-shadow: 0 1px 0 0 var(--t2-product);
}
.admin-search-kbd, .icon-cmd {
  font-family: var(--t2-font-mono);
  font-size: 10.5px;
  padding: var(--t2-s1) var(--t2-s1);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-control);
  color: var(--t2-ink-muted);
  background: transparent;
}
.admin-search-results, .dev-results, .icon-picker-results, .module-picker-list {
  background: var(--t2-floating);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-overlay);
  box-shadow: var(--t2-shadow-overlay);
  overflow-y: auto;
  overscroll-behavior-y: contain;
}
.admin-search-result, .dev-result, .icon-picker-item {
  position: relative;
  display: flex;
  padding: var(--t2-s2) var(--t2-s3);
  border: 0;
  border-radius: var(--t2-radius-structural);
  background: transparent;
  color: var(--t2-ink);
  font-size: 13px;
}
/* 면·여백은 셋이 같지만 축은 다르다. .admin-search-result 와 .icon-picker-item 은
   아이콘 + 라벨 한 줄이고, .dev-result 는 제목 / 메타 / 발췌를 쌓는 카드다.
   셋을 한 묶음으로 row 축에 세워 두었더니 .dev-result 의 세 블록이 나란히 서고
   제목 칸이 9px 로 눌렸다 — "T2Editor Developer Guide" 가 글자당 한 줄씩 21줄로
   쌓였고 171개 행이 전부 그랬다. admin-table.css 의 display:block 은 t2.legacy 라
   이 레이어에 지므로, 축은 여기서 갈라 준다. */
.admin-search-result, .icon-picker-item { align-items: center; gap: var(--t2-s2); }
.dev-result { flex-direction: column; align-items: stretch; }
.admin-search-result:hover, .admin-search-result.active,
.dev-result:hover, .icon-picker-item:hover { background: var(--t2-product-wash); }
.admin-search-result-description { font-size: 11.5px; color: var(--t2-ink-muted); }
.admin-search-target-highlight { background: var(--t2-product-select); }
.admin-search-status { font-family: var(--t2-font-mono); font-size: 11px; color: var(--t2-ink-muted); }

/* 토글 — 진짜 원형 컨트롤 예외 */
.toggle-track, .toggle {
  width: 34px; height: 18px;
  border-radius: 999px;
  background: var(--t2-toggle-off);
  border: 0;
  box-shadow: none;
  position: relative;
  flex: 0 0 auto;
  transition: background-color var(--t2-motion);
}
.toggle-thumb {
  width: 14px; height: 14px;
  border-radius: 50%;
  background: var(--t2-toggle-thumb);
  border: 0;
  box-shadow: none;
  position: absolute; top: 2px; left: 2px;
  transition: transform var(--t2-motion);
}
.toggle[aria-checked="true"], .toggle.on { background: var(--t2-toggle-on); }
.toggle[aria-checked="true"] .toggle-thumb, .toggle.on .toggle-thumb { transform: translateX(16px); }
.toggle-wrap { display: flex; align-items: center; gap: var(--t2-s2); font-size: 13px; color: var(--t2-ink-muted); cursor: pointer; }

/* 슬라이더 — 제품 레이어가 외형을, 관리자 레거시 레이어가 위치 계산을 소유한다. */
.t2-discrete-slider-track {
  height: calc(var(--t2-slider-thumb) + var(--t2-slider-pad) * 2);
  box-sizing: border-box;
  border: 0;
  border-radius: 999px;
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--t2-line);
  overflow: visible;
}
.t2-discrete-slider-fill {
  height: var(--t2-slider-rail);
  background: var(--t2-product);
  border-radius: 999px;
}
.t2-discrete-slider-thumb {
  width: var(--t2-slider-thumb);
  height: var(--t2-slider-thumb);
  border-radius: 50%;
  background: #fff;
  border: 0;
  box-shadow: 0 2px 10px rgba(17,24,39,.28);
}
.t2-discrete-slider-point {
  width: var(--t2-slider-dot);
  height: var(--t2-slider-dot);
  border-radius: 50%;
  background: color-mix(in srgb, #fff 62%, transparent);
}
.t2-discrete-slider-point[data-active="false"] { background: var(--t2-ink-muted); opacity: .45; }
.t2-discrete-slider-labels { font-family: var(--t2-font-mono); font-size: 11px; color: var(--t2-ink-muted); }
.t2-discrete-slider[aria-disabled="true"] .t2-discrete-slider-fill { background: var(--t2-ink-muted); }
.t2-discrete-slider[aria-disabled="true"] .t2-discrete-slider-thumb { background: var(--t2-inset); box-shadow: none; }
@media (max-width: 760px) {
  .t2-discrete-slider-labels { font-size: 10px; }
}

/* 진행 · 스피너 */
.t2-nsfw-progress, .t2-nsfw-model-bar {
  height: 2px; background: var(--t2-line); border-radius: 999px; overflow: hidden;
}
.t2-nsfw-progress-fill, .t2-nsfw-model-bar-fill { background: var(--t2-product); }
.t2-upload-progress .t2-progress-bar { height: 2px; background: var(--t2-line); border-radius: 999px; }
.t2-upload-progress .t2-progress-fill { background: var(--t2-product); }
.t2-upload-progress .t2-progress-text { font-family: var(--t2-font-mono); font-size: 11px; color: var(--t2-ink-muted); }
.spinner {
  width: 14px; height: 14px;
  border: 1px solid var(--t2-line-strong);
  border-top-color: var(--t2-product);
  border-radius: 50%;
  box-shadow: none;
}

/* 코드 · 진단 출력 */
:where(.admin-wrap, [data-t2-plugin-surface])
  :is(.dev-code, .api-example, .pkg-sec-code, .up-code-diff, .up-diff-code, .key-code, pre) {
  font-family: var(--t2-font-mono);
  font-size: 12px;
  background: var(--t2-inset);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-control);
  color: var(--t2-ink);
  padding: var(--t2-s3);
}
:where(.admin-wrap) .up-diff-old { color: var(--t2-danger); background: var(--t2-danger-soft); }
:where(.admin-wrap) :is(.dev-key, .dev-stat, .env-check-count) {
  font-family: var(--t2-font-mono); font-variant-numeric: tabular-nums;
}

/* ── 저장 바 — 해설 709 A ──────────────────────────────────────────────
   실행이 초점을 갖는다. 예전에는 save-hint 가 저장 버튼보다 앞에 놓여, 매번
   같은 설명을 지나야 실행에 닿았다. 마크업에서 버튼을 앞으로 옮겼고(읽기
   순서와 시각 위계 일치, 709 A-2), 여기서는 설명을 보조로 낮춘다.

   sticky 로 떠 있으므로 아래 콘텐츠 자리를 확보해야 한다 (708 F-2).
   자리 확보는 아래 .section-panel 규칙이 담당한다. */
.save-bar {
  display: flex; align-items: center; gap: var(--t2-gap-group);
  padding: var(--t2-inset-control) var(--t2-inset-section);
  background: var(--t2-surface);
  border: 0;
  border-top: 1px solid var(--t2-line);
  box-shadow: none;
}
.save-bar .save-hint {
  flex: 1 1 auto; min-width: 0;
  font-size: 12px; font-weight: 400;
  color: var(--t2-ink-muted);
}
.save-bar .save-hint-title { font-weight: 500; color: var(--t2-ink-muted); }
.save-bar :is(.btn, .btn-primary) { flex: 0 0 auto; }

/* 떠 있는 저장 바는 자기 뒤에 오는 콘텐츠를 영구히 덮는다 (708 F-2).
   ai 탭처럼 카드마다 저장 바가 있는 화면에서 실제로 그랬다. 스크롤 끝에서
   제자리로 돌아와 아무것도 가리지 않게 되는 위치 — 패널의 마지막 요소 —
   일 때만 떠 있을 수 있고, 그 밖의 저장 바는 흐름 안에 있는다. */
:where(.admin-wrap) .save-bar { position: static; }
:where(.admin-wrap) .section-panel > .save-bar:last-child {
  position: sticky;
  bottom: var(--t2-inset-control);
}

.admin-save-state { font-family: var(--t2-font-mono); font-size: 11px; color: var(--t2-ink-muted); }

/* ── 정본 위치 표시기 — 해설 709 C-3·C-4 ────────────────────────────────
   맥락 때문에 다른 탭에 나타난 설정이 자신의 원래 자리를 밝히고, 그 표시가
   그대로 이동 수단이 된다. 값은 정본과 같은 것을 읽으므로 여기서 편집하지
   않는다는 사실도 함께 말한다. 위치 표시의 리듬은 708 E 를 따른다. */
.admin-canonical {
  display: inline-flex; align-items: center; flex-wrap: wrap;
  gap: var(--t2-gap-tight);
  margin-top: var(--t2-trail-locator);
  padding: 0;
  font-family: var(--t2-font-mono);
  font-size: 11px;
  color: var(--t2-ink-muted);
  background: transparent;
  border: 0;
  text-align: start;
}
button.admin-canonical.is-link { cursor: pointer; min-height: var(--t2-hit); }
button.admin-canonical.is-link:hover { color: var(--t2-product-ink); }
button.admin-canonical.is-link:focus-visible { outline: 1px solid var(--t2-product); outline-offset: 2px; }
.admin-canonical-crumb[aria-current="true"] { color: var(--t2-ink-muted); font-weight: 500; }
/* 구분자는 선이 아니라 읽혀야 하는 텍스트다. 선 토큰으로 칠하면 1.56:1 까지
   내려가 계층 관계가 전달되지 않는다(해설 709 C-3, /law 08 제6조 제3항). */
.admin-canonical-sep { color: var(--t2-ink-muted); }
.admin-canonical-go { font-size: 14px; }
.admin-canonical-note { color: var(--t2-ink-muted); }

/* ── 위치 표시 — 해설 708 E-1 ──────────────────────────────────────────
   위치 표시는 아래 제목에 속한다. 위 콘텐츠와는 멀고 제목과는 가깝다. */
/* Rationale (708 C-1 예외): 위·아래 여백이 다르다. 이 표시는 아래 제목에
   속하므로 아래 간격이 위보다 좁아야 소속이 읽힌다(708 C-3·E-1). 값의 소유자는
   --t2-inset-control(위)과 --t2-trail-locator(아래)로 각각 하나씩이다. */
.admin-context-strip {
  font-family: var(--t2-font-mono);
  font-size: 11px;
  color: var(--t2-ink-muted);
  padding: var(--t2-inset-control) var(--t2-inset-shell) var(--t2-trail-locator);
  background: var(--t2-surface);
  border: 0;
  border-bottom: 1px solid var(--t2-line);
}
.admin-context-path {
  font-family: var(--t2-font-mono);
  color: var(--t2-ink-muted);
}

/* 인증 화면.
   가운데 정렬은 내용이 화면보다 짧을 때의 이야기다. 길어지면 center 는 위아래로
   똑같이 밀어내어 첫 화면(계정 생성 폼)을 뷰포트 위로 보내 버린다 — 최초 설치
   안내를 함께 품은 계정 생성 화면이 실제로 그랬다. safe 는 넘치는 순간에만
   start 로 물러서므로, 짧을 때의 가운데 정렬은 그대로 두고 길 때만 위를 지킨다.
   safe 를 모르는 브라우저를 위해 앞줄에 start 를 두고 뒷줄에서 덮는다. */
.auth-screen, .key-screen, .login-screen {
  background: var(--t2-base);
  display: grid;
  justify-items: center;
  align-content: start;
  align-content: safe center;
  min-height: 100vh;
  min-height: 100dvh;
  padding: var(--t2-s5);
}
.auth-card, .key-card {
  width: min(100%, 380px);
  background: var(--t2-surface);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-island);
  box-shadow: none;
  padding: var(--t2-s6);
}
.auth-title, .key-title { font-size: 16px; font-weight: 600; letter-spacing: var(--t2-track-title); margin: 0 0 var(--t2-s2); }
.auth-desc, .key-desc { font-size: 12.5px; color: var(--t2-ink-muted); line-height: 1.65; }
.auth-logo, .key-icon { color: var(--t2-ink-muted); background: transparent; border: 0; }

/* 토스트 */
.toast-container { display: grid; gap: var(--t2-s2); }
.toast {
  position: relative;
  display: flex; align-items: center; gap: var(--t2-s2);
  padding: var(--t2-s2) var(--t2-s3);
  background: var(--t2-floating);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-overlay);
  box-shadow: var(--t2-shadow-overlay);
  font-size: 12.5px;
  color: var(--t2-ink);
}
.toast::before {
  content: ""; position: absolute;
  left: var(--t2-rail-inset); right: var(--t2-rail-inset); top: 0;
  height: var(--t2-rail-w); background: var(--t2-product);
}
.toast[data-type="error"]::before   { background: var(--t2-danger); }
.toast[data-type="warning"]::before { background: var(--t2-warning); }
.toast[data-type="success"]::before { background: var(--t2-success); }
.toast-close { color: var(--t2-ink-muted); background: transparent; border: 0; }

/* ═══ 11. 플러그인 공통 표면 ═════════════════════════════════════════════
   플러그인은 제2의 시각 체계를 만들지 않는다. 토큰만 별칭한다. */
[data-t2-plugin-surface] { color: var(--t2-ink); font-family: var(--t2-font-ui); }
.t2-plugin-block {
  background: var(--t2-workspace);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-structural);
  box-shadow: none;
}
/* 플러그인 지역 토큰 재정렬 — 경쟁 accent 무력화 */
[data-t2-plugin-surface] {
  --t2-aic-border: var(--t2-line);
  --t2-aic-surface: var(--t2-surface);
  --t2-aic-surface-muted: var(--t2-inset);
  --t2-aic-accent: var(--t2-product);
  --t2-collab-accent: var(--t2-product);
  --t2-collab-border: var(--t2-line);
}
/* 협업 원격 캐럿 — 사용자 색은 기능이므로 유지, 두께만 통일 */
.t2-remote-caret { width: 2px; border-radius: 0; }
.t2-remote-selection { mix-blend-mode: multiply; }
html[data-t2editor-theme="dark"] .t2-remote-selection { mix-blend-mode: screen; }
.t2-search-highlight-active { background: var(--t2-product-select); outline: 1px solid var(--t2-product); }

/* 업로드 표면 */
/* 드롭존 — UI 스펙 08. 두 규격뿐이다.
     standard  단독으로 서는 경우. 위아래 인셋이 좌우보다 넓다 — 파일을 놓는
               표적은 세로로 여유가 있어야 "여기 놓으라"는 면으로 읽힌다.
     compact   모달·바텀시트 안. 표면이 이미 경계를 가지므로 표적만 남긴다.
   경계는 1.5px 파선이다. 1px 은 표면의 경계선과 구별되지 않고, 2px 은 파선이
   점선처럼 끊겨 보인다. */
.t2-upload-container {
  background: var(--t2-workspace);
  border: 1.5px dashed var(--t2-line-strong);
  border-radius: var(--t2-radius-island);
  color: var(--t2-ink-muted);
  padding: var(--t2-s8) var(--t2-s5);
  text-align: center;
}
/* 아이콘은 원형 표적이다. 원은 조작이 아니라 "놓는 자리" 를 가리키므로 하단 바의
   환경 스위치와 같은 형상을 써도 위계가 섞이지 않는다 — 이것은 버튼이 아니다. */
.t2-upload-container .material-icons {
  width: var(--t2-hit);
  height: var(--t2-hit);
  /* 아이콘 폰트의 공통 안전망이 max-width: 1.25em 을 걸어 둔다 — 그것이 그대로
     남으면 44px 짜리 원이 27.5px 로 눌려 타원이 된다. 여기서는 원이 표적이므로
     그 안전망을 풀고 상자를 직접 정한다. */
  max-width: none;
  /* 가운데 정렬을 간격 토큰과 한 선언에 묶지 않는다(위 셸과 같은 이유). */
  margin: 0 auto;
  margin-bottom: var(--t2-s3, 12px);
  display: grid;
  place-items: center;
  border-radius: var(--t2-radius-pill);
  background: var(--t2-inset);
  font-size: 22px;
}
/* 포커스는 이 면을 칠하지 않는다. 모달은 열자마자 안쪽 첫 조작(찾아보기 링크)
   에 포커스를 주므로, 포커스가 hover/drop 과 같은 채움을 쓰면 드롭존이 열자마자
   활성으로 고정되어 쉬는 상태를 한 번도 보여 주지 못한다 — 항상 켜져 있는 신호는
   신호가 아니다. 채움은 "여기 놓을 수 있다"(hover)와 "지금 놓는 중"(drag-over)만
   말하고, 키보드 포커스는 아래의 링이 따로 말한다. */
.t2-upload-container:hover .material-icons,
.t2-upload-container.drag-over .material-icons {
  background: var(--t2-product-tone);
  color: var(--t2-product-ink);
}
/* 좁은 표면 안에서는 compact 가 기본이다. 부르는 쪽이 변형 클래스를 붙이지
   않아도 모달과 바텀시트 안의 드롭존은 자동으로 줄어든다 — 규격을 고르는 일을
   모든 호출부에 미루면 한 곳은 반드시 빠뜨린다. */
.t2-upload-container--compact,
.t2-modal-surface .t2-upload-container,
[data-t2-plugin-surface] .t2-upload-container {
  padding: var(--t2-s5) var(--t2-s4);
  min-height: 0;
}
.t2-upload-container--compact .material-icons,
.t2-modal-surface .t2-upload-container .material-icons,
[data-t2-plugin-surface] .t2-upload-container .material-icons {
  /* 스펙 08 의 compact 아이콘은 36px 이다 — 44px 표적에서 한 칸 물러선 값이고,
     30px 까지 내리면 좁은 표면에서 "놓는 자리" 가 아니라 장식처럼 보인다. */
  width: calc(var(--t2-hit) - var(--t2-s2));
  height: calc(var(--t2-hit) - var(--t2-s2));
  max-width: none;
  margin-bottom: var(--t2-s2);
  font-size: 18px;
}
/* hover 도 포함한다. 예전에는 drag-over·focus-within 만 여기 있고 hover 는
   js/utils/upload-container.css(t2.module)에만 있었다. 레이어 순서상 이 파일의
   base .t2-upload-container background 가 module 의 :hover 를 이겨, 드롭존에
   마우스를 올려도 아무 반응이 없었다(죽은 :hover). 세 상태를 함께 소유한다. */
.t2-upload-container:hover,
.t2-upload-container.drag-over {
  border-color: var(--t2-product);
  border-style: solid;
  background: var(--t2-product-wash);
  color: var(--t2-ink);
}
/* 키보드로 드롭존 안에 닿았을 때의 링. 면을 칠하지 않으므로 hover·drag-over 와
   섞이지 않고, 안쪽 조작이 자기 윤곽선을 못 그리는 경우(면 전체를 덮는 파일
   인풋)에도 어디에 있는지 읽힌다. */
.t2-upload-container:has(:focus-visible) {
  outline: 2px solid var(--t2-product);
  outline-offset: 2px;
}
.t2-upload-container__hint { font-size: 11.5px; color: var(--t2-ink-muted); }
/* 스펙 08 의 caption 안에 있는 "내 파일에서 찾아보기". 면 전체가 이미 눌리지만
   끌어다 놓기가 익숙하지 않은 사람에게는 그 사실이 형상에서 읽히지 않는다 —
   밑줄 그은 링크 하나가 "누를 수 있다" 를 말한다. */
.t2-upload-container__browse {
  position: relative;
  display: inline-block;
  margin-top: var(--t2-s1);
  min-height: 0;
  padding: 0;
  background: transparent;
  border: 0;
  color: var(--t2-product-ink);
  font-size: 11.5px;
  font-weight: 600;
  text-decoration: underline;
  cursor: pointer;
}
.t2-upload-container__browse:hover { color: var(--t2-product-strong); background: transparent; }
/* 무엇을 받는지와 상한. 값이 아니라 규칙을 읽는 줄이므로 고정폭이다. */
.t2-upload-container__meta {
  margin-top: var(--t2-s2);
  font-family: var(--t2-font-mono);
  font-size: 10px;
  color: var(--t2-ink-muted);
}
@media (pointer: coarse) {
  .t2-upload-container__browse::after {
    content: "";
    position: absolute;
    top: 50%; left: 50%;
    width: 100%; height: var(--t2-hit);
    min-width: var(--t2-hit);
    transform: translate(-50%, -50%);
  }
}

/* 모달 컨테이너 밖 우측 상단 닫기 — 잉크와 면.
   이 버튼은 표면 위가 아니라 스크림 위에 선다. 스크림은 두 테마 모두 어두우므로
   (--md-overlay) 잉크 토큰을 쓰면 라이트 테마에서 어두운 회색이 어두운 스크림 위에
   놓여 읽히지 않는다 — 재 보면 4.09:1 이었다. 밝은 흰색 계열을 직접 적는다.
   기하는 모달 원형(js/utils/modal.css)이 소유하고, 여기서는 잉크만 정한다:
   §4 의 공통 버튼 규칙이 module 레이어를 이기므로 색을 거기에 적으면 죽은 선언이
   된다 — 초안이 실제로 그 실수를 했다. */
.t2-outside-modal-close {
  color: rgba(255, 255, 255, .82);
  background: transparent;
  border: 0;
  box-shadow: none;
}
.t2-outside-modal-close:hover { color: #FFFFFF; background: transparent; }

/* 개인정보 · NSFW — critical surface. 의미가 지배한다. */
.t2-privacy-surface, .t2privacy-dialog {
  background: var(--t2-floating);
  border: 1px solid var(--t2-line);
  border-radius: var(--t2-radius-modal);
  box-shadow: var(--t2-shadow-modal);
}
.t2privacy-backdrop, .t2-nsfw-dialog-overlay, .t2-safeai-legal-overlay { background: var(--md-overlay); }
/* 개인정보 동의 버튼 — 동의/거부는 되돌리기 어려운 결정이므로 두 선택지가
   형태로 구별되어야 한다. §4 의 공통 버튼 규칙이 background 를 transparent,
   color 를 muted 로 잡아 두 버튼이 똑같이 보였다. 표면 토큰으로 되돌린다.
   런타임도 같은 토큰을 인라인으로 굳혀(호스트 CSS 방어) 값이 갈라지지 않는다. */
.t2-privacy-surface .t2privacy-btn {
  background: var(--t2-privacy-surface);
  color: var(--t2-privacy-on-surface);
  border: 1px solid var(--t2-privacy-border);
}
.t2-privacy-surface .t2privacy-btn-primary {
  background: var(--t2-privacy-primary);
  color: var(--t2-privacy-primary-text);
  border-color: transparent;
  font-weight: 600;
}
/* 안내 배너 닫기 버튼 — §4 의 공통 버튼 규칙이 position:relative 와 컨트롤
   radius 를 선언해 모듈의 absolute 배치와 원형을 덮었다. 결과는 배너 오른쪽에
   빈 자리(52px 패딩)만 남고 닫기 버튼이 본문 아래로 흘러내린 상태였다.
   진짜 원형 컨트롤은 law 704 의 radius 예외에 해당한다. */
.t2privacy-notice > button {
  position: absolute;
  top: var(--t2-s2); right: var(--t2-s2);
  width: var(--t2-hit); height: var(--t2-hit);
  padding: 0;
  border: 0;
  border-radius: var(--t2-radius-pill);
  background: transparent;
  color: inherit;
}
.t2-nsfw-dialog-blocked { border-color: var(--t2-danger); }
.t2-nsfw-status-badge-simple, .t2-safeai-legal-chip {
  font-family: var(--t2-font-mono); font-size: 10.5px;
  border: 1px solid var(--t2-line); border-radius: var(--t2-radius-control);
  background: transparent; color: var(--t2-ink-muted); padding: var(--t2-s1) var(--t2-s1);
}
.t2-nsfw-timer-seconds { font-family: var(--t2-font-mono); font-variant-numeric: tabular-nums; }
.t2-nsfw-dialog-separator, .t2-safeai-legal-divider { height: 1px; background: var(--t2-line); border: 0; }
.t2privacy-btn { position: relative; }

/* ═══ 12. 반응형 · 안전 영역 ════════════════════════════════════════════ */
@media (max-width: 767px), (pointer: coarse) {
  .t2-editor-container { margin-block: var(--t2-s2, 8px); margin-inline: auto; }
  /* 스마트폰의 본문 인셋은 스펙이 12~14px 로 적어 둔 자리다. 격자로 옮기면 3/8u
     — 393px 화면에서 14.7px 로 그 범위 위끝에 선다. 폰 프레임이 6u 이던 때에는
     같은 픽셀이 3/16u 였고, 프레임이 10u 로 바로잡힌 뒤 그 값을 그대로 두면
     7.4px 이 되어 스펙의 절반 아래로 떨어진다(710 A-3). 데스크톱의 1/2u 를
     그대로 쓰면 화면폭의 1/10 을 좌우로 버려 글줄이 남지 않는다. */
  .t2-editor { padding: var(--t2-u38); }
  .content-area, .admin-flat-page { padding: var(--t2-s4) var(--t2-s3); }
  .card, .stat-card, .api-card { padding: var(--t2-s3); }

  /* 기능을 숨기지 않고 geometry 만 재구성한다 (law 707 I) */
  .t2-toolbar { flex-wrap: nowrap; }
  .t2-toolbar .t2-btn.active::after { left: 11px; right: 11px; }

  .t2-modal-surface, .t2-plugin-surface-shell {
    max-height: 92vh; max-height: 92dvh;
    padding-bottom: env(safe-area-inset-bottom);
  }
  .sidebar { padding-bottom: env(safe-area-inset-bottom); }
  /* [iOS⚠️] 화면 아래에 머무는 저장 바는 홈 인디케이터 위로 자기 여백을 확보한다.
     아래쪽 sticky 오프셋도 같은 만큼 밀어야 바가 인디케이터에 물리지 않는다. */
  :where(.admin-wrap) .save-bar {
    padding-bottom: max(var(--t2-inset-control), env(safe-area-inset-bottom));
    padding-inline: max(var(--t2-inset-section), env(safe-area-inset-left))
                    max(var(--t2-inset-section), env(safe-area-inset-right));
  }
  :where(.admin-wrap) .section-panel > .save-bar:last-child {
    bottom: max(var(--t2-inset-control), env(safe-area-inset-bottom));
  }
  .topbar { padding-inline: max(var(--t2-s3), env(safe-area-inset-left)) max(var(--t2-s3), env(safe-area-inset-right)); }
  .stat-grid { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
  .t2-plugin-surface-footer { flex-wrap: wrap; }
}
/* 좁은 화면의 열 수는 관리 화면이 정해 두었지만(admin/css/admin-mobile.css 의
   700px 2열 · 420px 1열) 그 선언은 레거시 레이어라 위의 auto-fit 을 이기지
   못했다 — 재 보니 393px 폰에서 170.5px 짜리 두 열이 섰다. 값의 주인을 옮긴다. */
@media (max-width: 700px) { .stat-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 420px) { .stat-grid { grid-template-columns: 1fr; } }

/* 관리자 반응형 외형도 제품 레이어가 소유한다. 레거시 레이어의 @media 규칙은
   이 레이어의 기본 선언을 이길 수 없으므로, 실제로 달라져야 하는 값은 여기 둔다. */
@media (min-width: 561px) {
  .module-viewport-tab { min-height: 36px; }
}
@media (max-width: 900px) {
  .sidebar { padding-top: max(0, env(safe-area-inset-top)); }
  .topbar { padding: var(--t2-s2) max(var(--t2-s3), env(safe-area-inset-right)) var(--t2-s2) max(var(--t2-s3), env(safe-area-inset-left)); }
  .content-area { padding: var(--t2-s5) var(--t2-s4) 72px; }
  .admin-setting-row { gap: var(--t2-s2); }
}
@media (max-width: 860px) {
  .nav-purpose-toggle { min-height: 48px; }
  .admin-context-strip {
    padding-inline: max(var(--t2-s3), env(safe-area-inset-left)) max(var(--t2-s3), env(safe-area-inset-right));
  }
}
@media (max-width: 820px) {
  .plugin-item { gap: var(--t2-s2) var(--t2-s3); }
  .plugin-modal { border-radius: var(--t2-radius-structural); }
  .plugin-modal-body { padding: var(--t2-s4); }
}
@media (max-width: 700px) {
  .content-area { padding: var(--t2-s4) var(--t2-s3) 68px; }
  .section-header { padding-bottom: var(--t2-s3); }
  .card-body { padding: var(--t2-s4); }
  .card-header { padding: var(--t2-s3) var(--t2-s4); }
  .stat-card { padding: var(--t2-s3); }
  .topbar-title { font-size: 14px; }
  .auth-card, .key-card { padding: var(--t2-s6) var(--t2-s4); }
}
@media (max-width: 720px) {
  .t2-plugin-surface-shell { border-radius: var(--t2-radius-control); }
  .t2-plugin-surface-title { font-size: 16px; }
}
@media (max-width: 680px) {
  .icon-picker-modal { border-radius: var(--t2-radius-structural); }
}
@media (max-width: 640px) {
  .t2privacy-dialog { border-radius: var(--t2-radius-structural); }
}
@media (max-width: 620px) {
  .admin-context-strip { padding-block: var(--t2-s1); }
}
@media (max-width: 600px) {
  .topbar { gap: var(--t2-s2); }
  .up-release-card { padding: var(--t2-s3); }
}
@media (max-width: 560px) {
  .module-viewport-tab { min-width: 44px; min-height: 44px; }
}
@media (max-width: 480px) {
  .plugin-item { padding: var(--t2-s3) var(--t2-s2); }
}
@media (max-width: 420px) {
  .content-area { padding-inline: var(--t2-s3); }
}
@media (max-width: 767px), (pointer: coarse) {
  /* 셸의 각을 좁은 규모에서 조작부 단계로 낮추던 규칙을 걷어냈다. 각은 크기가
     아니라 역할을 말한다 — 좁아졌다고 셸이 조작부가 되지는 않으므로, 셸은 어느
     폭에서나 셸 단계(--t2-radius-shell)를 쓴다. 게다가 이 규칙은 폭이 아니라
     포인터 종류만으로도 걸려, 터치 노트북의 넓은 화면에서 셸만 각이 달랐다. */
  /* 하단 바의 높이(50px)와 좌우 비대칭 인셋(12px/8px)을 여기서 다시 정하던 규칙을
     걷어냈다. 밴드 높이는 --t2-chrome-band 가, 인셋은 격자가 소유하며 좁은 규모의
     축약은 위의 컨테이너 질의가 한다(710 B-9·C-10·C-11). 폭이 아니라 포인터
     종류만으로도 걸리던 규칙이라, 터치 노트북에서 넓은 화면인데도 격자가 정한
     밴드를 50px 로 눌렀다. */
  .t2-translate-menu-item, .t2-font-size-option { font-size: 16px; }
}

/* 축소된 모션 */
@media (prefers-reduced-motion: reduce) {
  :where(.t2-editor-container, .admin-wrap, .t2-modal-overlay, [data-t2-plugin-surface]) *,
  :where(.t2-editor-container, .admin-wrap, .t2-modal-overlay, [data-t2-plugin-surface]) *::before,
  :where(.t2-editor-container, .admin-wrap, .t2-modal-overlay, [data-t2-plugin-surface]) *::after {
    transition-duration: .01ms;
    animation-duration: .01ms;
  }
}

/* 고대비 환경 — 색 외 단서를 유지한다 */
@media (forced-colors: active) {
  .t2-toolbar .t2-btn.active::after,
  .module-viewport-tab.active::after { forced-color-adjust: none; }
  :where(.t2-editor-container, .admin-wrap) :is(button, input, select, textarea):focus-visible {
    outline: 2px solid CanvasText;
  }
}

} /* @layer t2.product */
