/* 셸 레이아웃. DevExpress 테마 뒤에 실린다. 한 업무만 쓰는 스타일은 모듈 wwwroot 로 간다. 색은 DevExpress 변수(--dxbl-*)를 먼저 써야 테마가 한 화면에서 갈라지지 않는다. */

:root {
/* 사이드바 시작 폭 기본값. MainLayout._sidebarWidth 의 기본값과 같아야 첫 그림에서 격자와 판이 어긋나지 않는다. */
  --jsini-sidebar-width: 330px;
/* 헤더 높이의 보통값(3.5rem). 크기 단계에 따라 --jsini-chrome-scale 로 줄어들며, 띠 높이도 함께 줄여야 아이콘이 빈 띠 가운데 뜨지 않는다. */
  --jsini-header-base-height: calc(3.5rem * var(--jsini-chrome-scale));
  --jsini-header-height: var(--jsini-header-base-height);

/* 헤더 아이콘 한 변. 글자와 같은 1em 이면 글자처럼 보여 단추로 안 읽히므로 따로 둔다. 크기 모드는 따라간다. */
  --jsini-icon-size: calc(var(--jsini-fs-base) * 1.32);

/* DevExpress 가 주는 값이 있으면 그것을 쓰고, 두 번째 값은 그 테마가 변수를 내보내지 않을 때만 쓴다. */
  --jsini-bg: var(--dxbl-body-bg, #ffffff);
  --jsini-surface: var(--dxbl-panel-bg, #f7f8fa);
  --jsini-border: var(--dxbl-border-color, #e3e5e8);
  --jsini-text: var(--dxbl-body-color, #1f2328);
  --jsini-text-muted: #6b7280;

/* 강조색 변수는 테마마다 이름이 다르다. var() 를 겹쳐 차례로 본다. --dxbl-primary 는 어디에도 없어 쓰면 늘 맨 뒤 색으로 굳는다. */
  --jsini-accent: var(--dxds-color-surface-primary-default-rest, var(--bs-primary, #7b1fa2));
}

:root[data-theme='dark'] {
  --jsini-bg: var(--dxbl-body-bg, #16181d);
  --jsini-surface: var(--dxbl-panel-bg, #1e2128);
  --jsini-border: var(--dxbl-border-color, #2d313a);
  --jsini-text: var(--dxbl-body-color, #e6e8ea);
  --jsini-text-muted: #9aa1ab;
  --jsini-accent: var(--dxds-color-surface-primary-default-rest, var(--bs-primary, #c07ee0));
}

/* 글자 크기 사다리. rem 을 박으면 본문(14px)과 어긋나고 크기를 바꿔도 움직이지 않으므로 모든 글자는 이 사다리로 적는다. 뿌리는 DevExpress 크기 모드 값을 따른다. */

:root {
  --jsini-fs-base: 0.875rem;

/* 헤더 배율(--jsini-chrome-scale). 작아질 때만 따라가고 조금작게에서 멈춘다. CSS 는 길이를 길이로 나눌 수 없어 미리 계산해 적는다. 사다리를 고치면 이 줄도 고친다. */
  --jsini-chrome-scale: 0.86;

  --jsini-fs-2xs: calc(var(--jsini-fs-base) * 0.78);
  --jsini-fs-xs: calc(var(--jsini-fs-base) * 0.86);
  --jsini-fs-sm: calc(var(--jsini-fs-base) * 0.93);
  --jsini-fs-md: var(--jsini-fs-base);
  --jsini-fs-lg: calc(var(--jsini-fs-base) * 1.1);
  --jsini-fs-xl: calc(var(--jsini-fs-base) * 1.22);
  --jsini-fs-2xl: calc(var(--jsini-fs-base) * 1.45);
  --jsini-fs-3xl: calc(var(--jsini-fs-base) * 1.75);
  --jsini-fs-4xl: calc(var(--jsini-fs-base) * 2.05);
}

/* 사다리 뿌리. DevExpress 가 정한 세 값(Small·Medium·Large)에 우리 여섯 단계를 끼웠다. 부품 크기는 이 사다리와 별개이며 이웃한 DevExpress 모드로 접힌다. */
:root[data-dx-size='xxsmall'] { --jsini-fs-base: 0.625rem; --jsini-chrome-scale: 0.66; }
:root[data-dx-size='xsmall'] { --jsini-fs-base: 0.6875rem; --jsini-chrome-scale: 0.73; }
:root[data-dx-size='small'] { --jsini-fs-base: 0.75rem; --jsini-chrome-scale: 0.80; }
/* 여기가 껍데기의 **최대**다. 아래 다섯이 이 값을 그대로 쓴다. */
:root[data-dx-size='compact'] { --jsini-fs-base: 0.8125rem; --jsini-chrome-scale: 0.86; }

/* 「보통」부터 위로는 배율이 「조금작게」와 같다. 글자만 커지고 껍데기는 0.86 에서 멈춘다. */
:root[data-dx-size='medium'] { --jsini-fs-base: 0.875rem; --jsini-chrome-scale: 0.86; }
:root[data-dx-size='large'] { --jsini-fs-base: 1rem; --jsini-chrome-scale: 0.86; }
:root[data-dx-size='xlarge'] { --jsini-fs-base: 1.125rem; --jsini-chrome-scale: 0.86; }
:root[data-dx-size='xxlarge'] { --jsini-fs-base: 1.25rem; --jsini-chrome-scale: 0.86; }
:root[data-dx-size='xxxlarge'] { --jsini-fs-base: 1.5rem; --jsini-chrome-scale: 0.86; }

/* 간격·모서리·그림자 사다리. 자리마다 다른 여백이 박히면 나란한 판이 1~2px 씩 어긋나므로 DevExpress 기본 토큰(--dxds-*)을 따른다. 두 번째 값은 토큰이 없는 Classic·Bootstrap 용이다. */

:root {
/* 셸 본문의 바깥 여백. 위·아래·좌우가 따로 다르고 폭에 따라 갈린다. 눈금을 쓰지 않는 것은 좌우 2px 이 눈금 밖이라서다. 쓰는 곳은 .jsini-shell__content 뿐이다. */
  --jsini-shell-pad-top: 1rem;
  --jsini-shell-pad-bottom: 1rem;
  --jsini-shell-pad-inline: 0.5rem;

/* 업무 화면 바닥 단추 줄의 높이. 휴대폰에서 떠다니는 단추가 그 위로 올라서는 거리로 쓰인다. 3.5rem 은 재어 나온 값이다. */
  --jsini-actbar-height: 3.5rem;

/* 휴대폰 아래 띠 높이. 안전 영역은 쓰는 자리마다 따로 더한다. 띠를 쓰는 자리가 아홉이라 숫자를 베끼지 않고 이 토큰만 본다. 이름을 감추면 띠가 얇아지고, 줄어든 16px 은 본문이 가져간다. */
  --jsini-bottomnav-h: 60px;

  --jsini-sp-3xs: var(--dxds-spacing-20, 0.125rem);   /*  2px */
  --jsini-sp-2xs: var(--dxds-spacing-40, 0.25rem);    /*  4px */
  --jsini-sp-xs: var(--dxds-spacing-60, 0.375rem);    /*  6px */
  --jsini-sp-sm: var(--dxds-spacing-80, 0.5rem);      /*  8px */
  --jsini-sp-md: var(--dxds-spacing-120, 0.75rem);    /* 12px */
  --jsini-sp-lg: var(--dxds-spacing-160, 1rem);       /* 16px */
  --jsini-sp-xl: var(--dxds-spacing-200, 1.25rem);    /* 20px */
  --jsini-sp-2xl: var(--dxds-spacing-240, 1.5rem);    /* 24px */
  --jsini-sp-3xl: var(--dxds-spacing-320, 2rem);      /* 32px */

  --jsini-radius-sm: var(--dxds-border-radius-40, 0.25rem);
  --jsini-radius-md: var(--dxds-border-radius-60, 0.375rem);
  --jsini-radius-lg: var(--dxds-border-radius-80, 0.5rem);
  --jsini-radius-xl: var(--dxds-border-radius-120, 0.75rem);
  --jsini-radius-full: var(--dxds-border-radius-full, 62rem);

  --jsini-shadow-xs: var(--dxds-box-shadow-xs, 0 1px 2px rgb(0 0 0 / 14%));
  --jsini-shadow-sm: var(--dxds-box-shadow-sm, 0 2px 4px rgb(0 0 0 / 14%));
  --jsini-shadow-md: var(--dxds-box-shadow-md, 0 4px 8px rgb(0 0 0 / 14%));
  --jsini-shadow-lg: var(--dxds-box-shadow-lg, 0 8px 16px rgb(0 0 0 / 14%));

/* 라벨이 안에 떠 있는 입력 칸의 높이. DevExpress 기본 편집기 32px 에는 라벨 자리가 없어 그 칸만 키운다. 모든 편집기를 키우면 표 줄 높이가 늘어난다. */
/* 라벨이 테두리에 걸쳐 앉는 칸 높이. DevExpress 기본 편집기와 같은 32px 다. */
  --jsini-field-h: 2rem;

/* 떠오른 라벨을 테두리 가운데에 맞추려고 위로 반 줄 올린다(0.6 = 글자 크기 × 줄 간격 1.2 의 절반). */
  --jsini-field-label-top: calc(var(--jsini-fs-2xs) * -0.6);


}

/* 휴대폰 본문 여백. 좌우를 2px 까지 좁힌다(좁은 화면에서는 한 뼘이 자리값이다). 같은 토큰을 보는 자리가 네 군데이므로 토큰만 고치고 규칙마다 값을 적지 않는다. 아래쪽은 하단 띠 높이가 calc 에서 더해진다. */
@media (max-width: 767px) {
  :root {
    --jsini-shell-pad-top: 0.5rem;
    --jsini-shell-pad-bottom: 1rem;
    --jsini-shell-pad-inline: 2px;
  }
}

/* 본문 글꼴은 테마가 거는 것을 쓰되 글자 크기는 사다리 뿌리로 덮는다. 줄 간격은 고정 길이 대신 비율로 두어 글자와 함께 늘어난다. */
html,
body {
  height: 100%;
  margin: 0;
  background: var(--jsini-bg);
  color: var(--jsini-text);
}

body {
  font-size: var(--jsini-fs-md);

/* 데모(14px 글자 · 21px 줄) 기준의 줄 간격 비율. */
  line-height: 1.5;
}

.jsini-hidden { display: none !important; }

/* 셸 뼈대 치수. DevExpress 데모(dx-demo.css)에서 옮긴 값이다. 사이드바를 접을 때는 격자를 바꾸지 않고 폭만 0 으로 만든다(격자를 바꾸면 본문이 다시 배치되어 스크롤이 튄다). */

.jsini-shell {
  display: grid;
  grid-template-columns: var(--jsini-sidebar-width) 1fr;
  grid-template-rows: var(--jsini-header-height) 1fr;
  height: 100vh;
  overflow: hidden;
  transition: grid-template-columns 0.2s ease-in-out, grid-template-rows 0.25s ease-in-out;
}

.jsini-shell--collapsed {
  grid-template-columns: 0 1fr;
}

/* 스크롤 다운 시 상단 헤더 숨김으로 화면의 세로 공간을 더 확보한다 */
.jsini-shell--header-hidden {
  --jsini-header-height: 0px;
}

.jsini-shell--header-hidden .jsini-header {
  transform: translateY(-100%);
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
}

/* 아래 띠에서 이름을 감춘 사람에게 붙는 표시(MainLayout.ShellCss). 띠는 아이콘 한 줄 높이로 얇아지고 그 차이만큼 본문이 돌려받는다. 44px 은 손가락이 빗나가지 않는 최소 높이다. */
.jsini-shell--bottomnav-nolabel {
  --jsini-bottomnav-h: 44px;
}

/* 좁은 화면의 덮개(__scrim)와 떠다니는 단추(__fab). 생김새는 여기 적고 보이는지는 미디어 쿼리가 정한다. 마크업은 화면 크기와 무관하게 늘 그린다(프리렌더와 회로가 같은 그림이어야 한다). */

.jsini-shell__scrim {
  display: none;
  position: fixed;

/* 헤더는 덮지 않는다. 메뉴가 펴져 있어도 알림·프로필은 눌려야 하고, 누르면 닫히는 것은 레이아웃이 받는다. */
  inset: var(--jsini-header-height) 0 0 0;

/* 사이드바 아래, 본문 위. 판은 덮개 위에 떠 있어야 한다. */
  z-index: 1029;

/* 덮개는 옅게 어둡힌다. 투명하면 눌러 닫는 자리인 줄 모르고, 짙으면 본문이 사라진 것처럼 보인다. */
  background: rgb(0 0 0 / 35%);
}

/* 떠다니는 메뉴 단추는 왼쪽 아래(엄지가 닿는 쪽). 안전 영역 여백을 더해 홈 막대 아래 절반이 안 눌리지 않게 한다. */
.jsini-shell__fab {
  display: none;
  position: fixed;
  left: calc(0.75rem + env(safe-area-inset-left));
  bottom: calc(0.75rem + env(safe-area-inset-bottom));
  z-index: 1031;

  /* 손가락으로 누르는 단추다. 접근성 안내가 말하는 최소 44px 를 넘긴다. */
  width: 3rem;
  height: 3rem;
  align-items: center;
  justify-content: center;
  padding: 0;
  border-radius: 50%;
  box-shadow: 0 3px 10px rgb(0 0 0 / 30%);
  cursor: pointer;

/* 단추는 속을 채우지 않는다. 판 색을 깔고 테두리와 그림만 강조색으로 둔다. 구분은 그림이 한다(☰ ↔ ✎). 바탕을 칠하면 뒤의 글줄이 지워진다. */
  background: var(--jsini-surface, #fff);
  color: var(--jsini-accent);
  border: 1px solid color-mix(in srgb, var(--jsini-accent) 45%, transparent);

  /* 뒤에 글자가 가려져도 존재를 인지할 수 있도록 평상시 살짝 반투명하게 둔다. */
  opacity: 0.8;
  transition: opacity 0.2s ease, background 0.15s ease, transform 0.15s ease;

/* 길게 누르면 설정이 뜨는 단추이므로 브라우저의 말풍선과 글자 선택을 막는다. 안드로이드 바로가기 차림표는 JS 쪽에서 막는다. */
  -webkit-touch-callout: none;
  user-select: none;
}

/* 누르고 있는 동안의 표시(is-held, JS 가 붙였다 뗀다). 0.5초 동안 반응이 없으면 먹통으로 읽히므로 살짝 줄어든다. */
.jsini-shell__fab.is-held {
  opacity: 1;
  transform: scale(0.92);
}

.jsini-shell__fab:hover,
.jsini-shell__fab:focus-visible,
.jsini-shell__fab:active,
.jsini-shell--nav-open .jsini-shell__fab {
  opacity: 1;
}

.jsini-shell__fab:active {
  background: color-mix(in srgb, var(--jsini-accent) 12%, var(--jsini-surface, #fff));
}

/* 그림은 단추보다 한 뼘 크게. 기본값이면 동그라미 가운데가 비어 보인다. */
.jsini-shell__fab [class^='jsini-icon-'] {
  width: 1.6rem;
  height: 1.6rem;
}

/* 모바일 메뉴 단추 위치 4가지(왼쪽·오른쪽, 위·아래)를 환경설정에서 고른다. 위쪽은 헤더 띠 아래에 안전 여백을 더해 놓는다. */
.jsini-shell__fab--bottom-left {
  left: calc(0.75rem + env(safe-area-inset-left));
  bottom: calc(0.75rem + env(safe-area-inset-bottom));
  top: auto;
  right: auto;
}

.jsini-shell__fab--top-left {
  left: calc(0.75rem + env(safe-area-inset-left));
  top: calc(var(--jsini-header-height) + 0.75rem + env(safe-area-inset-top));
  bottom: auto;
  right: auto;
}

.jsini-shell__fab--bottom-right {
  right: calc(0.75rem + env(safe-area-inset-right));
  bottom: calc(0.75rem + env(safe-area-inset-bottom));
  top: auto;
  left: auto;
}

.jsini-shell__fab--top-right {
  right: calc(0.75rem + env(safe-area-inset-right));
  top: calc(var(--jsini-header-height) + 0.75rem + env(safe-area-inset-top));
  bottom: auto;
  left: auto;
}

/* 요청 등록 단추(--help). 생김새는 메뉴 단추와 같은 꼴이라 여기 적지 않고, 이름표로만 자리를 가리킨다. 감추기와 밀기가 이 이름으로 이 단추만 집는다. */

/* 넓은 화면에도 띄우되 물어서 띄운다(늘 띄우면 책상에서 안 쓰는 사람의 오른쪽 아래를 먹는다). 표시는 MainLayout.ShellCss 가 붙이고 저장은 브라우저의 jsini-hdfab-desktop 이다. */
.jsini-shell--hdfab-desktop .jsini-shell__fab--help { display: inline-flex; }

/* 환경설정에서 요청 등록 단추를 감춘 사람(jsini-hdfab-hidden). 메뉴 단추의 감추기와 따로 켜고 끈다. 폭을 가리지 않으며 바로 위 규칙보다 뒤에 둔다(같은 자릿수라 뒤엣것이 이긴다). */
.jsini-shell--hdfab-hidden .jsini-shell__fab--help { display: none; }

/* 같은 귀퉁이에 둘이 설 때(--stacked). 자리를 정하는 top·bottom 은 다른 규칙이 !important 로 덮으므로 margin 으로 한 칸을 더한다. 밀리는 쪽은 요청 등록 단추다. */
.jsini-shell__fab--stacked.jsini-shell__fab--bottom-left,
.jsini-shell__fab--stacked.jsini-shell__fab--bottom-right {
  margin-bottom: 3.75rem;
}

.jsini-shell__fab--stacked.jsini-shell__fab--top-left,
.jsini-shell__fab--stacked.jsini-shell__fab--top-right {
  margin-top: 3.75rem;
}

/* 넓은 화면에서는 밀지 않는다. 같은 귀퉁이의 메뉴 단추는 넓은 화면에서 안 보이므로 한 칸만 되돌린다. 위로 가기 단추는 반대로 여기서 비켜 선다. */
@media (min-width: 768px) {
  .jsini-shell--hdfab-desktop .jsini-shell__fab--help.jsini-shell__fab--stacked {
    margin-top: 0;
    margin-bottom: 0;
  }
}

/* 같은 귀퉁이에 셋이 설 때. 위짝도 함께 적는다(한쪽만 있으면 위 귀퉁이로 옮긴 날 포갠다). 값은 한 칸(3.75rem)을 한 번 더 더한 것이다. */
.jsini-shell__fab--stacked2.jsini-shell__fab--bottom-left,
.jsini-shell__fab--stacked2.jsini-shell__fab--bottom-right {
  margin-bottom: 7.5rem;
}

.jsini-shell__fab--stacked2.jsini-shell__fab--top-left,
.jsini-shell__fab--stacked2.jsini-shell__fab--top-right {
  margin-top: 7.5rem;
}

/* 위로 가기 단추(--scroll-top). 본문을 300px 넘게 내려 읽을 때만 뜬다(is-visible 는 theme.js 가 붙인다). 구르는 상자는 본문 안의 상자일 수 있다. 자리는 늘 잡아 두고 opacity 로 스며 나온다. */
.jsini-shell__fab--scroll-top {
  display: inline-flex;
  right: calc(0.75rem + env(safe-area-inset-right));
  bottom: calc(0.75rem + env(safe-area-inset-bottom));
  left: auto;
  top: auto;
}

.jsini-shell .jsini-shell__fab--scroll-top:not(.is-visible) {
  opacity: 0;
  pointer-events: none;

/* 살짝 가라앉혀 두었다가 떠오른다(누르는 표시의 scale 과 같은 칸을 쓴다). */
  transform: translateY(0.5rem);
}

/* 휴대폰에서 메뉴가 펴져 있을 때는 걷는다(덮개 위에서 가리키는 본문이 뜻이 없다). 뒤에 둔다. */
.jsini-shell--nav-open .jsini-shell__fab--scroll-top {
  opacity: 0;
  pointer-events: none;
}

/* AI 에이전트 대화(/site/ai/chat) 화면에서는 위로 가기 단추를 보이지 않는다. */
.jsini-shell:has(.site-ai-chat) .jsini-shell__fab--scroll-top,
.jsini-shell:has(.site-ai-chat) #jsiniScrollToTop {
  display: none !important;
}

/* 바닥 칸(actbar)이 있는 화면에서는 그 줄 위에 선다. 넓은 화면에서도 그렇다(그대로 두면 맨 끝 단추를 덮는다). 비켜 설 몫은 토큰으로 적는다. */
.jsini-shell:has(.jsini-actbar) .jsini-shell__fab--scroll-top {
  bottom: calc(var(--jsini-shell-pad-bottom) + var(--jsini-sp-lg)
               + var(--jsini-actbar-height) + 0.75rem);
}

/* 사이드바와 본문 사이 구분선(DxSplitter). 넓은 화면에서는 스플리터가 두 칸에 걸쳐 앉고 폭은 부품의 Size 가 정한다. 높이는 판과 안쪽까지 min-height 0 으로 흘려야 각자 안에서 구른다. */

.jsini-shell__split {
  grid-row: 2;
  grid-column: 1 / -1;
  min-width: 0;
  min-height: 0;
}

/* 판 안쪽 여백을 걷는다. DevExpress 가 주는 여백은 문서 안 분할에는 맞지만 셸의 판에서는 사이드바를 안쪽으로 밀어 헤더와 어긋난다. 그 여백을 쓰는 규칙이 읽는 변수를 덮는다. */
.jsini-shell .jsini-shell__split.dxbl-splitter {
  --dxbl-splitter-pane-padding-x: 0;
  --dxbl-splitter-pane-padding-y: 0;
}

.jsini-shell__split .dxbl-splitter-pane-content {
  min-width: 0;
  min-height: 0;
}

/* 사이드바·본문이 판을 꽉 채우고 넘치는 것은 각자 안에서 구른다. */
.jsini-shell__split .dxbl-splitter-pane-content > .jsini-shell__sidebar,
.jsini-shell__split .dxbl-splitter-pane-content > .jsini-shell__main {
  height: 100%;
  min-height: 0;
}

/* 구분선 바탕은 비운다(사이드바가 이미 선과 그림자를 가졌다). 마우스를 올리면 강조색을 옅게 깔아 끌 수 있음을 알린다. */

.jsini-shell__split > .dxbl-splitter-separator {
  background-color: transparent;

  /* 잡이(아래 `::before`)가 상자 밖으로 나가야 한다. */
  position: relative;
  overflow: visible;

/* 본문 판보다 위에 있어야 한다. 본문 판이 DOM 에서 뒤라 층을 올리지 않으면 잡이의 오른쪽 절반이 본문에 먹힌다. */
  z-index: 1;
}

/* 구분선은 자리를 차지하지 않는다. 폭 0 으로 두고 잡는 자리만 상자 밖의 의사요소(::before)로 10px 넓힌다. 겹치게 두면 본문 첫 칸을 가려 누른 것이 안 눌린다. */

.jsini-shell__split > .dxbl-splitter-separator::before {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline: -5px;
}

/* 마우스를 올리면 경계에 2px 선만 뜬다. 잡이를 통째로 칠하면 벌어져 보이는 것이 되돌아온다. */
.jsini-shell__split > .dxbl-splitter-separator:hover::before {
  background: linear-gradient(
    to right,
    transparent calc(50% - 1px),
    var(--jsini-accent) calc(50% - 1px) calc(50% + 1px),
    transparent calc(50% + 1px));
}

/* ── 끄는 도중에 미리 접어 보이기 ──────────────────────────

   구분선을 100px(`SidebarSnapPx`) 안쪽까지 끌면 **놓기 전에** 사이드바가
   접힌 모습이 된다. 손을 되돌려 문턱을 넘으면 그 자리에서 다시 펴진다 —
   놓아 봐야 알 수 있는 것을 끌면서 알 수 있게 하는 표시다.

   붙이고 떼는 일은 theme.js 가 한다(같은 이름). 여기서는 **판의 폭만**
   0 으로 만든다 — DevExpress 가 끌면서 적어 넣는 `flex`·`min-width` 를
   덮어야 해서 `!important` 다. 부품의 접힘(`Collapsed`)은 건드리지 않는다.
   진짜 접힘은 놓을 때 C# 이 하고, 그때 이 표시는 걷힌다.
   ---------------------------------------------------------- */

.jsini-shell--snap .jsini-shell__split > .dxbl-splitter-pane:first-of-type {
  flex-basis: 0 !important;
  min-width: 0 !important;
  overflow: hidden !important;

  /* 접히는 쪽만 미끄러진다. 표시를 뗄 때는 이 규칙째 사라지므로 **되펴짐은
     그 자리에서 일어난다** — 그래야 마우스가 있는 폭으로 곧바로 돌아온다.
     판에 늘 걸어 두면 끄는 내내 폭이 마우스보다 늦게 따라온다. */
  transition: flex-basis 0.12s ease-out;
}

/* 접힘과 같은 모습이라야 한다 — 폭만 0 이고 테두리가 남으면 본문 왼쪽에
   선 한 줄이 떠 있는 것으로 보인다. */
.jsini-shell--snap .jsini-shell__sidebar {
  border-right-width: 0;
  box-shadow: none;
}

/* ── 헤더 ─────────────────────────────────────────────────

   데모: 높이 3.5rem, 아래에 옅은 그림자(테두리가 아니다).
   그림자를 쓰면 스크롤한 본문이 헤더 밑으로 들어갈 때 층이 생긴다.
   ---------------------------------------------------------- */

.jsini-header {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  height: var(--jsini-header-base-height);
  min-height: var(--jsini-header-base-height);

  /* 헤더는 띠 하나고 여기 있는 것이 전부 아이콘 단추다. 본문보다 한 단계
     크게 — 데모의 상단 바가 그 정도다.

     그 1.35rem 이 「보통」의 값이고, 글자를 작게 하면 배율만큼 줄어든다.
     크게 해도 여기서 멈춘다 — 이유는 `--jsini-chrome-scale` 머리말에 있다.

     **글자 사다리(`--jsini-fs-*`)를 직접 쓰지 않는다.** 그러면 「본문보다
     한 단계 크게」가 없어지고, 크게 골랐을 때 아이콘이 띠를 꽉 채운다. */
  --jsini-icon-size: calc(1.35rem * var(--jsini-chrome-scale));
  padding: 0 0.5rem;
  background: var(--jsini-bg);
  box-shadow: 0 2px 6px 0 rgb(0 0 0 / 12%);
  z-index: 1030;
  transition: transform 0.25s ease-in-out, opacity 0.2s ease-in-out, visibility 0.25s;
}

/* 메뉴 접기 단추. 데모는 동그란 단추다. */
.jsini-header .jsini-header__toggle .dxbl-btn {
  border-radius: 50%;
  padding: 0.5rem;
}

.jsini-header__brand {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-left: 0.5rem;
  color: inherit;
  text-decoration: none;
}

/* 32px 이 「보통」의 값이다. 아이콘과 같은 배율을 쓴다 — 로고만 그대로 두면
   아이콘이 줄어든 옆에서 혼자 커 보인다. */
.jsini-header__logo {
  width: calc(32px * var(--jsini-chrome-scale));
  height: calc(32px * var(--jsini-chrome-scale));
}

/* ── 브랜드 심볼 (BrandMark) ──────────────────────────────────

   두 획의 색을 테마 토큰으로 준다. 어두운 테마에서는 두 값이 함께 뒤집혀
   **고르는 일 없이** 따라온다. 밝은 것과 녹아웃 두 벌을 들고 있지 않는다. */
.jsini-brand-mark { display: block; }

/* 헤더의 심볼은 배율을 따른다.

   [왜 CSS 로 덮어야 하나]

   `BrandMark` 는 크기를 **SVG 속성**(`width` · `height`)으로 준다. 속성은
   CSS 가 덮을 수 있지만 CSS 를 안 쓰면 그 값이 그대로 남는다 — 그래서
   한동안 아이콘만 줄고 **로고는 꿈쩍하지 않았다.**

   `height: auto` 로 두면 `viewBox`(84×60)가 비율을 지킨다. 높이를 함께
   계산해 적으면 그 비율을 두 곳에서 관리하게 된다.

   [38 은 `BrandMark.Size` 의 기본값이다]

   그 기본값을 고치면 이 줄도 같이 고쳐야 한다. 헤더로 좁혀 둔 이유는
   다른 자리에서 `Size` 를 주고 쓸 때 그 값이 이기게 하려는 것이다 —
   지금 쓰는 곳은 헤더 하나뿐이지만, 좁혀 두면 늘어나도 안 깨진다. */
.jsini-header .jsini-brand-mark {
  width: calc(38px * var(--jsini-chrome-scale));
  height: auto;
}

/* J 가 본문 글자색, S 가 옅은 색이다. 둘을 같은 색으로 두면 겹친 자리가
   한 덩어리로 뭉개져 「JS」 로 읽히지 않는다. */
.jsini-brand-mark__j { fill: var(--jsini-text); }
.jsini-brand-mark__s { fill: var(--jsini-text-muted); }

/* 로고와 글자 사이의 세로 선. 데모의 `.logo-sep` 과 같다. */
.jsini-header__sep {
  width: 1px;

  /* 로고와 같은 배율. 선만 그대로 두면 낮아진 띠를 위아래로 넘친다. */
  height: calc(2rem * var(--jsini-chrome-scale));
  background: currentcolor;
  opacity: 0.15;
}

/* 로고 옆의 권한 그룹. 역할이 둘이면 「시스템관리자 · 프로젝트관리 관리자」
   처럼 길어져서 헤더의 다른 것들을 밀어낸다. 자르고 `title` 로 남긴다.

   글자를 벌리지 않는다(`letter-spacing`). 「JSINI」 를 적던 자리라 벌려
   두었는데, 사람이 읽는 이름에 그것을 걸면 한글 자모가 성기게 보인다. */
.jsini-header__brand-text {
  font-weight: 600;
  max-width: 16rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.jsini-header__spacer { flex: 1; }

.jsini-header__login {
  padding: 0 0.6rem;
  color: var(--jsini-accent-text);
  text-decoration: none;
}

/* ── 헤더의 사용자 얼굴과 그 목록 ─────────────────────────

   눌러서 펼치는 판이다. DevExpress 드롭다운을 쓰지 않은 이유는
   `UserMenu.razor` 머리말에 있다 — 단추 얼굴이 사진이고 판 맨 위에
   이름·소속 두 줄이 들어가서, 항목만 넣을 수 있는 부품으로는 안 된다.

   자리를 잡는 것은 `position: absolute` 한 줄이다. 기준은 `.jsini-user` 고,
   그것이 헤더 오른쪽 끝이라 판을 오른쪽에 붙이면 화면 밖으로 나가지 않는다.
   ---------------------------------------------------------- */

.jsini-user {
  position: relative;
  display: flex;
  align-items: center;
  padding-left: 0.35rem;
}

.jsini-user__face {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--jsini-border);
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
}

.jsini-user__face:hover,
.jsini-user__face.is-on {
  border-color: var(--jsini-accent);
}

/* 얼굴은 공용 동그라미(`UserAvatar` · `jsini-face`)다. 단추가 테두리를 갖고
   동그라미는 그 안을 꽉 채운다. 사진이 없는 계정이 훨씬 많아 첫 글자가
   **비어 보이지 않게** 그 자리를 지킨다. `.jsini-face` 가 이 파일에서 더 뒤에
   있으므로 두 클래스를 겹쳐 적어야 이긴다. */
.jsini-face.jsini-user__avatar {
  --jsini-face-size: 100%;
  --jsini-face-bg: transparent;
  --jsini-face-ink: var(--jsini-text-muted);
  --jsini-face-initial-size: var(--jsini-fs-md);
}

/* 덮개는 헤더를 덮지 않는다 — 덮으면 방금 누른 얼굴로 다시 닫을 수 없다.
   테마 서랍과 같은 이유이고 같은 자리다. */
.jsini-user__scrim {
  position: fixed;
  inset: var(--jsini-header-height) 0 0 0;
  z-index: 1040;
}

.jsini-user__menu {
  position: absolute;
  top: calc(100% + 0.35rem);
  right: 0;
  z-index: 1041;
  width: 250px;
  max-height: calc(100vh - var(--jsini-header-height) - 1rem);
  overflow-y: auto;
  padding: 0.35rem 0;
  border: 1px solid var(--jsini-border);
  border-radius: 0.35rem;
  background: var(--jsini-bg);
  color: var(--jsini-text);
  box-shadow: 0 8px 24px rgb(0 0 0 / 18%);

  /* 닫혀 있을 때는 이 판을 아예 그리지 않는다(UserMenu.razor 참고).
     그래서 감췄다 보이는 상태가 없고, 여기에 적을 것도 없다. */
}

@media (max-width: 767px) {
  .jsini-user__menu {
    position: fixed;
    top: var(--jsini-header-height);
    left: 0;
    right: 0;
    bottom: 0;
    width: auto;
    border: none;
    border-radius: 0;
    overflow-y: auto;
    /* 모바일 하단 네비게이션바 높이만큼 여백을 주어 로그아웃 버튼 등이 가려지지 않게 한다. */
    padding-bottom: calc(var(--jsini-bottomnav-h) + env(safe-area-inset-bottom) + 0.35rem);
  }

  /* 환경설정에서 하단 띠를 숨긴 경우 여백을 되돌린다. */
  .jsini-shell--bottomnav-hidden .jsini-user__menu {
    padding-bottom: calc(env(safe-area-inset-bottom) + 0.35rem);
  }
}


.jsini-user__head {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.6rem 0.75rem;
  border-bottom: 1px solid var(--jsini-border);
}

.jsini-face.jsini-user__head-avatar {
  --jsini-face-size: 40px;
  --jsini-face-bg: color-mix(in srgb, currentcolor 10%, transparent);
  --jsini-face-ink: var(--jsini-text-muted);
  --jsini-face-initial-size: var(--jsini-fs-lg);
}

.jsini-user__head-text { min-width: 0; }

.jsini-user__head-name {
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.jsini-user__head-sub {
  font-size: var(--jsini-fs-xs);
  color: var(--jsini-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 묶음 제목. 항목이 열 개가 넘어서 없으면 한 덩어리로 읽힌다. */
.jsini-user__group {
  padding: 0.45rem 0.75rem 0.15rem;
  font-size: var(--jsini-fs-2xs);
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--jsini-text-muted);
}

.jsini-user__item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.4rem 0.75rem;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.jsini-user__item:hover {
  background: color-mix(in srgb, currentcolor 8%, transparent);
}

.jsini-user__item-text {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--jsini-fs-sm);
}

/* 아이콘 집안이 둘이다. 이 판에 박아 둔 것은 `jsini-icon-*`(크기를
   `--jsini-icon-size` 로 잰다)이고, 「개인화」 묶음은 **메뉴에서 온**
   `jsini-mi--*`(크기를 `em` 으로 잰다)다. 둘을 같은 자로 맞춰 둔다 —
   안 맞추면 그 묶음만 그림이 미묘하게 작아 줄이 들쭉날쭉해 보인다.
   아래 띠(`.jsini-bottom-nav__icon`)·칸 바꾸기 판과 같은 규칙이다. */
.jsini-user__item > .jsini-mi {
  width: var(--jsini-icon-size);
  height: var(--jsini-icon-size);
}

.jsini-user__sep {
  margin: 0.35rem 0;
  border-top: 1px solid var(--jsini-border);
}

/* 로그아웃만 색을 달리한다. 되돌리기 어려운 항목이라 나머지와 같아 보이면
   안 된다 — 목록 맨 끝이라 잘못 누르기도 쉽다. */
.jsini-user__item--out { color: #c0392b; }

.jsini-user__item--out [class^='jsini-icon-'] { background-color: #c0392b; }

/* ── 사이드바 ─────────────────────────────────────────────

   데모: 330px, 오른쪽에 안쪽 그림자와 1px 선.
   안쪽 그림자가 있어야 본문과 판이 갈려 보인다 — 선만으로는 밋밋하다.
   ---------------------------------------------------------- */

.jsini-shell__sidebar {
  grid-row: 2;
  grid-column: 1;
  background: var(--jsini-bg);
  border-right: 1px solid var(--jsini-border);
  box-shadow: inset -140px -8px 30px -100px rgb(0 0 0 / 8%);
  overflow: hidden;
  min-width: 0;
}

.jsini-shell--collapsed .jsini-shell__sidebar {
  border-right-width: 0;
  box-shadow: none;
}

/* 사이드바 안쪽 판. **폭을 스스로 정하지 않는다.**

   한동안 `--jsini-sidebar-width`(330px)를 그대로 물고 있었다. 격자가 폭을
   정하던 시절에는 같은 값이라 문제가 없었는데, 구분선을 끌어 폭을 바꾸게
   되면서 **판만 330px 에 남았다** — 사이드바를 520px 로 넓혀도 검색 칸과
   트리는 330px 자리에 그대로 있고 오른쪽 190px 이 빈 채로 남았다.

   지금 폭의 임자는 스플리터의 판이다(`DxSplitterPane.Size`). 여기서는
   받은 자리를 꽉 채운다. 좁은 화면에서는 사이드바가 본문을 덮는 고정
   덮개라 그때만 아래 미디어 쿼리가 폭을 다시 정한다. */
.jsini-sidebar {
  display: flex;
  flex-direction: column;
  height: 100%;
  width: 100%;
  min-width: 0;
}

/* ── 사이드바 검색 ───────────────────────────────────────

   **검색 칸은 우리가 그린다.** DxTreeView 의 `ShowFilterPanel` 은 트리 **안쪽**
   맨 위에 붙어 탭보다 아래로 내려가므로 쓰지 않는다. 거르는 일만 DevExpress 가 한다.

   거르는 일은 여전히 DevExpress 몫이다 — 여기서 받은 글자를 `FilterString`
   으로 넘기기만 한다(SidebarMenu.razor 참고).

   생김새는 DevExpress 데모의 "Find demo by keyword" 줄 그대로다.
   높이 3rem · 바탕 한 톤 짙게 · 아래 선 하나 · 테두리 없음.
   ---------------------------------------------------------- */

.jsini-sidebar__search {
  flex: 0 0 auto;
  height: 2.75rem !important;
  display: flex;
  align-items: center;
  padding: 0 0.75rem;
  background: var(--jsini-surface);
  border-bottom: 1px solid var(--jsini-border);
}

/* 줄 전체가 검색 칸처럼 보이게 편집기의 테두리·바탕을 걷어낸다. */
.jsini-sidebar__search-box {
  width: 100%;
  --dxbl-text-edit-bg: transparent;
  --dxbl-text-edit-border-color: transparent;
  --dxbl-text-edit-box-shadow: none;

  /* ── Fluent 은 **아래 선을 따로 그린다** ──────────────────

     Classic(Bootstrap 계열)에서는 위 세 줄로 깔끔했는데 Fluent 에서는
     칸 아래에 짙은 선 하나가 남아 있었다. 테두리 색을 안 따라간 것이 아니라
     **다른 변수로 그리기 때문**이다 —

         .dxbl-text-edit { border-bottom-color: var(--dxbl-text-edit-underline-color) }

     그 밑줄이 Fluent 입력칸의 표식이다. 상태마다 변수가 따로 있고, 고른
     상태에서는 테마가 `--…-underline-color` 를 `--…-underline-focused-color`
     로 갈아 끼우므로 **넷을 다 비워야** 어느 상태에서도 선이 안 남는다.
     (그래서 하나만 비웠을 때는 눌러 넣는 순간 선이 되돌아왔다.)

     [고른 상태에도 아무것도 그리지 않는다]

     한동안 선 대신 옅은 바탕(강조색 8%)을 깔아 「지금 여기에 있다」를
     알렸는데, 그 바탕이 둥근 네모로 보여서 **테두리가 되돌아온 것처럼**
     읽혔다. 이 칸에서 원하는 것은 깔끔함이고, 그래서 바탕도 뺐다.

     남는 단서는 **깜박이는 글자 자리(캐럿)와 지우기 단추**다. 자판으로만
     옮겨 다니는 사람에게는 약한 단서지만, 이 칸은 사이드바 맨 위에 하나뿐이라
     「어디에 있는지」를 잃을 자리가 아니다. 표 안의 편집칸처럼 여러 칸이
     나란한 자리에서는 이 방식을 그대로 쓰지 않는다. */
  --dxbl-text-edit-underline-color: transparent;
  --dxbl-text-edit-underline-hovered-color: transparent;
  --dxbl-text-edit-underline-active-color: transparent;
  --dxbl-text-edit-underline-focused-color: transparent;

  /* 테두리 쪽도 상태마다 변수가 따로다. **`--…-border-color` 하나만 비우면
     마우스를 올릴 때 네 변이 다시 나타난다** — 그때는 테마가 그 값을
     `--…-border-hovered-color` 로 갈아 끼우기 때문이다(누를 때는
     `-active-color`). 위 밑줄과 같은 얘기라 둘을 나란히 적어 둔다.

     [왜 갈아 끼우는 규칙과 다투지 않는가]

     그 규칙들은 `:not()` 을 넷 달아 자릿수가 다섯이라 우리가 이길 수 없다.
     대신 **그 규칙이 읽어 가는 변수**를 비운다 — 변수를 정하는 쪽은
     `.dxbl-text-edit` 하나뿐이고, app.css 가 테마보다 뒤에 꽂히므로
     같은 자릿수로도 우리 값이 남는다. */
  --dxbl-text-edit-border-hovered-color: transparent;
  --dxbl-text-edit-border-active-color: transparent;
}

/* ── 사이드바 탭 (메뉴 · 즐겨찾기) ────────────────────────

   `DxTabs` 를 쓰지 않는 이유는 SidebarMenu.razor 에 적어 두었다.
   고른 탭은 색이 아니라 **아래 선**으로 표시한다 — 색만으로 가르면
   색각 이상이 있는 사용자에게는 두 탭이 같아 보인다.
   ---------------------------------------------------------- */

.jsini-sidebar__tabs {
  flex: 0 0 auto;
  display: flex;
  border-bottom: 1px solid var(--jsini-border);
}

/* ── 좁은 사이드바에서는 탭이 그림만 남는다 ────────────────

   구분선으로 90px 까지 좁힐 수 있게 되면서 이 두 칸이 **가로로 넘쳤다** —
   「메뉴」와 「즐겨찾기 4」에 99px 이 필요한데 판이 89px 이었다. 넘친 것은
   잘리지 않고 판을 밀어서, 사이드바 안에 가로 스크롤이 생겼다.

   [미디어 질의가 아니라 **컨테이너 질의**다]

   기준이 화면 폭이 아니라 **사이드바 폭**이다. 사람이 구분선을 끌어 정하는
   값이라 화면 크기와 아무 상관이 없다 — 1920px 모니터에서도 90px 사이드바를
   쓸 수 있다. `container-type: inline-size` 를 사이드바에 걸어 두면 그 폭으로
   물을 수 있다.

   경계값 180px 은 **재서 정했다.** 「즐겨찾기 4」가 그림·글자·개수까지
   들어가려면 69px, 「메뉴」가 30px, 둘 사이 여백까지 99px 이다. 여유를 두어
   180px 아래에서 글자를 접는다 — 그 밑에서는 두 칸 중 하나가 반드시 잘린다.
   ---------------------------------------------------------- */

.jsini-sidebar {
  container-type: inline-size;
  container-name: jsini-sidebar;
}

/* 「메뉴」 칸의 그림은 좁을 때만 나온다. 넓을 때 함께 두면 글자와 그림이
   같은 말을 두 번 하고, 옆 칸(별표+즐겨찾기)과 무게가 어긋난다.

   **자릿수를 하나 올려 적는다.** 아이콘 묶음의 `[class^='jsini-icon-']` 이
   `display: inline-block` 을 걸고 그 규칙이 이 파일에서 **뒤에** 있어서,
   클래스 하나로 적으면 안 감춰진다 — 재어 보니 330px 에서도 그림이 나와
   있었다. */
.jsini-sidebar__tab > .jsini-sidebar__tab-icon { display: none; }

@container jsini-sidebar (max-width: 180px) {
  .jsini-sidebar__tab > .jsini-sidebar__tab-icon { display: inline-block; }

  .jsini-sidebar__tab-text,
  .jsini-sidebar__tab-count {
    display: none;
  }
}

.jsini-sidebar__tab {
  flex: 1;

/* min-width: 0 이 없으면 flex 칸이 줄지 않는다(기본 최소 크기는 내용 크기). */
  min-width: 0;
  overflow: hidden;

  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  padding: 0.6rem 0.5rem;
  border: 0;
  border-bottom: 2px solid transparent;
  background: none;
  color: inherit;
  font: inherit;

/* 사다리 단으로 적는다. rem 을 박으면 탭만 크기를 따라가지 않아 바로 아래 트리와 어긋난다. */
  font-size: var(--jsini-fs-sm);
  cursor: pointer;

/* 굵기 자리를 미리 잡는다. font-weight 만 바꾸면 고를 때마다 글자 폭이 늘어 탭이 흔들린다. */
  letter-spacing: 0.01em;
}

.jsini-sidebar__tab:hover {
  background: color-mix(in srgb, currentcolor 7%, transparent);
}

.jsini-sidebar__tab.is-on {
  border-bottom-color: var(--jsini-accent);
  color: var(--jsini-accent-text);
}

/* 담아 둔 개수. 탭 이름과 같은 크기면 어느 쪽이 이름인지 헷갈린다. */
.jsini-sidebar__tab-count {
  font-size: var(--jsini-fs-2xs);
  opacity: 0.7;
  flex-shrink: 0;   /* 개수가 글자에 밀려 사라지지 않는다 */
}

/* 탭 글자가 세로로 나열되지 않게 한다. 공간이 모자라면 잘리는 것이 세로로 쪼개지는 것보다 낫다. */
.jsini-sidebar__tab-text {
  white-space: nowrap;     /* 줄바꿈 금지 */
  overflow: hidden;        /* 넘치는 글자 숨김 */
  text-overflow: ellipsis; /* 잘릴 때 … 표시 */
  min-width: 0;            /* flex shrink 허용 */
}

/* 즐겨찾기가 비었을 때, 그리고 메뉴에 없는 즐겨찾기를 알릴 때. */
.jsini-sidebar__empty {
  margin: 0;
  padding: 1rem;
  font-size: var(--jsini-fs-sm);
  line-height: 1.6;
  color: var(--jsini-text-muted);
}

/* 사이드바 트리. 겉껍데기가 세 겹(dxbl-treeview · -container · scroll-viewer)이라 셋 모두에 flex 를 걸어야 검색·탭이 고정되고 트리만 구른다. */

.jsini-sidebar__tree {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 0;

/* 데모가 바꾸는 것은 이 둘과 최상위 글자 굵기뿐이다. 색과 선을 직접 칠하면 테마 스물둘이 어긋난다. */
  --dxbl-treeview-spacing-x: 0.5rem;
  --dxbl-treeview-spacing-y: 0.5rem;
  --dxbl-treeview-navigation-filter-bottom-spacing: 0;
}

.jsini-sidebar__tree > .dxbl-treeview-container {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* 트리 본문만 구른다. 트리 전체를 굴리면 검색 칸이 화면 밖으로 나간다. */
.jsini-sidebar__tree .dxbl-scroll-viewer {
  flex: 1;
  min-height: 0;
  padding: 0 var(--dxbl-treeview-spacing-x) var(--dxbl-treeview-spacing-y);

/* 스크롤 막대는 감춘다. 보이는 막대는 DevExpress 가 따로 그린 div 라 폭 변수(hover-width 포함)를 0 으로 돌린다. 구르기는 네이티브 overflow 그대로라 휠이 살아 있다. */
  --dxbl-scroll-viewer-vert-scroll-bar-width: 0;
  --dxbl-scroll-viewer-vert-scroll-bar-hover-width: 0;
}

/* 최상위(업무 묶음)만 굵게. aria-level 로 고른다. */
.jsini-sidebar__tree .dxbl-treeview-item-container[aria-level='1'] {
  --dxbl-treeview-font-weight: 600;
}

/* 메뉴 제목은 대문자로 바꾸지 않는다(한글에는 뜻이 없고 영문만 커 보인다). */
.jsini-sidebar__tree .dxbl-treeview-item-container {
  text-transform: none;
}

/* 검색 결과가 없을 때의 줄. 영어 기본 문구를 글자만 갈아 끼운다. 트리를 감추고 안내를 띄우면 있는 결과를 감추는 쪽으로 틀릴 수 있어 쓰지 않는다. */
.jsini-sidebar__tree .dxbl-treeview-empty-data {
  font-size: 0;
}

.jsini-sidebar__tree .dxbl-treeview-empty-data::after {
  content: "찾는 메뉴가 없습니다.";
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text-muted);
}

/* 메뉴 한 줄: [아이콘] 제목 … [펼침]. 펼침 단추는 DevExpress 가 제목 앞에 그린 것을 절대 자리로 오른쪽 끝에 옮긴다(order 로 보내면 강조 알약 바깥에 남는다). 자릿수가 같은 DevExpress 규칙을 이기려고 .dxbl-btn-tool 을 하나 더 붙인다. */

.jsini-sidebar__tree .dxbl-treeview-item-content {
  position: relative;
  /* li 와 li 사이 간격을 item-content 의 마진으로 줘서 li 끼리 붙어 있어도 시각적 간격이 유지되게 한다 */
  padding-bottom: 0;
  margin-bottom: var(--dxbl-treeview-item-spacing-y);
}

/* 선택·호버 배경이 li 전체 폭을 차지하도록 들여쓰기 폭을 0 으로 두고, 계층 들여쓰기는 padding-left 로 준다. */
.jsini-sidebar .jsini-sidebar__tree .dxbl-treeview-item > .dxbl-treeview-item-content > .dxbl-treeview-item-indent {
  min-width: 0;
  width: 0;
}

.jsini-sidebar .jsini-sidebar__tree .dxbl-treeview-item > .dxbl-treeview-item-content > .dxbl-treeview-item-container {
  width: 100%;
  padding-left: calc(0.5rem + var(--dxbl-treeview-item-indent, 0) * var(--dxbl-treeview-item-content-indent, 0.9375rem));
}

.jsini-sidebar__tree .dxbl-treeview-item-content > .dxbl-btn.dxbl-btn-tool {
  position: absolute;
  right: 0;

/* 단추 높이를 지킨 채 가운데에 놓는다(top:0 · bottom:0 으로 잡으면 단추가 줄 높이만큼 늘어나 화살표가 위로 치우친다). */
  top: 50%;
  transform: translateY(-50%);
}

/* 화살표 크기는 글자 크기를 따라간다. DevExpress 기본의 두꺼운 SVG 대신 선 두께 1.2px 의 꺽쇠 마스크로 바꾼다(이 값을 정하는 테마 규칙보다 자릿수를 높여 셋으로 적었다). */
.jsini-sidebar .jsini-sidebar__tree.dxbl-treeview {
  --dxbl-treeview-font-size: var(--jsini-fs-base);

  --dxbl-treeview-btn-icon-size: var(--dxbl-treeview-font-size);
}

/* 펼침·접힘 꺽쇠 공통 처리. 접힘은 오른쪽(>), 펼침은 아래(v). */
.dxbl-treeview .dxbl-treeview-item-content > .dxbl-btn.dxbl-btn-tool > .dxbl-image {
  width: var(--dxbl-treeview-btn-icon-size, 12px);
  height: var(--dxbl-treeview-btn-icon-size, 12px);
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 4l4 4-4 4'/%3E%3C/svg%3E") no-repeat center;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 4l4 4-4 4'/%3E%3C/svg%3E") no-repeat center;
  -webkit-mask-size: contain;
  mask-size: contain;
  transition: transform 0.15s ease;
}

.dxbl-treeview .dxbl-treeview-item-content > .dxbl-btn.dxbl-btn-tool > .dxbl-image > * {
  display: none !important;
}

/* 펼쳐진 상태: 아래쪽 꺽쇠 (v) */
.dxbl-treeview .dxbl-treeview-item-content > .dxbl-btn.dxbl-btn-tool[data-expanded="True"] > .dxbl-image,
.dxbl-treeview .dxbl-treeview-item-content > .dxbl-btn.dxbl-btn-tool[data-expanded="true"] > .dxbl-image,
.dxbl-treeview .dxbl-treeview-item-content > .dxbl-btn.dxbl-btn-tool:has(use[href*="collapse"]) > .dxbl-image,
.dxbl-treeview .dxbl-treeview-item-content > .dxbl-btn.dxbl-btn-tool[aria-expanded="true"] > .dxbl-image {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
}

/* 화살표가 앉을 자리. em 이라 화살표와 함께 커진다. 테마가 이 칸에 padding 을 약칭으로 걸어 두므로 선택자를 길게 적어야 우리 값이 이긴다. */
.jsini-sidebar .jsini-sidebar__tree .dxbl-treeview-item > .dxbl-treeview-item-content > .dxbl-treeview-item-container {
  padding-right: 1.9em;
}

/* 아이콘은 줄지 않고 제목만 줄어야 하므로 제목에 min-width: 0 을 준다. */
.jsini-sidebar__node {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}

/* 아이콘은 제목보다 한 톤 옅게 둔다. 같은 진하기면 목록을 훑을 때 그림과 글자가 서로를 가린다. */
.jsini-sidebar__node > .jsini-mi {
  opacity: 0.65;
}

.jsini-sidebar__tree .dxbl-treeview-item-container.dxbl-active .jsini-mi,
.jsini-sidebar__tree .dxbl-treeview-item-container:hover .jsini-mi {
  opacity: 1;
}

/* 긴 제목은 자른다(줄바꿈하면 트리가 들쭉날쭉해진다). 우리 템플릿의 제목과 DevExpress 가 그리는 제목 두 자리에 건다. */
.jsini-sidebar__node-title,
.jsini-sidebar__tree .dxbl-treeview-item-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 사이드바 바닥 띠(접속 정보). 세로 flex 에서 트리가 남는 높이를 먹고 띠는 바닥에 남는다. 높이는 고정하지 않는다(줄 수가 계정마다 다르다). */

.jsini-sidebar__foot {
  flex: 0 0 auto;
  padding: 0.6rem 0.75rem 0.7rem;
  border-top: 1px solid var(--jsini-border);
  background: var(--jsini-surface);

  /* 메뉴보다 한 단계 작게. 늘 보이는 띠라 본문과 같은 크기면 시선을 끈다. */
  font-size: var(--jsini-fs-xs);
  line-height: 1.45;
}

.jsini-sidebar__foot-top {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* 이름이 길면 자른다 — 나가기 단추를 밀어내면 안 된다. */
.jsini-sidebar__foot-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--jsini-fs-sm);
  font-weight: 600;
}

.jsini-sidebar__foot-out {
  flex: 0 0 auto;
  margin: 0;
}

/* 나가기는 DxButton 을 쓰지 않는다. 폼 제출 단추여야 회로가 끊긴 뒤에도 눌린다. */
.jsini-sidebar__foot-button {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.2rem 0.5rem;
  border: 1px solid var(--jsini-border);
  border-radius: 0.25rem;
  background: var(--jsini-bg);
  color: var(--jsini-text-muted);
  font: inherit;
  font-size: var(--jsini-fs-2xs);
  cursor: pointer;
}

.jsini-sidebar__foot-button:hover {
  color: var(--jsini-text);
  background: var(--jsini-surface);
}

.jsini-sidebar__foot-line {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--jsini-text-muted);
}

.jsini-sidebar__foot-ip {
  display: flex;
  gap: 0.3rem;

  /* 주소는 숫자와 점이라 고정폭이 읽기 쉽다. */
  font-variant-numeric: tabular-nums;
}

.jsini-sidebar__foot-label {
  flex: 0 0 auto;
  opacity: 0.75;
}

/* 휴대폰·태블릿에서는 이 띠를 통째로 감춘다. 좁은 화면의 사이드바는 본문을 덮고 곧 접히는데 그 판의 바닥 네 줄이 메뉴를 줄인다. 나가기는 헤더의 프로필 메뉴가 맡는다. 경계 1023px 은 MainLayout 의 구분과 같다. */
@media (max-width: 1023px) {
  .jsini-sidebar__foot {
    display: none;
  }
}

/* 아이콘 고르개(IconPicker). 메뉴 수정 창 안에 들어가므로 떠 있는 판을 쓰지 않는다. */

.jsini-iconpick__row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* 지금 값의 그림. 고르기 전에도 보여야 저장된 이름이 맞는지 확인할 수 있다. */
.jsini-iconpick__preview {
  flex: 0 0 auto;
  width: 1.5rem;
  height: 1.5rem;
}

.jsini-iconpick__text { flex: 1; min-width: 0; }

/* 펼친 판은 창 바탕보다 한 단 눌러 앉은 색이라야 창 안의 판으로 읽힌다(--jsini-surface 는 밝은 쪽에서 창과 같은 흰색이 된다). */
.jsini-iconpick__panel {
  margin-top: 0.5rem;
  padding: 0.6rem;
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-md);
  background: var(--jsini-surface-muted);
}

.jsini-iconpick__search { width: 100%; }

/* 격자는 auto-fill 로 폭을 따라간다. 높이를 묶어 여기만 구른다(묶지 않으면 아래의 저장 단추가 멀어진다). */
.jsini-iconpick__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(2.4rem, 1fr));
  gap: 0.25rem;
  max-height: 13rem;
  overflow-y: auto;
  margin-top: 0.5rem;
}

.jsini-iconpick__cell {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 0.25rem;
  background: transparent;
  color: inherit;
  cursor: pointer;
}

.jsini-iconpick__cell > span {
  width: 1.35rem;
  height: 1.35rem;
}

.jsini-iconpick__cell:hover { background: var(--jsini-theme-hover); }

/* 고른 칸은 면으로 칠한다(테두리를 두르면 1px 커져 격자가 들썩인다). 면 색은 표의 고른 줄과 같은 값이다. */
.jsini-iconpick__cell.is-on {
  background: color-mix(in srgb, var(--jsini-accent) 18%, var(--jsini-surface-raised));
  border-color: var(--jsini-accent);
}

.jsini-iconpick__empty,
.jsini-iconpick__note {
  margin: 0.5rem 0 0;
  font-size: var(--jsini-fs-2xs);
  color: var(--jsini-text-muted);
}

/* ── 본문 쪽 ─────────────────────────────────────────────── */

.jsini-shell__main {
  grid-row: 2;
  grid-column: 2;
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
  transition: margin-right 0.22s cubic-bezier(0.2, 0, 0, 1);
}

@media (min-width: 768px) {
  body.jsini-has-pinned-drawer .jsini-shell__main {
    margin-right: var(--jsini-drawer-width, min(26rem, 100vw));
  }
}

body.jsini-drawer-resizing .jsini-shell__main {
  transition: none !important;
}

body.jsini-drawer-resizing {
  user-select: none !important;
  cursor: col-resize !important;
}

/* 본문 여백은 3px 다. 넓은 화면과 좁은 화면이 같다. 읽는 데 필요한 틈은 안쪽 판이 이미 주므로 바깥에서 한 겹 더 두지 않는다. 휴대폰에서 아래쪽만 하단 띠 높이만큼 비운다(여백이 아니라 자리 비킴이다). */
.jsini-shell__content {
  flex: 1;
  padding: var(--jsini-shell-pad-top) var(--jsini-shell-pad-inline)
           var(--jsini-shell-pad-bottom);
  overflow: auto;

/* 화면을 옮기는 동안의 덮개(.jsini-pageload)가 여기에 자리를 잡는다. */
  position: relative;
}

/* 본문들의 자리. 「화면 유지」 화면은 저마다 제 본문을 갖고(KeepAliveScreens), 안 보이는 본문은 같은 자리에 겹쳐 세워 둔다. 덮개(.jsini-pageload)도 여기 깔린다. */
.jsini-shell__stack {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  position: relative;
}

/* 떠난 「화면 유지」 화면. display: none 으로 감추지 않는다 — 상자가 없어지면 구른 자리가 0 으로 돌아가고 표가 치수를 다시 잰다. 같은 크기로 겹쳐 두고 그리지만 않는다. content-visibility 가 있어야 안쪽에서 visibility: visible 을 쓴 부품이 비쳐 나오지 않는다. */
.jsini-shell__content--parked {
  position: absolute;
  inset: 0;
  visibility: hidden;
  content-visibility: hidden;
  pointer-events: none;
}

/* 탭 줄. 데모에는 없지만 브레드크럼 줄과 같은 높이·바탕으로 맞춰 두 줄이 한 덩어리로 보이게 한다. */

.jsini-tabs {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  height: 2.5rem;
  padding: 0 0.5rem;
  background: var(--jsini-surface);
  border-bottom: 1px solid var(--jsini-border);
}

/* 탭이 많아지면 가로로 넘긴다(줄바꿈하면 본문이 그만큼 좁아진다). */
.jsini-tabs__list {
  display: flex;
  flex: 1;
  gap: 0.15rem;
  height: 100%;
  align-items: stretch;
  overflow-x: auto;
  scrollbar-width: none;
}

.jsini-tabs__list::-webkit-scrollbar { display: none; }

.jsini-tab {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  max-width: 190px;
  border-bottom: 2px solid transparent;
}

.jsini-tab:hover {
  background: color-mix(in srgb, currentcolor 6%, transparent);
}

/* 켜진 탭은 아래 선으로 표시한다. 배경을 칠하면 테마마다 색이 부딪친다. */
.jsini-tab--active {
  border-bottom-color: var(--jsini-accent);
}

/* 글자 크기를 정하지 않는다 — 테마가 주는 기본값을 쓴다(파일 첫머리 참고). */
.jsini-tab__link {
  display: block;
  padding: 0 0.3rem 0 0.65rem;
  line-height: 2.4rem;
  color: inherit;
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.jsini-tab--active .jsini-tab__link { font-weight: 600; }

.jsini-tab__close {
  border: 0;
  background: none;
  cursor: pointer;
  padding: 0 0.45rem 0 0.15rem;
  opacity: 0.5;
  font: inherit;
  line-height: 1;
  color: inherit;
}

.jsini-tab__close:hover { opacity: 1; }

/* 넘칠 때 서는 단추들(앞·뒤 화살표 · 열린 화면 목록). 넘칠 때만 그려지고, flex 0 0 auto 로 묶어 단추가 먼저 쪼그라들지 않게 한다. */

.jsini-tabs__nav,
#jsini-tabs-all,
#jsini-tabs-more {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
}

/* 끝에 닿으면 꺼진다. 테마마다 바탕이 달라 흐림만으로는 구분이 안 되므로 한 단 더 옅게 하고 손 모양을 지운다. */
.jsini-tabs__nav .dxbl-btn:disabled {
  opacity: 0.3;
  cursor: default;
}

/* 브레드크럼. 헤더 안, 로고 옆에 얹는다(본문 위에 제 줄을 두면 9rem 이 사라진다). 제 배경·아래 선·정해진 높이가 없고, 구분은 작은 삼각형이다. */

.jsini-crumbs {
  display: flex;
  align-items: center;

/* 늘이지 않고 줄기만 한다. 늘이면 헤더의 빈자리를 먹어 도구 단추가 가운데로 딸려 온다. min-width 0 이 있어야 실제로 줄어든다. */
  flex: 0 1 auto;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}

/* 로고·권한 그룹을 가르는 세로 선. 줄지 않게만 묶는다(1px 짜리라 좁아지면 0 으로 눌려 사라진다). */
.jsini-crumbs__lead {
  flex: 0 0 auto;

/* 왼쪽 간격은 브랜드 묶음의 gap 과 같은 값이다(이 nav 는 그 묶음 밖이라 gap 이 안 걸린다). */
  margin: 0 0.8rem 0 0.75rem;
}

.jsini-crumbs::-webkit-scrollbar { display: none; }

/* 줄지 않게 묶는다. 눌리면 칸마다 조금씩 찌그러져 글자가 잘리고, 넘친 만큼은 가로 스크롤이 받는다. */
.jsini-crumbs__item {
  display: inline-block;
  flex: 0 0 auto;
  margin-right: 0.8rem;
  white-space: nowrap;
  opacity: 0.6;
  color: inherit;
  text-decoration: none;
}

/* 묶음과 현재 화면은 button 이다(눌러서 사이드바를 그 자리로 편다). 생김새는 링크와 같아야 무엇이 눌리는 칸인지 헷갈리지 않는다. */
button.jsini-crumbs__item {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  line-height: inherit;
  cursor: pointer;
}

/* 누를 수 있는 칸은 마우스를 올리면 또렷해진다. 이것이 눌린다는 유일한 표시다. */
a.jsini-crumbs__item:hover,
button.jsini-crumbs__item:hover { opacity: 1; }

/* 키보드로 짚었을 때. 헤더가 어두운 테마에서도 보이게 현재 글자색으로 긋는다. */
.jsini-crumbs__item:focus-visible {
  opacity: 1;
  outline: 2px solid currentcolor;
  outline-offset: 2px;
  border-radius: 2px;
}

.jsini-crumbs__item--current {
  opacity: 1;
  font-weight: 600;
}

/* 삼각형 구분자. 글꼴에 없는 글자를 쓰지 않아 어느 환경에서나 같게 보인다. */
.jsini-crumbs__sep {
  display: inline-block;
  flex: 0 0 auto;
  width: 0;
  height: 0;
  margin-right: 0.8rem;
  border: 5px solid transparent;
  border-left-color: currentcolor;
  opacity: 0.45;
}

/* 아이콘은 글꼴을 싣지 않고 그림 문자를 마스크로 쓴다. 테마 색을 그대로 따라오고, 컬러 이모지는 다크 모드에서 혼자 튀기 때문이다. */

[class^='jsini-icon-'],
[class*=' jsini-icon-'] {
  display: inline-block;
  width: var(--jsini-icon-size);
  height: var(--jsini-icon-size);

/* 글자보다 큰 그림이라 middle 로 세운다(밑선 기준이면 머리가 무거워 보인다). */
  vertical-align: middle;
  background-color: currentColor;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

.jsini-icon-blank { background-color: transparent; }

.jsini-icon-menu {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2 3.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5m0 4a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5m0 4a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2 3.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5m0 4a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5m0 4a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5'/%3E%3C/svg%3E");
}

.jsini-icon-home {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.313 1.262a1 1 0 0 1 1.374 0l4.844 4.579c.3.283.469.678.469 1.09v5.57a1.5 1.5 0 0 1-1.5 1.5h-2A1.5 1.5 0 0 1 9 12.5V10a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v2.5A1.5 1.5 0 0 1 5.5 14h-2A1.5 1.5 0 0 1 2 12.5V6.93c0-.412.17-.807.47-1.09zM8 1.988l-4.844 4.58A.5.5 0 0 0 3 6.93v5.57a.5.5 0 0 0 .5.5h2a.5.5 0 0 0 .5-.5V10a1.5 1.5 0 0 1 1.5-1.5h1A1.5 1.5 0 0 1 10 10v2.5a.5.5 0 0 0 .5.5h2a.5.5 0 0 0 .5-.5V6.93a.5.5 0 0 0-.156-.363z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.313 1.262a1 1 0 0 1 1.374 0l4.844 4.579c.3.283.469.678.469 1.09v5.57a1.5 1.5 0 0 1-1.5 1.5h-2A1.5 1.5 0 0 1 9 12.5V10a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v2.5A1.5 1.5 0 0 1 5.5 14h-2A1.5 1.5 0 0 1 2 12.5V6.93c0-.412.17-.807.47-1.09zM8 1.988l-4.844 4.58A.5.5 0 0 0 3 6.93v5.57a.5.5 0 0 0 .5.5h2a.5.5 0 0 0 .5-.5V10a1.5 1.5 0 0 1 1.5-1.5h1A1.5 1.5 0 0 1 10 10v2.5a.5.5 0 0 0 .5.5h2a.5.5 0 0 0 .5-.5V6.93a.5.5 0 0 0-.156-.363z'/%3E%3C/svg%3E");
}

/* 접었다 펴는 판·검색 조건·바로가기의 공통 꺾쇠. 펴지면 CSS 가 뒤집는다(방향별로 따로 두면 각도와 굵기가 어긋난다). */
.jsini-icon-chevron-down {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
}

/* 탭 줄 양끝 화살표. 위 꺾쇠와 굵기·모서리가 같아야 해서 chevron 을 돌려 쓰지 않고 같은 획으로 다시 그렸다. */
.jsini-icon-chevron-left {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10 4L6 8l4 4'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10 4L6 8l4 4'/%3E%3C/svg%3E");
}

.jsini-icon-chevron-right {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 4l4 4-4 4'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 4l4 4-4 4'/%3E%3C/svg%3E");
}

/* 「열린 화면」 목록 단추. 햄버거 그림은 사이드바 여닫기에 쓰이므로 점을 붙여 목록으로 읽히게 한다. */
.jsini-icon-tab-list {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.75 4.5a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0M6 4.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5M3.75 8a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0M6 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 6 8m-2.25 3.5a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0M6 11.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.75 4.5a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0M6 4.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5M3.75 8a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0M6 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 6 8m-2.25 3.5a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0M6 11.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5'/%3E%3C/svg%3E");
}

/* 표 관리 칸의 아이콘 단추. 글자 없이 그림만 쓴다(까닭은 .commgrd__actions 머리말). 출처는 DevExpress Icon Library 다. */

/* 첨부 고르기 단추는 글자 없는 그림이라 그림이 곧 이름이다. 읽어 주는 글은 title 에 있다. */
.jsini-icon-paperclip {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.5 3a2.5 2.5 0 0 1 5 0v9a1.5 1.5 0 0 1-3 0V5a.5.5 0 0 1 1 0v7a.5.5 0 0 0 1 0V3a1.5 1.5 0 1 0-3 0v9a2.5 2.5 0 0 0 5 0V5a.5.5 0 0 1 1 0v7a3.5 3.5 0 1 1-7 0z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.5 3a2.5 2.5 0 0 1 5 0v9a1.5 1.5 0 0 1-3 0V5a.5.5 0 0 1 1 0v7a.5.5 0 0 0 1 0V3a1.5 1.5 0 1 0-3 0v9a2.5 2.5 0 0 0 5 0V5a.5.5 0 0 1 1 0v7a3.5 3.5 0 1 1-7 0z'/%3E%3C/svg%3E");
}

/* 소속·역할을 뗀다 (subtract) */
.jsini-icon-subtract {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 8a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9A.5.5 0 0 1 3 8'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 8a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9A.5.5 0 0 1 3 8'/%3E%3C/svg%3E");
}

/* 승인 · 완료 (checkmark_circle) */
.jsini-icon-check-circle {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 2a6 6 0 1 1 0 12A6 6 0 0 1 8 2m0 1a5 5 0 1 0 0 10A5 5 0 0 0 8 3m-.75 6.042 2.87-2.878a.5.5 0 0 1 .766.637l-.058.07-3.224 3.232a.5.5 0 0 1-.638.059l-.07-.058-1.75-1.75a.5.5 0 0 1 .638-.765l.07.057zl2.87-2.878z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 2a6 6 0 1 1 0 12A6 6 0 0 1 8 2m0 1a5 5 0 1 0 0 10A5 5 0 0 0 8 3m-.75 6.042 2.87-2.878a.5.5 0 0 1 .766.637l-.058.07-3.224 3.232a.5.5 0 0 1-.638.059l-.07-.058-1.75-1.75a.5.5 0 0 1 .638-.765l.07.057zl2.87-2.878z'/%3E%3C/svg%3E");
}

/* 거절 (close_circle) */
.jsini-icon-close-circle {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 2a6 6 0 1 1 0 12A6 6 0 0 1 8 2m0 1a5 5 0 1 0 0 10A5 5 0 0 0 8 3M5.839 5.966l.057-.07a.5.5 0 0 1 .638-.057l.07.057L8 7.293l1.396-1.397a.5.5 0 0 1 .638-.057l.07.057a.5.5 0 0 1 .057.638l-.057.07L8.707 8l1.397 1.396a.5.5 0 0 1 .057.638l-.057.07a.5.5 0 0 1-.638.057l-.07-.057L8 8.707l-1.396 1.397a.5.5 0 0 1-.638.057l-.07-.057a.5.5 0 0 1-.057-.638l.057-.07L7.293 8 5.896 6.604a.5.5 0 0 1-.057-.638l.057-.07z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 2a6 6 0 1 1 0 12A6 6 0 0 1 8 2m0 1a5 5 0 1 0 0 10A5 5 0 0 0 8 3M5.839 5.966l.057-.07a.5.5 0 0 1 .638-.057l.07.057L8 7.293l1.396-1.397a.5.5 0 0 1 .638-.057l.07.057a.5.5 0 0 1 .057.638l-.057.07L8.707 8l1.397 1.396a.5.5 0 0 1 .057.638l-.057.07a.5.5 0 0 1-.638.057l-.07-.057L8 8.707l-1.396 1.397a.5.5 0 0 1-.638.057l-.07-.057a.5.5 0 0 1-.057-.638l.057-.07L7.293 8 5.896 6.604a.5.5 0 0 1-.057-.638l.057-.07z'/%3E%3C/svg%3E");
}

/* 발인 · 출상 처리 (arrow_export_right) */
.jsini-icon-export {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M1.5 3a.5.5 0 0 1 .5.5V12a.5.5 0 1 1-1 0V3.5a.5.5 0 0 1 .5-.5m9.146.646a.5.5 0 0 1 .708 0l3.5 3.5a.5.5 0 0 1 0 .707l-3.5 3.5a.5.5 0 0 1-.708-.707L13.293 8H4.5a.5.5 0 0 1 0-1h8.793l-2.647-2.647a.5.5 0 0 1 0-.707'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M1.5 3a.5.5 0 0 1 .5.5V12a.5.5 0 1 1-1 0V3.5a.5.5 0 0 1 .5-.5m9.146.646a.5.5 0 0 1 .708 0l3.5 3.5a.5.5 0 0 1 0 .707l-3.5 3.5a.5.5 0 0 1-.708-.707L13.293 8H4.5a.5.5 0 0 1 0-1h8.793l-2.647-2.647a.5.5 0 0 1 0-.707'/%3E%3C/svg%3E");
}

/* 영정 사진 (image) */
.jsini-icon-image {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M11.5 5.502a1.002 1.002 0 1 1-2.004 0 1.002 1.002 0 0 1 2.004 0M2 4.5A2.5 2.5 0 0 1 4.5 2h7A2.5 2.5 0 0 1 14 4.5v7a2.5 2.5 0 0 1-2.5 2.5h-7A2.5 2.5 0 0 1 2 11.5zM4.5 3A1.5 1.5 0 0 0 3 4.5v7c0 .232.052.45.146.647l3.652-3.652a1.7 1.7 0 0 1 2.404 0l3.652 3.652c.094-.196.146-.415.146-.647v-7A1.5 1.5 0 0 0 11.5 3zm7.647 9.854L8.495 9.202a.7.7 0 0 0-.99 0l-3.652 3.652c.196.094.415.146.647.146h7c.232 0 .45-.053.647-.146'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M11.5 5.502a1.002 1.002 0 1 1-2.004 0 1.002 1.002 0 0 1 2.004 0M2 4.5A2.5 2.5 0 0 1 4.5 2h7A2.5 2.5 0 0 1 14 4.5v7a2.5 2.5 0 0 1-2.5 2.5h-7A2.5 2.5 0 0 1 2 11.5zM4.5 3A1.5 1.5 0 0 0 3 4.5v7c0 .232.052.45.146.647l3.652-3.652a1.7 1.7 0 0 1 2.404 0l3.652 3.652c.094-.196.146-.415.146-.647v-7A1.5 1.5 0 0 0 11.5 3zm7.647 9.854L8.495 9.202a.7.7 0 0 0-.99 0l-3.652 3.652c.196.094.415.146.647.146h7c.232 0 .45-.053.647-.146'/%3E%3C/svg%3E");
}

/* 화면을 끈다 (visibility_eye_off) */
.jsini-icon-eye-off {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='m10.12 10.827 4.026 4.027a.5.5 0 0 0 .708-.708l-13-13a.5.5 0 1 0-.708.708l3.23 3.23A6 6 0 0 0 3.2 6.182a6.7 6.7 0 0 0-1.117 1.982c-.021.061-.047.145-.047.145l-.018.062s-.076.497.355.611a.5.5 0 0 0 .611-.355l.001-.003.008-.025.035-.109a5.7 5.7 0 0 1 .945-1.674 5 5 0 0 1 1.124-1.014l1.578 1.578a2.5 2.5 0 1 0 3.446 3.446m-.74-.74A1.5 1.5 0 1 1 7.413 8.12zM6.32 4.2l.854.854Q7.564 5 8 5c2.044 0 3.286.912 4.028 1.817a5.7 5.7 0 0 1 .945 1.674q.025.073.035.109l.008.025v.003l.001.001a.5.5 0 0 0 .966-.257v-.003l-.001-.004-.004-.013a2 2 0 0 0-.06-.187 6.7 6.7 0 0 0-1.117-1.982C11.905 5.089 10.396 4 8.002 4c-.618 0-1.177.072-1.681.199'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='m10.12 10.827 4.026 4.027a.5.5 0 0 0 .708-.708l-13-13a.5.5 0 1 0-.708.708l3.23 3.23A6 6 0 0 0 3.2 6.182a6.7 6.7 0 0 0-1.117 1.982c-.021.061-.047.145-.047.145l-.018.062s-.076.497.355.611a.5.5 0 0 0 .611-.355l.001-.003.008-.025.035-.109a5.7 5.7 0 0 1 .945-1.674 5 5 0 0 1 1.124-1.014l1.578 1.578a2.5 2.5 0 1 0 3.446 3.446m-.74-.74A1.5 1.5 0 1 1 7.413 8.12zM6.32 4.2l.854.854Q7.564 5 8 5c2.044 0 3.286.912 4.028 1.817a5.7 5.7 0 0 1 .945 1.674q.025.073.035.109l.008.025v.003l.001.001a.5.5 0 0 0 .966-.257v-.003l-.001-.004-.004-.013a2 2 0 0 0-.06-.187 6.7 6.7 0 0 0-1.117-1.982C11.905 5.089 10.396 4 8.002 4c-.618 0-1.177.072-1.681.199'/%3E%3C/svg%3E");
}

/* 새 판을 받아 간다 (arrow_download) */
.jsini-icon-download {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 13h9a.5.5 0 0 1 .09.992L12.5 14h-9a.5.5 0 0 1-.09-.992zh9zM7.91 1.008 8 1a.5.5 0 0 1 .492.41l.008.09v8.792l2.682-2.681a.5.5 0 0 1 .638-.058l.07.058a.5.5 0 0 1 .057.638l-.058.069-3.535 3.536a.5.5 0 0 1-.638.057l-.07-.057-3.535-3.536a.5.5 0 0 1 .638-.765l.069.058L7.5 10.292V1.5a.5.5 0 0 1 .41-.492L8 1z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 13h9a.5.5 0 0 1 .09.992L12.5 14h-9a.5.5 0 0 1-.09-.992zh9zM7.91 1.008 8 1a.5.5 0 0 1 .492.41l.008.09v8.792l2.682-2.681a.5.5 0 0 1 .638-.058l.07.058a.5.5 0 0 1 .057.638l-.058.069-3.535 3.536a.5.5 0 0 1-.638.057l-.07-.057-3.535-3.536a.5.5 0 0 1 .638-.765l.069.058L7.5 10.292V1.5a.5.5 0 0 1 .41-.492L8 1z'/%3E%3C/svg%3E");
}

/* 즐겨찾기. 빈 별과 채운 별은 모양이 달라 색을 못 보는 사람에게도 갈린다. */
.jsini-icon-star {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.194 2.102a.9.9 0 0 1 1.614 0l1.521 3.082 3.401.494a.9.9 0 0 1 .5 1.535l-2.462 2.4.581 3.387a.9.9 0 0 1-1.306.948L8.001 12.35l-3.042 1.6a.9.9 0 0 1-1.306-.95l.58-3.387-2.46-2.399a.9.9 0 0 1 .499-1.535l3.4-.494zM8 2.726 6.546 5.673a.9.9 0 0 1-.677.493l-3.253.472L4.97 8.933a.9.9 0 0 1 .258.796l-.555 3.24 2.91-1.53a.9.9 0 0 1 .837 0l2.91 1.53-.556-3.24a.9.9 0 0 1 .258-.796l2.354-2.295-3.253-.472a.9.9 0 0 1-.677-.493z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.194 2.102a.9.9 0 0 1 1.614 0l1.521 3.082 3.401.494a.9.9 0 0 1 .5 1.535l-2.462 2.4.581 3.387a.9.9 0 0 1-1.306.948L8.001 12.35l-3.042 1.6a.9.9 0 0 1-1.306-.95l.58-3.387-2.46-2.399a.9.9 0 0 1 .499-1.535l3.4-.494zM8 2.726 6.546 5.673a.9.9 0 0 1-.677.493l-3.253.472L4.97 8.933a.9.9 0 0 1 .258.796l-.555 3.24 2.91-1.53a.9.9 0 0 1 .837 0l2.91 1.53-.556-3.24a.9.9 0 0 1 .258-.796l2.354-2.295-3.253-.472a.9.9 0 0 1-.677-.493z'/%3E%3C/svg%3E");
}

.jsini-icon-star-on {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.194 2.102a.9.9 0 0 1 1.614 0l1.521 3.082 3.401.494a.9.9 0 0 1 .5 1.535l-2.462 2.4.581 3.387a.9.9 0 0 1-1.306.948L8.001 12.35l-3.042 1.6a.9.9 0 0 1-1.306-.95l.58-3.387-2.46-2.399a.9.9 0 0 1 .499-1.535l3.4-.494zM8 2.726 6.546 5.673a.9.9 0 0 1-.677.493l-3.253.472L4.97 8.933a.9.9 0 0 1 .258.796l-.555 3.24 2.91-1.53a.9.9 0 0 1 .837 0l2.91 1.53-.556-3.24a.9.9 0 0 1 .258-.796l2.354-2.295-3.253-.472a.9.9 0 0 1-.677-.493z'/%3E%3Cpath d='M8.002 2.726 6.548 5.673a.9.9 0 0 1-.678.493l-3.253.472 2.354 2.295a.9.9 0 0 1 .259.796l-.556 3.24 2.91-1.53a.9.9 0 0 1 .837 0l2.91 1.53-.556-3.24a.9.9 0 0 1 .259-.796l2.353-2.295-3.252-.472a.9.9 0 0 1-.678-.493z' opacity='.4'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.194 2.102a.9.9 0 0 1 1.614 0l1.521 3.082 3.401.494a.9.9 0 0 1 .5 1.535l-2.462 2.4.581 3.387a.9.9 0 0 1-1.306.948L8.001 12.35l-3.042 1.6a.9.9 0 0 1-1.306-.95l.58-3.387-2.46-2.399a.9.9 0 0 1 .499-1.535l3.4-.494zM8 2.726 6.546 5.673a.9.9 0 0 1-.677.493l-3.253.472L4.97 8.933a.9.9 0 0 1 .258.796l-.555 3.24 2.91-1.53a.9.9 0 0 1 .837 0l2.91 1.53-.556-3.24a.9.9 0 0 1 .258-.796l2.354-2.295-3.253-.472a.9.9 0 0 1-.677-.493z'/%3E%3Cpath d='M8.002 2.726 6.548 5.673a.9.9 0 0 1-.678.493l-3.253.472 2.354 2.295a.9.9 0 0 1 .259.796l-.556 3.24 2.91-1.53a.9.9 0 0 1 .837 0l2.91 1.53-.556-3.24a.9.9 0 0 1 .259-.796l2.353-2.295-3.252-.472a.9.9 0 0 1-.678-.493z' opacity='.4'/%3E%3C/svg%3E");
}

/* 담긴 상태만 색으로 구분한다. 모양까지 바꾸면 두 아이콘을 관리하게 된다. */
.jsini-icon-star-on { background-color: #e2a400; }

/* 표 아래 띠의 칸별 검색 단추 둘. 깔때기와, 조건이 걸린 깔때기. */
/* 아이콘은 DevExpress Icon Library 의 모양을 옮겨 적었다(클래스로만 받는 자리라 파일을 참조할 수 없다). 색과 크기는 currentColor 와 --jsini-icon-size 가 쥔다. 라이브러리가 정식 배포되면 이 파일만 고친다. */
.jsini-icon-filter {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='m5.707 8.016.707-.745c.375.395.586.932.586 1.49v3.13l2 1.053V8.763c0-.56.21-1.096.586-1.491L12 4.726V3.054H4v1.672L6.414 7.27zM3.293 5.471l2.414 2.545c.188.198.293.466.293.746v3.128c0 .4.214.765.553.943l2 1.054c.665.35 1.447-.159 1.447-.943V8.763c0-.28.105-.548.293-.746l2.414-2.545c.188-.197.293-.466.293-.745V3.054C13 2.472 12.552 2 12 2H4c-.552 0-1 .472-1 1.054v1.672c0 .28.105.548.293.745'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='m5.707 8.016.707-.745c.375.395.586.932.586 1.49v3.13l2 1.053V8.763c0-.56.21-1.096.586-1.491L12 4.726V3.054H4v1.672L6.414 7.27zM3.293 5.471l2.414 2.545c.188.198.293.466.293.746v3.128c0 .4.214.765.553.943l2 1.054c.665.35 1.447-.159 1.447-.943V8.763c0-.28.105-.548.293-.746l2.414-2.545c.188-.197.293-.466.293-.745V3.054C13 2.472 12.552 2 12 2H4c-.552 0-1 .472-1 1.054v1.672c0 .28.105.548.293.745'/%3E%3C/svg%3E");
}

.jsini-icon-filter-off {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M9.996 2a1 1 0 0 1 1 1v2.022a5.6 5.6 0 0 0-1 .001V3h-8v3.293L4.703 9l.067.073a1 1 0 0 1 .226.634v2.44l.29.105c.155.46.368.895.633 1.292l-1.427-.514a.75.75 0 0 1-.496-.706V9.707l-2.78-2.78a.75.75 0 0 1-.22-.53V3a1 1 0 0 1 1-1z'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M10.5 15a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9m1.854-6.354a.5.5 0 0 1 0 .708L11.207 10.5l1.147 1.146a.5.5 0 0 1-.708.708L10.5 11.207l-1.146 1.147a.5.5 0 0 1-.708-.708L9.793 10.5 8.646 9.354a.5.5 0 1 1 .708-.708L10.5 9.793l1.146-1.147a.5.5 0 0 1 .708 0'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M9.996 2a1 1 0 0 1 1 1v2.022a5.6 5.6 0 0 0-1 .001V3h-8v3.293L4.703 9l.067.073a1 1 0 0 1 .226.634v2.44l.29.105c.155.46.368.895.633 1.292l-1.427-.514a.75.75 0 0 1-.496-.706V9.707l-2.78-2.78a.75.75 0 0 1-.22-.53V3a1 1 0 0 1 1-1z'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M10.5 15a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9m1.854-6.354a.5.5 0 0 1 0 .708L11.207 10.5l1.147 1.146a.5.5 0 0 1-.708.708L10.5 11.207l-1.146 1.147a.5.5 0 0 1-.708-.708L9.793 10.5 8.646 9.354a.5.5 0 1 1 .708-.708L10.5 9.793l1.146-1.147a.5.5 0 0 1 .708 0'/%3E%3C/svg%3E");
}

/* 조회 영역의 단추 둘. 돋보기와 되돌리기. */
.jsini-icon-search {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M9.309 10.016a4.5 4.5 0 1 1 .707-.707l3.838 3.837a.5.5 0 0 1-.708.708zM10 6.5a3.5 3.5 0 1 0-7 0 3.5 3.5 0 0 0 7 0'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M9.309 10.016a4.5 4.5 0 1 1 .707-.707l3.838 3.837a.5.5 0 0 1-.708.708zM10 6.5a3.5 3.5 0 1 0-7 0 3.5 3.5 0 0 0 7 0'/%3E%3C/svg%3E");
}

.jsini-icon-undo {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 2.5a.5.5 0 0 1 1 0v3.843l3.171-3.171a4 4 0 0 1 5.657 5.656l-5.025 5.026a.5.5 0 0 1-.707-.708l5.025-5.025A3 3 0 1 0 7.878 3.88L4.757 7H8.5a.5.5 0 0 1 0 1H3.6a.6.6 0 0 1-.6-.6z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 2.5a.5.5 0 0 1 1 0v3.843l3.171-3.171a4 4 0 0 1 5.657 5.656l-5.025 5.026a.5.5 0 0 1-.707-.708l5.025-5.025A3 3 0 1 0 7.878 3.88L4.757 7H8.5a.5.5 0 0 1 0 1H3.6a.6.6 0 0 1-.6-.6z'/%3E%3C/svg%3E");
}

.jsini-icon-refresh {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.146.646a.5.5 0 0 1 .708 0l1.5 1.5a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 1 1-.708-.708l.643-.642a5 5 0 0 0-2.937 8.88.5.5 0 1 1-.63.777A6 6 0 0 1 7.797 2.003l-.65-.65a.5.5 0 0 1 0-.707m3.929 2.766a.5.5 0 0 1 .703-.073 6 6 0 0 1-3.575 10.658l.65.65a.5.5 0 0 1-.707.707l-1.5-1.5a.5.5 0 0 1 0-.708l1.5-1.5a.5.5 0 0 1 .708.708l-.643.642a5 5 0 0 0 2.937-8.88.5.5 0 0 1-.073-.704'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.146.646a.5.5 0 0 1 .708 0l1.5 1.5a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 1 1-.708-.708l.643-.642a5 5 0 0 0-2.937 8.88.5.5 0 1 1-.63.777A6 6 0 0 1 7.797 2.003l-.65-.65a.5.5 0 0 1 0-.707m3.929 2.766a.5.5 0 0 1 .703-.073 6 6 0 0 1-3.575 10.658l.65.65a.5.5 0 0 1-.707.707l-1.5-1.5a.5.5 0 0 1 0-.708l1.5-1.5a.5.5 0 0 1 .708.708l-.643.642a5 5 0 0 0 2.937-8.88.5.5 0 0 1-.073-.704'/%3E%3C/svg%3E");
}

.jsini-icon-expand {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.75 3a.75.75 0 0 0-.75.75V5.5a.5.5 0 0 1-1 0V3.75C2 2.784 2.784 2 3.75 2H5.5a.5.5 0 0 1 0 1zM10 2.5a.5.5 0 0 1 .5-.5h1.75c.966 0 1.75.784 1.75 1.75V5.5a.5.5 0 0 1-1 0V3.75a.75.75 0 0 0-.75-.75H10.5a.5.5 0 0 1-.5-.5M2.5 10a.5.5 0 0 1 .5.5v1.75c0 .414.336.75.75.75H5.5a.5.5 0 0 1 0 1H3.75A1.75 1.75 0 0 1 2 12.25V10.5a.5.5 0 0 1 .5-.5m11 0a.5.5 0 0 1 .5.5v1.75A1.75 1.75 0 0 1 12.25 14H10.5a.5.5 0 0 1 0-1h1.75a.75.75 0 0 0 .75-.75V10.5a.5.5 0 0 1 .5-.5'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.75 3a.75.75 0 0 0-.75.75V5.5a.5.5 0 0 1-1 0V3.75C2 2.784 2.784 2 3.75 2H5.5a.5.5 0 0 1 0 1zM10 2.5a.5.5 0 0 1 .5-.5h1.75c.966 0 1.75.784 1.75 1.75V5.5a.5.5 0 0 1-1 0V3.75a.75.75 0 0 0-.75-.75H10.5a.5.5 0 0 1-.5-.5M2.5 10a.5.5 0 0 1 .5.5v1.75c0 .414.336.75.75.75H5.5a.5.5 0 0 1 0 1H3.75A1.75 1.75 0 0 1 2 12.25V10.5a.5.5 0 0 1 .5-.5m11 0a.5.5 0 0 1 .5.5v1.75A1.75 1.75 0 0 1 12.25 14H10.5a.5.5 0 0 1 0-1h1.75a.75.75 0 0 0 .75-.75V10.5a.5.5 0 0 1 .5-.5'/%3E%3C/svg%3E");
}

.jsini-icon-bell {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 2a4.5 4.5 0 0 0-4.5 4.5v2.401l-.964 2.414A.5.5 0 0 0 3 12h3c0 1.108.892 2 2 2s2-.892 2-2h3a.5.5 0 0 0 .464-.685L12.5 8.9V6.5A4.5 4.5 0 0 0 8 2m1 10c0 .556-.444 1-1 1s-1-.444-1-1zM4.5 6.5a3.5 3.5 0 1 1 7 0v2.498a.5.5 0 0 0 .036.185L12.262 11H3.738l.726-1.817a.5.5 0 0 0 .036-.185z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 2a4.5 4.5 0 0 0-4.5 4.5v2.401l-.964 2.414A.5.5 0 0 0 3 12h3c0 1.108.892 2 2 2s2-.892 2-2h3a.5.5 0 0 0 .464-.685L12.5 8.9V6.5A4.5 4.5 0 0 0 8 2m1 10c0 .556-.444 1-1 1s-1-.444-1-1zM4.5 6.5a3.5 3.5 0 1 1 7 0v2.498a.5.5 0 0 0 .036.185L12.262 11H3.738l.726-1.817a.5.5 0 0 0 .036-.185z'/%3E%3C/svg%3E");
}

/* 「음성으로 읽어주기」와 그 「멈추기」(AI 작업 상세의 처리 요약 · 결과).
   한동안 그 자리는 컬러 이모지(🔊 · ⏹)였다 — **글꼴이 그리는 그림**이라
   장비마다 모양이 다르고, 다크 모드에서 혼자 색을 들고 튄다. 옆의 접기
   화살표·새로고침과 굵기도 안 맞는다. 집의 다른 그림들과 같이 마스크로
   그린다(색은 `currentColor`, 크기는 `--jsini-icon-size`). */
.jsini-icon-volume {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M11.536 14.01A8.47 8.47 0 0 0 14.026 8a8.47 8.47 0 0 0-2.49-6.01l-.708.707A7.48 7.48 0 0 1 13.025 8c0 2.071-.84 3.946-2.197 5.303zM10.121 12.596A6.48 6.48 0 0 0 12.025 8a6.48 6.48 0 0 0-1.904-4.596l-.707.707A5.48 5.48 0 0 1 11.025 8a5.48 5.48 0 0 1-1.61 3.89zM8.707 11.182A4.49 4.49 0 0 0 10.025 8a4.49 4.49 0 0 0-1.318-3.182L8 5.525A3.49 3.49 0 0 1 9.025 8 3.49 3.49 0 0 1 8 10.475zM6.717 3.55A.5.5 0 0 1 7 4v8a.5.5 0 0 1-.812.39L3.825 10.5H1.5A.5.5 0 0 1 1 10V6a.5.5 0 0 1 .5-.5h2.325l2.363-1.89a.5.5 0 0 1 .529-.06'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M11.536 14.01A8.47 8.47 0 0 0 14.026 8a8.47 8.47 0 0 0-2.49-6.01l-.708.707A7.48 7.48 0 0 1 13.025 8c0 2.071-.84 3.946-2.197 5.303zM10.121 12.596A6.48 6.48 0 0 0 12.025 8a6.48 6.48 0 0 0-1.904-4.596l-.707.707A5.48 5.48 0 0 1 11.025 8a5.48 5.48 0 0 1-1.61 3.89zM8.707 11.182A4.49 4.49 0 0 0 10.025 8a4.49 4.49 0 0 0-1.318-3.182L8 5.525A3.49 3.49 0 0 1 9.025 8 3.49 3.49 0 0 1 8 10.475zM6.717 3.55A.5.5 0 0 1 7 4v8a.5.5 0 0 1-.812.39L3.825 10.5H1.5A.5.5 0 0 1 1 10V6a.5.5 0 0 1 .5-.5h2.325l2.363-1.89a.5.5 0 0 1 .529-.06'/%3E%3C/svg%3E");
}


/* ── 말로 묻기에 쓰는 그림 넷 ───────────────────────────── */

/* 말로 입력하기. 음성 모드와 입력칸 옆 마이크가 같이 쓴다. */
.jsini-icon-microphone {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M5 3a3 3 0 0 1 6 0v5a3 3 0 0 1-6 0zM3.5 6.5A.5.5 0 0 1 4 7v1a4 4 0 0 0 8 0V7a.5.5 0 0 1 1 0v1a5 5 0 0 1-4.5 4.975V15h2.5a.5.5 0 0 1 0 1h-6a.5.5 0 0 1 0-1H7.5v-2.025A5 5 0 0 1 3 8V7a.5.5 0 0 1 .5-.5'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M5 3a3 3 0 0 1 6 0v5a3 3 0 0 1-6 0zM3.5 6.5A.5.5 0 0 1 4 7v1a4 4 0 0 0 8 0V7a.5.5 0 0 1 1 0v1a5 5 0 0 1-4.5 4.975V15h2.5a.5.5 0 0 1 0 1h-6a.5.5 0 0 1 0-1H7.5v-2.025A5 5 0 0 1 3 8V7a.5.5 0 0 1 .5-.5'/%3E%3C/svg%3E");
}

/* 음성 모드를 여는 단추. **마이크가 아니다** — 마이크는 「한 마디를 받아쓴다」,
   이쪽은 「말로 주고받는다」라 그림을 갈라 둔다. */
.jsini-icon-soundwave {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8.5 2a.5.5 0 0 1 .5.5v11a.5.5 0 0 1-1 0v-11a.5.5 0 0 1 .5-.5m-2 2a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5m4 0a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5m-6 1.5A.5.5 0 0 1 5 6v4a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m8 0a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m-10 1A.5.5 0 0 1 3 7v2a.5.5 0 0 1-1 0V7a.5.5 0 0 1 .5-.5m12 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0V7a.5.5 0 0 1 .5-.5'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8.5 2a.5.5 0 0 1 .5.5v11a.5.5 0 0 1-1 0v-11a.5.5 0 0 1 .5-.5m-2 2a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5m4 0a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5m-6 1.5A.5.5 0 0 1 5 6v4a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m8 0a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m-10 1A.5.5 0 0 1 3 7v2a.5.5 0 0 1-1 0V7a.5.5 0 0 1 .5-.5m12 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0V7a.5.5 0 0 1 .5-.5'/%3E%3C/svg%3E");
}

/* 받아쓰는 중. 회전하는 동그라미를 쓰지 않는 까닭은 **기다리는 일의 결이
   다르기** 때문이다 — 이쪽은 보낸 것이 돌아오기를 기다리는 자리다. */
.jsini-icon-hourglass {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2 1.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-1v1a4.5 4.5 0 0 1-2.557 4.06c-.29.139-.443.377-.443.59v.7c0 .213.154.451.443.59A4.5 4.5 0 0 1 12.5 13v1h1a.5.5 0 0 1 0 1h-11a.5.5 0 0 1 0-1h1v-1a4.5 4.5 0 0 1 2.557-4.06c.29-.139.443-.377.443-.59v-.7c0-.213-.154-.451-.443-.59A4.5 4.5 0 0 1 3.5 3V2h-1a.5.5 0 0 1-.5-.5m2.5.5v1a3.5 3.5 0 0 0 1.989 3.158c.533.256 1.011.791 1.011 1.491v.702c0 .7-.478 1.235-1.011 1.491A3.5 3.5 0 0 0 4.5 13v1h7v-1a3.5 3.5 0 0 0-1.989-3.158C8.978 9.586 8.5 9.052 8.5 8.351v-.702c0-.7.478-1.235 1.011-1.491A3.5 3.5 0 0 0 11.5 3V2z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2 1.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-1v1a4.5 4.5 0 0 1-2.557 4.06c-.29.139-.443.377-.443.59v.7c0 .213.154.451.443.59A4.5 4.5 0 0 1 12.5 13v1h1a.5.5 0 0 1 0 1h-11a.5.5 0 0 1 0-1h1v-1a4.5 4.5 0 0 1 2.557-4.06c.29-.139.443-.377.443-.59v-.7c0-.213-.154-.451-.443-.59A4.5 4.5 0 0 1 3.5 3V2h-1a.5.5 0 0 1-.5-.5m2.5.5v1a3.5 3.5 0 0 0 1.989 3.158c.533.256 1.011.791 1.011 1.491v.702c0 .7-.478 1.235-1.011 1.491A3.5 3.5 0 0 0 4.5 13v1h7v-1a3.5 3.5 0 0 0-1.989-3.158C8.978 9.586 8.5 9.052 8.5 8.351v-.702c0-.7.478-1.235 1.011-1.491A3.5 3.5 0 0 0 11.5 3V2z'/%3E%3C/svg%3E");
}

/* 안내 한 줄 앞의 느낌표. 마이크 권한 거절처럼 **고장이 아닌 막힘**에 쓴다. */
.jsini-icon-alert {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.938 2.016a.13.13 0 0 1 .064-.016.13.13 0 0 1 .063.016.15.15 0 0 1 .054.057l6.857 11.667c.036.06.035.124.002.183a.2.2 0 0 1-.054.06.1.1 0 0 1-.066.017H1.146a.1.1 0 0 1-.066-.017.2.2 0 0 1-.054-.06.18.18 0 0 1 .002-.183L7.884 2.073a.15.15 0 0 1 .054-.057m1.044-.45a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5m.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.938 2.016a.13.13 0 0 1 .064-.016.13.13 0 0 1 .063.016.15.15 0 0 1 .054.057l6.857 11.667c.036.06.035.124.002.183a.2.2 0 0 1-.054.06.1.1 0 0 1-.066.017H1.146a.1.1 0 0 1-.066-.017.2.2 0 0 1-.054-.06.18.18 0 0 1 .002-.183L7.884 2.073a.15.15 0 0 1 .054-.057m1.044-.45a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5m.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2'/%3E%3C/svg%3E");
}

/* 멈추기. **속을 채운 네모**다 — 테두리만 그린 네모는 이 크기에서 「빈 칸」으로
   읽혀 누를 것처럼 보이지 않는다. */
.jsini-icon-stop {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M5 3.5h6A1.5 1.5 0 0 1 12.5 5v6a1.5 1.5 0 0 1-1.5 1.5H5A1.5 1.5 0 0 1 3.5 11V5A1.5 1.5 0 0 1 5 3.5'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M5 3.5h6A1.5 1.5 0 0 1 12.5 5v6a1.5 1.5 0 0 1-1.5 1.5H5A1.5 1.5 0 0 1 3.5 11V5A1.5 1.5 0 0 1 5 3.5'/%3E%3C/svg%3E");
}

/* 상단 띠의 「쪽지 쓰기」 단추. DevExpress 이름표로는 `mail` 이다. */
.jsini-icon-mail {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 3A2.5 2.5 0 0 0 1 5.5v5A2.5 2.5 0 0 0 3.5 13h9a2.5 2.5 0 0 0 2.5-2.5v-5A2.5 2.5 0 0 0 12.5 3zM2 5.5A1.5 1.5 0 0 1 3.5 4h9A1.5 1.5 0 0 1 14 5.5v.24l-6 3.2-6-3.2zm0 1.374 5.765 3.075a.5.5 0 0 0 .47 0L14 6.874V10.5a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 10.5z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 3A2.5 2.5 0 0 0 1 5.5v5A2.5 2.5 0 0 0 3.5 13h9a2.5 2.5 0 0 0 2.5-2.5v-5A2.5 2.5 0 0 0 12.5 3zM2 5.5A1.5 1.5 0 0 1 3.5 4h9A1.5 1.5 0 0 1 14 5.5v.24l-6 3.2-6-3.2zm0 1.374 5.765 3.075a.5.5 0 0 0 .47 0L14 6.874V10.5a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 10.5z'/%3E%3C/svg%3E");
}

.jsini-icon-user {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M11.524 8c.9 0 1.689.774 1.424 1.712-.216.769-.628 1.827-1.393 2.702C10.775 13.304 9.629 14 8 14s-2.774-.696-3.554-1.586c-.765-.875-1.177-1.933-1.394-2.702C2.788 8.774 3.576 8 4.477 8zM4.477 9a.47.47 0 0 0-.357.148.31.31 0 0 0-.065.302c.197.702.557 1.603 1.184 2.32C5.853 12.47 6.723 13 8 13s2.147-.53 2.76-1.23c.628-.717.988-1.618 1.185-2.32a.31.31 0 0 0-.065-.302.47.47 0 0 0-.357-.148zM8 1.5A2.75 2.75 0 1 1 8 7a2.75 2.75 0 0 1 0-5.5m0 1A1.75 1.75 0 1 0 8 6a1.75 1.75 0 0 0 0-3.5'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M11.524 8c.9 0 1.689.774 1.424 1.712-.216.769-.628 1.827-1.393 2.702C10.775 13.304 9.629 14 8 14s-2.774-.696-3.554-1.586c-.765-.875-1.177-1.933-1.394-2.702C2.788 8.774 3.576 8 4.477 8zM4.477 9a.47.47 0 0 0-.357.148.31.31 0 0 0-.065.302c.197.702.557 1.603 1.184 2.32C5.853 12.47 6.723 13 8 13s2.147-.53 2.76-1.23c.628-.717.988-1.618 1.185-2.32a.31.31 0 0 0-.065-.302.47.47 0 0 0-.357-.148zM8 1.5A2.75 2.75 0 1 1 8 7a2.75 2.75 0 0 1 0-5.5m0 1A1.75 1.75 0 1 0 8 6a1.75 1.75 0 0 0 0-3.5'/%3E%3C/svg%3E");
}

.jsini-icon-lock {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0M5 4h-.5A2.5 2.5 0 0 0 2 6.5v5A2.5 2.5 0 0 0 4.5 14h7a2.5 2.5 0 0 0 2.5-2.5v-5A2.5 2.5 0 0 0 11.5 4H11v-.5a3 3 0 0 0-6 0zm1-.5a2 2 0 1 1 4 0V4H6zM11.5 5A1.5 1.5 0 0 1 13 6.5v5a1.5 1.5 0 0 1-1.5 1.5h-7A1.5 1.5 0 0 1 3 11.5v-5A1.5 1.5 0 0 1 4.5 5z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0M5 4h-.5A2.5 2.5 0 0 0 2 6.5v5A2.5 2.5 0 0 0 4.5 14h7a2.5 2.5 0 0 0 2.5-2.5v-5A2.5 2.5 0 0 0 11.5 4H11v-.5a3 3 0 0 0-6 0zm1-.5a2 2 0 1 1 4 0V4H6zM11.5 5A1.5 1.5 0 0 1 13 6.5v5a1.5 1.5 0 0 1-1.5 1.5h-7A1.5 1.5 0 0 1 3 11.5v-5A1.5 1.5 0 0 1 4.5 5z'/%3E%3C/svg%3E");
}

.jsini-icon-logout {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8.37 2.018a.6.6 0 0 1 .63.6V7h4.293l-.646-.647-.065-.078a.5.5 0 0 1 .693-.693l.079.064 1.5 1.5.064.078q.013.022.023.043l.014.027a.5.5 0 0 1 .032.098l.003.022q.005.023.008.044v.028L15 7.5l-.002.01v.032q-.001.011-.004.022l-.003.02q0 .012-.003.021a.5.5 0 0 1-.033.1l-.01.02q-.013.026-.027.05l-.064.078-1.5 1.5-.079.065a.5.5 0 0 1-.693-.694l.064-.078.647-.646H9v5.382a.6.6 0 0 1-.63.6l-.068-.008-5.801-.967A.6.6 0 0 1 2 12.415v-8.83a.6.6 0 0 1 .501-.592l5.8-.967zM11 12.5a.5.5 0 0 1-.5.5H10V9h1zM3 3.923v8.153l5 .833V3.09zM6.25 8a.75.75 0 1 1 0 1.5.75.75 0 0 1 0-1.5m4.25-5a.5.5 0 0 1 .5.5V6h-1V3z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8.37 2.018a.6.6 0 0 1 .63.6V7h4.293l-.646-.647-.065-.078a.5.5 0 0 1 .693-.693l.079.064 1.5 1.5.064.078q.013.022.023.043l.014.027a.5.5 0 0 1 .032.098l.003.022q.005.023.008.044v.028L15 7.5l-.002.01v.032q-.001.011-.004.022l-.003.02q0 .012-.003.021a.5.5 0 0 1-.033.1l-.01.02q-.013.026-.027.05l-.064.078-1.5 1.5-.079.065a.5.5 0 0 1-.693-.694l.064-.078.647-.646H9v5.382a.6.6 0 0 1-.63.6l-.068-.008-5.801-.967A.6.6 0 0 1 2 12.415v-8.83a.6.6 0 0 1 .501-.592l5.8-.967zM11 12.5a.5.5 0 0 1-.5.5H10V9h1zM3 3.923v8.153l5 .833V3.09zM6.25 8a.75.75 0 1 1 0 1.5.75.75 0 0 1 0-1.5m4.25-5a.5.5 0 0 1 .5.5V6h-1V3z'/%3E%3C/svg%3E");
}

.jsini-icon-palette {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.5 5.5a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1m2.5 0a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0M11.5 7a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0M11 9.5a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1m-.5 1.5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0M8 2a6 6 0 0 0-6 6 2 2 0 0 0 2 2c.708 0 1.085-.378 1.34-.633l.014-.013C5.598 9.109 5.72 9 6 9a1 1 0 0 1 1 1v1a3 3 0 0 0 3 3c1.334 0 2.354-.746 3.017-1.854C13.672 11.05 14 9.579 14 8a6 6 0 0 0-6-6M3 8a5 5 0 0 1 10 0c0 1.458-.305 2.737-.841 3.632C11.63 12.515 10.9 13 10 13a2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2c-.708 0-1.085.378-1.34.633l-.014.013C4.402 8.891 4.28 9 4 9a1 1 0 0 1-1-1'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.5 5.5a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1m2.5 0a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0M11.5 7a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0M11 9.5a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1m-.5 1.5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0M8 2a6 6 0 0 0-6 6 2 2 0 0 0 2 2c.708 0 1.085-.378 1.34-.633l.014-.013C5.598 9.109 5.72 9 6 9a1 1 0 0 1 1 1v1a3 3 0 0 0 3 3c1.334 0 2.354-.746 3.017-1.854C13.672 11.05 14 9.579 14 8a6 6 0 0 0-6-6M3 8a5 5 0 0 1 10 0c0 1.458-.305 2.737-.841 3.632C11.63 12.515 10.9 13 10 13a2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2c-.708 0-1.085.378-1.34.633l-.014.013C4.402 8.891 4.28 9 4 9a1 1 0 0 1-1-1'/%3E%3C/svg%3E");
}

/* 밝기 세 칸의 그림. 데모에도 해와 달이 붙어 있다 —
   Light/Dark 는 글자만으로도 알지만, 그림이 있으면 굴리는 중에 눈이 먼저 잡는다.
   서랍에서는 **그림만** 세우므로(`tb-pill--icon`) 이름은 `title` 에 있다. */
.jsini-icon-sun {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0v-1A.5.5 0 0 1 8 1m0 10a3 3 0 1 0 0-6 3 3 0 0 0 0 6m0-1a2 2 0 1 1 0-4 2 2 0 0 1 0 4m6.5-1.5a.5.5 0 0 0 0-1h-1a.5.5 0 0 0 0 1zM8 13a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0v-1A.5.5 0 0 1 8 13M2.5 8.5a.5.5 0 0 0 0-1h-1a.5.5 0 0 0 0 1zm.646-5.354a.5.5 0 0 1 .708 0l1 1a.5.5 0 1 1-.708.708l-1-1a.5.5 0 0 1 0-.708m.708 9.708a.5.5 0 1 1-.708-.707l1-1a.5.5 0 0 1 .708.707zm9-9.708a.5.5 0 0 0-.708 0l-1 1a.5.5 0 0 0 .708.708l1-1a.5.5 0 0 0 0-.708m-.708 9.708a.5.5 0 0 0 .708-.707l-1-1a.5.5 0 0 0-.708.707z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0v-1A.5.5 0 0 1 8 1m0 10a3 3 0 1 0 0-6 3 3 0 0 0 0 6m0-1a2 2 0 1 1 0-4 2 2 0 0 1 0 4m6.5-1.5a.5.5 0 0 0 0-1h-1a.5.5 0 0 0 0 1zM8 13a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0v-1A.5.5 0 0 1 8 13M2.5 8.5a.5.5 0 0 0 0-1h-1a.5.5 0 0 0 0 1zm.646-5.354a.5.5 0 0 1 .708 0l1 1a.5.5 0 1 1-.708.708l-1-1a.5.5 0 0 1 0-.708m.708 9.708a.5.5 0 1 1-.708-.707l1-1a.5.5 0 0 1 .708.707zm9-9.708a.5.5 0 0 0-.708 0l-1 1a.5.5 0 0 0 .708.708l1-1a.5.5 0 0 0 0-.708m-.708 9.708a.5.5 0 0 0 .708-.707l-1-1a.5.5 0 0 0-.708.707z'/%3E%3C/svg%3E");
}

.jsini-icon-moon {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.85 3.015a5 5 0 1 1-4.585 7.712c1.403-.38 3.316-1.302 4.16-3.551.552-1.474.584-2.938.425-4.16M13.456 8a6 6 0 0 0-6.21-5.996.5.5 0 0 0-.475.592c.23 1.214.28 2.728-.283 4.228-.8 2.134-2.802 2.84-4.077 3.071a.5.5 0 0 0-.361.71A6 6 0 0 0 13.456 8'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.85 3.015a5 5 0 1 1-4.585 7.712c1.403-.38 3.316-1.302 4.16-3.551.552-1.474.584-2.938.425-4.16M13.456 8a6 6 0 0 0-6.21-5.996.5.5 0 0 0-.475.592c.23 1.214.28 2.728-.283 4.228-.8 2.134-2.802 2.84-4.077 3.071a.5.5 0 0 0-.361.71A6 6 0 0 0 13.456 8'/%3E%3C/svg%3E");
}

/* 「시스템」 — 기기의 밝기를 따른다는 뜻이라 해도 달도 아니다.
   **기기 자신**(모니터)을 그린다. */
.jsini-icon-display {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 2A2.5 2.5 0 0 0 1 4.5v5A2.5 2.5 0 0 0 3.5 12h2.19l-.44 2H4a.5.5 0 0 0 0 1h8a.5.5 0 0 0 0-1h-1.25l-.44-2h2.19A2.5 2.5 0 0 0 15 9.5v-5A2.5 2.5 0 0 0 12.5 2zM2 4.5A1.5 1.5 0 0 1 3.5 3h9A1.5 1.5 0 0 1 14 4.5v5A1.5 1.5 0 0 1 12.5 11h-9A1.5 1.5 0 0 1 2 9.5zm4.714 9.5.44-2h1.692l.44 2z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 2A2.5 2.5 0 0 0 1 4.5v5A2.5 2.5 0 0 0 3.5 12h2.19l-.44 2H4a.5.5 0 0 0 0 1h8a.5.5 0 0 0 0-1h-1.25l-.44-2h2.19A2.5 2.5 0 0 0 15 9.5v-5A2.5 2.5 0 0 0 12.5 2zM2 4.5A1.5 1.5 0 0 1 3.5 3h9A1.5 1.5 0 0 1 14 4.5v5A1.5 1.5 0 0 1 12.5 11h-9A1.5 1.5 0 0 1 2 9.5zm4.714 9.5.44-2h1.692l.44 2z'/%3E%3C/svg%3E");
}

.jsini-icon-check {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M13.864 3.656a.5.5 0 0 1-.021.707l-7.93 7.474a.6.6 0 0 1-.839-.016l-2.68-2.72a.5.5 0 0 1 .712-.702l2.406 2.442 7.645-7.206a.5.5 0 0 1 .707.021'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M13.864 3.656a.5.5 0 0 1-.021.707l-7.93 7.474a.6.6 0 0 1-.839-.016l-2.68-2.72a.5.5 0 0 1 .712-.702l2.406 2.442 7.645-7.206a.5.5 0 0 1 .707.021'/%3E%3C/svg%3E");
}

.jsini-icon-pin {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M10.071 2.442a1.503 1.503 0 0 0-2.39.353L5.658 6.587l-2.815.937a.5.5 0 0 0-.196.828l2.15 2.147-2.65 2.647L2 14l.855-.146 2.65-2.648 2.15 2.147a.5.5 0 0 0 .83-.195l.938-2.812 3.784-2.024a1.5 1.5 0 0 0 .355-2.381zm-1.506.823a.5.5 0 0 1 .797-.117l3.49 3.499a.5.5 0 0 1-.118.794l-3.96 2.117a.5.5 0 0 0-.239.283l-.745 2.233L3.93 8.217l2.235-.745a.5.5 0 0 0 .284-.239z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M10.071 2.442a1.503 1.503 0 0 0-2.39.353L5.658 6.587l-2.815.937a.5.5 0 0 0-.196.828l2.15 2.147-2.65 2.647L2 14l.855-.146 2.65-2.648 2.15 2.147a.5.5 0 0 0 .83-.195l.938-2.812 3.784-2.024a1.5 1.5 0 0 0 .355-2.381zm-1.506.823a.5.5 0 0 1 .797-.117l3.49 3.499a.5.5 0 0 1-.118.794l-3.96 2.117a.5.5 0 0 0-.239.283l-.745 2.233L3.93 8.217l2.235-.745a.5.5 0 0 0 .284-.239z'/%3E%3C/svg%3E");
}
.jsini-icon-pin-filled {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M10.071 2.442a1.503 1.503 0 0 0-2.39.353L5.658 6.587l-2.815.937a.5.5 0 0 0-.196.828l2.15 2.147-2.65 2.647L2 14l.855-.146 2.65-2.648 2.15 2.147a.5.5 0 0 0 .83-.195l.938-2.812 3.784-2.024a1.5 1.5 0 0 0 .355-2.381z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M10.071 2.442a1.503 1.503 0 0 0-2.39.353L5.658 6.587l-2.815.937a.5.5 0 0 0-.196.828l2.15 2.147-2.65 2.647L2 14l.855-.146 2.65-2.648 2.15 2.147a.5.5 0 0 0 .83-.195l.938-2.812 3.784-2.024a1.5 1.5 0 0 0 .355-2.381z'/%3E%3C/svg%3E");
}

.jsini-icon-calendar {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M12.5 2H12V1a.5.5 0 0 0-1 0v1H5V1a.5.5 0 0 0-1 0v1h-.5A2.5 2.5 0 0 0 1 4.5v8A2.5 2.5 0 0 0 3.5 15h9a2.5 2.5 0 0 0 2.5-2.5v-8A2.5 2.5 0 0 0 12.5 2m1.5 2.5V5H2v-.5A1.5 1.5 0 0 1 3.5 3H4v.5a.5.5 0 0 0 1 0V3h6v.5a.5.5 0 0 0 1 0V3h.5a1.5 1.5 0 0 1 1.5 1.5M14 12.5A1.5 1.5 0 0 1 12.5 14h-9A1.5 1.5 0 0 1 2 12.5V6h12z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M12.5 2H12V1a.5.5 0 0 0-1 0v1H5V1a.5.5 0 0 0-1 0v1h-.5A2.5 2.5 0 0 0 1 4.5v8A2.5 2.5 0 0 0 3.5 15h9a2.5 2.5 0 0 0 2.5-2.5v-8A2.5 2.5 0 0 0 12.5 2m1.5 2.5V5H2v-.5A1.5 1.5 0 0 1 3.5 3H4v.5a.5.5 0 0 0 1 0V3h6v.5a.5.5 0 0 0 1 0V3h.5a1.5 1.5 0 0 1 1.5 1.5M14 12.5A1.5 1.5 0 0 1 12.5 14h-9A1.5 1.5 0 0 1 2 12.5V6h12z'/%3E%3C/svg%3E");
}

.jsini-icon-close {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='m2.589 2.716.057-.07a.5.5 0 0 1 .638-.057l.07.057L8 7.293l4.646-4.647a.5.5 0 0 1 .708.708L8.707 8l4.647 4.646a.5.5 0 0 1 .057.638l-.057.07a.5.5 0 0 1-.638.057l-.07-.057L8 8.707l-4.646 4.647a.5.5 0 0 1-.708-.708L7.293 8 2.646 3.354a.5.5 0 0 1-.057-.638'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='m2.589 2.716.057-.07a.5.5 0 0 1 .638-.057l.07.057L8 7.293l4.646-4.647a.5.5 0 0 1 .708.708L8.707 8l4.647 4.646a.5.5 0 0 1 .057.638l-.057.07a.5.5 0 0 1-.638.057l-.07-.057L8 8.707l-4.646 4.647a.5.5 0 0 1-.708-.708L7.293 8 2.646 3.354a.5.5 0 0 1-.057-.638'/%3E%3C/svg%3E");
}

/* 탭 줄의 「URL 복사」. DevExpress 쪽 이름은 copy 다. */
.jsini-icon-copy {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M6.75 5A1.75 1.75 0 0 0 5 6.75v5.5c0 .966.784 1.75 1.75 1.75h5.5A1.75 1.75 0 0 0 14 12.25v-5.5A1.75 1.75 0 0 0 12.25 5zM6 6.75A.75.75 0 0 1 6.75 6h5.5a.75.75 0 0 1 .75.75v5.5a.75.75 0 0 1-.75.75h-5.5a.75.75 0 0 1-.75-.75z'/%3E%3Cpath d='M3.75 2A1.75 1.75 0 0 0 2 3.75v5.5c0 .966.784 1.75 1.75 1.75H4v-1h-.25a.75.75 0 0 1-.75-.75v-5.5A.75.75 0 0 1 3.75 3h5.5a.75.75 0 0 1 .75.75V4h1v-.25A1.75 1.75 0 0 0 9.25 2z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M6.75 5A1.75 1.75 0 0 0 5 6.75v5.5c0 .966.784 1.75 1.75 1.75h5.5A1.75 1.75 0 0 0 14 12.25v-5.5A1.75 1.75 0 0 0 12.25 5zM6 6.75A.75.75 0 0 1 6.75 6h5.5a.75.75 0 0 1 .75.75v5.5a.75.75 0 0 1-.75.75h-5.5a.75.75 0 0 1-.75-.75z'/%3E%3Cpath d='M3.75 2A1.75 1.75 0 0 0 2 3.75v5.5c0 .966.784 1.75 1.75 1.75H4v-1h-.25a.75.75 0 0 1-.75-.75v-5.5A.75.75 0 0 1 3.75 3h5.5a.75.75 0 0 1 .75.75V4h1v-.25A1.75 1.75 0 0 0 9.25 2z'/%3E%3C/svg%3E");
}

/* 자동 줄바꿈 토글 아이콘. (text-wrap) */
.jsini-icon-wrap {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M2 3.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm0 4a.5.5 0 0 1 .5-.5h7a2.5 2.5 0 0 1 0 5H6.707l1.147-1.146a.5.5 0 1 0-.708-.708l-2 2a.5.5 0 0 0 0 .708l2 2a.5.5 0 0 0 .708-.708L6.707 13H9.5a3.5 3.5 0 1 0 0-7H2.5a.5.5 0 0 1-.5-.5zm0 5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M2 3.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm0 4a.5.5 0 0 1 .5-.5h7a2.5 2.5 0 0 1 0 5H6.707l1.147-1.146a.5.5 0 1 0-.708-.708l-2 2a.5.5 0 0 0 0 .708l2 2a.5.5 0 0 0 .708-.708L6.707 13H9.5a3.5 3.5 0 1 0 0-7H2.5a.5.5 0 0 1-.5-.5zm0 5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
}


.jsini-icon-more {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M5 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0m4 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M5 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0m4 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2'/%3E%3C/svg%3E");
}

/* 세로 점 셋(⋮). 표의 관리 칸이 단추 하나로 쓴다. */
.jsini-icon-more-vertical {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M9.5 13a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M9.5 13a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0'/%3E%3C/svg%3E");
}

/* 헤더의 「빠른 지시」 단추. **DevExpress 라이브러리에 번개가 없어 직접 그렸다** —
   찾아본 것 중 뜻이 가장 가까운 것이 `flash`(카메라 플래시)였는데, 그 그림은
   전구에 가까워 「빠름」으로 안 읽힌다.

   말풍선(DB 메뉴 아이콘은 `lucide:message-square-plus` 다)을 쓰지 않는 이유:
   헤더 오른쪽에는 이미 AI 대화 서랍이 있고 그쪽이 진짜 말풍선 일을 한다.
   두 단추가 같은 계열 그림이면 **어느 쪽이 대화고 어느 쪽이 지시인지** 가
   그림으로는 안 갈린다. */
.jsini-icon-bolt {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M10.2 1 3.3 9.2h3.6L5.8 15l6.9-8.2H9.1z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M10.2 1 3.3 9.2h3.6L5.8 15l6.9-8.2H9.1z'/%3E%3C/svg%3E");
}

/* 새 창으로 열기 (open). DevExpress 쪽 이름은 open 이다. */
.jsini-icon-open,
.jsini-icon-open-in-new {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 2A1.5 1.5 0 0 0 2 3.5v9A1.5 1.5 0 0 0 3.5 14h9a1.5 1.5 0 0 0 1.5-1.5V8.5a.5.5 0 0 0-1 0V12.5a.5.5 0 0 1-.5.5h-9a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5H7.5a.5.5 0 0 0 0-1H3.5zM9.5 2a.5.5 0 0 0 0 1h2.793L6.146 9.146a.5.5 0 1 0 .708.708L13 3.707V6.5a.5.5 0 0 0 1 0V2.5A.5.5 0 0 0 13.5 2H9.5z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 2A1.5 1.5 0 0 0 2 3.5v9A1.5 1.5 0 0 0 3.5 14h9a1.5 1.5 0 0 0 1.5-1.5V8.5a.5.5 0 0 0-1 0V12.5a.5.5 0 0 1-.5.5h-9a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5H7.5a.5.5 0 0 0 0-1H3.5zM9.5 2a.5.5 0 0 0 0 1h2.793L6.146 9.146a.5.5 0 1 0 .708.708L13 3.707V6.5a.5.5 0 0 0 1 0V2.5A.5.5 0 0 0 13.5 2H9.5z'/%3E%3C/svg%3E");
}

/* 계정 결합 (link). Bootstrap Icons(MIT) 의 `link-45deg` 다. 이 규칙이 없으면
   묶음의 `background-color` 만 남아 **꽉 찬 네모**로 그려진다. */
.jsini-icon-link {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.715 6.542 3.343 7.914a3 3 0 1 0 4.243 4.243l1.828-1.829A3 3 0 0 0 8.586 5.5L8 6.086a1 1 0 0 0-.154.199 2 2 0 0 1 .861 3.337L6.88 11.45a2 2 0 1 1-2.83-2.83l.793-.792a4 4 0 0 1-.128-1.287z'/%3E%3Cpath d='M6.586 4.672A3 3 0 0 0 7.414 9.5l.775-.776a2 2 0 0 1-.896-3.346L9.12 3.55a2 2 0 1 1 2.83 2.83l-.793.792c.112.42.155.855.128 1.287l1.372-1.372a3 3 0 1 0-4.243-4.243z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.715 6.542 3.343 7.914a3 3 0 1 0 4.243 4.243l1.828-1.829A3 3 0 0 0 8.586 5.5L8 6.086a1 1 0 0 0-.154.199 2 2 0 0 1 .861 3.337L6.88 11.45a2 2 0 1 1-2.83-2.83l.793-.792a4 4 0 0 1-.128-1.287z'/%3E%3Cpath d='M6.586 4.672A3 3 0 0 0 7.414 9.5l.775-.776a2 2 0 0 1-.896-3.346L9.12 3.55a2 2 0 1 1 2.83 2.83l-.793.792c.112.42.155.855.128 1.287l1.372-1.372a3 3 0 1 0-4.243-4.243z'/%3E%3C/svg%3E");
}

/* 댓글 (comment). Bootstrap Icons(MIT) 의 chat-left 다. */
.jsini-icon-comment {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M14 1a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H4.414A2 2 0 0 0 3 11.586l-2 2V2a1 1 0 0 1 1-1zM2 0a2 2 0 0 0-2 2v12.793a.5.5 0 0 0 .854.353l2.853-2.853A1 1 0 0 1 4.414 12H14a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M14 1a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H4.414A2 2 0 0 0 3 11.586l-2 2V2a1 1 0 0 1 1-1zM2 0a2 2 0 0 0-2 2v12.793a.5.5 0 0 0 .854.353l2.853-2.853A1 1 0 0 1 4.414 12H14a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z'/%3E%3C/svg%3E");
}

/* 전화 (telephone). Bootstrap Icons(MIT) 의 telephone 이다. 사람 카드의 「전화하기」. */
.jsini-icon-telephone {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.654 1.328a.678.678 0 0 0-1.015-.063L1.605 2.3c-.483.484-.661 1.169-.45 1.77a17.6 17.6 0 0 0 4.168 6.608 17.6 17.6 0 0 0 6.608 4.168c.601.211 1.286.033 1.77-.45l1.034-1.034a.678.678 0 0 0-.063-1.015l-2.307-1.794a.68.68 0 0 0-.58-.122l-2.19.547a1.75 1.75 0 0 1-1.657-.459L5.482 8.062a1.75 1.75 0 0 1-.46-1.657l.548-2.19a.68.68 0 0 0-.122-.58zM1.884.511a1.745 1.745 0 0 1 2.612.163L6.29 2.98c.329.423.445.974.315 1.494l-.547 2.19a.68.68 0 0 0 .178.643l2.457 2.457a.68.68 0 0 0 .644.178l2.189-.547a1.75 1.75 0 0 1 1.494.315l2.306 1.794c.829.645.905 1.87.163 2.611l-1.034 1.034c-.74.74-1.846 1.065-2.877.702a18.6 18.6 0 0 1-7.01-4.42 18.6 18.6 0 0 1-4.42-7.009c-.362-1.03-.037-2.137.703-2.877z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.654 1.328a.678.678 0 0 0-1.015-.063L1.605 2.3c-.483.484-.661 1.169-.45 1.77a17.6 17.6 0 0 0 4.168 6.608 17.6 17.6 0 0 0 6.608 4.168c.601.211 1.286.033 1.77-.45l1.034-1.034a.678.678 0 0 0-.063-1.015l-2.307-1.794a.68.68 0 0 0-.58-.122l-2.19.547a1.75 1.75 0 0 1-1.657-.459L5.482 8.062a1.75 1.75 0 0 1-.46-1.657l.548-2.19a.68.68 0 0 0-.122-.58zM1.884.511a1.745 1.745 0 0 1 2.612.163L6.29 2.98c.329.423.445.974.315 1.494l-.547 2.19a.68.68 0 0 0 .178.643l2.457 2.457a.68.68 0 0 0 .644.178l2.189-.547a1.75 1.75 0 0 1 1.494.315l2.306 1.794c.829.645.905 1.87.163 2.611l-1.034 1.034c-.74.74-1.846 1.065-2.877.702a18.6 18.6 0 0 1-7.01-4.42 18.6 18.6 0 0 1-4.42-7.009c-.362-1.03-.037-2.137.703-2.877z'/%3E%3C/svg%3E");
}

/* 가는 선의 말풍선 + — 댓글 달기. */
.jsini-icon-comment-plus {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7.9 20A9 9 0 1 0 4 16.1L2 22z'/%3E%3Cpath d='M8 12h8M12 8v8'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7.9 20A9 9 0 1 0 4 16.1L2 22z'/%3E%3Cpath d='M8 12h8M12 8v8'/%3E%3C/svg%3E");
}

/* 답글 (reply). Bootstrap Icons(MIT) 의 reply 다. */
.jsini-icon-reply {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6.598 5.013a.144.144 0 0 1 .202.134V6.3a.5.5 0 0 0 .5.5c.667 0 2.013.005 3.3.822.984.624 1.99 1.76 2.595 3.876-1.02-.983-2.185-1.516-3.205-1.799a8.7 8.7 0 0 0-1.921-.306 7 7 0 0 0-.798.008h-.013l-.005.001h-.001L7.3 9.9v1.2a.145.145 0 0 1-.202.135L2.35 8.857a.145.145 0 0 1 0-.27zM5.598 3.513a1.144 1.144 0 0 0-1.606 1.074v1.896L1.646 8.829a1.144 1.144 0 0 0 0 1.632l2.346 2.346a1.144 1.144 0 0 0 1.606-1.074v-1.896c2.518.237 4.795 1.232 6.002 3.82a.5.5 0 0 0 .918-.284C12.33 9.404 10.518 7.02 7.3 6.643V4.587a1.144 1.144 0 0 0-1.606-1.074'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6.598 5.013a.144.144 0 0 1 .202.134V6.3a.5.5 0 0 0 .5.5c.667 0 2.013.005 3.3.822.984.624 1.99 1.76 2.595 3.876-1.02-.983-2.185-1.516-3.205-1.799a8.7 8.7 0 0 0-1.921-.306 7 7 0 0 0-.798.008h-.013l-.005.001h-.001L7.3 9.9v1.2a.145.145 0 0 1-.202.135L2.35 8.857a.145.145 0 0 1 0-.27zM5.598 3.513a1.144 1.144 0 0 0-1.606 1.074v1.896L1.646 8.829a1.144 1.144 0 0 0 0 1.632l2.346 2.346a1.144 1.144 0 0 0 1.606-1.074v-1.896c2.518.237 4.795 1.232 6.002 3.82a.5.5 0 0 0 .918-.284C12.33 9.404 10.518 7.02 7.3 6.643V4.587a1.144 1.144 0 0 0-1.606-1.074'/%3E%3C/svg%3E");
}

/* ── 좁은 화면 ───────────────────────────────────────────

   휴대폰에서는 사이드바가 본문을 **덮는다.** 밀어내면 240px 짜리 판 때문에
   본문이 화면 밖으로 나간다. 메뉴를 누르면 레이아웃이 알아서 접는다.
   ---------------------------------------------------------- */

@media (max-width: 767px) {
  .jsini-shell {
    grid-template-columns: 1fr;
  }

  /* ── 좁은 화면에서는 스플리터를 **상자만 없앤다** ────────

     사이드바가 본문을 덮어야 하는 폭이다. 판을 나눠 갖는 부품으로는 덮을 수
     없으므로 `display: contents` 로 판·판안쪽의 상자를 지운다. 그러면 그
     안의 사이드바와 본문이 **다시 격자 칸이 되어** 아래 규칙들이 그대로
     걸린다(사이드바는 `position: fixed` 로 덮고, 본문은 첫 칸을 쓴다).

     부품을 지우지 않고 상자만 없애는 이유는 **마크업을 갈아 끼우지 않기
     위해서**다. 화면 크기로 갈아 끼우면 경계를 넘을 때마다 사이드바 트리와
     열려 있던 화면이 통째로 다시 만들어진다.

     DevExpress 가 판에 인라인으로 넣는 폭·좌표는 상자가 없어지면 아무 일도
     하지 않는다. 구분선은 감춘다 — 끌 자리가 없다. */
  .jsini-shell__split,
  .jsini-shell__split > .dxbl-splitter-pane,
  .jsini-shell__split > .dxbl-splitter-pane > .dxbl-splitter-pane-content {
    display: contents !important;
    /* 상자가 없어도 DevExpress 판(`DxFlyout`)은 조상의 `overflow: hidden` 을
       잘라 내는 틀로 센다 — 0×0 틀이라 판이 열린 채 투명하게 숨는다(재어 확인했다). */
    overflow: visible !important;
  }

  .jsini-shell__split > .dxbl-splitter-separator {
    display: none !important;
  }

  /* 데모의 330px 를 그대로 두면 360px 짜리 화면을 거의 다 덮어서 본문이
     보이지 않는다. 화면 폭의 85% 로 묶는다. */
  .jsini-shell__sidebar,
  .jsini-sidebar {
    width: min(330px, 85vw);
  }

  .jsini-shell__sidebar {
    position: fixed;
    top: var(--jsini-header-height);
    bottom: calc(var(--jsini-bottomnav-h) + env(safe-area-inset-bottom));
    left: 0;
    z-index: 1030;
    box-shadow: 0 0 24px rgb(0 0 0 / 25%);
    transition: top 0.25s ease-in-out;
  }

  /* **펴진 표시가 붙었을 때만 그린다.** 접힘(`--collapsed`)을 보고 감추던
     자리인데, 그러면 **프리렌더가 나갈 때 사이드바가 펴진 채**라(화면 크기를
     모르니 휴대폰인 줄도 모른다) 첫 그림에서 판이 본문을 덮었다가 회로가
     붙은 뒤에 걷혔다.

     표시를 붙이는 쪽은 `MainLayout.ShellCss` 다 — 휴대폰이면서 펴져 있을
     때만 붙는다. */
  .jsini-shell__sidebar { display: none; }

  .jsini-shell--nav-open .jsini-shell__sidebar { display: block; }

  /* 덮개와 떠다니는 단추는 이 폭에서만 켠다(생김새는 위에 있다). */
  .jsini-shell__scrim { display: block; }

  .jsini-shell__fab { display: inline-flex; }

  /* 환경설정에서 **떠다니는 단추를 감춘 사람**(`jsini-fab-hidden`).
     표시를 붙이는 쪽은 `MainLayout.ShellCss` 다.

     **☰ 를 되돌리지 않는다.** 휴대폰에서는 **헤더의 로고가 이미 그 일을
     한다** — `MainLayout.OnBrandClick` 이 `_isPhone` 일 때 사이드바를 여닫고,
     기본 이동은 막는다. 감췄더니 ☰ 가 되살아나는 것은 「귀퉁이를 가리는 것이
     싫어서 감췄는데 이번에는 띠 왼쪽을 먹는다」로 읽힌다.

     그래서 감추면 **정말로 아무것도 안 보인다.** 여는 길은 로고, 닫는 길은
     로고·덮개·뒤로 가기·헤더 누르기다. */
  .jsini-shell--fab-hidden .jsini-shell__fab--menu { display: none; }

  /* 요청 등록 단추를 감추는 규칙은 **이 블록 안에 없다.** 그 단추는 넓은
     화면에도 띄울 수 있어서(`--hdfab-desktop`) 감추기도 폭을 가리지 않아야
     한다 — 위쪽의 `--help` 머리말 자리에 함께 적어 두었다. */

  /* 헤더의 ☰ 는 휴대폰에서 뺀다. **떠다니는 단추와 하는 일이 똑같아서**
     (`_sidebarOpen` 하나를 여닫는다) 같은 화면에 둘을 놓을 까닭이 없고,
     360px 짜리 띠에서 그 자리는 도구 단추가 더 잘 쓴다.

     여는 자리를 아래쪽 하나로 모으는 편이 손에도 낫다 — 한 손으로 쥔
     휴대폰에서 화면 맨 위 왼쪽 모서리는 쥔 손을 고쳐 잡아야 닿는다.

     **닫는 길은 그대로 넷이다.** 떠다니는 단추(덮개보다 위에 뜬다) ·
     덮개를 누르기 · 뒤로 가기 · 헤더를 누르기. ☰ 가 없어져도 막다른 곳이
     생기지 않는다. */
  .jsini-header__toggle { display: none; }

  .jsini-shell__main { grid-column: 1; }

/* 본문 좌우 여백은 거의 주지 않는다(좁은 화면에서 표 한 칸이 줄바꿈되는 것을 막는다). 아래만 하단 띠 높이만큼 비운다(여백이 아니라 자리 비킴). */
  .jsini-shell__content {
    padding: var(--jsini-shell-pad-top) var(--jsini-shell-pad-inline)
             calc(var(--jsini-bottomnav-h) + env(safe-area-inset-bottom) + var(--jsini-shell-pad-bottom));
  }

/* 모바일에서는 탭 줄을 숨기고 열린 화면 이름은 헤더 로고 옆에 둔다. 탭 메뉴는 그 이름을 눌러 연다(TabMenuRequest). 창 마크업이 이 상자 밖이라 여기서 감춰도 된다. */
  .jsini-tabs { display: none; }

/* 모바일 헤더의 브레드크럼은 앞 경로와 구분자를 숨기고 마지막 항목(열린 화면 이름)만 남긴다. */
  .jsini-crumbs { min-width: 0; }

  .jsini-crumbs__item:not(.jsini-crumbs__item--current),
  .jsini-crumbs__item:not(:last-child),
  .jsini-crumbs__sep { display: none; }

/* 휴대폰에서 이 칸은 탭 메뉴를 여는 단추다. 줄임표와 화살표를 나란히 두는 상자로 만든다(inline-block 에 text-overflow 만 걸면 화살표까지 잘린다). */
  .jsini-crumbs__item--current {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;

/* 268px 는 띠에서 이 이름 말고 다른 것이 쓰는 폭(헤더 좌우 여백 · 로고 · 오른쪽 단추 일곱)이다. 이름이 먼저 접히도록 여기서 뺀다. 모자라는 화면은 아래 min-width 0 이 받는다. */
    max-width: min(16rem, calc(100vw - 268px));
    margin-right: 0;

        /* 손가락이 닿는 자리를 헤더 높이 안에서 위아래로만 넓힌다(좌우로 주면 로고나 도구 단추를 민다). */
    padding: 0.35rem 0;
  }

/* 줄임표는 글자 쪽에만 건다. 화살표는 그 밖이라 늘 보인다. min-width 0 이 있어야 긴 이름이 접히는 대신 화살표를 밀어내지 않는다. */
  .jsini-crumbs__item--current > .jsini-crumbs__label {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

/* 눌린다는 유일한 표시. 넓은 화면의 「⋯」 이 그 노릇을 하는데 여기서는 그 줄이 감춰지므로 테두리 삼각형으로 아래를 가리키게 그린다. */
  .jsini-crumbs__caret {
    flex: 0 0 auto;
    width: 0;
    height: 0;
    margin-top: 2px;
    border: 4px solid transparent;
    border-top-color: currentcolor;
    opacity: 0.55;
  }

/* 좁은 화면에서 브랜드 글자와 그 옆의 세로 선을 함께 뺀다(선만 남으면 무엇이 빠진 것처럼 보인다). */
  .jsini-header__brand-text,
  .jsini-header__brand .jsini-header__sep { display: none; }

/* 헤더 오른쪽에 남는 것은 일곱(빠른 지시 · AI 작업 요청 · 쪽지 · 알림 · AI 대화 · 테마 · 프로필). 즐겨찾기·새로 고침·전체화면 셋은 책상에서만 쓰고 휴대폰에서는 뺀다. */
  .jsini-header__tool--wide { display: none; }

/* 아래 띠의 칸은 헤더에 서지 않는다(두 벌이 뜨고 360px 줄이 못 쓰게 된다). 감추는 쪽은 헤더다. 세우는 규칙이 뒤에 오므로 같은 무게로 적으면 뒤엣것이 이겨 한 칸 더 짚는다. */
  .jsini-shell .jsini-header__nav { display: none; }

/* 테마 단추는 휴대폰에서도 선다(밝기·색은 자주 바꾸는 것이라 메뉴 안으로 숨기지 않는다). */

/* 헤더 좌우 여백을 줄여 띠에 폭을 돌려준다. min-width 0 이 뒤를 받는다(없으면 넘친 내용이 화면 밖으로 밀려 프로필 얼굴이 잘린다). 칸을 하나 더 짚는 것은 뒤의 헤더 규칙을 이기려고다. */
  .jsini-shell > .jsini-header {
    padding: 0 0.4rem;
    min-width: 0;
  }
}

/* 공통 조회 영역(CommSch). 조건은 표 안이 아니라 판 하나로 따로 세운다. 표가 넓어져도 조건은 같은 자리·같은 모양이다. 바탕을 짙게 하고 테두리를 둘러 조건과 자료를 가른다. */

.commsch {
  display: flex;
  flex-wrap: wrap;

/* 단추는 위를 맞춘다. 가운데로 맞추면 조건이 여러 줄이 될 때 단추가 내려가 자리가 움직인다. */
  align-items: flex-start;
  gap: var(--jsini-sp-md) var(--jsini-sp-lg);
  padding: var(--jsini-sp-lg);
  margin-bottom: var(--jsini-sp-lg);

/* 흰 카드다. 회색 판이 겹치면 화면이 눌려 앉으므로 흰 바탕에 옅은 테두리로 가른다(테두리 1px · 모서리 4px). */
  background: var(--jsini-bg);
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-sm);
}

/* 조건은 왼쪽에 흘러 담기고 줄바꿈으로 받는다. 칸 수를 고정하면 조건이 늘 때마다 판이 깨진다. */
.commsch__fields {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.7rem 1rem;

/* 단추 묶음은 0 에서 자란다(auto 면 조건이 많을 때 단추 묶음이 통째로 다음 줄로 밀린다). */
  flex: 1 1 0;
  min-width: 0;
}

/* 라벨은 칸 왼쪽에 붙는다. 조건이 대개 둘에서 넷이라 한 줄에 다 들어가 판이 한 줄 높이로 끝난다. */
.commsch__item {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  min-width: 0;
}

/* 입력 부품에 고정 폭을 준 화면에서도 조건 판이 가로로 넘치지 않게 한다. 좁은 화면에서는 판의 남은 폭을 쓴다. */
.commsch__item > :last-child {
  min-width: 0;
  max-width: 100%;
}

/* 조건이 많은 판은 격자로 선다. 흘려 담으면 줄마다 다른 자리에서 시작해 모자이크가 되므로 기둥을 세운다. 조건이 여섯을 넘는 판에만 쓴다(적은 판은 휑해진다). 스택 판과 좁은 화면은 뺀다. */
@media (min-width: 768px) {
  .commsch:not(.commsch--stacked) .commsch__fields:has(> .commsch__item:nth-child(6)) {
    display: grid;
/* 기둥 하나는 17rem 이상이어야 가장 넓은 날짜 칸이 잘리지 않는다. 좁은 창에서 기둥이 하나가 되는 것은 고장이 아니다. */
    grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
    align-items: center;
    gap: 0.6rem 1rem;
  }

/* 기둥 안에서도 입력칸의 왼쪽 끝을 맞춘다. 짧은 라벨에 최소 폭을 주고 오른쪽으로 붙인다. */
  .commsch:not(.commsch--stacked) .commsch__fields:has(> .commsch__item:nth-child(6)) .commsch__label {
    flex: 0 0 auto;
    min-width: 4.25rem;
    text-align: right;
  }

/* 입력칸은 기둥의 남은 폭을 채우되 14rem 에서 멈춘다. 끝까지 채우면 날짜 칸 하나가 지나치게 넓어지고, 14rem 은 가장 넓은 칸(날짜 195px)이 잘리지 않는 기준이다. */
  .commsch:not(.commsch--stacked) .commsch__fields:has(> .commsch__item:nth-child(6)) .commsch__item > :last-child {
    flex: 1 1 auto;
    width: auto !important;
    max-width: 14rem;
  }
}

/* Stacked 판일 때만 세로로 돌린다. 판이 붙인 클래스를 받으므로 한 판의 칸 전부가 함께 돌아간다. */
/* 세로 배치에서는 칸끼리 아래를 맞춘다. 판까지 덮으면 단추가 다시 아래로 내려간다. */
.commsch--stacked .commsch__fields {
  align-items: flex-end;
}

.commsch--stacked .commsch__item {
  flex-direction: column;
  align-items: flex-start;
  gap: 0.2rem;
}

.commsch__label {
  font-size: var(--jsini-fs-xs);
  font-weight: 600;
  color: var(--jsini-text-muted);
  white-space: nowrap;
}

/* 단추는 오른쪽 위에 붙박는다. flex 0 0 auto 로 제 폭을 지켜 글자가 접히지 않게 하고, 조건은 그 나머지 안에서 접힌다. */
.commsch__actions {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex: 0 0 auto;
  margin-left: auto;

/* 판이 flex-start 라 이미 위에 붙지만, 판의 정렬이 바뀌는 화면에서도 단추는 위에 남도록 한 번 더 박는다. */
  align-self: flex-start;
}

/* 조회·초기화 묶음. 접힌 휴대폰 화면에서 이 둘만 사라지도록 따로 묶었다. */
.commsch__run {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

/* 조회 판의 접기 줄(CommSch). 데스크톱에는 없고 좁은 화면에서만 켠다. 접힘은 클래스로 표시하지만 그 클래스가 숨기는 것은 좁은 화면 안에서만 정의한다(창을 넓히면 펴진 모습이 된다). */

.commsch__toggle {
  display: none;
}

@media (max-width: 767px) {
  .commsch__toggle {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.15rem 0;
    border: 0;
    background: none;
    font: inherit;
    color: inherit;
    text-align: left;
    cursor: pointer;
  }

  .commsch__toggle-label {
    flex: 0 0 auto;
    font-size: var(--jsini-fs-xs);
    font-weight: 600;
    color: var(--jsini-text-muted);
  }

/* 고른 조건은 한 줄로 자른다(두세 줄이 되면 접어서 아낀 자리를 도로 내준다). */
  .commsch__toggle-sum {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    font-size: var(--jsini-fs-sm);
    font-weight: 600;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .commsch__toggle-caret {
    flex: 0 0 auto;
    background-color: var(--jsini-text-muted);
    transition: transform 0.15s ease;
  }

/* 펴져 있으면 꺾쇠가 위를 본다. 위를 보는 그림을 따로 두면 각도가 어긋나므로 뒤집는다. */
  .commsch:not(.commsch--folded) .commsch__toggle-caret {
    transform: rotate(180deg);
  }

/* 접히면 조건이 사라지고 머리줄과 화면 단추만 남으며 판의 여백도 줄인다. 클래스를 둘 겹쳐 적어 뒤의 좁은 화면 묶음과 값싸움에서 이긴다. */
  .commsch.commsch--folded {
    padding-block: 0.35rem;

/* 접힌 줄에서는 머리줄과 화면 단추를 가운데로 맞춘다(기본 flex-start 면 머리줄 글자만 위로 붙는다). */
    align-items: center;
  }

  .commsch--folded .commsch__fields {
    display: none;
  }

/* 접히면 머리줄은 제 폭만 차지한다. 100% 로 두면 화면 단추가 다음 줄로 밀려 접은 값이 반으로 준다. */
  .commsch--folded .commsch__toggle {
    width: auto;
    flex: 1 1 auto;
    min-width: 0;
  }

/* 화면 단추(등록 따위)는 접혀도 남는다. 조건 뒤에 숨으면 휴대폰에서 등록할 길이 없다. 아래 좁은 화면 묶음의 width 100% 를 여기서 되돌린다. */
  .commsch--folded .commsch__actions {
    width: auto;
    margin-left: auto;

/* 판이 접힌 줄에서만 가운데로 맞추므로 그 묶음에 박아 둔 flex-start 도 여기서 푼다. */
    align-self: center;
  }

/* 접으면 조회·초기화만 사라진다. 조건이 안 보이는 동안 누를 일이 없고, 조회는 접는 순간 이미 나갔다. */
  .commsch--folded .commsch__run {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .commsch__toggle-caret {
    transition: none;
  }
}

/* 조건 하나와 새로고침 아이콘만 있는 판(commsch--inline)은 좁은 화면에서도 한 줄로 선다. 단추 줄을 내리면 아이콘 하나가 판 한 줄을 먹는다. */
@media (max-width: 767px) {
  .commsch.commsch--inline {
    flex-wrap: nowrap;
    align-items: center;
    column-gap: 0.5rem;
  }

  .commsch--inline .commsch__fields {
    width: auto;
    flex: 1 1 0;
  }

  .commsch--inline .commsch__actions {
    width: auto;
    align-self: center;
  }

  .commsch--inline .commsch__actions .dxbl-btn {
    min-width: 0;
  }
}

/* 좁은 화면에서는 단추 줄이 통째로 아래로 내려간다. 오른쪽에 매달면 조건 한 칸과 단추 셋이 한 줄에 끼어 둘 다 못 읽는다. */
@media (max-width: 767px) {
  .commsch__actions {
    margin-left: 0;
    width: 100%;
    justify-content: flex-end;
  }

  .commsch__fields {
    width: 100%;
  }

  .commsch__item {
    flex: 1 1 100%;
    width: 100%;
  }

  .commsch__item > :last-child {
    flex: 1 1 auto;
    width: min(100%, 18rem) !important;
  }

/* 라벨이 칸 앞에 서는데 길이가 제각각이라 좁은 화면에서 칸의 왼쪽 끝이 들쭉날쭉해진다. 짧은 라벨에 최소 폭을 주어 기둥을 맞춘다. */
  .commsch__item > .commsch__label {
    flex: 0 0 auto;
    min-width: 3.25rem;
  }

  .commgrd:has(.dxbl-grid-bottom-panel) .dxbl-grid-bottom-panel {
    flex-wrap: wrap;
    min-height: 3.5rem;
    padding-left: 0.65rem;
  }

  .commgrd:has(.dxbl-grid-bottom-panel) .commgrd__footer {
    position: static;
    order: 2;
    width: 100%;
    height: auto;
    min-height: 2.5rem;
    justify-content: flex-start;
  }

  .jsini-dashboard-section__head {
    align-items: flex-start;
  }

  .jsini-dashboard-section__spacer {
    display: none;
  }
}

/* 자료 영역(CommCont). 조회 영역 아래에서 내용을 담는다. 테두리를 두르지 않는다(안의 표가 이미 테두리를 가져 선이 겹친다). 이 판은 제목 줄과 간격을 준다. */

.commcont {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* 업무 화면은 쪽 전체가 구르면 안 된다. 조건은 위에, 아래 띠는 아래에 두고 구르는 것은 자료뿐이다. CommCont 를 쓰는 화면에만 건다(아직 안 옮긴 화면은 쪽이 구른다). */

.jsini-shell__content:has(> .commcont) {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* 조회 영역과 제목 줄은 제 높이만큼 두고 자료 영역이 남은 높이를 다 먹는다. min-height 0 이 없으면 높이 계산이 어긋난다. */
.jsini-shell__content:has(> .commcont) > .commcont {
  flex: 1;
  min-height: 0;
}

.jsini-shell__content:has(> .commcont) > .commcont > .commcont__body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* 자료 영역이 둘 이상이면 사이를 벌린다. 마지막 것 아래는 벌리지 않는다(본문 여백이 이미 있다). */
.commcont + .commcont {
  margin-top: 1.4rem;
}

.commcont__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
  margin-bottom: 0.6rem;
}

/* 구역 제목(CommCont)은 화면 제목(PageHeading)보다 한 단계 작다. 같은 크기면 어느 쪽이 위인지 안 갈린다. */
.commcont__title {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 600;
}

.commcont__hint {
  color: var(--jsini-text-muted);
}

.commcont__spacer { flex: 1; }

.commcont__body { min-width: 0; }

/* 공통 그리드(CommGrd). 클래스 이름은 부품 이름에 맞춘다(개발자 도구에서 어느 파일을 고칠지 바로 보이도록). 겉모습이 같아도 다른 부품의 클래스를 빌려 쓰지 않는다. */

.commgrd {
  display: flex;
  flex-direction: column;
  min-width: 0;

/* 아래 띠의 높이. 페이저 줄과 같은 변수로 정해 한 줄로 보이게 한다. */
  --commgrd-bar-height: 3rem;

/* 표의 최대 높이. 없으면 자기 내용만큼 늘어나 아래 띠가 화면 밖으로 나간다. 묶어 두면 DevExpress 가 표 안쪽에서 굴려 칸 머리와 페이저 줄은 제자리에 남는다. 화면마다 위쪽 요소가 달라 화면에서 이 변수만 덮는다. */
  --commgrd-max-height: 58vh;
}

/* 표의 머리줄(제목 · 건수). 표에 붙은 띠이지 표 위에 뜬 글자가 아니다. 바탕과 선은 두지 않는다(아래 칸 머리가 이미 경계를 낸다). 단추를 두지 않는다(표의 동작은 오른쪽 클릭 창에 있다). 제목은 h3 다. */
.commgrd__head {
  display: flex;
  flex-wrap: wrap;

/* 제목과 건수는 크기가 다르므로 밑선을 맞춰야 한 줄로 읽힌다. */
  align-items: baseline;
  gap: 0.1rem 0.6rem;
  min-width: 0;
  min-height: 2.5rem;
  padding: 0.35rem 0.75rem;
  background: transparent;
  border: 0;
}

/* 제목이 선 머리줄은 왼쪽 여백을 갖지 않는다(제목 앞의 막대가 표의 왼쪽 선과 같은 자리에 서야 해서). */
.commgrd__head:has(.commgrd__title) {
  padding-left: 0;
}

/* 제목 앞에 테마색 막대를 세운다. 카드가 여럿 놓인 화면에서 무엇의 표인지 잡히고, 막대는 글자 높이만큼만 선다. */
.commgrd__title {
  position: relative;
  margin: 0;
  padding-left: 0.7rem;
  font-size: var(--jsini-fs-lg);
  line-height: 1.35;
}

.commgrd__title::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.14em;
  bottom: 0.14em;
  width: 3px;
  border-radius: 1px;
  background: var(--jsini-accent);
}

/* 건수는 단위를 옅게 두고 수만 진하게 한다. 등폭 숫자로 자릿수가 늘어도 옆 글자가 밀리지 않는다. */
.commgrd__total {
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-sm);
  font-variant-numeric: tabular-nums;
}

.commgrd__total b {
  font-weight: 600;
  color: var(--jsini-text);
}

/* 낱자(건·대·개)는 수와 다른 색으로 둔다. 같은 색이면 525건이 한 덩어리로 보인다. */
.commgrd__unit {
  color: var(--jsini-text-muted);
  font-weight: 400;
}

/* 표 위 도구줄. 조회 영역(CommSch)을 따로 둔 화면에서는 그려지지 않는다. */
.commgrd__toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  margin-bottom: 0.75rem;
}

/* 왼쪽 묶음과 오른쪽 묶음을 갈라 놓는 빈 칸. */
.commgrd__spacer { flex: 1; }

/* 관리 칸의 단추 묶음은 글자 없이 아이콘만 쓴다. 칸에는 「⋮」 하나만 세우고 단추들은 옆으로 펴지는 판에 담는다. 글자를 뗀 자리는 Title 이 받으므로 모든 단추에 반드시 적는다. 되돌릴 수 없는 것은 warn, 켜져 있는 것은 on 색으로 가른다. */
.commgrd__actions {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.25rem;
}

/* 관리 칸에는 「⋮」 하나만 서고, 누르면 그 왼쪽으로 판이 펴진다. 판 안의 단추 줄은 칸에 있던 것과 같은 클래스라 크기·색이 같다. */
.commgrd__actmenu {
  --dxbl-flyout-body-padding-x: 0.25rem;
  --dxbl-flyout-body-padding-y: 0.25rem;
}

.commgrd__actions--menu {
  flex-wrap: nowrap;
}

/* 판이 붙는 자리는 「⋮」 폭으로 묶는다. 칸 폭만큼 늘어나면 카드에서 판이 「⋮」에서 멀리 떨어져 선다. */
.commgrd__more {
  width: fit-content;
  margin-inline: auto;
}

/* 자료 칸의 기본 정렬. TextAlignment 를 직접 정한 칸에는 붙지 않는다. DevExpress 의 정렬 클래스보다 이기도록 붙는 자리를 좁힌다(넓히면 금액 칸까지 끌려온다). */
.commgrd .dxbl-grid-table > tbody > tr > td.commgrd__cell--center {
  text-align: center;
}

/* 순번은 자리 표시라 옅게 가운데로 둔다. 등폭 숫자로 자릿수가 늘어도 칸이 밀리지 않는다. */
.commgrd__no {
  display: block;
  text-align: center;
  color: var(--jsini-text-muted);
  font-variant-numeric: tabular-nums;
}

.commgrd__empty {
  padding: 2rem;
  text-align: center;
  color: var(--jsini-text-muted);
}

/* 빈 표는 오류가 아니라 현재 조건의 결과임을 표면으로 드러낸다. 고정 높이 대신 최소 높이만 둔다. */
.commgrd__empty::before {
  content: "⌕";
  display: block;
  margin-bottom: 0.35rem;
  font-size: 1.5rem;
  opacity: 0.55;
}

/* 카드·차트·표가 섞인 대시보드는 구역 사이 간격과 제목 계층을 공통으로 가져야 긴 화면에서 위치를 잃지 않는다. */
.jsini-dashboard-section {
  min-width: 0;
  margin-block: 0 1.25rem;
}

.jsini-dashboard-section__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 0.75rem;
  min-height: 2.5rem;
  margin-bottom: 0.65rem;
}

.jsini-dashboard-section__title {
  margin: 0;
  font-size: var(--jsini-fs-lg);
  line-height: 1.35;
}

.jsini-dashboard-section__hint {
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-sm);
}

.jsini-dashboard-section__spacer {
  flex: 1 1 auto;
}

.jsini-dashboard-section__body {
  min-width: 0;
}

/* 표 아래 띠. 왼쪽 끝이 엑셀 자리다. DxGrid 에는 하단 슬롯이 없어 우리가 그린다. 겹쳐 올리면 DevExpress 마크업에 묶여 버전이 오를 때 함께 깨지므로 페이저 아래 한 줄로 앉는다. */
.commgrd__footer {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  min-height: var(--commgrd-bar-height);
  padding: 0.25rem 0.15rem 0;

/* 아이콘만 있는 자리라 조금 키운다. 1.35rem 은 헤더와 같은 배율을 쓰고 글자 크기를 따라 줄어든다(고정이면 글자만 작아져 아래가 무거워 보인다). */
  --jsini-icon-size: calc(1.35rem * var(--jsini-chrome-scale));
}

/* 켜진 토글과 걸린 조건을 지우는 단추는 색으로만 구분한다(모양까지 바꾸면 아이콘을 두 벌 관리하게 된다). */
.commgrd__icon--on { color: var(--jsini-accent-text); }
.commgrd__icon--warn { color: #d9534f; }

/* 줄무늬. 짝수 줄에 옅은 바탕을 두되 고정색 대신 글자색을 섞어 만든다(테마마다 밝기가 달라도 한 톤 차이가 난다). 반투명이어야 한다(불투명하면 줄무늬가 선택색을 덮는다). 선택 규칙보다 약한 자릿수로 두어 고른 줄에서는 선택색이 이긴다. */
.commgrd .dxbl-grid-table > tbody > tr.commgrd__row--alt {
  background-color: color-mix(in srgb, var(--dxbl-grid-color, currentColor) 4%, transparent);
}

/* 붙박인 칸은 굴러가는 칸 위에 떠 있어 바탕이 비치면 지나가는 글자가 보인다. 여기서만 불투명하게 칠한다. DevExpress 의 고정 칸 규칙보다 상세한 선택자를 써야 이긴다. 고른·포커스·호버 줄에서는 inherit 로 강조색을 받는다. */
.commgrd .dxbl-grid .dxbl-grid-table > tbody > tr > td.dxbl-grid-fixed-cell {
  background-color: var(--jsini-bg);
}

.commgrd .dxbl-grid .dxbl-grid-table > tbody > tr.commgrd__row--alt > td.dxbl-grid-fixed-cell {
  background-color: color-mix(in srgb, var(--dxbl-grid-color, currentColor) 4%, var(--jsini-bg));
}

.commgrd .dxbl-grid .dxbl-grid-table > tbody > tr.dxbl-grid-focused-row > td.dxbl-grid-fixed-cell,
.commgrd .dxbl-grid .dxbl-grid-table > tbody > tr.dxbl-grid-selected-row > td.dxbl-grid-fixed-cell,
.commgrd .dxbl-grid .dxbl-grid-table.dxbl-grid-row-hover-enabled > tbody > tr:hover > td.dxbl-grid-fixed-cell {
  background-color: inherit;
}

/* 칸 머리는 가운데 정렬. DevExpress 머리 칸의 첫 span(글자)만 가운데로 보내면 정렬 화살표와 깔때기는 제자리에 남는다. 자료 칸은 건드리지 않는다. */
.commgrd .dxbl-grid-header-content > span:first-child {
  text-align: center;
}

/* 페이저 줄에 아이콘을 얹는다. 페이저는 DxGrid 안에 있어 우리 띠와 DOM 이 갈린다. 기대는 .dxbl-grid-bottom-panel 하나뿐이고, 이름이 바뀌면 띠는 예전처럼 표 아래 한 줄로 돌아간다(깨지지 않고 모양만 되돌아간다). */

/* 표를 화면 안에 묶는다. CommCont 안이면 남은 높이를 정확히 채우므로 어림값이 필요 없고, 이 규칙은 표를 본문에 바로 놓은 화면을 위한 것이다. */
.commgrd > .dxbl-grid {
  max-height: var(--commgrd-max-height);
}

/* 자료 영역 안에서는 남은 높이를 정확히 채운다. 모니터 크기와 무관하다 —
   vh 어림값이 아니라 부모가 준 높이를 그대로 쓴다. */
.commcont__body > .commgrd {
  flex: 1;
  min-height: 0;
}

/* 나무(`DxTreeList`)와 맨 `DxGrid` 는 `CommGrd` 로 감싸이지 않아 위 규칙이
   걸리지 않는다. 같은 일을 해 준다 — 안 해 주면 자료만큼 늘어나 판을 넘고,
   본문이 `overflow: hidden` 이라 **넘은 부분이 잘려** 아래 띠를 누를 수 없다.

   `max-height` 는 건드리지 않는다. 자료 판이 높이를 안 주는 좁은 화면에서는
   그 상한이 표를 붙잡아 주는 유일한 것이다. */
.commcont__body > .dxbl-grid {
  flex: 1;
  min-height: 0;
}

.commcont__body > .commgrd > .dxbl-grid {
  height: 100%;
  max-height: none;
}

/* 머리줄이 선 표는 `height: 100%` 로 두면 **판을 머리줄 높이만큼 넘는다** —
   그 높이는 판 전체를 가리키므로 위에 한 줄이 생긴 만큼 아래가 밀린다.
   본문이 `overflow: hidden` 이라 넘은 부분은 잘리고, 잘리는 것이 하필
   페이저 줄과 아래 띠다. 남은 높이를 flex 로 받게 한다. */
.commcont__body > .commgrd:has(> .commgrd__head) > .dxbl-grid {
  flex: 1;
  min-height: 0;
  height: auto;
}

.commgrd:has(.dxbl-grid-bottom-panel) { position: relative; }

/* 페이저 줄. 높이를 우리가 정하고 페이저를 오른쪽 끝으로 민다. */
.commgrd:has(.dxbl-grid-bottom-panel) .dxbl-grid-bottom-panel {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  min-height: var(--commgrd-bar-height);

  /* 왼쪽에 아이콘 자리를 비워 둔다. 표가 좁아도 페이저가 아이콘 위로
     올라오지 않는다. */
  padding-left: 10rem;
}

/* 그 줄 왼쪽에 얹는다. 폭은 내용만큼만 차지하므로 페이저 클릭을 가리지 않는다. */
.commgrd:has(.dxbl-grid-bottom-panel) .commgrd__footer {
  position: absolute;
  left: 0.15rem;
  bottom: 0;
  height: var(--commgrd-bar-height);
  min-height: 0;
  padding: 0;
}

/* ── 공통 나무 (CommTree) ────────────────────────────────

   `CommGrd` 와 나란히 두는 규칙이다. 이름을 부품 이름에 맞추는 것은 표와 같은
   이유다 — 개발자 도구에 `commtree__footer` 라고 적혀 있으면 어느 파일을
   고쳐야 하는지 바로 보인다.

   **표의 규칙에 묶어 쓰지 않는다.** 겉모습이 같아도 한 선택자로 합치면 표의
   아래 띠를 고칠 때 나무까지 함께 움직이고, 그 사실이 어디에도 안 적힌다.
   대신 값이 어긋나지 않게 두 곳에 같은 크기를 적어 둔다.

   [DevExpress 는 나무에도 `dxbl-grid*` 클래스를 쓴다]

   `DxTreeList` 의 뿌리 요소가 `<dxbl-tree-list class="dxbl-grid">` 이고 표·칸
   머리·페이저 줄의 클래스 이름도 그리드와 같다 — 메뉴 관리 화면의 DOM 에서
   확인했다. 그래서 안쪽을 겨누는 규칙은 **바깥 클래스만 바꿔** 그대로 옮길 수
   있다. 아래가 그렇게 옮겨 온 것들이다.
   ---------------------------------------------------------- */

.commtree {
  display: flex;
  flex-direction: column;
  min-width: 0;

  /* 아래 띠의 높이. 표(`--commgrd-bar-height`)와 같은 값이어야 표와 나무를
     오갈 때 아이콘 줄이 위아래로 튀지 않는다. */
  --commtree-bar-height: 3rem;

  /*
    나무의 최대 높이. 없으면 아래 띠가 화면 밖으로 나간다 — 자세한 것은 위
    `--commgrd-max-height` 머리말에 있다.

    **나무에서는 이것 말고 크기를 다스릴 것이 없다.** 쪽나누기를 끄고 있어서
    (CommTree 머리말 6번) 줄 수만큼 그대로 늘어난다. 묶어 두면 DevExpress 가
    나무 안쪽에서 굴린다 — 칸 머리는 제자리에 남고 자료만 움직인다.
  */
  --commtree-max-height: 58vh;
}

/* 나무 위 도구줄. 조회 영역(CommSch)을 따로 둔 화면에서는 그려지지 않는다. */
.commtree__toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  margin-bottom: 0.75rem;
}

/* 왼쪽 묶음과 오른쪽 묶음을 갈라 놓는 빈 칸. */
.commtree__spacer { flex: 1; }

/* 나무 안 관리 칸의 단추 묶음 (하위 추가 · 수정 · 삭제). */
.commtree__actions {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.25rem;
}

/* 자료 칸의 기본 정렬. 어느 칸에 붙일지는 CommTree 가 정한다.

   **첫 칸에는 붙지 않는다.** 나무의 첫 자료 칸이 계층 칸이라 들여쓰기와 펼침
   화살표가 그 셀 안에 함께 있다. 가운데로 맞추면 깊이마다 제목이 다른 자리에서
   시작해 계층이 눈에 안 들어온다. */
.commtree .dxbl-grid-table > tbody > tr > td.commtree__cell--center {
  text-align: center;
}

.commtree__empty {
  padding: 2rem;
  text-align: center;
  color: var(--jsini-text-muted);
}

/* 나무 아래 띠. 왼쪽 끝이 등록 자리다.

   표와 달리 **페이저 줄에 얹지 않는다** — 쪽나누기를 끄고 있어서 얹을 줄이
   없다. 화면에서 쪽나누기를 되살리면 이 띠는 페이저 줄 아래 한 줄로 남는다. */
.commtree__footer {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  min-height: var(--commtree-bar-height);
  padding: 0.25rem 0.15rem 0;

  /* 글자 없이 아이콘만 있는 자리라 조금 키운다. 표의 아래 띠와 같은 값이다. */
  --jsini-icon-size: 1.35rem;
}

/* 켜진 토글과, 걸린 조건을 지우는 단추. 색으로만 구분한다. */
.commtree__icon--on { color: var(--jsini-accent-text); }
.commtree__icon--warn { color: #d9534f; }

/* 줄무늬. 고정색을 쓰지 않는 이유와, 선택 줄을 이기지 않게 규칙을 낮게 두는
   이유는 표의 `commgrd__row--alt` 머리말에 적어 두었다. */
.commtree .dxbl-grid-table > tbody > tr.commtree__row--alt {
  background-color: color-mix(in srgb, var(--dxbl-grid-color, currentColor) 4%, transparent);
}

/* 붙박인 칸 불투명 처리 (commgrd 와 동일) */
.commtree .dxbl-grid .dxbl-grid-table > tbody > tr > td.dxbl-grid-fixed-cell {
  background-color: var(--jsini-bg);
}

.commtree .dxbl-grid .dxbl-grid-table > tbody > tr.commtree__row--alt > td.dxbl-grid-fixed-cell {
  background-color: color-mix(in srgb, var(--dxbl-grid-color, currentColor) 4%, var(--jsini-bg));
}

.commtree .dxbl-grid .dxbl-grid-table > tbody > tr.dxbl-grid-focused-row > td.dxbl-grid-fixed-cell,
.commtree .dxbl-grid .dxbl-grid-table > tbody > tr.dxbl-grid-selected-row > td.dxbl-grid-fixed-cell,
.commtree .dxbl-grid .dxbl-grid-table.dxbl-grid-row-hover-enabled > tbody > tr:hover > td.dxbl-grid-fixed-cell {
  background-color: inherit;
}

/* 칸 머리는 가운데 정렬. 계층 칸의 머리도 함께 맞춘다 — 머리에는 들여쓰기가
   없어서 자료 칸과 달리 어긋나지 않는다. */
.commtree .dxbl-grid-header-content > span:first-child {
  text-align: center;
}

/* 나무를 화면 안에 묶는다. `CommCont` 안에 있으면 아래 규칙이 이것을 대신한다 —
   그때는 남은 높이를 정확히 채우므로 어림값(58vh)이 필요 없다. */
.commtree > .dxbl-grid {
  max-height: var(--commtree-max-height);
}

/* 자료 영역 안에서는 남은 높이를 정확히 채운다. 모니터 크기와 무관하다. */
.commcont__body > .commtree {
  flex: 1;
  min-height: 0;
}

.commcont__body > .commtree > .dxbl-grid {
  height: 100%;
  max-height: none;
}

/* ── 로그인 · 안내 화면 ──────────────────────────────────────

   [디자인은 소개 사이트(:5556)를 따른다]

   같은 회사의 두 화면이 서로 다른 언어로 말하지 않게 한다. 값은 그쪽
   `site.css` 에서 **그대로 가져왔다** — 눈대중으로 비슷하게 만들지 않는다.

   | | 값 | 소개 사이트의 어디 |
   |---|---|---|
   | 먹 | `#0a0a0a` | `--ink` |
   | 흑연 | `#1c1c1e` | `--graphite` |
   | 강철 | `#6e6e73` | `--steel` |
   | 안개 | `#d2d2d7` | `--mist` |
   | 칸 이름 | 0.75rem · 대문자 · 자간 0.15em | `.field__label` |
   | 입력칸 | 1px `--mist` · 여백 `0.75rem 1rem` · 0.875rem | `.field input` |
   | 초점 | 테두리만 `--ink` | `.field input:focus` |
   | 단추 | 1px 테두리 · 누르면 안팎이 뒤집힌다 | `.btn-outline-dark` |
   | 조용한 링크 | 0.875rem · `--steel` → `--ink` | `.link-quiet` |
   | 눈썹 줄 | 0.75rem · 대문자 · 자간 0.3em | `.hero__eyebrow` |

   **강조색을 쓰지 않는다.** 「강조는 색이 아니라 대비로 낸다」가 그 사이트의
   규칙이다. 그래서 여기에는 `--jsini-accent` 가 한 번도 안 나온다 — 테마를
   스물둘 중 무엇으로 골라 두었든 로그인 화면은 같은 얼굴이다. 로그인 전에는
   그 선택을 읽을 수도 없다(테마는 사람의 것이고 아직 사람이 없다).

   **모서리를 둥글리지 않는다.** 그 사이트에 둥근 모서리가 한 군데도 없다.

   [글꼴은 가져오지 않았다]

   본문 글꼴(S-CoreDream)이 한 벌에 350KB 다. 이 화면은 모두가 가장 먼저 받는
   화면이라(회로와 DevExpress 를 걷어낸 자리다) 700KB 를 얹으면 그 일이
   헛것이 된다. 자간·굵기·크기만 같게 맞췄다.

   [쓰는 곳이 다섯이다]

   로그인 · 가입 신청 · 비밀번호 찾기 · 비밀번호 재설정 · 비밀번호 변경.
   좌우로 갈린 판(`jsini-auth`)은 **로그인만** 쓰고, 카드 안쪽
   (`jsini-login__*`)은 다섯이 함께 쓴다.
   ---------------------------------------------------------- */

.jsini-bare {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}

/* 로그인은 화면을 꽉 채운다. 가운데 정렬과 여백은 카드 하나를 세우는
   화면(가입 신청 등)의 것이라 이때는 걷어낸다. */
.jsini-bare:has(> .jsini-auth) {
  display: block;
  padding: 0;
}

/* ── 로그인만: 좌우로 갈린 판 ────────────────────────────── */

.jsini-auth {
  min-height: 100vh;
  display: grid;

  /* 좁은 화면에서는 브랜드 판이 통째로 빠진다(아래 `display: none`).
     한 칸으로 두면 폼이 그대로 가운데에 선다. */
  grid-template-columns: 1fr;
}

.jsini-auth__brand {
  display: none;
  position: relative;
  overflow: hidden;
  background: #0a0a0a;
  color: #fff;
}

.jsini-auth__brand-inner {
  display: flex;
  flex-direction: column;
  justify-content: center;
  height: 100%;
  padding: 4rem 3.5rem;
}

/* 로고를 글자 크기에 묶지 않는다 — SVG 라 폭만 주면 비율은 viewBox 가 지킨다. */
.jsini-auth__logo {
  width: 9rem;
  height: auto;
}

/* 히어로의 눈썹 줄과 같은 값이다(`.hero__eyebrow`). */
.jsini-auth__eyebrow {
  margin: 2.5rem 0 0;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.3em;
  color: #6e6e73;
}

.jsini-auth__lead {
  margin: 1.25rem 0 0;
  max-width: 24rem;
  font-size: 0.875rem;
  line-height: 1.7;
  color: #d2d2d7;
}

/* 브랜드 판의 사선. 소개 사이트의 상승 조각과 **같은 각도**(사선 22 : 높이
   100 → 102.4deg)다 — 그 사이트가 구분선·강조에 반복해서 쓰는 모양이다. */
.jsini-auth__brand::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    102.4deg,
    transparent 0 46%,
    rgb(255 255 255 / 4%) 46% 54%,
    transparent 54%
  );
  pointer-events: none;
}

.jsini-auth__panel {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem 1rem;
  background: #fff;
}

/* 브랜드 판은 넓을 때만 선다. 판 둘을 360px 화면에 나란히 두면 폼이 못 쓴다. */
@media (width >= 62rem) {
  .jsini-auth { grid-template-columns: 1.05fr 1fr; }
  .jsini-auth__brand { display: block; }

  /* 왼쪽에 이미 로고가 있으므로 카드 안의 것은 뺀다. */
  .jsini-login__logo { display: none; }
}

/* ── 다섯 화면이 함께 쓰는 카드 ──────────────────────────── */

.jsini-login__card {
  width: min(23rem, 100%);
  background: #fff;
  color: #0a0a0a;
}

/* 카드 안 로고. 좁은 화면에서만 보인다(위 질의). */
.jsini-login__logo {
  width: 7.5rem;
  height: auto;
  margin-bottom: 2rem;
}

/* 제목. 소개 사이트의 `.h-display` 와 같은 자간·굵기다. */
.jsini-login__title {
  margin: 0;
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.05;
  color: #0a0a0a;
}

.jsini-login__sub {
  margin: 0.75rem 0 2.25rem;
  font-size: 0.875rem;
  line-height: 1.7;
  color: #6e6e73;
}

.jsini-login__field {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-bottom: 1.25rem;
}

.jsini-login__label {
  display: block;
  margin: 0;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: #6e6e73;
}

.jsini-login__input {
  width: 100%;
  padding: 0.75rem 1rem;
  border: 1px solid #d2d2d7;
  border-radius: 0;
  background: #fff;
  color: #0a0a0a;
  font: inherit;
  font-size: 0.875rem;
  outline: none;
  box-sizing: border-box;
}

/* 초점은 테두리만 짙어진다. 그림자나 바탕을 깔면 강조색이 필요해져 이 화면의 규칙이 무너진다. */
.jsini-login__input:focus { border-color: #0a0a0a; }

.jsini-login__input::placeholder { color: #d2d2d7; }

/* 아이디 기억하기. 네모는 브라우저가 그린다 — 우리가 다시 그리지 않는다. */
.jsini-login__remember {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0.25rem 0 1.75rem;
  font-size: 0.875rem;
  color: #1c1c1e;
  cursor: pointer;
}

.jsini-login__remember input {
  width: 1rem;
  height: 1rem;
  margin: 0;
  flex-shrink: 0;
  accent-color: #0a0a0a;
  cursor: pointer;
}

/* 단추. 누르면 안팎이 뒤집힌다 — 소개 사이트의 `.btn-outline-dark` 그대로다. */
.jsini-login__submit {
  width: 100%;
  padding: 0.75rem 2rem;
  border: 1px solid #0a0a0a;
  border-radius: 0;
  background: transparent;
  color: #0a0a0a;
  font: inherit;
  font-size: 0.875rem;
  cursor: pointer;
  transition: background-color 0.2s, color 0.2s;
}

.jsini-login__submit:hover,
.jsini-login__submit:focus-visible {
  background: #0a0a0a;
  color: #fff;
}

/* 지문·얼굴 단추. 로그인 단추와 같은 모양의 반대말(회색 테두리)이라 기본 동작이 한눈에 보인다. 판은 기본이 감춤이고 passkey.js 가 지원하는 브라우저에서만 꺼낸다(display 를 정하면 hidden 을 이겨 못 쓰는 브라우저에도 보인다). */
.jsini-login__passkey {
  margin-top: 1.75rem;
}

/* 「또는」. 가운뎃줄은 양옆으로 늘어나고 글자 자리만 비운다. */
.jsini-login__or {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1.25rem;
  font-size: 0.75rem;
  letter-spacing: 0.15em;
  color: #6e6e73;
}

.jsini-login__or::before,
.jsini-login__or::after {
  content: '';
  flex: 1;
  height: 1px;
  background: #d2d2d7;
}

.jsini-login__passkey-btn {
  width: 100%;
  /* 손가락 과녁 44px 를 넘긴다. 휴대폰에서 누르는 단추다. */
  padding: 0.75rem 2rem;
  border: 1px solid #d2d2d7;
  border-radius: 0;
  background: transparent;
  color: #1c1c1e;
  font: inherit;
  font-size: 0.875rem;
  cursor: pointer;
  transition: border-color 0.2s, color 0.2s;
}

.jsini-login__passkey-btn:hover,
.jsini-login__passkey-btn:focus-visible {
  border-color: #0a0a0a;
  color: #0a0a0a;
}

/* 기기를 기다리는 동안 눌렸다는 표시를 둔다. 없으면 사용자가 한 번 더 누른다. */
.jsini-login__passkey-btn:disabled {
  color: #6e6e73;
  border-color: #e8e8ed;
  cursor: progress;
}

/* 기기가 거절했을 때의 한 줄. 실패 판과 무게가 같아지면 어느 쪽을 봐야 할지 알 수 없어 테두리를 세우지 않는다. */
.jsini-login__passkey-note {
  margin: 0.75rem 0 0;
  font-size: 0.8125rem;
  line-height: 1.6;
  color: #6e6e73;
}

/* 지문을 먼저 묻는 기기에서만 걸린다. 딱지는 passkey.js 가 붙인다(서버는 그 값을 모른다. 브라우저의 것이다). 마크업은 두 벌 두지 않고 차례만 바뀐다. */
.jsini-login__methods {
  display: flex;
  flex-direction: column;
}

.jsini-login__methods--passkey-first .jsini-login__passkey {
  order: -1;
  /* 맨 위로 올라갔다. 위 여백은 「또는」 아래에 붙어 있을 때 필요했던 값이다. */
  margin-top: 0;
}

/* 판 안의 차례도 뒤집는다(단추가 먼저, 또는이 마지막). :not([hidden]) 이 있어야 못 쓰는 브라우저에서 hidden 을 이긴 단추가 보이지 않는다. */
.jsini-login__methods--passkey-first .jsini-login__passkey:not([hidden]) {
  display: flex;
  flex-direction: column;
}

.jsini-login__methods--passkey-first .jsini-login__passkey-note { order: 1; }

/* 아래 여백을 0 으로 두면 이 줄과 비밀번호 폼이 딱 붙는다(flex 라 안쪽 여백이 합쳐지지 않는다). */
.jsini-login__methods--passkey-first .jsini-login__or {
  order: 2;
  margin: 1.75rem 0 1.25rem;
}

/* 기본 동작이 바뀌었으니 무게도 바뀐다. 색 없이 테두리 짙기로만 먼저인지를 말한다. */
.jsini-login__methods--passkey-first .jsini-login__passkey-btn {
  border-color: #0a0a0a;
  color: #0a0a0a;
}

.jsini-login__methods--passkey-first .jsini-login__passkey-btn:hover,
.jsini-login__methods--passkey-first .jsini-login__passkey-btn:focus-visible {
  background: #0a0a0a;
  color: #fff;
}

/* 기기가 답하기를 기다리는 동안은 이쪽이 이겨야 한다. 순서로 이기면 짙은 단추가 눌린 채 남아 눌렸다는 표시가 안 보인다. */
.jsini-login__methods--passkey-first .jsini-login__passkey-btn:disabled {
  background: transparent;
  color: #6e6e73;
  border-color: #e8e8ed;
}

.jsini-login__methods--passkey-first .jsini-login__submit {
  border-color: #d2d2d7;
  color: #1c1c1e;
}

.jsini-login__methods--passkey-first .jsini-login__submit:hover,
.jsini-login__methods--passkey-first .jsini-login__submit:focus-visible {
  background: transparent;
  border-color: #0a0a0a;
  color: #0a0a0a;
}

/* 실패 안내. 이 화면에는 색이 없어 붉은 줄이 유일한 색이 되면 그것만 보인다. 먹 테두리를 왼쪽에 세워 대비로 알린다. */
.jsini-login__error {
  margin-bottom: 1.5rem;
  border-left: 2px solid #0a0a0a;
  padding: 0.75rem 1rem;
  background: #f5f5f7;
  font-size: 0.875rem;
  line-height: 1.6;
  color: #1c1c1e;
}

/* 실패가 아닌 안내(소셜에서 되돌아온 가입 신청 접수 등). 실패 판과 갈라 둔다. 같은 판으로 그리면 접수된 사람이 자기가 실패한 줄 안다. */
.jsini-login__notice {
  margin-bottom: 1.5rem;
  border-left: 2px solid #86868b;
  padding: 0.75rem 1rem;
  background: #f5f5f7;
  font-size: 0.875rem;
  line-height: 1.6;
  color: #1c1c1e;
}

/* 소셜 로그인·가입. 공급자의 색을 쓴다(카카오·네이버는 로그인 단추의 바탕색과 상징을 지키라는 규정이 있다). 켜지 않은 공급자는 회색으로 가라앉혀 누를 수 없음을 보인다. 기본이 감춤이 아니다(목록을 서버가 준다). */
.jsini-login__social {
  margin-top: 1.75rem;
}

/* 한 줄에 하나씩 쌓는다. 셋을 나란히 두면 글자가 두 줄로 접히고 휴대폰에서 과녁이 좁아진다. */
.jsini-social {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

/* 상징은 왼쪽, 글자는 가운데. 공급자 단추 규정이 모두 이 배치이고, 손가락 과녁 44px 을 넘긴다. */
.jsini-social__btn {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 2.875rem;
  padding: 0 3rem;
  border: 1px solid transparent;
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.3;
  text-decoration: none;
  transition: filter 0.15s, box-shadow 0.15s;
}

a.jsini-social__btn:hover,
a.jsini-social__btn:focus-visible {
  filter: brightness(0.96);
  box-shadow: 0 2px 8px rgb(0 0 0 / 10%);
}

a.jsini-social__btn:focus-visible {
  outline: 2px solid #0a0a0a;
  outline-offset: 2px;
}

.jsini-social__icon {
  position: absolute;
  left: 1rem;
  top: 50%;
  width: 1.25rem;
  height: 1.25rem;
  transform: translateY(-50%);
}

/* 네이버의 N 은 네모를 꽉 채우는 글자라 같은 크기면 혼자 커 보인다. */
.jsini-social__icon--sm {
  width: 0.875rem;
  height: 0.875rem;
  left: 1.1875rem;
}

/* 그림이 없는 공급자(설정만으로 붙인 것). 머리글자를 동그라미에 담는다. */
.jsini-social__icon--letter {
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: #0a0a0a;
  color: #fff;
  font-size: 0.6875rem;
  font-weight: 700;
}

.jsini-social__btn--kakao {
  background: #fee500;
  color: rgb(0 0 0 / 85%);
}

.jsini-social__btn--kakao .jsini-social__icon { color: #000; }

.jsini-social__btn--naver {
  background: #03c75a;
  color: #fff;
}

.jsini-social__btn--google {
  border-color: #dadce0;
  background: #fff;
  color: #1f1f1f;
}

/* 모르는 공급자는 이 카드의 보통 단추 말투를 쓴다. */
.jsini-social__btn:not(.jsini-social__btn--kakao, .jsini-social__btn--naver, .jsini-social__btn--google) {
  border-color: #d2d2d7;
  background: #fff;
  color: #1c1c1e;
}

/* 준비 중. 색을 전부 걷고 점선으로 「아직 없는 자리」임을 보인다. */
.jsini-social__btn.is-soon {
  border: 1px dashed #d2d2d7;
  background: #fafafa;
  color: #aeaeb2;
  font-weight: 500;
  cursor: not-allowed;
}

.jsini-social__btn.is-soon .jsini-social__icon {
  color: #c7c7cc;
  filter: grayscale(1);
  opacity: 0.45;
}

.jsini-social__soon {
  position: absolute;
  right: 0.75rem;
  top: 50%;
  transform: translateY(-50%);
  padding: 0.125rem 0.5rem;
  border-radius: 999px;
  background: #ececf0;
  color: #8e8e93;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

/* 처음 누르는 사람이 로그인을 기대했다가 승인 대기를 만나면 고장으로 읽는다. 누르기 전에 말해 두는 한 줄이다. */
.jsini-login__social-note {
  margin: 0.75rem 0 0;
  font-size: 0.8125rem;
  line-height: 1.6;
  color: #6e6e73;
}

/* 칸 검증 메시지도 같은 규칙으로 조용히 둔다. */
.jsini-bare .validation-message {
  font-size: 0.75rem;
  color: #1c1c1e;
}

/* 가입 신청은 칸이 일곱이라 로그인 카드보다 넓어야 한다. */
.jsini-login__card--wide {
  width: min(30rem, 100%);
}

/* 가입 신청 카드. 회색 바탕 위에 카드를 종이 한 장으로 세운다(테두리와 여백이 없으면 칸들이 흩어진다). 이름표는 로그인의 대문자·넓은 자간을 풀어 준다(한글 일곱 줄에 걸면 글자가 흩어진다). */
.jsini-login__card--panel {
  margin: 2rem 0;
  padding: 2.5rem 2.25rem 2rem;
  border: 1px solid #e8e8ed;
  box-shadow:
    0 1px 2px rgb(0 0 0 / 4%),
    0 16px 40px -16px rgb(0 0 0 / 14%);
  box-sizing: border-box;
}

.jsini-login__card--panel .jsini-login__sub {
  margin-bottom: 1.75rem;
}

.jsini-login__card--panel .jsini-login__field {
  gap: 0.375rem;
  margin-bottom: 1rem;
  min-width: 0;
}

.jsini-login__card--panel .jsini-login__label {
  font-size: 0.8125rem;
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  color: #1c1c1e;
}

.jsini-login__card--panel .jsini-login__input {
  padding: 0.6875rem 0.875rem;
}

.jsini-login__card--panel .jsini-login__or {
  margin: 1.75rem 0 1.5rem;
  letter-spacing: 0.02em;
}

/* 소셜이 맨 위에 올 때는 위 여백이 제목 아래 여백과 겹친다. */
.jsini-login__social--top {
  margin-top: 0;
}

/* 「선택」 꼬리표. 반드시 넣을 칸과 아닌 칸을 이름표에서 가른다. */
.jsini-login__optional {
  margin-left: 0.25rem;
  font-size: 0.75rem;
  font-weight: 400;
  color: #86868b;
}

/* 짧은 칸 둘을 나란히. 좁은 화면에서는 한 줄씩 떨어진다. */
.jsini-login__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 0.75rem;
}

/* 가입 신청의 단추는 칸을 다 채운 뒤 누르는 하나뿐인 끝이라 먹으로 채운다. */
.jsini-login__submit--solid {
  margin-top: 0.5rem;
  padding-block: 0.875rem;
  background: #0a0a0a;
  color: #fff;
  font-weight: 600;
}

.jsini-login__submit--solid:hover,
.jsini-login__submit--solid:focus-visible {
  background: #2c2c2e;
}

.jsini-login__submit:disabled {
  opacity: 0.55;
  cursor: progress;
}

/* 카드 맨 아래 「이미 계정이 있으신가요? 로그인」. */
.jsini-login__foot {
  margin: 1.5rem 0 0;
  text-align: center;
  font-size: 0.875rem;
  color: #6e6e73;
}

.jsini-login__foot a {
  margin-left: 0.25rem;
  color: #0a0a0a;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

/* 휴대폰에서는 종이를 걷고 화면 전체를 카드로 쓴다(테두리가 가장자리와 겹치면 틀이 두 겹이 된다). */
@media (width < 34rem) {
  .jsini-login__card--panel {
    margin: 0;
    padding: 1.75rem 0.25rem;
    border: 0;
    box-shadow: none;
    background: transparent;
  }

  .jsini-login__row { grid-template-columns: 1fr; }
}

/* 여러 줄 입력도 같은 칸 모양을 쓴다. 높이만 내용에 맡긴다. */
.jsini-login__textarea {
  resize: vertical;
  min-height: 4.5rem;
}

/* 카드 아래 링크. 소개 사이트의 link-quiet 와 같다(평소 강철, 올리면 먹). */
.jsini-login__links {
  display: flex;
  justify-content: space-between;
  gap: 0.75rem;
  margin-top: 2rem;
  padding-top: 1.5rem;
  border-top: 1px solid #d2d2d7;
  font-size: 0.875rem;
}

.jsini-login__links a {
  color: #6e6e73;
  text-decoration: none;
  transition: color 0.2s;
}

.jsini-login__links a:hover { color: #0a0a0a; }

/* 부제가 없는 카드에서 제목과 첫 칸 사이를 벌린다(제목에는 아래 여백이 없다). */
.jsini-login__gap { height: 2.25rem; }

/* 비밀번호 찾기 뒤 할 일을 적는 차례 목록. 번호는 브라우저에 맡기지 않고 직접 찍는다(글꼴마다 들여쓰기가 달라진다). */
.jsini-steps {
  margin: 1.25rem 0;
  padding: 0;
  list-style: none;
  counter-reset: jsini-step;
  font-size: 0.875rem;
  line-height: 1.7;
  color: #1c1c1e;
}

.jsini-steps li {
  position: relative;
  padding: 0 0 0 1.75rem;
  margin-bottom: 0.6rem;
}

.jsini-steps li::before {
  counter-increment: jsini-step;
  content: counter(jsini-step);
  position: absolute;
  left: 0;
  top: 0.15rem;
  width: 1.15rem;
  height: 1.15rem;
  line-height: 1.15rem;
  text-align: center;
  font-size: 0.6875rem;
  background: #0a0a0a;
  color: #fff;
}

/* 카드 안의 안내 줄. 이 카드는 테마를 따라가지 않으므로(흰 바탕에 먹 글씨) 색은 테마 변수가 아니라 고정값을 쓴다. 어두운 테마에서 글자가 사라진 적이 있다. 세 말투는 색이 아니라 왼쪽 글자(i · ! · ×)로 가른다. */

.jsini-login__card .jsini-notice {
  margin: 0 0 1.5rem;
  line-height: 1.6;

  /* 모서리도 그림자도 없다. 이 화면의 다른 상자들이 각져 있다. */
  border: 0;
  border-left: 2px solid #0a0a0a;
  border-radius: 0;
  box-shadow: none;
  background: #f5f5f7;
  color: #1c1c1e;
  font-size: 0.875rem;
}

.jsini-login__card .jsini-notice::before {
  color: #0a0a0a;
}

/* 비밀번호 변경 화면에서 「나중에 하기」. 만료로 끌려온 사람에게는 안 보인다. */
/* 조용한 링크. 색과 크기를 테마 변수에서 얻지 않는다(카드가 테마를 따르지 않아 어두운 테마에서 사라진다). 카드 아래 링크와 같은 값이다. */
.jsini-password__back {
  display: block;
  margin-top: 0.9rem;
  text-align: center;
  font-size: 0.875rem;
  color: #6e6e73;
  text-decoration: none;
  transition: color 0.2s;
}

.jsini-password__back:hover { color: #0a0a0a; }

/* 카드 안의 곁들이는 한 줄도 같은 이유로 고정한다. */
.jsini-login__card .jsini-hint {
  font-size: 0.8125rem;
  color: #6e6e73;
}

/* 단추처럼 생긴 링크. jsini-login__submit 은 button 기준이라 링크에는 이 둘이 더 필요하다. */
.jsini-password__go {
  display: block;
  text-align: center;
  text-decoration: none;
  box-sizing: border-box;
}

.jsini-message {
  text-align: center;
  max-width: 480px;
}

.jsini-message__trace code {
  user-select: all; /* 사용자가 추적 번호를 한 번에 집을 수 있게 */
}

/* 추적 번호가 길어(55자) 손으로 집기 어렵다. 그래서 집기 단추를 둔다(휴대폰에서 난 오류를 전화로 불러 주는 자리). */
.jsini-message__copy {
  margin-left: 0.5rem;
  padding: 0.1rem 0.5rem;
  font-size: 0.75rem;
  color: #6e6e73;
  background: transparent;
  border: 1px solid #d2d2d7;
  border-radius: 4px;
  cursor: pointer;
}

.jsini-message__copy:hover { color: #0a0a0a; }

/* ── 진단 화면 ───────────────────────────────────────────── */

.jsini-diagnostics__facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.35rem 1rem;
  margin-top: 1.5rem;
  font-size: var(--jsini-fs-md);
}

.jsini-diagnostics__facts dt {
  color: var(--jsini-text-muted);
}

.jsini-diagnostics__facts dd {
  margin: 0;
}

/* ── 준비 중 안내 (아직 안 옮긴 화면) ────────────────────── */

.jsini-pending {
  max-width: 620px;
  padding: 2rem 0;
}

.jsini-pending__title {
  margin-bottom: 0.75rem;
}

.jsini-pending__lead {
  font-size: var(--jsini-fs-lg);
  line-height: 1.6;
}

.jsini-pending__facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.35rem 1rem;
  margin: 1.25rem 0;
  padding: 0.9rem 1rem;
  border-radius: 6px;
  background: var(--jsini-surface-muted, rgba(127, 127, 127, 0.08));
  font-size: var(--jsini-fs-md);
}

.jsini-pending__facts dt {
  color: var(--jsini-text-muted);
}

.jsini-pending__facts dd {
  margin: 0;
}

.jsini-pending__facts code {
  user-select: all; /* 주소를 한 번에 집어 신고에 붙일 수 있게 */
}

.jsini-pending__hint {
  font-size: var(--jsini-fs-md);
  color: var(--jsini-text-muted);
  line-height: 1.6;
}

.jsini-pending__actions {
  display: flex;
  gap: 0.5rem;
  margin-top: 1.5rem;
}

/* ── 화면 공통 (모듈 여섯이 함께 쓴다) ───────────────────── */

.jsini-page-head {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1rem;
}

.jsini-page-head__title {
  margin: 0;
  font-size: var(--jsini-fs-2xl);
  font-weight: 700;
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}

/* 업무 이름. 제목과 같은 크기로 두면 어느 쪽이 화면 이름인지 헷갈린다. */
.jsini-page-head__scope {
  font-size: var(--jsini-fs-sm);
  font-weight: 400;
  color: var(--jsini-text-muted);
}

.jsini-page-head__actions {
  display: flex;
  gap: 0.5rem;
  align-items: center;
}

/* 조건줄은 좁은 화면에서 줄바꿈되게 둔다(가로 스크롤이 생기면 오른쪽 조회 단추가 화면 밖으로 나간다). */
.jsini-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  margin-bottom: 1rem;
}

.jsini-toolbar__spacer { flex: 1; }

/* ── 배치 — Bootstrap 유틸리티 대신 쓰는 이름들 ───────────── */
/* 기본 테마 Fluent 에는 Bootstrap 이 없다. d-flex 같은 유틸리티는 오류 없이 배치만 어긋난다. web/CLAUDE.md 의 규칙을 본다. */

/* 단추 묶음(표의 관리 칸)과 이름 앞 딱지 묶음. 좁은 칸에서 줄바꿈되게 둔다(넘쳐 잘리면 삭제 단추가 사라진다). */
.jsini-actions,
.jsini-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  align-items: center;
}

/* 한 칸 안에 조작을 나란히 놓는 자리. 체크박스와 입력칸이 붙으면 어느 이름이 어느 것인지 흐려져 넉넉히 띄운다. */
.jsini-inline {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
}

/* 왼쪽에 설명, 오른쪽에 조작을 두고 벌리는 줄. */
.jsini-split {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  justify-content: space-between;
}

/* 테두리 상자. Bootstrap `.card` 자리다. */
.jsini-card {
  padding: 0.85rem 1rem;
  margin-bottom: 1rem;
  border: 1px solid var(--jsini-border, #e0e0e0);
}

/* 흐린 글씨. 여러 화면이 이 이름을 쓰고 있었지만 정의가 없어 여느 글씨와 같았다. */
.jsini-text-muted { color: var(--jsini-text-muted); }

/* 곁들이는 한 줄(관측 시각 · 읽기 전용 안내). 크기는 사다리에서 얻는다. */
.jsini-hint {
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text-muted);
}

/* 차트 툴팁 속 내용. DxChartTooltip 은 테두리·바탕만 그리고 여백이 없어 글자가 모서리에 붙는다. */
.jsini-tip { padding: 0.35rem 0.5rem; }

/* 확인 창(ConfirmDialog). 글은 줄바꿈을 살린다(무엇이 사라지는지 두 줄로 적는 경우가 있다). */
.jsini-confirm__body {
  padding: 0.5rem 0.25rem;
  white-space: pre-line;
  line-height: 1.5;

/* 묻는 글에 사람이 지은 이름이 들어가면 띄어쓰기 없이 길어 창을 넘는다. overflow-wrap 을 anywhere 로 주되 넘칠 때만 끊는다. */
  overflow-wrap: anywhere;
}

.jsini-notice {
  margin: 0.75rem 0;
  padding: 0.6rem 0.85rem;
  font-size: var(--jsini-fs-md);
  border-left: 3px solid;
}

.jsini-notice--info {
  border-color: var(--jsini-text-muted);
  background: color-mix(in srgb, var(--jsini-text-muted) 8%, transparent);
}

.jsini-notice--warning {
  border-color: #b26a00;
  background: color-mix(in srgb, #b26a00 10%, transparent);
}

.jsini-notice--error {
  border-color: #d32f2f;
  background: color-mix(in srgb, #d32f2f 10%, transparent);
  color: #d32f2f;
}

/* 상태 표시. 글자로도 읽히게 두고 색은 거들기만 한다(색각 이상 사용자를 위해). */
/* 설정 줄(.jsini-setting). 왼쪽 이름·설명, 오른쪽 스위치. 알림 설정과 환경설정이 함께 쓰므로 공용에 둔다. */

.jsini-settings { margin-bottom: 1rem; }

.jsini-setting {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 0;
  border-bottom: 1px solid var(--jsini-border);
}

.jsini-setting__name { font-weight: 600; }

.jsini-setting__desc {
  margin-top: 0.15rem;
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text-muted);
}

/* 화면 안의 작은 제목. 한 화면이 여러 묶음으로 나뉠 때 그 사이를 가른다. */
.jsini-subhead {
  margin: 1.5rem 0 0.75rem;
  font-size: var(--jsini-fs-lg);
  font-weight: 700;
}

/* 알림 구독을 권하는 창(PushAskPopup). 본문은 한 줄과 목록이 전부다. 길어지면 사람이 읽지 않고 닫는다. */

.jsini-pushask__lead { margin: 0 0 0.75rem; }

.jsini-pushask__facts {
  margin: 0;
/* 점 자리만큼만 들여쓴다(기본 2.5rem 은 좁은 창에서 글을 오른쪽으로 밀어 한 줄에 몇 글자 못 들어간다). */
  padding-left: 1.1rem;
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text-muted);
}

.jsini-pushask__facts li + li { margin-top: 0.35rem; }

/* 사파리에 적어 주는 설치 순서. 단추를 놓을 수 없는 자리를 글이 대신하므로 흐리게 두지 않는다. 시키는 대로 따라 해야 하는 대목이다. */
.jsini-pushask__howto {
  margin: 0.75rem 0 0;
  padding: 0.6rem 0.75rem 0.6rem 1.9rem;
  border-radius: var(--jsini-radius-md);
  background: var(--jsini-surface-muted);
  font-size: var(--jsini-fs-sm);
}

.jsini-pushask__howto li + li { margin-top: 0.35rem; }

/* 알림 판의 덩어리 셋(NotificationPanel). 상자를 만들지 않는다(display: contents). 화면이 격자를 깔면 셋이 그 칸으로 바로 들어가므로, 벌려 놓는 규칙은 그렇게 쓰는 화면이 갖는다. */
.jsini-npanel { display: contents; }

/* 알림 서버에 못 닿았을 때 구역 자리에 접히는 한 덩어리. 자리가 좁으면 단추가 눌려 글자가 쪼개지므로 아래로 내려 보낸다. */
.jsini-npanel__down {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1rem;
}

.jsini-npanel__down-text {
  min-width: 0;
  flex: 1 1 12rem;
}

/* 휴대폰의 등록된 기기 카드. 칸이 셋(기기 · 등록 · 빼기)이고 카드로 펴면 한 장이 길어 쪽이 통째로 이 표가 된다. 빼기 칸은 이름이 없어 값 없는 줄로 떨어지므로 귀퉁이로 올리고, 이름표를 걷어 값이 폭을 얻게 한다. 날짜는 흐리게 눕혀 제목과 덧말 꼴로 둔다. */
@media (max-width: 767px) {

  /* 오른쪽 위의 「⋮」 가 설 자리를 비운다. 안 비우면 긴 기기 이름이 그 밑으로
     들어가 글자가 가려진다. */
  .jsini-npanel__devices .commgrd--cards .dxbl-grid-table > tbody > tr[data-visible-index] {
    padding-right: 3.2rem;
  }

  /* 이름표를 걷는다. `display: flex` 도 함께 되돌린다 — 이름이 빠지면
     나란히 세울 것이 값 하나뿐이다. */
  .jsini-npanel__devices .commgrd--cards .dxbl-grid-table > tbody > tr > td[data-caption] {
    display: block;
  }

  .jsini-npanel__devices .commgrd--cards .dxbl-grid-table > tbody > tr > td[data-caption]::after {
    content: none;
  }

  /* 제목 다음에 오는 칸 — 여기서는 등록 날짜 하나다. 흐린 덧말로 눕힌다. */
  .jsini-npanel__devices .commgrd--cards .dxbl-grid-table > tbody > tr > td[data-caption] ~ td[data-caption] {
    font-size: var(--jsini-fs-xs);
    color: var(--jsini-text-muted);
  }
}

/* 상태 넷을 나란히 놓는 자리(「이 브라우저」 — 실행 방식·서비스워커·권한·등록).
   칸 하나가 「이름 / 딱지」 한 쌍이고, 자리가 좁으면 한 줄씩 쌓인다.

   `<dl>` 을 쓰되 격자는 `<div>` 칸에 건다 — dt·dd 를 직접 격자 항목으로
   두면 이름과 값이 서로 다른 줄에 흩어져 짝이 안 보인다. */
.jsini-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 0.5rem 1rem;
  margin: 0 0 1rem;
}

.jsini-fact { min-width: 0; }

.jsini-fact dt {
  font-size: var(--jsini-fs-xs);
  color: var(--jsini-text-muted);
  margin-bottom: 0.2rem;
}

.jsini-fact dd { margin: 0; }

/* ── 내 위치 날씨 (`NotificationPanel` 의 위치 칸) ──────────────

   스위치 목록(`.jsini-settings`)과 **선 하나로 가른다.** 여기서 하는 일은
   켜고 끄는 것이 아니라 좌표 하나를 잡아 두는 일이고, 잡은 뒤에 「여기가
   맞나」를 되묻는 자리라 줄 하나로는 안 된다. 선이 없으면 위 체크들과
   같은 목록의 연장으로 읽힌다.
   ---------------------------------------------------------- */
.jsini-weather-loc {
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--jsini-border);
}

/* 저장된 자리의 이름 한 줄. 아래 칸들은 단계별로 갈라 놓은 것이라
   「어디인가」를 한눈에 말해 주지 못한다. */
.jsini-weather-loc__place {
  margin-bottom: 0.6rem;
  font-size: var(--jsini-fs-lg);
}

/* ── 내 위치 지도 (`LocationMap`) ──────────────────────────────

   OpenStreetMap 의 끼워 넣기 판이다. **높이를 여기서 정한다** — iframe 은
   내용에 맞춰 늘지 않아서 높이를 안 주면 기본 150px 로 찌부러진다.

   폭은 판을 꽉 채우고 높이는 220px 로 고정한다. 비율로 두면 넓은 화면에서
   지도만 한 화면을 차지하고, 좁은 화면에서는 동네 이름이 안 읽힌다.
   ---------------------------------------------------------- */
.jsini-map { margin: 0.75rem 0; }

.jsini-map__frame {
  display: block;
  width: 100%;
  height: 220px;
  border: 1px solid var(--jsini-border);
  background: var(--jsini-surface-alt, rgba(127, 127, 127, 0.08));
}

/* 좌표와 「큰 지도로 보기」. **판 밖이다** — 타일이 막힌 망에서 지도가
   비어도 확인할 것은 남아야 한다. */
.jsini-map__foot {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.5rem;
  margin-top: 0.35rem;
  font-size: var(--jsini-fs-sm);
}

.jsini-map__coord { color: var(--jsini-text-muted); }

/* 알림창에 뜰 모양 그대로 보여 주는 자리. **바탕을 깔아 둘러싼다** —
   이것이 화면의 설명이 아니라 **밖에서 올 알림**이라는 것이 보여야 한다. */
.jsini-weather-preview {
  margin: 0.75rem 0;
  padding: 0.75rem 0.9rem;
  background: var(--jsini-surface-alt, rgba(127, 127, 127, 0.08));
  border: 1px solid var(--jsini-border);
}

.jsini-weather-preview__title {
  margin-top: 0.35rem;
  font-weight: 600;
}

/* 본문은 줄바꿈이 있는 한 덩이 글이다(서버가 `\n` 으로 만든다).
   `pre-line` 이 아니면 두세 줄이 한 줄로 붙어 알림과 다른 모양이 된다. */
.jsini-weather-preview__body {
  white-space: pre-line;
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text-muted);
}

/* 「마지막 저장 …」처럼 작게 덧붙이는 한 줄. */
.jsini-stamp {
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text-muted);
}

.jsini-badge {
  display: inline-block;
  padding: 0.1rem 0.5rem;
  font-size: var(--jsini-fs-xs);
  border: 1px solid;
}

.jsini-badge--on { border-color: #2e7d32; color: #2e7d32; }
.jsini-badge--off { border-color: var(--jsini-text-muted); color: var(--jsini-text-muted); }
.jsini-badge--warn { border-color: #b26a00; color: #b26a00; }
.jsini-badge--err { border-color: #d32f2f; color: #d32f2f; }

/* 통계 타일 묶음. 화면 위쪽에 숫자 넷 안팎을 늘어놓는 자리다. */
.jsini-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 0.75rem;
  margin-bottom: 1.25rem;
}

.jsini-stat {
  padding: 0.85rem 1rem;
  border: 1px solid var(--jsini-border, #e0e0e0);
}

.jsini-stat__label {
  font-size: var(--jsini-fs-xs);
  color: var(--jsini-text-muted);
}

.jsini-stat__value {
  font-size: var(--jsini-fs-3xl);
  font-weight: 700;
  line-height: 1.2;
}

/* 사람의 얼굴(jsini-face). 모양만 여기 있고 치수·색은 변수로 연다. 사진을 못 받았을 때의 되돌림은 한 곳에서 와야 같은 사람이 화면마다 다르게 뜨지 않는다. */

.jsini-face {
  --jsini-face-size: 1.5rem;
  --jsini-face-bg: var(--jsini-surface-muted, rgba(127, 127, 127, 0.18));
  --jsini-face-ink: var(--jsini-text-muted);
  --jsini-face-initial-size: var(--jsini-fs-2xs);

  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: var(--jsini-face-size);
  height: var(--jsini-face-size);
  overflow: hidden;
  border-radius: 50%;
  background: var(--jsini-face-bg);
  vertical-align: middle;
}

.jsini-face__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.jsini-face__initial {
  font-size: var(--jsini-face-initial-size);
  font-weight: 700;
  color: var(--jsini-face-ink);
  line-height: 1;
}

/* 큰 얼굴. 생활과환경의 「오늘 생일」 칸이 쓰던 값이다. */
.jsini-face--lg {
  --jsini-face-size: 2.2rem;
}

/* 사람 카드(UserCardHost). 얼굴을 누르면 뜬다. 책상에서는 말풍선, 휴대폰에서는 가운데 창이다. DevExpress 창보다 위, 그림 미리보기보다 아래에 둔다. */

.jsini-face--card {
  cursor: pointer;
}

.jsini-face--card:hover,
.jsini-face--card:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--jsini-accent) 55%, transparent);
  outline-offset: 1px;
}

.jsini-ucard-scrim {
  position: fixed;
  inset: 0;
  z-index: 1540;
  background: transparent;
}

.jsini-ucard-scrim--dim {
  background: rgba(0, 0, 0, 0.4);
}

.jsini-ucard {
  --jsini-ucard-arrow: 50%;

  position: fixed;
  z-index: 1541;
  box-sizing: border-box;
  width: 19rem;
  max-width: calc(100vw - 16px);
  padding: 1rem;
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-lg, 12px);
  background: var(--jsini-surface-raised, var(--jsini-surface));
  color: var(--jsini-text);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.18);
  outline: none;
}

/* 자리를 재기 전에는 왼쪽 위 귀퉁이에 한 번 그려지므로 감춰 둔다. */
.jsini-ucard--bubble:not([data-placed]) {
  visibility: hidden;
}

/* 말풍선 꼬리. 테두리를 잇도록 같은 색 네모를 45° 돌려 반만 내민다. */
.jsini-ucard--bubble::before {
  content: "";
  position: absolute;
  left: var(--jsini-ucard-arrow);
  width: 12px;
  height: 12px;
  margin-left: -6px;
  background: inherit;
  border: inherit;
  transform: rotate(45deg);
}

.jsini-ucard--bubble[data-side="bottom"]::before {
  top: -7px;
  border-right: 0;
  border-bottom: 0;
}

.jsini-ucard--bubble[data-side="top"]::before {
  bottom: -7px;
  border-left: 0;
  border-top: 0;
}

.jsini-ucard--center {
  top: 50%;
  left: 50%;
  width: min(20rem, calc(100vw - 2rem));
  padding: 1.25rem 1.25rem 1rem;
  transform: translate(-50%, -50%);
}

.jsini-ucard__close {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--jsini-text-muted);
  cursor: pointer;
}

.jsini-ucard__close:hover {
  background: color-mix(in srgb, currentColor 10%, transparent);
}

.jsini-ucard__head {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  min-width: 0;
}

.jsini-ucard--center .jsini-ucard__head {
  flex-direction: column;
  text-align: center;
}

.jsini-face.jsini-ucard__face {
  --jsini-face-size: 4rem;
  --jsini-face-initial-size: var(--jsini-fs-xl, 1.5rem);
}

.jsini-ucard--center .jsini-face.jsini-ucard__face {
  --jsini-face-size: 5.5rem;
}

.jsini-ucard__who {
  min-width: 0;
}

.jsini-ucard__name {
  font-size: var(--jsini-fs-lg);
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.jsini-ucard__sub {
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 회사 · 부서 한 줄. 회사는 본문색으로 세워 소속이 먼저 읽히게 한다. */
.jsini-ucard__company {
  color: var(--jsini-text);
  font-weight: 600;
}

.jsini-ucard__dept::before {
  content: "·";
  margin: 0 0.35em;
}

.jsini-ucard__contact {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 0.35rem 0.6rem;
  margin: 0.9rem 0 0;
  padding: 0.7rem 0 0;
  border-top: 1px solid var(--jsini-border);
  font-size: var(--jsini-fs-sm);
}

.jsini-ucard__contact dt {
  color: var(--jsini-text-muted);
  line-height: 1;
}

.jsini-ucard__contact dd {
  min-width: 0;
  margin: 0;
  overflow-wrap: anywhere;
}

.jsini-ucard__muted {
  grid-column: 1 / -1;
  color: var(--jsini-text-muted);
}

.jsini-ucard__actions {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.4rem;
  margin-top: 0.9rem;
}

.jsini-ucard__act {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  padding: 0.55rem 0.25rem;
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-md, 8px);
  background: transparent;
  color: var(--jsini-text);
  font: inherit;
  font-size: var(--jsini-fs-xs);
  text-decoration: none;
  cursor: pointer;
}

.jsini-ucard__act [class^='jsini-icon-'] {
  --jsini-icon-size: 1.15rem;
  color: var(--jsini-accent);
}

.jsini-ucard__act:hover:not(:disabled) {
  border-color: var(--jsini-accent);
  background: color-mix(in srgb, var(--jsini-accent) 8%, transparent);
  color: var(--jsini-text);
}

.jsini-ucard__act:disabled {
  opacity: 0.45;
  cursor: default;
}

/* ── 묶음 메뉴 길잡이 ────────────────────────────────────── */

.jsini-group-links {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 0.75rem;
  margin-top: 1rem;
}

.jsini-group-link {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.9rem 1rem;
  border: 1px solid var(--jsini-border);
  color: inherit;
  text-decoration: none;
  transition: border-color 0.15s, background-color 0.15s;
}

.jsini-group-link:hover {
  border-color: var(--jsini-accent, #3b6cff);
  background: color-mix(in srgb, var(--jsini-accent, #3b6cff) 6%, transparent);
}

.jsini-group-link__title { font-weight: 600; }

.jsini-group-link__hint {
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text-muted);
}

/* 테마 서랍. 떠 있는 창이 아니라 헤더 아래부터 바닥까지 오른쪽을 채우는 서랍이다. 닫혀 있을 때도 화면 밖으로 밀어 둔다(display none 이 아니라 미끄러져 보이게). 가로 스크롤을 만들지 않는다. */

/* 서랍 안에서 얹은 것과 고른 것을 나타내는 면. 테마 변수(--jsini-surface)는 판 배경과 같아지는 테마가 있어 쓰지 않는다. 글자색을 옅게 섞어 만든다. */
:root {
  --jsini-theme-hover: color-mix(in srgb, var(--jsini-text) 6%, transparent);
  --jsini-theme-picked: color-mix(in srgb, var(--jsini-text) 11%, transparent);
}

.jsini-theme-anchor { display: inline-flex; }

/* 덮개는 헤더 아래부터 깐다. 헤더까지 덮으면 방금 누른 테마 아이콘으로 다시 닫을 수 없다. */
.jsini-theme-scrim {
  position: fixed;
  inset: var(--jsini-header-height) 0 0 0;
  background: rgb(0 0 0 / 28%);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s ease-in-out, visibility 0.2s;
  z-index: 1035;
}

.jsini-theme-scrim--on {
  opacity: 1;
  visibility: visible;
}

.jsini-theme-panel {
  position: fixed;
  top: var(--jsini-header-height);
  right: 0;
  bottom: 0;
  width: min(340px, 100vw);
  display: flex;
  flex-direction: column;
  background: var(--jsini-bg);
  color: var(--jsini-text);
  border-left: 1px solid var(--jsini-border);
  box-shadow: -10px 0 28px rgb(0 0 0 / 14%);
  transform: translateX(100%);
  transition: transform 0.22s ease-in-out;
  z-index: 1036;
}

.jsini-theme-panel--on { transform: none; }

/* 접힌 서랍 안으로 탭 이동이 들어가면 안 된다(보이지 않는 단추에 초점이 가서 화면이 엉뚱한 곳으로 굴러간다). */
.jsini-theme-panel:not(.jsini-theme-panel--on) {
  visibility: hidden;
  transition: transform 0.22s ease-in-out, visibility 0.22s;
}

.jsini-theme-panel__head {
  display: flex;
  align-items: center;
  height: 3rem;
  min-height: 3rem;
  padding: 0 0.5rem 0 1rem;
  background: var(--jsini-surface);
  border-bottom: 1px solid var(--jsini-border);
}

.jsini-theme-panel__title {
  flex: 1;
  font-weight: 600;
}

/* 묶음이 셋이라 낮은 화면에서는 넘친다. 머리말은 붙박이로 두고 목록만 구른다. */
.jsini-theme {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.75rem 1rem 1.25rem;
  overflow: hidden auto;
}

/* 세로 flex 자식은 기본으로 줄어들 수 있어 내용이 판보다 길어지면 전부 납작해진다. 줄이지 않게 막는다(밝기·크기 칸이 찌그러져 글자가 잘린 적이 있다). */
.jsini-theme > * { flex: 0 0 auto; }

/* 묶음 제목. 밑줄은 제목 아래에 온다(위에 있으면 무엇을 가르는 선인지 반대로 읽힌다). */
.jsini-theme__group {
  font-size: var(--jsini-fs-md);
  font-weight: 700;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--jsini-border);
}

.jsini-theme__group:not(:first-child) { margin-top: 0.9rem; }

.jsini-theme__label {
  font-size: var(--jsini-fs-md);
  color: var(--jsini-text-muted);
  margin-top: 0.35rem;
}

/* 한 줄에서 하나만 고르는 덩어리(밝기 · 크기). 칸이 붙어 있어 셋 중 하나라는 것이 생김새로 드러난다. 폭은 늘리지 않는다(늘리면 허전해 보인다). */

.jsini-theme__segmented {
  display: flex;
  width: max-content;
  max-width: 100%;
  border: 1px solid var(--jsini-border);
  border-radius: 0.25rem;
  overflow: hidden;
}

/* 크기 칸은 여섯이라 판 너비를 다 쓴다. max-content 로 두면 판을 넘어 마지막 칸이 잘려도 오류가 나지 않아 눈치채기 어렵다. */
.jsini-theme__segmented--fill {
  width: 100%;
}

.jsini-theme__seg {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  min-width: 5.2rem;
  padding: 0.45rem 0.7rem;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: var(--jsini-fs-md);
  cursor: pointer;
}

/* 채우는 줄의 칸. min-width 0 이 없으면 여섯 칸이 줄지 않아 판을 넘는다. 글자는 사다리에서 두 단 내린다(「크게」에서 네 글자 이름이 칸을 넘지 않도록). */
.jsini-theme__segmented--fill > .jsini-theme__seg {
  flex: 1 1 0;
  min-width: 0;
  padding: 0.45rem 0.15rem;
  font-size: var(--jsini-fs-2xs);
  white-space: nowrap;
}

.jsini-theme__seg + .jsini-theme__seg {
  border-left: 1px solid var(--jsini-border);
}

.jsini-theme__seg:hover { background: var(--jsini-theme-hover); }

/* 고른 칸은 옅은 면으로 칠한다. 데모가 목록에서 쓰는 것과 같은 표시다. */
.jsini-theme__seg--on {
  background: var(--jsini-theme-picked);
  font-weight: 700;
}

/* 색 네모 + 이름 목록(Preset · Classic · Bootstrap). 셋 다 같은 생김새이고 두 칸으로 두면 줄 수가 줄어 굴리지 않고 다 보인다. */

.jsini-theme__list {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.25rem;
}

.jsini-theme__item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-width: 0;
  padding: 0.45rem 0.5rem;
  border: 0;
  border-radius: 0.375rem;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: var(--jsini-fs-md);
  text-align: left;
  cursor: pointer;
}

.jsini-theme__item:hover { background: var(--jsini-theme-hover); }

/* 고른 것은 면으로 칠한다(테두리를 두르면 그 줄만 키가 커져 목록이 들썩인다). */
.jsini-theme__item--on {
  background: var(--jsini-theme-picked);
  font-weight: 700;
}

/* 색 네모에 옅은 테두리를 함께 둔다. 흰색에 가까운 색은 테두리가 없으면 판 배경에 묻힌다. */
.jsini-theme__chip {
  flex: 0 0 auto;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 0.3rem;
  background: var(--swatch);
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / 10%);
}

/* 이름이 길면 줄이지 않고 …로 자른다. 두 칸이라 자리가 넉넉하지 않다. */
.jsini-theme__name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.jsini-theme__custom {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

/* 글자 칸이 남는 자리를 다 먹는다. 데모의 `#hex` 칸이 그렇다. */
.jsini-theme__hex { flex: 1 1 auto; min-width: 0; }

.jsini-theme__picker {
  flex: 0 0 auto;
  width: 2.1rem;
  height: 2.1rem;
  padding: 0;
  border: 1px solid var(--jsini-border);
  border-radius: 0.25rem;
  background: none;
  cursor: pointer;
}

/* 도구줄 아이콘 두 개 더. 나머지는 위 아이콘 묶음에 있다. */

.jsini-icon-plus {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8.545 2.545a.545.545 0 1 0-1.09 0v4.91h-4.91a.545.545 0 1 0 0 1.09h4.91v4.91a.545.545 0 1 0 1.09 0v-4.91h4.91a.545.545 0 1 0 0-1.09h-4.91z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8.545 2.545a.545.545 0 1 0-1.09 0v4.91h-4.91a.545.545 0 1 0 0 1.09h4.91v4.91a.545.545 0 1 0 1.09 0v-4.91h4.91a.545.545 0 1 0 0-1.09h-4.91z'/%3E%3C/svg%3E");
}

.jsini-icon-excel {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 13h9a.5.5 0 0 1 .09.992L12.5 14h-9a.5.5 0 0 1-.09-.992zh9zM7.91 1.008 8 1a.5.5 0 0 1 .492.41l.008.09v8.792l2.682-2.681a.5.5 0 0 1 .638-.058l.07.058a.5.5 0 0 1 .057.638l-.058.069-3.535 3.536a.5.5 0 0 1-.638.057l-.07-.057-3.535-3.536a.5.5 0 0 1 .638-.765l.069.058L7.5 10.292V1.5a.5.5 0 0 1 .41-.492L8 1z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 13h9a.5.5 0 0 1 .09.992L12.5 14h-9a.5.5 0 0 1-.09-.992zh9zM7.91 1.008 8 1a.5.5 0 0 1 .492.41l.008.09v8.792l2.682-2.681a.5.5 0 0 1 .638-.058l.07.058a.5.5 0 0 1 .057.638l-.058.069-3.535 3.536a.5.5 0 0 1-.638.057l-.07-.057-3.535-3.536a.5.5 0 0 1 .638-.765l.069.058L7.5 10.292V1.5a.5.5 0 0 1 .41-.492L8 1z'/%3E%3C/svg%3E");
}

/* 위로 가기 단추 안의 그림. 크기·색은 여기서 적지 않는다(아이콘 묶음이 이미 적고, 떠다니는 단추 안에서는 그쪽 규칙이 키운다). 여기서 크기를 박으면 이 그림만 묶음의 값을 벗어난다. */

.jsini-icon-arrow-up {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.646 2.646a.5.5 0 0 1 .708 0l4 4a.5.5 0 0 1-.708.708L8.5 4.207V13.5a.5.5 0 0 1-1 0V4.207L4.354 7.354a.5.5 0 1 1-.708-.708z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.646 2.646a.5.5 0 0 1 .708 0l4 4a.5.5 0 0 1-.708.708L8.5 4.207V13.5a.5.5 0 0 1-1 0V4.207L4.354 7.354a.5.5 0 1 1-.708-.708z'/%3E%3C/svg%3E");
}

/* 표 안 관리 칸. 연필과 휴지통은 아이콘만으로도 뜻이 갈리는 몇 안 되는 짝이다. */

.jsini-icon-edit {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M14.236 1.764a2.62 2.62 0 0 0-3.707 0L2.657 9.635a2.96 2.96 0 0 0-.772 1.355l-.87 3.386a.5.5 0 0 0 .61.608l3.385-.869c.512-.131.98-.398 1.354-.772l7.872-7.872a2.62 2.62 0 0 0 0-3.707m-3 .707a1.621 1.621 0 1 1 2.293 2.293l-.779.779-2.293-2.293zM9.75 3.957l2.293 2.293-6.386 6.386a1.95 1.95 0 0 1-.896.51l-2.567.66.66-2.567c.086-.34.263-.649.51-.896z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M14.236 1.764a2.62 2.62 0 0 0-3.707 0L2.657 9.635a2.96 2.96 0 0 0-.772 1.355l-.87 3.386a.5.5 0 0 0 .61.608l3.385-.869c.512-.131.98-.398 1.354-.772l7.872-7.872a2.62 2.62 0 0 0 0-3.707m-3 .707a1.621 1.621 0 1 1 2.293 2.293l-.779.779-2.293-2.293zM9.75 3.957l2.293 2.293-6.386 6.386a1.95 1.95 0 0 1-.896.51l-2.567.66.66-2.567c.086-.34.263-.649.51-.896z'/%3E%3C/svg%3E");
}

.jsini-icon-trash {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7 3h2a1 1 0 0 0-2 0M6 3a2 2 0 1 1 4 0h4a.5.5 0 0 1 0 1h-.564l-1.205 8.838A2.5 2.5 0 0 1 9.754 15H6.246a2.5 2.5 0 0 1-2.477-2.162L2.564 4H2a.5.5 0 0 1 0-1zm1 3.5a.5.5 0 0 0-1 0v5a.5.5 0 0 0 1 0zM9.5 6a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-1 0v-5a.5.5 0 0 1 .5-.5m-4.74 6.703A1.5 1.5 0 0 0 6.246 14h3.508a1.5 1.5 0 0 0 1.487-1.297L12.427 4H3.573z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7 3h2a1 1 0 0 0-2 0M6 3a2 2 0 1 1 4 0h4a.5.5 0 0 1 0 1h-.564l-1.205 8.838A2.5 2.5 0 0 1 9.754 15H6.246a2.5 2.5 0 0 1-2.477-2.162L2.564 4H2a.5.5 0 0 1 0-1zm1 3.5a.5.5 0 0 0-1 0v5a.5.5 0 0 0 1 0zM9.5 6a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-1 0v-5a.5.5 0 0 1 .5-.5m-4.74 6.703A1.5 1.5 0 0 0 6.246 14h3.508a1.5 1.5 0 0 0 1.487-1.297L12.427 4H3.573z'/%3E%3C/svg%3E");
}

/* 미리보기. 눈은 「고치지 않고 보기만 한다」를 연필·휴지통과 한눈에 가른다. */
.jsini-icon-eye {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2.984 8.625v.003a.5.5 0 0 1-.612.355c-.431-.114-.355-.611-.355-.611l.018-.062s.026-.084.047-.145a6.7 6.7 0 0 1 1.117-1.982C4.096 5.089 5.605 4 8 4s3.904 1.089 4.802 2.183a6.7 6.7 0 0 1 1.117 1.982 4 4 0 0 1 .06.187l.003.013v.004l.001.002a.5.5 0 0 1-.966.258l-.001-.004-.008-.025-.035-.109a5.696 5.696 0 0 0-.945-1.674C11.286 5.912 10.045 5 8 5s-3.285.912-4.028 1.817a5.7 5.7 0 0 0-.945 1.674l-.035.109zM8 7a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5M6.5 9.5a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2.984 8.625v.003a.5.5 0 0 1-.612.355c-.431-.114-.355-.611-.355-.611l.018-.062s.026-.084.047-.145a6.7 6.7 0 0 1 1.117-1.982C4.096 5.089 5.605 4 8 4s3.904 1.089 4.802 2.183a6.7 6.7 0 0 1 1.117 1.982 4 4 0 0 1 .06.187l.003.013v.004l.001.002a.5.5 0 0 1-.966.258l-.001-.004-.008-.025-.035-.109a5.696 5.696 0 0 0-.945-1.674C11.286 5.912 10.045 5 8 5s-3.285.912-4.028 1.817a5.7 5.7 0 0 0-.945 1.674l-.035.109zM8 7a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5M6.5 9.5a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0'/%3E%3C/svg%3E");
}

/* 나무 관리 칸의 하위 추가. 아래 띠의 등록과 다른 그림이어야 한다(같은 더하기면 여기 아래와 최상위가 구분되지 않는다). 꺾인 선이 한 단 아래를 말한다. */
.jsini-icon-child-plus {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2 4.5A2.5 2.5 0 0 1 4.5 2h7A2.5 2.5 0 0 1 14 4.5v7a2.5 2.5 0 0 1-2.5 2.5h-7A2.5 2.5 0 0 1 2 11.5zM4.5 3A1.5 1.5 0 0 0 3 4.5v7A1.5 1.5 0 0 0 4.5 13h7a1.5 1.5 0 0 0 1.5-1.5v-7A1.5 1.5 0 0 0 11.5 3zM8 4.5a.5.5 0 0 1 .5.5v2.5H11a.5.5 0 0 1 0 1H8.5V11a.5.5 0 0 1-1 0V8.5H5a.5.5 0 0 1 0-1h2.5V5a.5.5 0 0 1 .5-.5'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2 4.5A2.5 2.5 0 0 1 4.5 2h7A2.5 2.5 0 0 1 14 4.5v7a2.5 2.5 0 0 1-2.5 2.5h-7A2.5 2.5 0 0 1 2 11.5zM4.5 3A1.5 1.5 0 0 0 3 4.5v7A1.5 1.5 0 0 0 4.5 13h7a1.5 1.5 0 0 0 1.5-1.5v-7A1.5 1.5 0 0 0 11.5 3zM8 4.5a.5.5 0 0 1 .5.5v2.5H11a.5.5 0 0 1 0 1H8.5V11a.5.5 0 0 1-1 0V8.5H5a.5.5 0 0 1 0-1h2.5V5a.5.5 0 0 1 .5-.5'/%3E%3C/svg%3E");
}

/* 나무 아래 띠의 펼치기·접기 한 짝. 삼각형이 벌어지면 펼치기, 모이면 접기다. */
.jsini-icon-unfold {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2 2.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5m0 12a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5m7-4a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5M4.5 12a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7M5 6.5a.5.5 0 0 0-1 0V8H2.5a.5.5 0 0 0 0 1H4v1.5a.5.5 0 0 0 1 0V9h1.5a.5.5 0 0 0 0-1H5z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2 2.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5m0 12a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5m7-4a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5M4.5 12a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7M5 6.5a.5.5 0 0 0-1 0V8H2.5a.5.5 0 0 0 0 1H4v1.5a.5.5 0 0 0 1 0V9h1.5a.5.5 0 0 0 0-1H5z'/%3E%3C/svg%3E");
}

.jsini-icon-fold {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2 2.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5m0 12a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5m7-4a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5M8 8.5a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0m-6 0a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 0-1h-4a.5.5 0 0 0-.5.5'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2 2.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5m0 12a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5m7-4a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5M8 8.5a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0m-6 0a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 0-1h-4a.5.5 0 0 0-.5.5'/%3E%3C/svg%3E");
}

/* 공지 팝업(NoticePopup). 본문은 서식 편집기로 쓴 HTML 이라 그 서식의 모양을 여기서 정한다. 테마 CSS 는 DevExpress 부품만 겨누므로 제목·목록·표는 기본 모양이 되어 직접 세운다. */

/* 끌 수 있는 창의 머리는 잡는 자리다. DevExpress 는 끌기를 켜도 표시를 붙이지 않으므로 커서를 여기서 직접 세운다. 테마 CSS 의 .dxbl-drag 규칙은 겹친 구조를 전제해 걸리지 않는 죽은 규칙이다. 표시는 끌기를 켠 쪽이 함께 붙인다. 테마가 바뀌어도 남도록 !important 를 둔다. */
.jsini-drag-head {
  cursor: move !important;

/* 글자 위에서 끌면 글이 선택되면서 창이 안 따라온다. 머리에 있는 것은 제목뿐이라 골라 담을 일이 없다. */
  user-select: none;
}

/* 잡고 있는 동안은 손이 닫힌 모양이다. :active 만으로는 모자라 DevExpress 가 끄는 동안 붙이는 dxbl-popup-dragging 도 함께 본다. */
.jsini-drag-head:active,
.dxbl-popup-dragging .jsini-drag-head,
.dxbl-popup-dragging.dxbl-popup .jsini-drag-head,
.jsini-snotice--dragging .jsini-drag-head {
  cursor: grabbing !important;
}

/* 머리 안의 단추(닫기 ×)는 누르는 자리다. 머리에서 물려받은 move 커서를 되돌린다. */
.jsini-drag-head button,
.jsini-drag-head .dxbl-btn {
  cursor: pointer !important;
}

.jsini-notice-popup__title {
  min-width: 0;
}

.jsini-notice-popup__meta {
  margin-top: 0.15rem;
  font-size: var(--jsini-fs-xs);
  font-weight: 400;
  color: var(--jsini-text-muted);
}

.jsini-notice-popup__foot {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.jsini-notice-popup__gap { flex: 1; }

/* 한 줄짜리 공지도 최소 높이가 있어야 알림처럼 쪼그라들지 않는다. 넘칠 때는 창째로 구르지 않고 본문만 구른다(창째로 구르면 제목이 밀려 올라간다). */
.jsini-notice-popup__body {
  min-height: 8.5rem;
  max-height: 56vh;
  overflow-y: auto;
  line-height: 1.7;
}

.jsini-notice-popup__body > :first-child { margin-top: 0; }
.jsini-notice-popup__body > :last-child { margin-bottom: 0; }

.jsini-notice-popup__body p { margin: 0 0 0.6rem; }

.jsini-notice-popup__body h1,
.jsini-notice-popup__body h2,
.jsini-notice-popup__body h3,
.jsini-notice-popup__body h4 {
  margin: 0.9rem 0 0.4rem;
  font-weight: 600;
}

.jsini-notice-popup__body h1 { font-size: var(--jsini-fs-xl); }
.jsini-notice-popup__body h2 { font-size: var(--jsini-fs-lg); }
.jsini-notice-popup__body h3,
.jsini-notice-popup__body h4 { font-size: var(--jsini-fs-md); }

.jsini-notice-popup__body ul,
.jsini-notice-popup__body ol {
  margin: 0 0 0.6rem;
  padding-left: 1.4rem;
  list-style: revert;
}

.jsini-notice-popup__body blockquote {
  margin: 0 0 0.6rem;
  padding-left: 0.75rem;
  border-left: 3px solid var(--jsini-border);
  color: var(--jsini-text-muted);
}

.jsini-notice-popup__body pre {
  margin: 0 0 0.6rem;
  padding: 0.5rem 0.75rem;
  overflow-x: auto;
  background: var(--jsini-surface);
  border-radius: 0.25rem;
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: var(--jsini-fs-sm);
}

.jsini-notice-popup__body img {
  max-width: 100%;
  height: auto;
  border-radius: 0.25rem;
}

.jsini-notice-popup__body a {
  color: var(--jsini-accent-text);
  text-decoration: underline;
}

.jsini-notice-popup__body table {
  width: 100%;
  margin: 0 0 0.6rem;
  border-collapse: collapse;
}

.jsini-notice-popup__body th,
.jsini-notice-popup__body td {
  padding: 0.25rem 0.5rem;
  border: 1px solid var(--jsini-border);
  text-align: left;
}

.jsini-notice-popup__body th {
  font-weight: 600;
  background: var(--jsini-surface);
}

/* 공지가 여럿일 때 위치를 알려 주는 점. 지금 보는 것만 길쭉해진다. 색으로만 가르면 옅은 테마에서 구분이 안 된다. */
.jsini-notice-popup__dots {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}

.jsini-notice-popup__dot {
  width: 0.4rem;
  height: 0.4rem;
  padding: 0;
  border: 0;
  border-radius: 9999px;
  background: color-mix(in srgb, currentcolor 30%, transparent);
  cursor: pointer;
  transition: width 0.15s, background-color 0.15s;
}

.jsini-notice-popup__dot:hover {
  background: color-mix(in srgb, currentcolor 55%, transparent);
}

.jsini-notice-popup__dot.is-on {
  width: 1rem;
  background: var(--jsini-accent);
}

/* 첨부. 이름을 자르지 않고 줄바꿈으로 흘린다(확장자가 잘리면 무엇을 내려받는지 알 수 없다). */
.jsini-notice-popup__files {
  margin-top: 0.9rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--jsini-border);
}

.jsini-notice-popup__files-head {
  margin-bottom: 0.4rem;
  font-size: var(--jsini-fs-xs);
  font-weight: 600;
  color: var(--jsini-text-muted);
}

.jsini-notice-popup__file-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0;
  padding: 0;
  list-style: none;
  min-width: 0;
}

.jsini-notice-popup__file-list li { max-width: 100%; }

.jsini-notice-popup__file {
  display: inline-flex;
  align-items: flex-start;
  gap: 0.35rem;
  max-width: 100%;
  padding: 0.2rem 0.5rem;
  border: 1px solid var(--jsini-border);
  border-radius: 0.25rem;
  font-size: var(--jsini-fs-xs);
  text-decoration: none;
  color: inherit;
}

.jsini-notice-popup__file:hover {
  border-color: var(--jsini-accent);
  color: var(--jsini-accent-text);
}

.jsini-notice-popup__file-name {
  min-width: 0;
  word-break: break-all;
}

.jsini-notice-popup__file-size {
  flex: 0 0 auto;
  white-space: nowrap;
  color: var(--jsini-text-muted);
}

/* 로그인 화면의 공개 공지 팝업(.jsini-snotice). 회로가 붙기 전에 보여야 해서 DxPopup 을 쓰지 않고 골격을 순수 HTML·CSS 로 세웠다. 안의 본문·파일·점·메타 클래스는 로그인 뒤 팝업과 같은 것을 쓴다. 치수는 떠 있는 DevExpress 팝업에서 잰 값이다. */
.jsini-snotice {
  position: fixed;
  inset: 0;
  z-index: 1500;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}

/* `hidden` 을 flex 가 이긴다 — 속성만으로는 안 감춰진다. */
.jsini-snotice[hidden] {
  display: none;
}

.jsini-snotice__back {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
}

/* 바탕을 눌러도 닫히지 않는다. 공지는 읽어야 하는 것이라 스치듯 닫히면 안 된다. */
.jsini-snotice__card {
  position: relative;
  width: min(640px, calc(100vw - 2rem));
  max-height: 86vh;
  display: flex;
  flex-direction: column;

/* 창의 표면은 본문 카드가 아니라 한 단 떠 있는 쪽을 쓴다. 로그인 뒤 팝업과 같은 값이라야 같은 공지가 로그인 앞뒤로 같아 보인다. */
  background: var(--jsini-surface-raised);
  color: var(--jsini-text);
  border-radius: 8px;
  box-shadow:
    0 32px 64px rgba(0, 0, 0, 0.24),
    0 0 8px rgba(0, 0, 0, 0.2);
}

/* 닫기(×)는 머리띠가 아니라 카드에 붙인다. 오른쪽 여백은 머리띠와 같다. */
.jsini-snotice__x {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 1;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: none;
  border-radius: 4px;
  background: transparent;
  color: var(--jsini-text-muted);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
}

.jsini-snotice__x:hover {
  background: color-mix(in srgb, var(--jsini-text) 8%, transparent);
  color: var(--jsini-text);
}

/* 공지 한 건. 한 번에 하나만 보인다(`hidden` 을 JS 가 옮긴다). */
.jsini-snotice__page {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.jsini-snotice__page[hidden] {
  display: none;
}

.jsini-snotice__head {
  flex: 0 0 auto;
  /* 오른쪽만 넉넉히 — 닫기 단추가 앉을 자리다. */
  padding: 24px 3.5rem 12px 24px;
  font-size: 20px;
  font-weight: 600;
}

/* 공지가 길면 **본문만** 구른다. 카드째로 구르면 제목이 밀려 올라간다. */
.jsini-snotice__scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 4px 24px;
}

/* 본문 안쪽 높이는 로그인 뒤 팝업의 규칙이 잡으므로 여기서는 풀어 준다(카드가 이미 86vh 로 묶여 구르는 자리도 위 상자다). */
.jsini-snotice__scroll .jsini-notice-popup__body {
  max-height: none;
  overflow-y: visible;
}

.jsini-snotice__foot {
  flex: 0 0 auto;
  padding: 12px 24px 24px;
}

/* 끌어 옮기는 동안 글이 선택되지 않게 한다. 포인터가 머리를 벗어나면 뒤의 글자가 잡히므로 본문 선택을 막는다. */
.jsini-snotice--dragging,
.jsini-snotice--dragging * {
  user-select: none;
}

/* 「오늘 하루 보지 않기」. DxCheckBox 자리라 글자 크기를 본문에 맞춘다. */
.jsini-snotice__check {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  cursor: pointer;
  font-size: var(--jsini-fs-md);
}

.jsini-snotice__check input {
  margin: 0;
  cursor: pointer;
}

/* 「공지 2 / 3」. 제목 아래 곁줄이라 색·크기는 `__meta` 가 정한다. */
.jsini-snotice__count:empty {
  display: none;
}

/* 단추 둘(이전 · 다음/닫기). DevExpress 기본 단추에서 잰 값이다. */
.jsini-snotice__btn {
  flex: 0 0 auto;
  padding: 5px 12px;
  border: 1px solid transparent;
  border-radius: 4px;
  background: transparent;
  color: var(--jsini-text);
  font: inherit;
  font-size: var(--jsini-fs-md);
  font-weight: 600;
  cursor: pointer;
}

.jsini-snotice__btn:hover:not(:disabled) {
  background: color-mix(in srgb, var(--jsini-text) 8%, transparent);
}

.jsini-snotice__btn:disabled {
  color: var(--jsini-text-muted);
  cursor: default;
}

.jsini-snotice__btn[hidden] {
  display: none;
}

.jsini-snotice__btn--primary {
  background: var(--jsini-accent);
  color: #fff;
}

.jsini-snotice__btn--primary:hover:not(:disabled) {
  background: color-mix(in srgb, var(--jsini-accent) 88%, #000);
}

/* 잠금화면(D7). 레이아웃 격자 바깥에 덮는다(격자 안에 두면 본문 자리에만 깔린다). z-index 는 DevExpress 드롭다운·팝업(1000 대)보다 위에 둔다. */
.jsini-lock {
  position: fixed;
  inset: 0;
  z-index: 20000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--jsini-bg);
/* 불투명하게 덮되 살짝 비치게 두어 어디로 돌아가는지 알 수 있게 한다(뒤가 아예 안 보이면 멈춘 것처럼 보인다). */
  background: color-mix(in srgb, var(--jsini-bg) 92%, transparent);
  backdrop-filter: blur(6px);
}

.jsini-lock__panel {
  width: min(22rem, calc(100vw - 2rem));
  padding: 2rem 1.75rem;
  border: 1px solid var(--jsini-border);
  border-radius: 0.75rem;
  /* 다른 창과 같은 표면. 이 판은 바탕막 위에 떠 있으므로 본문 카드가 아니다. */
  background: var(--jsini-surface-raised);
  box-shadow: 0 1.5rem 3rem rgb(0 0 0 / 25%);
  text-align: center;
}

.jsini-lock__icon {
  width: 2.5rem;
  height: 2.5rem;
  margin: 0 auto 0.75rem;
  background-color: var(--jsini-accent);
}

.jsini-lock__title {
  margin: 0 0 0.25rem;
  font-size: var(--jsini-fs-xl);
}

.jsini-lock__who {
  margin: 0 0 1.25rem;
  color: var(--jsini-text-muted);
}

.jsini-lock__form {
  display: grid;
  gap: 0.6rem;
}

/* ── 푸는 길이 둘이다 (비밀번호 · 지문) ──────────────────────
   **마크업을 두 벌 두지 않는다.** 차례만 바뀌고 안에 든 것은 같다 —
   로그인 화면의 `.jsini-login__methods` 와 같은 수법이다. */
.jsini-lock__methods {
  display: grid;
  gap: 1rem;
}

/* 갈라 주는 줄. 두 길이 붙어 있으면 아래 단추가 위 단추의 짝으로 읽힌다.
   기본 차례(비밀번호 먼저)에서는 지문이 아래라 **위쪽**에 줄을 긋는다. */
.jsini-lock__passkey {
  padding-top: 1rem;
  border-top: 1px solid var(--jsini-border);
}

.jsini-lock__passkey-btn {
  width: 100%;
}

/* 지문을 먼저 묻는 기기. 단추가 위로 올라가므로 줄도 **아래쪽**으로 옮긴다.
   안 옮기면 카드 맨 위에 걸린 줄 하나가 제목과 단추를 갈라놓는다. */
.jsini-lock__methods--passkey-first .jsini-lock__passkey {
  order: -1;
  padding-top: 0;
  padding-bottom: 1rem;
  border-top: 0;
  border-bottom: 1px solid var(--jsini-border);
}

.jsini-lock__hint {
  margin: 1.25rem 0 0.25rem;
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-sm);
  line-height: 1.5;
}

.jsini-lock__logout {
  margin: 0;
}

/* ── AI 대화 — ChatGPT UI/UX 스타일 (D11) ──────────────────────
   화면(/site/ai/chat)과 헤더 서랍이 같은 부품을 쓰며,
   ChatGPT 특유의 미려하고 집중도 높은 대화 UI를 제공한다.
   모바일 터치 환경을 주력으로 삼아 하단 캡슐 입력바와 슬라이드 히스토리 패널을 완벽 지원한다. */
.jsini-chat {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
  background: var(--jsini-bg);
  position: relative;
  overflow: hidden;
}

/* ── 상단 헤더 바 (ChatGPT Header) ── */
.jsini-chat__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--jsini-border);
  background: var(--jsini-surface);
  flex-shrink: 0;
  z-index: 10;
}

.jsini-chat__header-left {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  min-width: 0;
  flex-shrink: 0;
}

.jsini-chat__header-center {
  flex: 1;
  min-width: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 0 0.25rem;
}

/* 헤더 버튼 공통 */
.jsini-chat__header-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  height: 2.1rem;
  padding: 0 0.55rem;
  border-radius: 0.5rem;
  border: 1px solid var(--jsini-border);
  background: var(--jsini-surface);
  color: var(--jsini-text);
  font-size: var(--jsini-fs-sm);
  cursor: pointer;
  transition: all 0.15s ease;
  user-select: none;
}

.jsini-chat__header-btn:hover:not(:disabled) {
  background: color-mix(in srgb, var(--jsini-accent) 10%, var(--jsini-surface));
  border-color: var(--jsini-accent);
}

.jsini-chat__header-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

@media (max-width: 480px) {
  .jsini-chat__header-btn-text {
    display: none;
  }
  .jsini-chat__header-btn {
    padding: 0 0.5rem;
  }
}

/* 모델 알약 뱃지 */
.jsini-chat__model-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.2rem 0.55rem;
  border-radius: 9999px;
  background: color-mix(in srgb, var(--jsini-text-muted) 12%, transparent);
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-xs);
  white-space: nowrap;
  max-width: 140px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.jsini-chat__who-dot {
  flex: 0 0 auto;
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  border: 1px solid currentColor;
}

.jsini-chat__who-dot.is-live {
  border-color: #10a37f;
  background: #10a37f;
}

.jsini-chat__model-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 대화 제목 (클릭 시 이름 변경) */
.jsini-chat__header-title {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  max-width: 100%;
  padding: 0.2rem 0.5rem;
  border-radius: 0.4rem;
  cursor: pointer;
  transition: background 0.15s ease;
}

.jsini-chat__header-title:hover {
  background: color-mix(in srgb, var(--jsini-text-muted) 10%, transparent);
}

.jsini-chat__title-text {
  font-size: var(--jsini-fs-base);
  font-weight: 600;
  color: var(--jsini-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: center;
}

.jsini-chat__title-edit-icon {
  width: 0.9rem;
  height: 0.9rem;
  opacity: 0.5;
  flex-shrink: 0;
}

/* 제목 인라인 편집 */
.jsini-chat__header-rename {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  width: 100%;
  max-width: 280px;
}

.jsini-chat__rename-box {
  flex: 1;
  min-width: 0;
}

.jsini-chat__header-action-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 0.35rem;
  border: 1px solid var(--jsini-border);
  background: var(--jsini-surface);
  color: var(--jsini-text);
  cursor: pointer;
}

/* ── 대화 히스토리 슬라이드 오프캔버스 ── */
.jsini-chat__history-scrim {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(2px);
  z-index: 80;
  animation: jsiniFadeIn 0.2s ease;
}

.jsini-chat__history-panel {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  width: min(320px, 86vw);
  background: var(--jsini-surface);
  border-right: 1px solid var(--jsini-border);
  box-shadow: 4px 0 24px rgba(0, 0, 0, 0.18);
  z-index: 85;
  display: flex;
  flex-direction: column;
  animation: jsiniSlideInLeft 0.22s ease-out;
}

@keyframes jsiniSlideInLeft {
  from { transform: translateX(-100%); }
  to { transform: translateX(0); }
}

@keyframes jsiniFadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

.jsini-chat__history-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--jsini-border);
}

.jsini-chat__history-header-left {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.jsini-chat__history-header-icon {
  width: 1.2rem;
  height: 1.2rem;
  color: var(--jsini-accent);
}

.jsini-chat__history-title {
  font-weight: 600;
  font-size: var(--jsini-fs-md);
  color: var(--jsini-text);
}

.jsini-chat__history-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: 0.4rem;
  border: none;
  background: transparent;
  color: var(--jsini-text-muted);
  cursor: pointer;
}

.jsini-chat__history-close:hover {
  background: color-mix(in srgb, var(--jsini-text-muted) 15%, transparent);
  color: var(--jsini-text);
}

.jsini-chat__history-new {
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--jsini-border);
}

.jsini-chat__history-new-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.65rem 1rem;
  border-radius: 0.6rem;
  border: 1px dashed var(--jsini-border);
  background: color-mix(in srgb, var(--jsini-accent) 6%, transparent);
  color: var(--jsini-accent);
  font-weight: 500;
  font-size: var(--jsini-fs-sm);
  cursor: pointer;
  transition: all 0.15s ease;
}

.jsini-chat__history-new-btn:hover {
  background: color-mix(in srgb, var(--jsini-accent) 14%, transparent);
  border-color: var(--jsini-accent);
}

.jsini-chat__history-list {
  flex: 1;
  overflow-y: auto;
  padding: 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.jsini-chat__history-empty {
  padding: 2.5rem 1rem;
  text-align: center;
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-sm);
}

.jsini-chat__history-empty span {
  display: block;
  margin-top: 0.25rem;
  font-size: var(--jsini-fs-xs);
  opacity: 0.75;
}

.jsini-chat__history-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.65rem 0.75rem;
  border-radius: 0.55rem;
  cursor: pointer;
  transition: background 0.15s ease;
  user-select: none;
}

.jsini-chat__history-item:hover {
  background: color-mix(in srgb, var(--jsini-text-muted) 8%, transparent);
}

.jsini-chat__history-item.is-selected {
  background: color-mix(in srgb, var(--jsini-accent) 12%, transparent);
  border-left: 3px solid var(--jsini-accent);
}

.jsini-chat__history-item-main {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  min-width: 0;
  flex: 1;
}

.jsini-chat__history-item-icon {
  width: 0.95rem;
  height: 0.95rem;
  flex-shrink: 0;
  opacity: 0.65;
}

.jsini-chat__history-item-title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text);
  font-weight: 500;
}

.jsini-chat__history-item-meta {
  display: flex;
  align-items: center;
  gap: 0.15rem;
  flex-shrink: 0;
}

.jsini-chat__history-item {
  position: relative;
}

.jsini-chat__history-more {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border: none;
  border-radius: 0.4rem;
  background: transparent;
  color: var(--jsini-text-muted);
  cursor: pointer;
}

.jsini-chat__history-more:hover,
.jsini-chat__history-item.is-menu-open .jsini-chat__history-more {
  background: color-mix(in srgb, var(--jsini-text-muted) 15%, transparent);
  color: var(--jsini-text);
}

/* 메뉴 바깥을 누르면 닫는 투명한 막. 목록(z 없음)보다 위, 펼친 항목보다 아래. */
.jsini-chat__history-menu-scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
}

.jsini-chat__history-item.is-menu-open {
  z-index: 2;
}

.jsini-chat__history-menu {
  position: absolute;
  top: calc(100% - 0.25rem);
  right: 0.5rem;
  min-width: 8rem;
  padding: 0.25rem;
  border: 1px solid var(--jsini-border);
  border-radius: 0.5rem;
  background: var(--jsini-surface);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
  z-index: 3;
  cursor: default;
}

/* 목록 끝자락은 위로 연다 — 아래로 열면 목록 스크롤 밖으로 숨는다. */
.jsini-chat__history-item:nth-last-child(-n+2):not(:first-child) .jsini-chat__history-menu {
  top: auto;
  bottom: calc(100% - 0.25rem);
}

.jsini-chat__history-menu-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.5rem 0.65rem;
  border: none;
  border-radius: 0.35rem;
  background: transparent;
  color: var(--jsini-text);
  font-size: var(--jsini-fs-sm);
  text-align: left;
  cursor: pointer;
}

.jsini-chat__history-menu-item--danger {
  color: #e03131;
}

.jsini-chat__history-menu-item:hover:not(:disabled) {
  background: color-mix(in srgb, #e03131 12%, transparent);
}

.jsini-chat__history-menu-item:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.jsini-chat__history-item-time {
  font-size: 11px;
  color: var(--jsini-text-muted);
}

/* ── 대화 피드 (Message Feed) ── */
.jsini-chat__log {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 1rem 0.75rem;
  background: var(--jsini-bg);
  scroll-behavior: smooth;
}

.jsini-chat__feed-container {
  width: 100%;
  max-width: 48rem;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

.jsini-chat--drawer .jsini-chat__feed-container {
  max-width: 100%;
  gap: 0.9rem;
}

/* 환영 화면 (ChatGPT Empty State) */
.jsini-chat__welcome {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 2rem 0.5rem 1.5rem;
  margin: auto 0;
}

.jsini-chat__welcome-symbol {
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 50%;
  background: color-mix(in srgb, var(--jsini-accent) 15%, transparent);
  color: var(--jsini-accent);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 1rem;
  box-shadow: 0 4px 12px color-mix(in srgb, var(--jsini-accent) 20%, transparent);
}

.jsini-chat__welcome-symbol .jsini-icon-robot {
  width: 1.8rem;
  height: 1.8rem;
}

.jsini-chat__welcome-heading {
  font-size: calc(var(--jsini-fs-xl) * 1.1);
  font-weight: 700;
  color: var(--jsini-text);
  margin: 0 0 0.5rem;
}

.jsini-chat__welcome-sub {
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text-muted);
  max-width: 28rem;
  margin: 0 0 1.5rem;
  line-height: 1.6;
}

/* 추천 프롬프트 그리드 (ChatGPT Prompt Cards) */
.jsini-chat__prompt-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.65rem;
  width: 100%;
  max-width: 42rem;
}

@media (max-width: 600px) {
  .jsini-chat__prompt-grid {
    grid-template-columns: 1fr;
  }
}

.jsini-chat__prompt-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  padding: 0.85rem 1rem;
  border-radius: 0.85rem;
  border: 1px solid var(--jsini-border);
  background: var(--jsini-surface);
  cursor: pointer;
  transition: all 0.18s cubic-bezier(0.16, 1, 0.3, 1);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.03);
}

.jsini-chat__prompt-card:hover {
  transform: translateY(-2px);
  border-color: var(--jsini-accent);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
}

.jsini-chat__prompt-badge {
  font-size: 11px;
  font-weight: 600;
  color: var(--jsini-accent);
  margin-bottom: 0.3rem;
}

.jsini-chat__prompt-title {
  font-size: var(--jsini-fs-sm);
  font-weight: 600;
  color: var(--jsini-text);
  margin-bottom: 0.2rem;
}

.jsini-chat__prompt-desc {
  font-size: var(--jsini-fs-xs);
  color: var(--jsini-text-muted);
  line-height: 1.4;
}

/* 메시지 행 (Message Rows) */
.jsini-chat__row {
  display: flex;
  gap: 0.75rem;
  width: 100%;
}

.jsini-chat__row--user {
  justify-content: flex-end;
}

.jsini-chat__row--ai {
  justify-content: flex-start;
}

.jsini-chat__avatar {
  flex: 0 0 2rem;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background: color-mix(in srgb, var(--jsini-accent) 15%, transparent);
  color: var(--jsini-accent);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 0.15rem;
}

.jsini-chat__avatar .jsini-icon-robot {
  width: 1.15rem;
  height: 1.15rem;
}

.jsini-chat__message-content {
  display: flex;
  flex-direction: column;
  max-width: 82%;
  min-width: 0;
}

.jsini-chat__row--ai .jsini-chat__message-content {
  max-width: 100%;
}

.jsini-chat__author-name {
  font-size: var(--jsini-fs-xs);
  font-weight: 600;
  color: var(--jsini-text-muted);
  margin-bottom: 0.3rem;
}

/* 말풍선 스타일 */
.jsini-chat__bubble {
  padding: 0.75rem 1rem;
  font-size: var(--jsini-fs-base);
  line-height: 1.65;
  white-space: pre-wrap;
  word-break: break-word;
}

/* 사용자 말풍선: 알약형 라운드 버블 */
.jsini-chat__row--user .jsini-chat__bubble {
  border-radius: 1.25rem 1.25rem 0.3rem 1.25rem;
  background: var(--jsini-accent);
  color: #fff;
  box-shadow: 0 2px 8px color-mix(in srgb, var(--jsini-accent) 25%, transparent);
}

/* AI 말풍선: ChatGPT 스타일 자연스러운 텍스트 카드 */
.jsini-chat__row--ai .jsini-chat__bubble {
  border-radius: 0.3rem 1.25rem 1.25rem 1.25rem;
  background: var(--jsini-surface);
  border: 1px solid var(--jsini-border);
  color: var(--jsini-text);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.03);
}

/* 깜빡이는 커서 */
.jsini-chat__cursor {
  display: inline-block;
  width: 0.45rem;
  height: 1.05rem;
  vertical-align: -0.15rem;
  margin-left: 0.2rem;
  background-color: var(--jsini-accent);
  animation: jsiniBlink 0.8s infinite;
}

@keyframes jsiniBlink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}

/* AI 답변 하단 도구(복사 등) */
.jsini-chat__message-tools {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin-top: 0.35rem;
  padding-left: 0.2rem;
}

.jsini-chat__tool-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.25rem 0.5rem;
  border-radius: 0.35rem;
  border: 1px solid transparent;
  background: transparent;
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-xs);
  cursor: pointer;
  transition: all 0.15s ease;
}

.jsini-chat__tool-btn:hover {
  background: color-mix(in srgb, var(--jsini-text-muted) 12%, transparent);
  color: var(--jsini-text);
  border-color: var(--jsini-border);
}

/* 안내 및 노티스 */
.jsini-chat__notices {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-top: 0.5rem;
}

.jsini-chat__notice {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.5rem 0.8rem;
  border-radius: 0.5rem;
  border-left: 3px solid var(--jsini-accent);
  background: color-mix(in srgb, var(--jsini-accent) 8%, transparent);
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-sm);
}

.jsini-chat__notice-icon {
  width: 1rem;
  height: 1rem;
  color: var(--jsini-accent);
  flex-shrink: 0;
}

/* 답변 생성 중 인디케이터 */
.jsini-chat__typing {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.4rem 0;
}

.jsini-chat__avatar--small {
  width: 1.6rem;
  height: 1.6rem;
  flex: 0 0 1.6rem;
}

.jsini-chat__avatar--small .jsini-icon-robot {
  width: 0.95rem;
  height: 0.95rem;
}

.jsini-chat__typing-bubble {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.5rem 0.75rem;
  border-radius: 1rem;
  background: var(--jsini-surface);
  border: 1px solid var(--jsini-border);
}

.jsini-chat__typing-dot {
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 50%;
  background: var(--jsini-accent);
  animation: jsiniTypingBounce 1.4s infinite ease-in-out both;
}

.jsini-chat__typing-dot:nth-child(1) { animation-delay: -0.32s; }
.jsini-chat__typing-dot:nth-child(2) { animation-delay: -0.16s; }

@keyframes jsiniTypingBounce {
  0%, 80%, 100% { transform: scale(0); opacity: 0.4; }
  40% { transform: scale(1); opacity: 1; }
}

.jsini-chat__typing-text {
  font-size: var(--jsini-fs-xs);
  color: var(--jsini-text-muted);
}

/* ── 하단 입력창 (ChatGPT Pill Capsule Input Bar) ───────────── */
.jsini-chat__input-area {
  flex-shrink: 0;
  padding: 0.6rem 0.75rem 0.8rem;
  background: var(--jsini-bg);
  border-top: 1px solid var(--jsini-border);
  position: relative;
  z-index: 10;
}

.jsini-chat__input-container {
  width: 100%;
  max-width: 48rem;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
}

.jsini-chat__capsule {
  display: flex;
  align-items: flex-end;
  gap: 0.5rem;
  padding: 0.35rem 0.5rem 0.35rem 0.85rem;
  border-radius: 1.75rem;
  border: 1px solid var(--jsini-border);
  background: var(--jsini-surface);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.jsini-chat__capsule:focus-within {
  border-color: var(--jsini-accent);
  box-shadow: 0 4px 20px color-mix(in srgb, var(--jsini-accent) 20%, transparent);
}

.jsini-chat__textarea {
  flex: 1;
  min-width: 0;
  border: none !important;
  background: transparent !important;
}

.jsini-chat__textarea textarea {
  border: none !important;
  background: transparent !important;
  padding: 0.4rem 0 !important;
  resize: none !important;
  box-shadow: none !important;
  line-height: 1.5;
  font-size: var(--jsini-fs-base);
}

.jsini-chat__capsule-actions {
  flex-shrink: 0;
  padding-bottom: 0.15rem;
}

/* 원형 전송/중지 버튼 */
.jsini-chat__circle-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 50%;
  border: none;
  cursor: pointer;
  transition: all 0.15s ease;
  user-select: none;
}

.jsini-chat__circle-btn--send {
  background: color-mix(in srgb, var(--jsini-text-muted) 20%, transparent);
  color: var(--jsini-text-muted);
}

.jsini-chat__circle-btn--send.is-active {
  background: var(--jsini-accent);
  color: #fff;
  box-shadow: 0 2px 8px color-mix(in srgb, var(--jsini-accent) 35%, transparent);
}

.jsini-chat__circle-btn--send.is-active:hover {
  filter: brightness(1.1);
  transform: scale(1.05);
}

.jsini-chat__circle-btn--stop {
  background: #e03131;
  color: #fff;
  box-shadow: 0 2px 8px rgba(224, 49, 49, 0.35);
  animation: jsiniPulseStop 1.8s infinite;
}

@keyframes jsiniPulseStop {
  0% { transform: scale(1); }
  50% { transform: scale(1.05); }
  100% { transform: scale(1); }
}

/* 입력칸 아래 고르개 줄. 「AI는 실수를 할 수 있습니다」 안내가 있던 자리다. */
.jsini-chat__opts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  margin: 0.45rem 0 0;
}

.jsini-chat__opt {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: var(--jsini-fs-xs);
  color: var(--jsini-text-muted);
  cursor: pointer;
  user-select: none;
}

/* 켜면 글자도 함께 또렷해진다 — 체크 하나만으로는 작은 글씨에서 잘 안 보인다. */
.jsini-chat__opt:has(.jsini-chat__opt-box:checked) .jsini-chat__opt-text {
  color: var(--jsini-text);
  font-weight: 600;
}

.jsini-chat__opt-box {
  width: 0.9rem;
  height: 0.9rem;
  margin: 0;
  accent-color: var(--tb-primary);
  cursor: pointer;
}

/* 「사내 자료 전용」을 켰을 때만 서는 범위 고르개. 서랍 폭에서도 한 줄에 서게 폭을 묶는다. */
.jsini-chat__scope {
  max-width: 14rem;
  min-width: 0;
  padding: 0.1rem 0.35rem;
  font-size: var(--jsini-fs-xs);
  color: var(--jsini-text);
  background: var(--jsini-surface);
  border: 1px solid var(--jsini-border);
  border-radius: 0.375rem;
  text-overflow: ellipsis;
  cursor: pointer;
}


/* 말로 묻기 ①: 캡슐의 마이크. 보내기 단추와 반대편에 둔다(한쪽에 몰면 말하려다 보내기를 누른다). 손가락으로 누르는 단추라 36px 을 지킨다. */
.jsini-chat__mic-btn {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  margin-bottom: 0.15rem;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--jsini-text-muted);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.jsini-chat__mic-btn:hover:not(:disabled) {
  background: color-mix(in srgb, var(--jsini-text-muted) 14%, transparent);
  color: var(--jsini-text);
}

.jsini-chat__mic-btn:disabled {
  opacity: 0.4;
  cursor: default;
}

/* 듣는 중. 붉게 두지 않는다. 붉은색은 이 화면에서 멈추기가 이미 쓰고 있다. */
.jsini-chat__mic-btn.is-listening {
  background: var(--jsini-accent);
  color: #fff;
  animation: jsiniMicPulse 1.6s ease-in-out infinite;
}

.jsini-chat__mic-btn.is-busy {
  color: var(--jsini-accent);
}

@keyframes jsiniMicPulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--jsini-accent) 55%, transparent); }
  70%      { box-shadow: 0 0 0 0.5rem color-mix(in srgb, var(--jsini-accent) 0%, transparent); }
}

/* 음성 모드를 여는 단추. 입력칸이 비었을 때만 보내기 자리에 선다. */
.jsini-chat__circle-btn--voice {
  background: color-mix(in srgb, var(--jsini-accent) 14%, var(--jsini-surface));
  color: var(--jsini-accent);
  border: 1px solid color-mix(in srgb, var(--jsini-accent) 30%, transparent);
}

.jsini-chat__circle-btn--voice:hover {
  background: var(--jsini-accent);
  color: #fff;
  transform: scale(1.05);
}

/* 마이크가 막혔을 때의 안내. 입력칸 바로 아래에 둔다(누른 자리에서 멀면 무엇에 대한 말인지 안 읽힌다). */
.jsini-chat__mic-notice {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  margin: 0.4rem 0 0;
  font-size: var(--jsini-fs-xs);
  color: var(--jsini-text-muted);
}

/* 말로 묻기 ②: 음성 모드. 전체화면이다(새 라우트가 아니라 같은 부품의 상태라 닫으면 뒤의 대화가 남는다). 헤더 서랍 안에서 열어도 화면 전체를 덮어야 해서 서랍보다 위에 둔다. */
.jsini-voice {
  position: fixed;
  inset: 0;
  z-index: 1100;
  display: flex;
  flex-direction: column;
  background: var(--jsini-bg);

  /* 노치·홈 인디케이터·주소창. 표준 가이드 5-2 와 같은 셈이다. */
  padding:
    calc(env(safe-area-inset-top) + 0.75rem)
    1rem
    calc(env(safe-area-inset-bottom) + 1rem);
}

/* 서랍 안에서 열면 position: fixed 가 화면이 아니라 서랍에 갇힌다(transform 이 새 기준 상자를 만든다). 음성 모드가 떠 있는 동안에만 그 transform 을 거둔다(늘 거두면 서랍의 여닫는 모습을 건드린다). */
.jsini-right-drawer--open:has(.jsini-voice) {
  transform: none;
}

.jsini-voice__top {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

.jsini-voice__toggles {
  display: flex;
  gap: 0.4rem;
}

.jsini-voice__toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  min-height: 2.25rem;
  padding: 0 0.7rem;
  border-radius: 1.25rem;
  border: 1px solid var(--jsini-border);
  background: transparent;
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-xs);
  cursor: pointer;
  transition: all 0.15s ease;
}

.jsini-voice__toggle.is-on {
  border-color: color-mix(in srgb, var(--jsini-accent) 40%, transparent);
  background: color-mix(in srgb, var(--jsini-accent) 12%, transparent);
  color: var(--jsini-accent);
}

.jsini-voice__close {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--jsini-text-muted);
  cursor: pointer;
}

.jsini-voice__close:hover {
  background: color-mix(in srgb, var(--jsini-text-muted) 14%, transparent);
  color: var(--jsini-text);
}

.jsini-voice__stage {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  min-height: 0;
}

/* 오브. 소리 크기는 JS 가 --jsini-voice-level 에 직접 적는다(회로로 올리면 말하는 동안 서버가 초당 수십 번 깨어난다). */
.jsini-voice__orb {
  --jsini-voice-level: 0;

  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 10rem;
  height: 10rem;
  border: none;
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.jsini-voice__orb:disabled {
  cursor: default;
}

.jsini-voice__orb-core {
  position: absolute;
  inset: 1.5rem;
  border-radius: 50%;
  background: linear-gradient(
    135deg,
    var(--jsini-accent),
    color-mix(in srgb, var(--jsini-accent) 55%, #fff)
  );
  box-shadow: 0 0.5rem 2rem color-mix(in srgb, var(--jsini-accent) 35%, transparent);
  transition: transform 0.12s ease-out;
}

/* 말소리에 따라 커졌다 작아진다. 1 + 0.22 는 120px 짜리 원이 26px 쯤 움직이는 값이다(더 키우면 글자를 가리고 더 줄이면 안 움직여 보인다). */
.jsini-voice__orb.is-listening .jsini-voice__orb-core {
  transform: scale(calc(1 + var(--jsini-voice-level) * 0.22));
}

/* 바깥 고리. 듣는 중에만 퍼져 나간다. */
.jsini-voice__orb-ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--jsini-accent) 30%, transparent);
  opacity: 0;
}

.jsini-voice__orb.is-listening .jsini-voice__orb-ring {
  animation: jsiniOrbRing 2s ease-out infinite;
}

@keyframes jsiniOrbRing {
  0%   { transform: scale(0.75); opacity: 0.7; }
  100% { transform: scale(1.1);  opacity: 0; }
}

/* 생각하는 중은 맥박이 아니라 천천히 도는 것으로 가른다(맥박이면 듣는 중과 구별이 안 돼 사람이 말을 계속 건다). */
.jsini-voice__orb.is-thinking .jsini-voice__orb-core {
  background: conic-gradient(
    from 0deg,
    color-mix(in srgb, var(--jsini-accent) 20%, transparent),
    var(--jsini-accent),
    color-mix(in srgb, var(--jsini-accent) 20%, transparent)
  );
  animation: jsiniOrbSpin 1.6s linear infinite;
}

@keyframes jsiniOrbSpin {
  to { transform: rotate(360deg); }
}

/* 읽어 주는 중 — 숨 쉬듯 느리게. 이때 누르면 말을 끊는다. */
.jsini-voice__orb.is-speaking .jsini-voice__orb-core {
  animation: jsiniOrbBreathe 2.4s ease-in-out infinite;
}

@keyframes jsiniOrbBreathe {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.06); }
}

.jsini-voice__orb.is-idle .jsini-voice__orb-core {
  background: color-mix(in srgb, var(--jsini-text-muted) 25%, var(--jsini-surface));
  box-shadow: none;
}

.jsini-voice__orb-icon {
  position: relative;
  z-index: 1;
  --jsini-icon-size: 2rem;
  width: 2rem;
  height: 2rem;
  color: #fff;
}

.jsini-voice__orb.is-idle .jsini-voice__orb-icon {
  color: var(--jsini-text-muted);
}

.jsini-voice__state {
  margin: 0;
  font-size: var(--jsini-fs-base);
  font-weight: 600;
  color: var(--jsini-text);
}

.jsini-voice__notice {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  margin: 0;
  max-width: 28rem;
  text-align: center;
  font-size: var(--jsini-fs-xs);
  color: var(--jsini-text-muted);
}

/* 자막은 반드시 띄운다(받아쓰기가 틀렸을 때 사람이 알아야 한다). 높이를 고정해 오브가 위아래로 뛰지 않게 한다. */
.jsini-voice__caption {
  flex-shrink: 0;
  width: 100%;
  max-width: 48rem;
  margin: 0 auto;
  height: 9rem;
  overflow-y: auto;
  padding: 0.75rem 0.9rem;
  border-radius: 1rem;
  border: 1px solid var(--jsini-border);
  background: var(--jsini-surface);
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.jsini-voice__said-label {
  display: block;
  margin-bottom: 0.15rem;
  font-size: 11px;
  color: var(--jsini-text-muted);
}

.jsini-voice__said-text {
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text);
}

.jsini-voice__reply-text {
  font-size: var(--jsini-fs-sm);
  line-height: 1.55;
  color: var(--jsini-text);
  white-space: pre-wrap;
  word-break: break-word;
}

.jsini-voice__hint {
  margin: auto;
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text-muted);
}

.jsini-voice__actions {
  flex-shrink: 0;
  display: flex;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 0.9rem;
}

.jsini-voice__action {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  min-height: 2.5rem;
  padding: 0 1rem;
  border-radius: 1.25rem;
  border: 1px solid var(--jsini-border);
  background: var(--jsini-surface);
  color: var(--jsini-text);
  font-size: var(--jsini-fs-sm);
  cursor: pointer;
}

.jsini-voice__action:hover {
  border-color: var(--jsini-accent);
  color: var(--jsini-accent);
}

.jsini-voice__action--stop {
  border-color: #e03131;
  color: #e03131;
}

/* 휴대폰에서는 오브를 줄이고 자막을 낮춰 한 화면에 다 들어오게 한다(가상 키보드가 없는 화면이라 스크롤이 생기면 안 된다). */
@media (max-width: 767px) {
  .jsini-voice__orb {
    width: 8rem;
    height: 8rem;
  }

  .jsini-voice__caption {
    height: 7.5rem;
  }

  .jsini-voice__toggle-text {
    display: none;
  }

  .jsini-voice__toggle {
    padding: 0 0.6rem;
  }
}

/* 움직임을 줄여 달라고 한 사람에게는 상태를 색과 글로만 알린다. */
@media (prefers-reduced-motion: reduce) {
  .jsini-chat__mic-btn.is-listening,
  .jsini-voice__orb-ring,
  .jsini-voice__orb.is-thinking .jsini-voice__orb-core,
  .jsini-voice__orb.is-speaking .jsini-voice__orb-core {
    animation: none;
  }

  .jsini-voice__orb.is-listening .jsini-voice__orb-core {
    transform: none;
  }
}

/* AI 대화 서랍. 본문 위에 겹친다. 닫혀도 DOM 에 남아 대화가 사라지지 않게 한다. visibility 도 함께 끄는 것은 밀어 둔 칸이 탭 순서에 남지 않게 하려고다. 휴대폰에서는 아래 띠 높이만큼 올려 세운다. */
.jsini-ai-drawer {
  position: fixed;
  top: var(--jsini-header-height);
  right: 0;
  bottom: 0;
  width: min(26rem, 100vw);
  z-index: 1040;
  display: flex;
  flex-direction: column;
  background: var(--jsini-bg);
  border-left: 1px solid var(--jsini-border);
  box-shadow: -0.5rem 0 1.5rem rgb(0 0 0 / 12%);
  transform: translateX(100%);
  visibility: hidden;
  transition: transform 0.2s ease-in-out, visibility 0.2s;
}

.jsini-ai-drawer--open {
  transform: translateX(0);
  visibility: visible;
}

.jsini-ai-drawer__head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.5rem 0.5rem 0.9rem;
  border-bottom: 1px solid var(--jsini-border);
}

.jsini-ai-drawer__title {
  flex: 1;
  font-weight: 600;
}

.jsini-ai-drawer__body {
  flex: 1;
  min-height: 0;
  padding: 0.75rem;
}

.jsini-ai-drawer__foot {
  margin: 0;
  padding: 0 0.9rem 0.75rem;
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-xs);
  line-height: 1.5;
}

/* 알림함 서랍. 헤더의 종을 누르면 오른쪽에서 밀려 들어온다. 머리·몸통·발로 가른다(굴리는 것은 몸통뿐이라 발의 모두 읽음 단추는 제자리다). 휴대폰에서는 화면을 다 채운다. 닫혀 있을 때도 화면 밖에 둔다. */

/* 덮개는 헤더 아래부터 깐다(헤더까지 덮으면 방금 누른 종으로 다시 닫을 수 없다). */
.jsini-note-scrim {
  position: fixed;
  inset: var(--jsini-header-height) 0 0 0;
  background: rgb(0 0 0 / 28%);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s ease-in-out, visibility 0.2s;
  z-index: 1042;
}

.jsini-note-scrim--on {
  opacity: 1;
  visibility: visible;
}

.jsini-note-drawer {
  position: fixed;
  top: var(--jsini-header-height);
  right: 0;
  bottom: 0;
  width: min(24rem, 100vw);
  display: flex;
  flex-direction: column;
  background: var(--jsini-bg);
  color: var(--jsini-text);
  border-left: 1px solid var(--jsini-border);
  box-shadow: -10px 0 28px rgb(0 0 0 / 14%);
  transform: translateX(100%);
  transition: transform 0.22s ease-in-out;
  z-index: 1043;
}

.jsini-note-drawer--on { transform: none; }

/* 접힌 서랍 안으로 탭 이동이 들어가면 안 된다(보이지 않는 카드에 초점이 간다). */
.jsini-note-drawer:not(.jsini-note-drawer--on) {
  visibility: hidden;
  transition: transform 0.22s ease-in-out, visibility 0.22s;
}

.jsini-note-drawer__head {
  display: flex;
  align-items: center;
  height: 3rem;
  min-height: 3rem;
  padding: 0 0.5rem 0 1rem;
  background: var(--jsini-surface);
  border-bottom: 1px solid var(--jsini-border);
}

.jsini-note-drawer__title {
  flex: 1;
  font-weight: 600;
}

/* 여기만 구른다. min-height 0 이 없으면 세로 flex 자식이 알맹이만큼 벌어져 발을 화면 밖으로 민다. */
.jsini-note-drawer__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0.75rem;
}

.jsini-note-drawer-custom,
.jsini-note-drawer {
  font-size: var(--jsini-fs-md);
}

.jsini-note-drawer__status {
  font-size: var(--jsini-fs-sm);
}

.jsini-note-drawer__card {
  cursor: pointer;
  font-size: var(--jsini-fs-md);
}

/* 한 줄의 짜임 — [프로필(얼굴+단추)] [글]. 글 영역이 남은 폭을 온전히 쓴다. */
.jsini-note-drawer__row {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
}

/* 프로필 사진과 그 아래 읽음·삭제 단추 묶음 — 가로 공간을 아끼기 위해 단추를 얼굴 아래로 모은다. */
.jsini-note-drawer__profile {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
}

/* 앱 알림에 떴던 그림. 동그랗게 자른다(휴대폰 알림창의 모양과 같아야 같은 것으로 읽힌다). object-fit cover 가 없으면 세로로 긴 사진이 눌리고, 바탕은 받아 오기 전의 빈 네모를 가린다. */
.jsini-note-drawer__face {
  flex: 0 0 auto;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  object-fit: cover;
  background: var(--jsini-surface);
  border: 1px solid var(--jsini-border);
}

/* min-width 0 이 있어야 긴 글이 접힌다(없으면 긴 제목 하나가 줄을 밀어내 단추가 카드 밖으로 나간다). */
.jsini-note-drawer__text {
  flex: 1;
  min-width: 0;
}

.jsini-note-drawer__text .card-title {
  font-size: var(--jsini-fs-md);
  font-weight: 600;
  line-height: 1.35;
  overflow-wrap: anywhere;
}

.jsini-note-drawer__text .card-text {
  font-size: var(--jsini-fs-sm);
  line-height: 1.45;
  overflow-wrap: anywhere;
}

.jsini-note-drawer__date {
  font-size: var(--jsini-fs-2xs);
  line-height: 1.25;
}

/* 데스크톱 알림 관리 단추(읽음 · 삭제). 평소에는 눈에 덜 띄고 카드에 손이 가면 또렷해진다. 휴대폰에서는 스와이프로 처리하므로 데스크톱 폭에서만 보인다. */
.jsini-note-drawer__actions {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.15rem;
  --jsini-icon-size: calc(var(--jsini-fs-base) * 1.05);
}

.jsini-note-drawer__mark {
  flex: 0 0 auto;
  display: inline-flex;
  opacity: 0.55;
  transition: opacity 0.15s ease-in-out;
}

.jsini-note-drawer__mark .dxbl-btn {
  padding: 0.05rem;
  min-width: 0;
  line-height: 1;
}

.jsini-note-drawer__card:hover .jsini-note-drawer__mark,
.jsini-note-drawer__mark:hover {
  opacity: 1;
}

@media (hover: none) {
  .jsini-note-drawer__mark { opacity: 0.8; }
}

@media (max-width: 767px) {
  .jsini-note-drawer__actions {
    display: none;
  }
}

/* 밀어서 읽음·삭제. 오른쪽은 읽음, 왼쪽은 삭제(휴대폰 알림함에서 손이 이미 아는 짝). 움직이는 값은 클래스가 아니라 인라인 사용자 지정 속성으로 받는다(Blazor 가 카드 class 를 갱신하면 JS 가 붙인 클래스가 지워져 카드가 밀린 자리에 굳는다). touch-action pan-y 가 핵심 한 줄이다(세로 끌기는 브라우저가 가져간다). 대는 쪽은 js/note-swipe.js. */
.jsini-note-drawer__card[data-note] {
  --note-swipe: 0px;
  --note-swipe-ms: 0ms;
  --note-ready: 0;
  --note-alive: 1;

  position: relative;
  overflow: hidden;
  touch-action: pan-y;
  opacity: var(--note-alive);
  transition: opacity 180ms ease;
}

/* 좌우에서 열리는 띠는 미는 만큼만 넓어진다. 카드 밑에 깔고 본문으로 덮는 방법은 쓰지 않는다(테마 바탕과 어긋난다). 안쪽 여백을 주지 않는다(주면 폭이 0 이어도 실오라기가 선다). */
.jsini-note-drawer__act {
  position: absolute;
  inset-block: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 0.25rem;
  overflow: hidden;
  color: #fff;
  font-size: var(--jsini-fs-2xs);
  white-space: nowrap;
  pointer-events: none;
  transition: width var(--note-swipe-ms) ease;
}

/* 초록은 ✓ 단추와 같은 뜻이고, 빨강은 되돌릴 수 없다는 뜻이라 둘이 갈려야 문턱 앞에서 놓으면 어느 쪽이 되는지 가린다. */
.jsini-note-drawer__act--read {
  left: 0;
  width: max(0px, var(--note-swipe));
  background: #2e7d32;
}

.jsini-note-drawer__act--del {
  right: 0;
  justify-content: flex-end;
  width: max(0px, calc(-1 * var(--note-swipe)));
  background: #c62828;
}

/* 문턱을 넘으면 그림이 커지고 글자가 또렷해져 손을 떼기 전에 놓으면 처리된다는 것을 알린다. 넘기 전에 놓으면 카드는 제자리로 돌아간다. */
.jsini-note-drawer__act-mark {
  flex: 0 0 auto;
  font-size: var(--jsini-fs-sm);
  line-height: 1;
  transform: scale(calc(0.7 + (0.3 * var(--note-ready))));
  transition: transform 120ms ease;
}

.jsini-note-drawer__act--read .jsini-note-drawer__act-mark { margin-left: 0.6rem; }
.jsini-note-drawer__act--del .jsini-note-drawer__act-mark { margin-right: 0.6rem; }

.jsini-note-drawer__act-text {
  opacity: calc(0.45 + (0.55 * var(--note-ready)));
  transition: opacity 120ms ease;
}

/* 밀리는 것은 본문이다(카드를 옮기면 띠도 가서 아무것도 열리지 않는다). 띠 위에 놓는 z-index 도 필요하다(띠가 절대 자리라 그대로면 본문을 덮어 제목이 가린다). */
.jsini-note-drawer__card[data-note] > .jsini-note-drawer__row {
  position: relative;
  z-index: 2;
  transform: translateX(var(--note-swipe));
  transition: transform var(--note-swipe-ms) ease;
}

/* 움직임을 줄여 달라고 한 사람에게는 결과만 준다. 카드는 바로 사라지고 띠는 미끄러지지 않는다. */
@media (prefers-reduced-motion: reduce) {
  .jsini-note-drawer__card[data-note],
  .jsini-note-drawer__card[data-note] > .jsini-note-drawer__row,
  .jsini-note-drawer__act,
  .jsini-note-drawer__act-mark,
  .jsini-note-drawer__act-text {
    transition: none;
  }
}

/* 발. 휴대폰에서 화면을 다 채울 때 아래 홈 바에 단추가 물리지 않게 safe-area 만큼 띄운다(넓은 화면에서는 0). */
.jsini-note-drawer__foot,
.jsini-note-drawer-custom .jsini-right-drawer__foot {
  flex: 0 0 auto;
  display: flex;
  justify-content: flex-end;
  padding: 0.75rem 0.9rem;
  padding-bottom: calc(0.75rem + env(safe-area-inset-bottom));
  background: var(--jsini-surface);
  border-top: 1px solid var(--jsini-border);
}

.jsini-note-drawer__readall {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* 모바일에서는 단추가 가로 폭을 꽉 채우고 세로 터치 영역(최소 44px)과 여백을 넉넉히 준다. */
.jsini-note-drawer__readall--phone {
  width: 100% !important;
  min-height: 44px;
  padding: 0.625rem 1rem;
  font-size: var(--jsini-fs-sm, 0.875rem);
  font-weight: 600;
}

/* 좁은 화면에서는 단추가 발을 꽉 채운다(한 줄에 그것 하나뿐이라 오른쪽 끝에 작게 붙으면 누를 데를 찾게 된다). */
@media (max-width: 767px) {
  .jsini-note-scrim {
    inset: 0;
  }

  .jsini-note-drawer,
  .jsini-note-drawer-custom {
    top: 0;
    width: 100vw;
    border-left: none;
  }

  .jsini-note-drawer__readall {
    width: 100% !important;
    min-height: 44px;
    padding: 0.625rem 1rem;
    font-size: var(--jsini-fs-sm, 0.875rem);
    font-weight: 600;
  }

  .jsini-note-drawer__foot,
  .jsini-note-drawer-custom .jsini-right-drawer__foot {
    justify-content: stretch;
  }
}

@media (pointer: coarse) and (max-width: 991px) {
  .jsini-note-drawer__readall {
    width: 100% !important;
    min-height: 44px;
    padding: 0.625rem 1rem;
    font-size: var(--jsini-fs-sm, 0.875rem);
    font-weight: 600;
  }

  .jsini-note-drawer__foot,
  .jsini-note-drawer-custom .jsini-right-drawer__foot {
    justify-content: stretch;
  }
}

/* 빠른 지시 서랍. AI 대화 서랍과 같은 자리·움직임이며 더 넓다(카드를 가로 둘씩 까는 알맹이라 26rem 에는 제목이 잘린다). 굴리는 것은 판이 아니라 몸통이다. 휴대폰에서는 그리지 않는다(창을 좁힐 때 막는다). */
.jsini-quick-drawer {
  position: fixed;
  top: var(--jsini-header-height);
  right: 0;
  bottom: 0;
  width: min(42rem, 100vw);
  z-index: 1040;
  display: flex;
  flex-direction: column;
  background: var(--jsini-bg);
  border-left: 1px solid var(--jsini-border);
  box-shadow: -0.5rem 0 1.5rem rgb(0 0 0 / 12%);
  transform: translateX(100%);
  visibility: hidden;
  transition: transform 0.2s ease-in-out, visibility 0.2s;
}

.jsini-quick-drawer--open {
  transform: translateX(0);
  visibility: visible;
}

.jsini-quick-drawer__head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.5rem 0.5rem 0.9rem;
  border-bottom: 1px solid var(--jsini-border);
}

.jsini-quick-drawer__title {
  flex: 1;
  font-weight: 600;
}

.jsini-quick-drawer__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0.75rem;
}

/* 판 안에서는 화면의 읽기 폭(52rem)이 걸리면 안 된다. 판을 꽉 채우게 풀어 둔다. */
.jsini-quick-drawer .pm-ask,
.jsini-quick-drawer-custom .pm-ask {
  max-width: none;
}

@media (max-width: 767px) {
  .jsini-quick-drawer {
    display: none;
  }
}

.jsini-icon-robot {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.2 1h1.6v1.6h2.4A2.8 2.8 0 0 1 14 5.4v5.2a2.8 2.8 0 0 1-2.8 2.8H4.8A2.8 2.8 0 0 1 2 10.6V5.4a2.8 2.8 0 0 1 2.8-2.8h2.4zM5.8 6.6a1.2 1.2 0 1 0 0 2.4 1.2 1.2 0 0 0 0-2.4zm4.4 0a1.2 1.2 0 1 0 0 2.4 1.2 1.2 0 0 0 0-2.4zM0 6.4h1.2v3.2H0zm14.8 0H16v3.2h-1.2z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M7.2 1h1.6v1.6h2.4A2.8 2.8 0 0 1 14 5.4v5.2a2.8 2.8 0 0 1-2.8 2.8H4.8A2.8 2.8 0 0 1 2 10.6V5.4a2.8 2.8 0 0 1 2.8-2.8h2.4zM5.8 6.6a1.2 1.2 0 1 0 0 2.4 1.2 1.2 0 0 0 0-2.4zm4.4 0a1.2 1.2 0 1 0 0 2.4 1.2 1.2 0 0 0 0-2.4zM0 6.4h1.2v3.2H0zm14.8 0H16v3.2h-1.2z'/%3E%3C/svg%3E");
}

/* 첨부 고르기(D5). 생김새는 DevExpress DxFileInput 이 그린다. 여기 남는 것은 부품과 안내 줄을 세로로 쌓는 것뿐이다. DxUpload 은 BFF 라 토큰이 없어 쓸 수 없다. */
.jsini-picker {
  display: grid;
  gap: 0.5rem;

        /* 단추가 칸 너비를 다 먹지 않게 한다(grid 기본 stretch 를 막는다). */
  justify-items: start;
}

/* 고르기 단추는 글자가 없어 정사각으로 세운다(글자가 있을 때 잰 좌우 여백을 두면 아이콘이 가로로 긴 칸 가운데 떠 보인다). */
.jsini-picker__pick {
  padding-inline: 0;
  min-width: 2.25rem;
}

/* 단추를 끈 자리와 고른 것이 없을 때 빈 판이 격자 간격을 한 번 더 먹는다. 둘 다 비었을 때만 접는다(:empty 는 Blazor 의 주석 노드를 세지 않아 그대로 걸린다). */
.jsini-picker .dxbl-upload-panel:empty,
.jsini-picker .dxbl-upload-file-list-view:empty {
  display: none;
}

/* 그 둘을 접고 나서도 빈 통의 위 여백이 남아 서므로, 사이는 격자 간격 하나로 둔다. */
.jsini-picker .dxbl-upload,
.jsini-picker .dxbl-upload-file-list-view {
  padding-top: 0;
}

.jsini-picker__hint {
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-sm);
}

/* 사진 그룹(ImageGroup). 여러 장을 격자로 늘어놓고 마지막 칸이 사진 추가다. 칸 크기를 고정해 사진마다 비율이 달라도 줄과 창 높이가 춤추지 않게 한다. */

.jsini-imggroup {
  display: grid;
  gap: 0.5rem;
}

.jsini-imggroup__hint {
  font-size: var(--jsini-fs-xs);
  color: var(--jsini-text-muted);
}

.jsini-imggroup__list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.jsini-imggroup__item {
  position: relative;
  width: 8rem;
  height: 6rem;
  margin: 0;
  overflow: hidden;
  border: 1px solid var(--jsini-border);
  border-radius: 0.4rem;
}

/* 대표 사진. 테두리 색만 바꾼다 — 굵기를 바꾸면 그 칸만 크기가 달라진다. */
.jsini-imggroup__item--rep { border-color: var(--jsini-accent); }

.jsini-imggroup__item img {
  width: 100%;
  height: 100%;

  /* 비율은 그대로 두고 넘치는 가장자리만 자른다. */
  object-fit: cover;
}

/* 단추는 사진 위에 겹치고 마우스를 올렸을 때만 보인다(늘 보이면 사진 여덟 장이 단추 열여섯 개로 읽힌다). */
.jsini-imggroup__tools {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  display: flex;
  gap: 0.25rem;
  justify-content: center;
  padding: 0.2rem;
  background: rgb(0 0 0 / 55%);
  opacity: 0;
  transition: opacity 0.12s ease-out;
}

.jsini-imggroup__item:hover .jsini-imggroup__tools,
.jsini-imggroup__item:focus-within .jsini-imggroup__tools {
  opacity: 1;
}

.jsini-imggroup__tools button {
  padding: 0.1rem 0.35rem;
  border: 0;
  border-radius: 0.25rem;
  background: transparent;
  color: #fff;
  font-size: var(--jsini-fs-2xs);
  cursor: pointer;
}

.jsini-imggroup__tools button:disabled { opacity: 0.6; cursor: default; }

.jsini-imggroup__add {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 8rem;
  height: 6rem;
  border: 1px dashed var(--jsini-border);
  border-radius: 0.4rem;
  background: var(--jsini-surface);
  font-size: var(--jsini-fs-xs);
  color: var(--jsini-text-muted);
  text-align: center;
  cursor: pointer;
}

/* 파일 고르개는 숨기고 칸 전체를 누르게 한다. display none 이 아니라 투명하게 덮어 둬야 키보드 초점이 살아 있다. */
.jsini-imggroup__add input[type='file'] {
  position: absolute;
  width: 8rem;
  height: 6rem;
  opacity: 0;
  cursor: pointer;
}

.jsini-imggroup__empty {
  font-size: var(--jsini-fs-xs);
  color: var(--jsini-text-muted);
}

/* 바깥 화면 끼워 넣기(Embedded.razor). 유형이 EMBEDDED 인 메뉴를 받는 공용 화면이라 셸에 둔다. 높이는 숫자를 세지 않고 본문 칸을 세로 flex 로 세워 iframe 에 남은 높이를 준다(CommCont 와 같은 수). */

/* 끼워 넣은 화면의 세로 막대는 끼운 문서가 그리므로 우리 CSS 가 닿지 않는다. iframe 을 막대 폭만큼 넓혀 감싸개 밖으로 밀어 잘라 낸다(휠과 손가락 스크롤은 그대로다). 잃는 것은 진행 위치 표시뿐이다. 막대 폭 17px 은 Windows·Linux 값이라 macOS 에서는 어긋난다(이 한 줄만 고치면 된다). */

:root {
  --jsini-embed-bar: 17px;
}

.jsini-embed-clip {
/* 밖으로 민 막대를 여기서 잘라 낸다. 모서리도 이쪽이 쥔다(iframe 에 두면 넓힌 만큼 오른쪽 끝이 잘린다). */
  overflow: hidden;

  /* 겹쳐 둔 「새 창으로 열기」가 여기에 자리를 잡는다(아래). */
  position: relative;

  /* :has() 를 모르는 브라우저를 위한 바닥. 아래 규칙이 이기면 안 쓰인다. */
  height: calc(100vh - 56px - 10rem);

  /* 창이 아주 낮을 때의 바닥. 그보다 줄이면 끼워 넣은 화면을 읽을 수 없다. */
  min-height: 360px;

  background: #fff;
}

.jsini-embed {
  display: block;
  width: calc(100% + var(--jsini-embed-bar));
  height: 100%;
  border: 0;
}

/* 끼운 화면이 본문 칸을 통째로 쓴다. 바깥 여백과 테두리를 걷어 액자가 둘로 보이지 않게 한다. 아래 여백만은 휴대폰 아래 띠가 쥐고 있으므로 padding 0 을 적어도 되돌아온다(자리 비킴이다). */
.jsini-shell__content:has(> .jsini-embed-clip) {
  display: flex;
  flex-direction: column;
  padding: 0;
}

/* 안내 줄은 제 높이만큼만. 없으면 iframe 에 자리를 뺏겨 글이 눌린다. */
.jsini-shell__content:has(> .jsini-embed-clip) > *:not(.jsini-embed-clip) {
  flex: none;
}

.jsini-shell__content:has(> .jsini-embed-clip) > .jsini-embed-clip {
  flex: 1;
  height: auto;
}

/* 겹쳐 둔 새 창으로 열기. 감싸개 안 오른쪽 위에 앉히고 iframe 위에 겹친다(차지하는 자리가 끼운 화면의 높이에서 빠진다). 테두리·바탕 없이 그림만 남기고 밑줄을 끈다. 남의 화면 위라 테두리 대신 그림자를 쓴다. 그림자는 아이콘이 아니라 단추에 건다(마스크가 그림자를 오려 낸다). */
.jsini-embed__toolbar {
  position: absolute;
  top: 0.375rem;
  right: 0.375rem;

        /* 끼운 화면이 무엇을 띄우든 이 단추가 위에 있어야 누를 수 있다(iframe 안쪽은 별도 레이어다). */
  z-index: 2;
}

.jsini-embed__toolbar .jsini-embed__open {
  padding: 0.25rem;
  line-height: 0;

  /* 링크 단추의 밑줄을 끈다. 위 머리말 참고. */
  --dxbl-btn-link-text-decoration: none;

        /* 아이콘만을 테마에 맡기지 않는다. 테마가 상자를 되돌리면 오류 없이 생김새만 바뀌므로 상자를 직접 끈다. */
  background: transparent;
  border: 0;
  box-shadow: none;

        /* 색도 박는다. 이 그림이 서는 자리는 남의 화면 위라 테마 색을 따르면 바탕에 따라 묻히므로 흰 테와 짝으로 둔다. */
  color: #374151;

        /* 어느 바탕에서도 가장자리가 남게 하는 흰 테. 이 줄을 아이콘 쪽으로 옮기면 마스크가 오려 내어 조용히 사라진다. */
  filter: drop-shadow(0 1px 0 #fff) drop-shadow(0 -1px 0 #fff)
          drop-shadow(1px 0 0 #fff) drop-shadow(-1px 0 0 #fff);

  /* 남의 화면을 가리는 자리라 평소에는 한 겹 물러나 있고, 손이 가면 선다. */
  opacity: 0.6;
  transition: opacity 0.15s ease;
}

.jsini-embed__toolbar .jsini-embed__open:hover,
.jsini-embed__toolbar .jsini-embed__open:focus-visible {
  opacity: 1;
}

/* 화면을 옮기는 동안의 표시(PageSpinner). 늘 DOM 에 두고 클래스만 바꿔 전환이 걸리게 한다. 나타날 때만 늦춘다(0.12초보다 빨리 끝난 이동은 표시가 뜨지 않는다). Blazor 에서는 타이머가 왕복이라 CSS 로 한다. 본문 안에 깔고 머리줄·탭 줄·길잡이는 덮지 않는다. */

.jsini-pageload {
  position: absolute;
  inset: 0;
  z-index: 30;

  display: flex;
  align-items: center;
  justify-content: center;

        /* 바탕을 덮되 비치게 둔다. 꽉 막으면 화면이 사라진 것으로 읽힌다. */
  background: color-mix(in srgb, var(--jsini-surface, #fff) 72%, transparent);
  backdrop-filter: blur(2px);

  visibility: hidden;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.28s ease, visibility 0.28s ease;
}

.jsini-pageload.is-on {
  visibility: visible;
  opacity: 1;

  /* 여기가 「깜빡임 막기」다. 위 머리말 참고. */
  transition-delay: 0.12s;
}

        /* 상승 조각 넷. Vue 판의 좌표와 엇박을 그대로 옮겼다. 심볼(JS 인터록)은 굴리지 않는다(브랜드 규칙이 기울이기·회전을 막는다). */
.jsini-loader {
  width: 3rem;
  height: 3rem;
  color: var(--jsini-text, currentColor);
}

.jsini-loader__bar {
  fill: currentColor;
  transform-origin: center bottom;
  animation: jsini-loader-rise 1.1s ease-in-out infinite;
}

.jsini-loader__bar:nth-child(1) { animation-delay: 0s; }
.jsini-loader__bar:nth-child(2) { animation-delay: 0.12s; }
.jsini-loader__bar:nth-child(3) { animation-delay: 0.24s; }
.jsini-loader__bar:nth-child(4) { animation-delay: 0.36s; }

@keyframes jsini-loader-rise {
  0%, 100% { transform: scaleY(0.55); }
  50% { transform: scaleY(1); }
}

/* 안 보일 때는 애니메이션을 멈춘다. 이동이 없을 때도 계속 돌면 그만큼
   브라우저가 매 프레임 다시 그린다 — 하루 종일 띄워 두는 화면들이다. */
.jsini-pageload:not(.is-on) .jsini-loader__bar { animation-play-state: paused; }

/* 움직임을 원하지 않는 사람에게는 멈춘 그림을 준다. */
@media (prefers-reduced-motion: reduce) {
  .jsini-loader__bar { animation: none; }
}

/* ── 연결이 끊겼을 때 뜨는 띠 ────────────────────────────────

   **띠는 셸이 그린다**(`App.razor` 의 `#components-reconnect-modal`).
   프레임워크는 그 요소가 있으면 자기 상자를 만들지 않고 **클래스만 갈아
   끼운다**(`UserSpecifiedDisplay`). 무엇을 보일지는 전부 여기서 정한다.

   [상자를 걷어냈다 — 2026-10-11]

   한동안 여기에 화면을 덮는 대화상자가 있었다. 상태마다 다른 글을 보여 주고
   새로고침·다시 시도·잠시 멈추기를 고르게 했고, 거절당한 자리에서는
   「5초 뒤에 화면을 새로 엽니다」를 셌다.

   **고를 것이 없는 선택이었다.** 거절당했다는 것은 하던 일이 서버에 없다는
   뜻이라 어느 단추를 눌러도 끝은 새로고침 하나이고, 서버에 닿지도 못한
   자리에서는 새로고침이 브라우저 오류 화면만 부른다. 창이 한 일은 죽은 화면
   앞에서 다섯을 세는 것뿐이었다.

   그래서 **끊겨 있는 동안 보이는 것은 위쪽의 작은 띠 하나**다. 화면을 덮지
   않으므로 읽던 것을 그대로 읽고 굴릴 수 있고, 누르면 그 자리에서 한 번 더
   이어 본다(`js/reconnect.js`). 새로고침이 정말 필요한 자리(rejected)에서는
   묻지 않고 스스로 연다 — 보여 줄 창이 없다.

   [상태 일곱은 여전히 다 적는다]

   show · retrying · paused · failed · resume-failed · rejected · hide.
   보이는 모습은 같지만 **하나라도 빠뜨리면 띠가 안 뜨거나 안 꺼진다** —
   프레임워크가 붙인 클래스에 규칙이 없으면 그 상태에서 요소가 `display: none`
   그대로 남는다.

   `hide` 를 **더 세게** 끄는 이유가 거기 있다. 프레임워크는 성공했을 때
   `show` 를 떼고 `hide` 를 붙이는데, 둘이 함께 붙어 있는 판이 있어도 꺼지는
   쪽이 이겨야 한다.

   [숨죽임 · 귀띔 — 0.9초로는 터널을 못 지난다]

   한동안 조용한 구간이 **0.9초짜리 투명 구간** 하나였다. 회선이 한 번 튀는
   것은 그 안에 끝나니까 사람은 아무것도 못 보고 하던 일을 이었다. 그런데
   터널은 0.9초가 아니다 — 지하 구간 하나가 수십 초이고, 승강기·주차장·건물
   안쪽도 몇 초씩 끊긴다. **그때마다 화면이 통째로 어두워지고 상자가 떴다.**

   그래서 둘로 나눈다.

   1. **숨죽임**(`--hush`, 2초) — 아무것도 안 보이고 **아무것도 안 막는다.**
      회선이 한 번 튄 것은 여기서 끝난다.
   2. **귀띔**(`--hint`) — 위쪽에 작은 띠 하나만 뜬다. 읽고 굴리는 것은
      그대로 되고, **누름만 삼킨다**(`js/reconnect.js`).

   그 위는 없다. 끊겨 있는 동안 끝까지 띠 하나이고, 눌린 것은 어차피 서버에
   닿지 못하므로 삼키고 띠를 한 번 흔들어(`--nudge`) 그 자리에서 알린다.
   ------------------------------------------------------------ */

#components-reconnect-modal {
  display: none;
}

/* 다시 이어졌다. **여기서 끝난다** — 새로고침도, 알림도 없다(머리말). */
#components-reconnect-modal.components-reconnect-hide {
  display: none !important;
}

/* 끊겨 있는 동안. **덮개가 아니다** — 바탕을 칠하지 않고 누름도 통과시킨다.
   되돌려 받는 것은 띠 하나뿐이다(아래). 이것이 「터널을 지나는 동안 읽던
   것을 계속 읽는다」의 전부다.

   **화면에서 가장 위다** — 잠금화면(20000)보다도 높다. 잠긴 화면은 **서버가 있어야 풀린다**(비밀번호·지문을
   서버가 대조한다). 회로가 끊긴 채로 덮개가 위에 있으면 띠가 덮개 너머로
   비쳐 보이기만 하고 눌리지는 않아, **잠근 사람이 화면을 되찾을 길이 아예
   없다.** 자리를 비운 사이에 끊기는 것은 가장 흔한 조합이기도 하다. */
#components-reconnect-modal.components-reconnect-show,
#components-reconnect-modal.components-reconnect-retrying,
#components-reconnect-modal.components-reconnect-paused,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-resume-failed,
#components-reconnect-modal.components-reconnect-rejected {
  position: fixed;
  inset: 0;
  z-index: 20500;

  display: flex;
  align-items: flex-start;
  justify-content: center;

  background: transparent;
  pointer-events: none;
}

/* 숨죽임 동안은 띠조차 없다. 회선이 한 번 튄 것은 여기서 끝난다. */
#components-reconnect-modal.jsini-reconnect--hush,
#components-reconnect-modal.jsini-reconnect--hint {
  background: transparent;
  pointer-events: none;
}

/* ── 귀띔 띠 ──────────────────────────────────────────────────

   화면 위쪽 가운데에 뜨는 작은 알약. **이것만이 누름을 되돌려 받는다**
   (바깥은 `pointer-events: none`). 누르면 기다리지 않고 한 번 더 이어 본다.
   ------------------------------------------------------------ */

.jsini-reconnect__band {
  display: none;
}

#components-reconnect-modal.jsini-reconnect--hint .jsini-reconnect__band {
  display: inline-flex;
  align-items: center;
  gap: var(--jsini-sp-xs, 0.375rem);

  /* 위쪽 가운데. 노치와 상태표시줄을 피한다. */
  margin-top: calc(env(safe-area-inset-top, 0px) + var(--jsini-sp-sm, 0.5rem));
  max-width: calc(100vw - 2rem);
  padding: 0.3rem 0.75rem;

  border: 1px solid var(--jsini-border, #e3e5e8);
  border-radius: 999px;

  /* 바탕과 글자를 **둘 다** 못 박는다 — 어두운 테마에서 흰 글씨가 흰 바탕에
     얹히던 사고가 이 자리에서 한 번 났다. */
  background: var(--jsini-surface-raised, #ffffff);
  color: var(--jsini-text, #1f2328);

  box-shadow: var(--jsini-shadow-md, 0 4px 10px rgb(0 0 0 / 18%));
  font: inherit;
  font-size: var(--jsini-fs-sm, 0.875rem);
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;

  /* 띠 자신은 눌려야 한다. */
  pointer-events: auto;

  animation: jsini-reconnect-appear 200ms ease-out both;
}

@keyframes jsini-reconnect-appear {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* 도는 점. 「멈춘 것이 아니라 하는 중」이라는 것을 글 없이 알린다. */
.jsini-reconnect__band-dot {
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
  border: 2px solid var(--jsini-accent, #0f6cbd);
  border-right-color: transparent;
  border-radius: 50%;
  animation: jsini-reconnect-spin 900ms linear infinite;
}

@keyframes jsini-reconnect-spin {
  to { transform: rotate(360deg); }
}

/* 삼킨 누름을 알린다 — 띠를 한 번 흔든다. 클래스는 `js/reconnect.js` 가
   붙이고, 애니메이션이 끝나면 뗀다. */
#components-reconnect-modal.jsini-reconnect--nudge .jsini-reconnect__band {
  animation:
    jsini-reconnect-appear 200ms ease-out both,
    jsini-reconnect-nudge 420ms ease-out;
}

@keyframes jsini-reconnect-nudge {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-4px); }
  60% { transform: translateX(4px); }
}

/* 움직임을 원하지 않는 사람에게는 멈춘 그림을 준다. 그래도 **띠는 뜬다** —
   이것은 꾸밈이 아니라 「지금은 안 눌린다」는 알림이다. */
@media (prefers-reduced-motion: reduce) {
  .jsini-reconnect__band { animation: none; }
  .jsini-reconnect__band-dot { animation: none; }
  #components-reconnect-modal.jsini-reconnect--nudge .jsini-reconnect__band {
    animation: none;
    border-color: var(--jsini-accent, #0f6cbd);
  }
}

/* ── 좁은 화면에서는 창을 화면 안에 가둔다 ────────────────────

   창 너비는 화면이 픽셀로 준다(`Width="420px"` · `"880px"` …). 전부
   데스크톱을 보고 고른 값이라 **폭이 360px 인 기기에서는 창이 화면보다
   넓다.** DevExpress 는 창을 가운데 두므로(`justify-self: center`) 넘친
   만큼이 양옆으로 똑같이 잘린다 — 묻는 글의 첫 글자와 끝 글자가 함께
   사라지고, 바닥의 「취소」도 화면 밖으로 나간다.

   `max-width` 로 가두면 화면이 준 `width` 는 그대로 두고 좁은 기기에서만
   접힌다. 창은 `box-sizing: border-box` 라 테두리까지 이 안이다.

   **우리 창에만 건다**(`jsini-popup` · `jsini-gridpopup`). `.dxbl-popup` 을
   통째로 겨누면 DevExpress 가 좁은 화면에서 바닥부터 올리는 판
   (`dxbl-btn-dropdown-modal`)과 달력·목록 드롭다운까지 같이 걸려, 화면을
   꽉 채워야 할 판이 양옆에 틈을 두고 뜬다.

   `1.5rem` 은 좌우 0.75rem 씩이다. 창이 화면에 딱 붙으면 뒤가 덮인 것인지
   페이지가 그렇게 생긴 것인지 구별이 안 된다.

   **`!important` 가 필요하다.** 폭을 직접 준 창에는 DevExpress 가
   `dxbl-popup-fixed-width` 를 붙이고, 테마에

       .dxbl-modal > .dxbl-modal-root > .dxbl-popup.dxbl-popup-fixed-width
       { max-width: unset }

   가 있다 — 「폭을 박았으니 아무것도 줄이지 마라」는 뜻이고, 그 규칙은
   미디어 쿼리 밖이라 휴대폰에서도 산다. 겨누는 자리를 늘려 이기는 방법도
   있지만 그러면 DevExpress 가 다음 판에서 마크업을 한 겹 바꾸는 순간
   말없이 풀린다.
   ------------------------------------------------------------ */

@media (max-width: 767px) {
  .jsini-popup,
  .jsini-gridpopup {
    max-width: calc(100vw - 1.5rem) !important;
    margin: 0 auto !important;
  }

  /* 휴대폰(≤767px)에서 창이 화면을 꽉 채우도록 한다.
     CommPopup 의 FullOnMobile(기본값 true)이 이 규칙을 탄다. */
  .dxbl-modal > .dxbl-modal-root > .dxbl-popup.jsini-popup--full-mobile {
    width: 100vw !important;
    max-width: 100vw !important;
    height: 100% !important;
    height: 100dvh !important;
    max-height: 100% !important;
    max-height: 100dvh !important;
    margin: 0 !important;
    border-radius: 0 !important;
  }

  .jsini-popup--full-mobile .dxbl-modal-content {
    height: 100% !important;
    max-height: none !important;
    border-radius: 0 !important;
  }

  .jsini-popup--full-mobile .dxbl-modal-body {
    display: flex;
    flex-direction: column;
    min-height: 0;
    flex: 1 1 auto;
    overflow-y: auto;
  }

/* 키보드가 올라오면 창을 보이는 만큼으로 줄인다. 브라우저는 키보드가 떠도 레이아웃 뷰포트를 줄이지 않으므로 visualViewport 로 재어 내려 준다(keyboard-inset.js). 창을 담는 판부터 줄여야 아래에 틈이 생기지 않는다. kb-open 이 붙었을 때만 돌고 값이 없으면 100dvh 로 되돌아간다. */
  html.jsini-kb-open .dxbl-modal > .dxbl-modal-root:has(> .dxbl-popup.jsini-popup),
  html.jsini-kb-open .dxbl-modal > .dxbl-modal-root:has(> .dxbl-popup.jsini-gridpopup) {
    top: var(--jsini-vv-top, 0px) !important;
    height: var(--jsini-vvh, 100dvh) !important;
  }

  html.jsini-kb-open .dxbl-modal > .dxbl-modal-root > .dxbl-popup.jsini-popup--full-mobile {
    height: var(--jsini-vvh, 100dvh) !important;
    max-height: var(--jsini-vvh, 100dvh) !important;
  }

/* 꽉 채우지 않는 창은 자리를 옮기지 않고 키만 가둔다(판이 알아서 가운데 세운다). */
  html.jsini-kb-open .dxbl-modal > .dxbl-modal-root > .dxbl-popup.jsini-popup:not(.jsini-popup--full-mobile),
  html.jsini-kb-open .dxbl-modal > .dxbl-modal-root > .dxbl-popup.jsini-gridpopup {
    max-height: calc(var(--jsini-vvh, 100dvh) - 1rem) !important;
  }

/* 키보드가 올라오면 셸 높이를 보이는 만큼으로 줄인다(100vh 면 본문 아래쪽이 키보드 뒤로 숨는다). 타이핑 중에는 하단 띠와 떠다니는 단추를 감춘다. */
  html.jsini-kb-open .jsini-shell {
    height: var(--jsini-vvh, 100dvh) !important;
    max-height: var(--jsini-vvh, 100dvh) !important;
  }

  html.jsini-kb-open .jsini-bottom-nav {
    display: none !important;
  }

  html.jsini-kb-open .jsini-shell__content {
    padding-bottom: var(--jsini-shell-pad-bottom) !important;
  }

  html.jsini-kb-open .jsini-shell__fab--bottom-left,
  html.jsini-kb-open .jsini-shell__fab--bottom-right,
  html.jsini-kb-open .jsini-shell__fab--scroll-top {
    display: none !important;
  }
}

/* 표의 편집 창은 CommPopup 이 아니다(DevExpress 가 PopupEditForm 으로 그린다). 부품을 바꾸면 등록·수정 경로가 다 바뀌므로 부품은 그대로 두고 몸가짐만 맞춘다. 키가 큰 폼은 본문만 구르고, 머리를 잡는 손 모양을 jsini-drag-head 와 같게 한다. */

.jsini-gridpopup {
  max-height: 86vh;
}

.jsini-gridpopup .dxbl-modal-content {
  max-height: inherit;
  overflow: hidden;
}

/* 머리와 바닥은 제자리에 남고 본문만 구른다. */
.jsini-gridpopup .dxbl-modal-body {
  overflow: auto;
  min-height: 0;
}

/* 손 모양은 jsini-drag-head 와 같은 값이다(머리를 DevExpress 가 그려 클래스를 못 붙인다). DevExpress 가 머리에 cursor 를 더 세게 걸어 두어 !important 가 필요하다. */
.jsini-gridpopup .dxbl-modal-header {
  cursor: move !important;
  user-select: none;
}

.dxbl-popup-dragging.jsini-gridpopup .dxbl-modal-header {
  cursor: grabbing !important;
}

/* 머리 안의 닫기 단추는 **누르는** 자리다. 물려받은 `move` 를 되돌린다. */
.jsini-gridpopup .dxbl-modal-header .dxbl-btn {
  cursor: pointer !important;
}

/* 스크롤 막대는 3px 로 맞춘다(DevExpress 가 그리는 엄지 3.2px 와 굵기를 같게). 웹킷은 ::-webkit-scrollbar 로, 파이어폭스는 scrollbar-width 로 갈라 적는다. 두 방법을 한 요소에 같이 적으면 webkit 규칙이 통째로 무시되므로 섞지 않는다. 사이드바 트리와 iframe 안쪽 막대는 여기서 닿지 않는다. */

:root {
  --jsini-scroll-thumb: color-mix(in srgb, var(--jsini-text) 28%, transparent);
  --jsini-scroll-thumb-hover: color-mix(in srgb, var(--jsini-text) 45%, transparent);
}

@media (max-width: 767px) {
  :root {
    --jsini-scroll-thumb: color-mix(in srgb, var(--jsini-text) 5%, transparent);
    --jsini-scroll-thumb-hover: color-mix(in srgb, var(--jsini-text) 10%, transparent);
  }
}

/* 파이어폭스 전용. 크로미움은 selector(::-webkit-scrollbar) 를 알아서 이 블록에 들어오지 않는다. */
@supports not selector(::-webkit-scrollbar) {
  * {
    scrollbar-width: thin;
    scrollbar-color: var(--jsini-scroll-thumb) transparent;
  }
}

*::-webkit-scrollbar {
  width: 3px;
  height: 3px;
}

@media (max-width: 767px) {
  * {
    scrollbar-width: none;
    -ms-overflow-style: none;
  }
  *::-webkit-scrollbar {
    display: none;
  }
}

/* 홈은 비운다. 칠해 두면 내용이 짧아 막대가 필요 없는 칸에도 회색 띠가 남는다. */
*::-webkit-scrollbar-track {
  background: transparent;
}

*::-webkit-scrollbar-thumb {
  border-radius: 999px;
  background: var(--jsini-scroll-thumb);
}

*::-webkit-scrollbar-thumb:hover {
  background: var(--jsini-scroll-thumb-hover);
}

/* 가로·세로가 만나는 모서리. 기본값이 옅은 회색 네모라 홈만 비우면
   그 칸만 남아 점처럼 보인다. */
*::-webkit-scrollbar-corner {
  background: transparent;
}

/* DevExpress 테마가 DxMemo textarea 에 걸어 둔 thin 을 웹킷에서만 되돌린다(그 한 줄이 webkit 규칙을 죽인다). 파이어폭스는 thin 을 그대로 둔다. */
@supports selector(::-webkit-scrollbar) {
  .dxbl-text-edit.dxbl-memo-edit > textarea {
    scrollbar-width: auto;
    scrollbar-color: auto;
  }
}

/* 서식 편집기 멘션 목록의 막대는 DevExpress 가 div 로 그려 폭 변수를 3px 로 돌린다. */
.dx-popup-wrapper.dx-suggestion-list-wrapper {
  --dxbl-html-editor-mentions-scrollbar-width: 3px;
}

/* 포털 셸 리프레시. 업무용 다크 톤을 기준으로 셸의 기본 인상을 정리한다. 선택 상태는 블루 액센트와 얕은 배경으로 나타내고, 색만으로 구분하지 않는다. */

:root {
  --jsini-bg: #f4f7fb;
  --jsini-surface: #ffffff;
  --jsini-border: #e4eaf2;
  --jsini-text: #182433;
  --jsini-text-muted: #708096;
  --jsini-accent: var(--jsini-theme-primary, #206bc4);

/* 강조색을 앞에 세울 때(글자·아이콘·밑줄·표시선)의 색. 어두운 쪽은 :root[data-theme='dark'] 에 적는다. */
  --jsini-accent-text: var(--jsini-accent);

  --jsini-surface-raised: #ffffff;
  --jsini-surface-muted: #eef3f8;
  --jsini-focus: rgb(32 107 196 / 24%);
  --jsini-shadow-card: 0 1px 2px rgb(24 36 51 / 5%), 0 8px 24px rgb(24 36 51 / 4%);
}

:root[data-theme='dark'] {
  /* Tabler dark 기준: body #182433 / surface #1a2b3e / border #26334a */
  --jsini-bg: #182433;
  --jsini-surface: #1a2b3e;
  --jsini-border: #26334a;
  --jsini-text: #e7edf7;
  --jsini-text-muted: #91a0b5;
  --jsini-accent: var(--jsini-theme-primary, #4299ff);

/* 어두운 바탕에서 강조색을 글자로 쓰면 바탕과 붙는다. 띄운 강조색(--jsini-theme-primary 를 흰색과 반씩 섞은 것)은 WCAG 4.5 를 넘는다. 앞에 세울 때(글자·아이콘·얇은 선)에만 쓰고, 바탕·옅은 칠·굵은 테두리에는 --jsini-accent 를 쓴다. */
  --jsini-accent-text: color-mix(in srgb, var(--jsini-accent), #ffffff 50%);

  --jsini-surface-raised: #1e3048;
  --jsini-surface-muted: #142032;
  --jsini-focus: rgb(66 153 255 / 28%);
  --jsini-shadow-card: 0 1px 2px rgb(0 0 0 / 22%), 0 12px 28px rgb(0 0 0 / 18%);
}

body {
  font-family:
    Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
    "Segoe UI", sans-serif;
  background: var(--jsini-bg);
}

.jsini-header {
  padding: 0 0.75rem;
  background: var(--jsini-surface);
  border-bottom: 1px solid var(--jsini-border);
  box-shadow: none;
}

.jsini-header__brand {
  gap: 0.65rem;
  margin-left: 0.35rem;
}

.jsini-header .dxbl-btn,
.jsini-header__toggle .dxbl-btn {
  border-radius: var(--jsini-radius-md);
}

.jsini-header .dxbl-btn:hover,
.jsini-header .dxbl-btn:focus-visible {
  background: color-mix(in srgb, var(--jsini-accent) 10%, transparent);
}

.jsini-shell__sidebar {
  background: var(--jsini-surface-muted);
  border-right-color: var(--jsini-border);
  box-shadow: inset -1px 0 0 rgb(255 255 255 / 2%);
}

.jsini-sidebar__search {
  height: 2.17rem;
  padding: 0 0.875rem;
  background: var(--jsini-surface-muted);
  border-bottom-color: var(--jsini-border);
}

.jsini-sidebar__search-box {
  padding: 0.1rem 0.35rem;
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-md);
  background: var(--jsini-surface);
}

.jsini-sidebar__tabs {
  gap: 0.25rem;
  padding: 0.5rem 0.65rem 0;
  background: var(--jsini-surface-muted);
  border-bottom: 1px solid var(--jsini-border);
}

.jsini-sidebar__tab {
  border-radius: var(--jsini-radius-md) var(--jsini-radius-md) 0 0;
  border-bottom-width: 3px;
  color: var(--jsini-text-muted);
}

.jsini-sidebar__tab:hover {
  background: color-mix(in srgb, var(--jsini-accent) 8%, transparent);
  color: var(--jsini-text);
}

/* 고른 탭의 글자와 밑줄에 띄운 강조색을 쓴다(어두운 쪽에서 원래 강조색은 바탕에 붙는다). 밑줄까지 옮기는 것은 선택을 말하는 표시가 둘이기 때문이다. */
.jsini-sidebar__tab.is-on {
  background: var(--jsini-surface);
  color: var(--jsini-accent-text);
  border-bottom-color: var(--jsini-accent-text);
}

.jsini-sidebar__tree .dxbl-treeview-item-container {
  border-radius: var(--jsini-radius-md);
  transition: background-color 0.15s ease, color 0.15s ease;
}

.jsini-sidebar__tree .dxbl-treeview-item-container:hover {
  background: color-mix(in srgb, var(--jsini-accent) 8%, transparent);
}

.jsini-sidebar__tree .dxbl-treeview-item-container.dxbl-active {
  background: color-mix(in srgb, var(--jsini-accent) 16%, transparent);
  color: var(--jsini-accent-text);
  font-weight: 600;
}

.jsini-shell__main {
  background: var(--jsini-bg);
}

.jsini-tabs {
  height: 2.75rem;
  padding: 0 0.75rem;
  background: var(--jsini-surface);
  border-bottom-color: var(--jsini-border);
}

.jsini-tab {
  border-radius: var(--jsini-radius-md) var(--jsini-radius-md) 0 0;
}

.jsini-tab:hover {
  background: color-mix(in srgb, var(--jsini-accent) 8%, transparent);
}

/* 켜진 탭의 밑줄에도 띄운 강조색을 쓴다(사이드바 탭과 같은 까닭). 바탕의 옅은 칠은 원래 강조색을 그대로 둔다. */
.jsini-tab--active {
  background: color-mix(in srgb, var(--jsini-accent) 9%, transparent);
  border-bottom-color: var(--jsini-accent-text);
}

/* 좁은 화면은 건드리지 않는다. 이 규칙이 본문 여백 규칙보다 뒤에 있어 폭 조건 없이 적으면 휴대폰의 하단 띠 자리 비킴을 지운다. 그래서 하한을 박는다. */
@media (min-width: 768px) {
  .jsini-shell__content {
    padding: var(--jsini-shell-pad-top) var(--jsini-shell-pad-inline)
             var(--jsini-shell-pad-bottom);
  }
}

.jsini-page-head {
  margin-bottom: 1.25rem;
}

.jsini-page-head__title {
  letter-spacing: -0.02em;
}

.commsch {
  background: var(--jsini-surface-raised);
  --jsini-field-label-bg: var(--jsini-surface-raised);
  border-color: var(--jsini-border);
  border-radius: var(--jsini-radius-lg);
  box-shadow: var(--jsini-shadow-card);
}

.commcont {
  padding: var(--jsini-sp-lg);
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-lg);
  background: var(--jsini-surface-raised);
  --jsini-field-label-bg: var(--jsini-surface-raised);
  box-shadow: var(--jsini-shadow-card);
}

.commcont__head {
  margin-bottom: var(--jsini-sp-md);
}

.commcont__title {
  font-size: var(--jsini-fs-lg);
  letter-spacing: -0.01em;
}

.commgrd > .dxbl-grid,
.commtree > .dxbl-grid {
  border-color: var(--jsini-border);
  border-radius: var(--jsini-radius-md);
  overflow: hidden;
}

/* 창 안에서도 같다 — 까닭은 아래 「창은 `.jsini-shell__content` 밖에 있다」. */
.jsini-shell__content :is(button, a, input, textarea, select):focus-visible,
.jsini-popup :is(button, a, input, textarea, select):focus-visible,
.jsini-gridpopup :is(button, a, input, textarea, select):focus-visible {
  outline: 2px solid var(--jsini-focus);
  outline-offset: 2px;
}

/* 공통 업무 부품 마감. 표·페이저·입력·팝업이 같은 표면과 상태색을 공유한다. DevExpress 구조와 기능은 그대로 두고 공용 부품이 그리는 영역만 토큰으로 마감한다. */

:root {
  --jsini-success: #2fb344;
  --jsini-warning: #f59f00;
  --jsini-danger: #d63939;
  --jsini-info: #4299ff;
  /* Tabler light thead: #f6f8fb (bg-surface-tertiary) */
  --jsini-grid-header: #f0f4f8;
  --jsini-grid-hover: #edf3fd;
}

:root[data-theme='dark'] {
  /* Tabler dark thead: bg-surface-secondary / bg-surface-tertiary 계열 #1e3048~#243b57 */
  --jsini-grid-header: #1e3050;
  --jsini-grid-hover: #243b57;
}

/* 창은 .jsini-shell__content 밖에 있다. DevExpress 창은 body 밑의 dxbl-popup-root 로 옮겨 담기므로 본문에만 건 마감 규칙은 창에 닿지 않는다. 본문과 우리 창 둘을 함께 적는다. 바탕색은 선택자로 이길 수 없어 토큰으로 덮는다. */

/* 버튼·입력·선택 상자 */
.jsini-shell__content .dxbl-btn:not(.dxbl-btn-link),
.jsini-shell__content .dxbl-btn-group,
.jsini-shell__content .dxbl-text-edit,
.jsini-shell__content .dxbl-combobox,
.jsini-shell__content .dxbl-spin-edit,
.jsini-shell__content .dxbl-date-edit,
.jsini-popup .dxbl-btn:not(.dxbl-btn-link),
.jsini-popup .dxbl-btn-group,
.jsini-popup .dxbl-text-edit,
.jsini-popup .dxbl-combobox,
.jsini-popup .dxbl-spin-edit,
.jsini-popup .dxbl-date-edit,
.jsini-gridpopup .dxbl-btn:not(.dxbl-btn-link),
.jsini-gridpopup .dxbl-btn-group,
.jsini-gridpopup .dxbl-text-edit,
.jsini-gridpopup .dxbl-combobox,
.jsini-gridpopup .dxbl-spin-edit,
.jsini-gridpopup .dxbl-date-edit {
  border-radius: var(--jsini-radius-md);
}

.jsini-shell__content .dxbl-btn:not(.dxbl-btn-link),
.jsini-popup .dxbl-btn:not(.dxbl-btn-link),
.jsini-gridpopup .dxbl-btn:not(.dxbl-btn-link) {
  font-weight: 600;
}

.jsini-shell__content .dxbl-btn:not(.dxbl-btn-link):hover,
.jsini-popup .dxbl-btn:not(.dxbl-btn-link):hover,
.jsini-gridpopup .dxbl-btn:not(.dxbl-btn-link):hover {
  filter: brightness(1.04);
}

.jsini-shell__content .dxbl-text-edit:focus-within,
.jsini-shell__content .dxbl-combobox:focus-within,
.jsini-shell__content .dxbl-spin-edit:focus-within,
.jsini-shell__content .dxbl-date-edit:focus-within,
.jsini-popup .dxbl-text-edit:focus-within,
.jsini-popup .dxbl-combobox:focus-within,
.jsini-popup .dxbl-spin-edit:focus-within,
.jsini-popup .dxbl-date-edit:focus-within,
.jsini-gridpopup .dxbl-text-edit:focus-within,
.jsini-gridpopup .dxbl-combobox:focus-within,
.jsini-gridpopup .dxbl-spin-edit:focus-within,
.jsini-gridpopup .dxbl-date-edit:focus-within {
  box-shadow: 0 0 0 0.2rem var(--jsini-focus);
}

/* 연월 캘린더 등 입력부를 눌러도 달력 다이얼로그가 열리게 한다(드롭다운 버튼의 가상 요소로 입력창 전체를 덮는다). */
.jsini-dateedit--click-open,
.jsini-dateedit--click-open input {
  cursor: pointer;
}

.jsini-dateedit--click-open button {
  position: static !important;
}

.jsini-dateedit--click-open button::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  cursor: pointer;
}

@media (max-width: 767px), (pointer: coarse) {
  .jsini-dateedit--click-open input {
    caret-color: transparent;
    -webkit-user-select: none;
    user-select: none;
  }
}

/* 조회 조건은 카드 안의 조용한 작업 영역으로 보이게 한다. */
.commsch {
  gap: var(--jsini-sp-md) var(--jsini-sp-xl);
  box-shadow: var(--jsini-shadow-card);
}

.commsch__actions .dxbl-btn {
  min-width: 4.5rem;
}

.commsch__actions .dxbl-btn.dxbl-btn-icon-only,
.commsch__actions .dxbl-btn:has(> [class*='jsini-icon-']:only-child) {
  min-width: 2.25rem;
  padding-left: 0.5rem;
  padding-right: 0.5rem;
}

/* 표의 표면·머리·행 상태 */
.commgrd > .dxbl-grid,
.commtree > .dxbl-grid {
  background: var(--jsini-surface-raised);
  --jsini-field-label-bg: var(--jsini-surface-raised);
  box-shadow: var(--jsini-shadow-card);
}

.commgrd .dxbl-grid-table,
.commtree .dxbl-grid-table {
  color: var(--jsini-text);
}

.commgrd .dxbl-grid-header,
.commtree .dxbl-grid-header {
  background: var(--jsini-grid-header);
  border-top: 1px solid var(--jsini-border);
  border-bottom: 2px solid var(--jsini-border);
}

.commgrd .dxbl-grid-header-content,
.commtree .dxbl-grid-header-content {
  min-height: 1rem;
  padding: 0.6rem 0.75rem;
  /* Tabler: thead 텍스트는 muted보다 약간 선명한 중간 톤 */
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.commgrd .dxbl-grid-table > tbody > tr,
.commtree .dxbl-grid-table > tbody > tr {
  border-bottom: 1px solid var(--jsini-border);
  transition: background-color 0.12s ease;
}

.commgrd .dxbl-grid-table > tbody > tr:last-child,
.commtree .dxbl-grid-table > tbody > tr:last-child {
  border-bottom: 0;
}

.commgrd .dxbl-grid-table > tbody > tr:hover,
.commtree .dxbl-grid-table > tbody > tr:hover {
  background-color: var(--jsini-grid-hover);
}

.commgrd .dxbl-grid-table > tbody > tr > td,
.commtree .dxbl-grid-table > tbody > tr > td {
  padding: 0.7rem 0.75rem;
  border-color: var(--jsini-border);
}

.commgrd .dxbl-grid .dxbl-grid-table > tbody > tr > td.dxbl-grid-fixed-cell,
.commtree .dxbl-grid .dxbl-grid-table > tbody > tr > td.dxbl-grid-fixed-cell {
  background-color: var(--jsini-surface-raised);
}

.commgrd .dxbl-grid .dxbl-grid-table > tbody > tr.commgrd__row--alt > td.dxbl-grid-fixed-cell,
.commtree .dxbl-grid .dxbl-grid-table > tbody > tr.commtree__row--alt > td.dxbl-grid-fixed-cell {
  background-color: color-mix(in srgb, var(--jsini-grid-header) 72%, var(--jsini-surface-raised));
}

.commgrd .dxbl-grid-table > tbody > tr.dxbl-grid-focused-row,
.commgrd .dxbl-grid-table > tbody > tr.dxbl-grid-selected-row,
.commtree .dxbl-grid-table > tbody > tr.dxbl-grid-focused-row,
.commtree .dxbl-grid-table > tbody > tr.dxbl-grid-selected-row {
  background-color: color-mix(in srgb, var(--jsini-accent) 14%, var(--jsini-surface-raised));
  box-shadow: inset 3px 0 0 var(--jsini-accent);
}

.commgrd .dxbl-grid-table > tbody > tr.dxbl-grid-focused-row > td.dxbl-grid-fixed-cell,
.commgrd .dxbl-grid-table > tbody > tr.dxbl-grid-selected-row > td.dxbl-grid-fixed-cell,
.commtree .dxbl-grid-table > tbody > tr.dxbl-grid-focused-row > td.dxbl-grid-fixed-cell,
.commtree .dxbl-grid-table > tbody > tr.dxbl-grid-selected-row > td.dxbl-grid-fixed-cell {
  background-color: inherit;
}

.commgrd__actions .dxbl-btn,
.commgrd__footer .dxbl-btn,
.commtree__actions .dxbl-btn,
.commtree__footer .dxbl-btn {
  width: 2rem;
  height: 2rem;
  border-radius: var(--jsini-radius-md);
}

.commgrd__actions .dxbl-btn:hover,
.commgrd__footer .dxbl-btn:hover,
.commtree__actions .dxbl-btn:hover,
.commtree__footer .dxbl-btn:hover {
  background: color-mix(in srgb, var(--jsini-accent) 12%, transparent);
}

.commgrd__icon--warn,
.commtree__icon--warn {
  color: var(--jsini-danger);
}

.commgrd__empty,
.commtree__empty {
  min-height: 9rem;
  display: grid;
  place-items: center;
  padding: var(--jsini-sp-2xl);
  background: var(--jsini-surface-raised);
}

/* DevExpress 페이저를 Tabler 의 작은 알약형 페이지 네비게이션처럼 보이게 한다. */
.commgrd:has(.dxbl-grid-bottom-panel) .dxbl-grid-bottom-panel {
  min-height: var(--commgrd-bar-height);
  gap: 0.35rem;
  padding: 0.4rem 0.65rem 0.4rem 10rem;
  border-top: 1px solid var(--jsini-border);
  background: var(--jsini-surface-raised);
  color: var(--jsini-text-muted);
}

.commgrd .dxbl-grid-bottom-panel :is(.dxbl-pager, .dxbl-pager-container) {
  display: flex;
  align-items: center;
  gap: 0.25rem;
}

.commgrd .dxbl-grid-bottom-panel :is(button, a, .dxbl-btn) {
  min-width: 2rem;
  height: 2rem;
  padding: 0 0.55rem;
  border: 1px solid transparent;
  border-radius: var(--jsini-radius-md);
  background: transparent;
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-sm);
  font-weight: 600;
  text-decoration: none;
}

.commgrd .dxbl-grid-bottom-panel :is(button, a, .dxbl-btn):hover {
  background: color-mix(in srgb, var(--jsini-accent) 10%, transparent);
  color: var(--jsini-accent-text);
}

.commgrd .dxbl-grid-bottom-panel :is(.dxbl-pager-page-item-active, .dxbl-pager-active, [aria-current='page']) {
  border-color: var(--jsini-accent);
  background: var(--jsini-accent);
  color: #fff;
}

.commgrd .dxbl-grid-bottom-panel :is(.dxbl-pager-page-item-disabled, :disabled, [aria-disabled='true']) {
  opacity: 0.45;
  cursor: default;
}

.commgrd .dxbl-grid-bottom-panel :is(.dxbl-pager-summary, .dxbl-pager-page-size) {
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-xs);
}

/* 상태 배지 */
.jsini-badge {
  border: 0;
  border-radius: var(--jsini-radius-full);
  padding: 0.25rem 0.6rem;
  background: var(--jsini-surface-muted);
  font-size: var(--jsini-fs-xs);
  font-weight: 700;
}

.jsini-badge--on,
.jsini-badge--success {
  background: color-mix(in srgb, var(--jsini-success) 15%, transparent);
  border-color: transparent;
  color: var(--jsini-success);
}

.jsini-badge--off,
.jsini-badge--muted {
  background: color-mix(in srgb, var(--jsini-text-muted) 13%, transparent);
  border-color: transparent;
}

/* 갈래를 말하는 딱지(대표 · 소셜결합). 좋고 나쁨이 아니라 갈래를 말하므로 info 색을 따로 둔다(없으면 글자만 다르고 생김새가 같다). */
.jsini-badge--info {
  background: color-mix(in srgb, var(--jsini-accent) 14%, transparent);
  border-color: transparent;
  color: var(--jsini-accent-text);
}

.jsini-badge--warn {
  background: color-mix(in srgb, var(--jsini-warning) 16%, transparent);
  border-color: transparent;
  color: var(--jsini-warning);
}

.jsini-badge--err {
  background: color-mix(in srgb, var(--jsini-danger) 15%, transparent);
  border-color: transparent;
  color: var(--jsini-danger);
}

/* AI 사용량 띠(0~100%, 5단위 스물한 칸). 밝기를 단조로 떨어뜨려 짙을수록 많이 썼다로 읽히게 하고, 글자색은 먹·흰 둘뿐이다(스물한 칸 모두 WCAG AA). 값은 OKLCh 에서 뽑았다. 한 칸이라도 고치면 AiUsageBadgePaletteTests 로 단조성·이웃 색차·대비를 다시 잰다. 이 띠는 세 벌이고 실제로 이기는 것은 tabler.css 다. */
.jsini-badge--usage-0   { background: #affbdd; color: #0a0600; }
.jsini-badge--usage-5   { background: #a5f4cc; color: #0a0600; }
.jsini-badge--usage-10  { background: #9cedb6; color: #0a0600; }
.jsini-badge--usage-15  { background: #9de499; color: #0a0600; }
.jsini-badge--usage-20  { background: #a4da77; color: #0a0600; }
.jsini-badge--usage-25  { background: #b2cc53; color: #0a0600; }
.jsini-badge--usage-30  { background: #bdbe26; color: #0a0600; }
.jsini-badge--usage-35  { background: #c6af00; color: #0a0600; }
.jsini-badge--usage-40  { background: #c8a200; color: #0a0600; }
.jsini-badge--usage-45  { background: #cd9200; color: #0a0600; }
.jsini-badge--usage-50  { background: #cf8400; color: #0a0600; }
.jsini-badge--usage-55  { background: #d37200; color: #0a0600; }
.jsini-badge--usage-60  { background: #d85c01; color: #0a0600; }
.jsini-badge--usage-65  { background: #d54c25; color: #0a0600; }
.jsini-badge--usage-70  { background: #cd4030; color: #ffffff; }
.jsini-badge--usage-75  { background: #c43534; color: #ffffff; }
.jsini-badge--usage-80  { background: #b92e38; color: #ffffff; }
.jsini-badge--usage-85  { background: #ad2640; color: #ffffff; }
.jsini-badge--usage-90  { background: #a12049; color: #ffffff; }
.jsini-badge--usage-95  { background: #931c54; color: #ffffff; }
.jsini-badge--usage-100 { background: #821d5e; color: #ffffff; }

/* 어두운 테마에는 따로 주지 않는다. 바탕이 불투명해 판 색을 타지 않고, 글자색은 그 바탕에 맞춘 값이다. */


/* 창·판의 바탕색은 선택자가 아니라 토큰으로 준다. 우리 창 규칙은 DevExpress 의 (0,4,0)~(0,5,0) 규칙에 힘이 져서 한 줄도 먹지 않았다. 힘으로 이기지 않고 DevExpress 가 읽는 변수(--dxbl-popup-bg 등)를 덮으면 그 변수를 읽는 창이 모두 따라온다. 여백과 모서리는 DevExpress 사다리를 따른다. */

.dxbl-modal,
.dxbl-window {
  --dxbl-popup-bg: var(--jsini-surface-raised);
  --dxbl-popup-color: var(--jsini-text);
  --dxbl-popup-border-color: var(--jsini-border);
  --dxbl-popup-header-bg: var(--jsini-surface-raised);
  --dxbl-popup-header-color: var(--jsini-text);
  --dxbl-popup-body-bg: var(--jsini-surface-raised);
  --dxbl-popup-body-color: var(--jsini-text);

/* 바닥 띠만 한 단 눌러 앉는다(아이콘 고르개의 펼친 판과 같은 값). */
  --dxbl-popup-footer-bg: var(--jsini-surface-muted);
  --dxbl-popup-footer-color: var(--jsini-text);

/* DxWindow 는 아직 쓰는 화면이 없지만 같은 값을 적어 둔다(창 하나를 띄우면 그것만 회색으로 남는다). */
  --dxbl-window-bg: var(--jsini-surface-raised);
  --dxbl-window-color: var(--jsini-text);
  --dxbl-window-border-color: var(--jsini-border);
  --dxbl-window-header-bg: var(--jsini-surface-raised);
  --dxbl-window-header-color: var(--jsini-text);
  --dxbl-window-body-bg: var(--jsini-surface-raised);
  --dxbl-window-body-color: var(--jsini-text);
  --dxbl-window-footer-bg: var(--jsini-surface-muted);
  --dxbl-window-footer-color: var(--jsini-text);
}

/* 눌러서 펼치는 판(콤보·태그·달력·오른쪽 단추 메뉴)도 창과 같은 표면을 쓴다. 하나만 남기면 한 화면에서 색 계열이 둘이 된다. 달력은 자기 바탕 변수를 따로 적는다. */
.dxbl-dropdown,
.dxbl-itemlist-dropdown,
.dxbl-edit-dropdown,
.dxbl-context-menu,
.dxbl-context-menu-submenu,
.dxbl-menu,
.dxbl-menu-dropdown,
.dxbl-calendar {
  --dxbl-dropdown-bg: var(--jsini-surface-raised);
  --dxbl-edit-dropdown-bg: var(--jsini-surface-raised);
  --dxbl-edit-dropdown-color: var(--jsini-text);
  --dxbl-edit-dropdown-border-color: var(--jsini-border);
  --dxbl-context-menu-bg: var(--jsini-surface-raised);
  --dxbl-context-menu-color: var(--jsini-text);
  --dxbl-menu-bg: var(--jsini-surface-raised);
  --dxbl-menu-color: var(--jsini-text);
  --dxbl-calendar-bg: var(--jsini-surface-raised);
  --dxbl-calendar-color: var(--jsini-text);
  --dxbl-calendar-border-color: var(--jsini-border);
}

/* 창 바깥 껍데기(테두리·모서리·그림자). 이쪽은 DevExpress 가 `.dxbl-popup`
   하나(0,1,0)로만 적어 두어 **선택자로 이긴다** — 위 토큰과 달리 그대로 둔다.

   `--jsini-field-label-bg` 는 떠오르는 라벨이 테두리를 파고들 때 뒤에 까는
   색이다. 창 안에서는 본문 바탕(`--jsini-bg`)이 아니라 창의 표면이어야
   글자가 지워 낸 자리에 맞는다. */
.jsini-popup .dxbl-modal-content,
.jsini-gridpopup .dxbl-modal-content,
.dxbl-popup {
  --jsini-field-label-bg: var(--jsini-surface-raised);
}

.dxbl-popup {
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-lg);
  background: var(--jsini-surface-raised);
  box-shadow: 0 18px 48px rgb(0 0 0 / 24%);
}

/* 모바일 하단 네비게이션바 */
.jsini-bottom-nav {
    display: none;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    height: calc(var(--jsini-bottomnav-h) + env(safe-area-inset-bottom));
    padding-bottom: env(safe-area-inset-bottom);
    background-color: var(--jsini-bg, #ffffff);
    border-top: 1px solid var(--jsini-border, #e0e0e0);
    z-index: 1030;
    box-shadow: 0 -2px 10px rgba(0,0,0,0.05);

    /* 칸 글자의 좌우 여백(아래 `> span:last-child` 의 `0 2px`)이 폭을 꽉 채운
       칸에서 2px 넘친다. 띠가 `position: fixed` 라 쪽 스크롤이 생기지는
       않지만, 넘친 글자가 테두리 밖으로 비어져 나온다. */
    overflow: hidden;
}

.jsini-bottom-nav__item {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    color: var(--jsini-text-muted, #6c757d);
    text-decoration: none;
    flex: 1;
    height: 100%;
    font-size: 11px;
    border: none;
    background: none;
    padding: 0;
    cursor: pointer;

    /* **이 한 줄이 칸을 줄어들게 한다.** flex 항목의 기본 최소 폭은
       `auto`(= 내용 폭)이고, 그 내용에는 `white-space: nowrap` 인 이름이
       들어 있다 — 그래서 이름이 긴 칸은 `flex: 1` 을 주어도 **제 글자 폭을
       쥐고 버틴다.**

       칸이 다섯일 때는 드러나지 않았다. 사람이 칸을 여덟까지 놓을 수 있게
       되면서(`BottomNav.MaxItems`) 320px 에 네 글자 이름 여덟을 채워 재 보니
       띠가 **376px** 이 되어 끝 칸(대개 「메뉴」 — 띠에서 못 닿는 화면을 전부
       여는 칸이다)이 화면 밖으로 밀려 아예 안 눌렸다.

       0 으로 두면 칸이 고르게 줄고, 남는 글자는 아래 규칙이 말줄임으로
       자른다. 같은 조건에서 다시 재니 320px·360px 모두 띠가 화면 폭에
       딱 맞고 끝 칸이 돌아왔다. */
    min-width: 0;
}

.jsini-bottom-nav__item:hover,
.jsini-bottom-nav__item:active {
    color: var(--jsini-accent-text, #007bff);
}

.jsini-bottom-nav__icon {
    font-size: 20px;
    margin-bottom: 4px;

    /* 아이콘 집안이 둘이다. 띠에 박아 둔 것은 `jsini-icon-*`(크기를
       `--jsini-icon-size` 로 잰다)이고, 사람이 고른 칸은 메뉴에서 온
       `jsini-mi--*`(크기를 `em` 으로 잰다)다. 둘을 같은 20px 로 맞춰 둔다 —
       안 맞추면 고른 칸만 미묘하게 크거나 작아 띠가 들쭉날쭉해 보인다. */
    --jsini-icon-size: 20px;
}

.jsini-bottom-nav__icon.jsini-mi {
    width: 20px;
    height: 20px;
}

/* 프로필 칸의 얼굴. **아이콘 자리를 그대로 쓴다**(20px + 아래 4px) — 조금만
   키워도 그 칸만 글자가 한 픽셀 내려앉아 띠가 어긋나 보인다.
   동그라미와 테두리는 헤더의 얼굴(`.jsini-user__face`)과 같은 뜻이다. */
.jsini-bottom-nav__avatar {
    --jsini-face-size: 20px;
    --jsini-face-bg: color-mix(in srgb, currentColor 12%, transparent);
    --jsini-face-ink: currentColor;
    --jsini-face-initial-size: var(--jsini-fs-2xs);

    margin-bottom: 4px;
}

/* 띠의 글자는 한 줄로 자른다. 이름을 사람이 짓는 자리라(환경설정) 길게
   적으면 칸 하나가 두 줄이 되고, 그 칸만 높아져 띠가 어긋난다. */
.jsini-bottom-nav__item > span:last-child {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding: 0 2px;
}

/* ── 이름을 감춘 띠 (`.jsini-shell--bottomnav-nolabel`) ──────────

   환경설정의 「아이콘만 보기」다. 아이콘 아래의 11px 짜리 이름 줄이 빠지고,
   띠는 그만큼 얇아진다(`--jsini-bottomnav-h` 60px → 44px). **얇아진 몫은
   본문이 가져간다** — 띠를 피해 서 있던 아홉 자리가 모두 그 토큰을 본다.

   [글자를 `display: none` 으로 지우지 않는다]

   칸은 `<button>` 이고 그 안에 든 것은 아이콘과 이름 둘뿐인데, 아이콘은
   `aria-hidden` 이고 얼굴 그림은 `alt=""` 다. 그래서 이름을 지우면
   **단추의 읽을 이름이 통째로 사라진다** — 화면 낭독기에는 「단추」만 다섯
   줄로 남고, 어느 것이 홈인지 알 길이 없다. 눈으로 보는 화면은 멀쩡하므로
   이 고장은 눈으로는 안 드러난다.

   자리만 뺏고 글자는 남긴다(1px 상자 + `clip-path`). 띠가
   `position: fixed` 라 이 `absolute` 는 띠를 기준으로 서고, 띠는
   `overflow: hidden` 이라 비어져 나올 자리도 없다. */
.jsini-shell--bottomnav-nolabel .jsini-bottom-nav__item > span:last-child {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* 아이콘 아래 틈(4px)도 함께 걷는다. 그 틈은 **이름과의 사이**를 벌리려고
   둔 것이라, 이름이 없는 띠에서는 아이콘을 4px 위로 밀어 올리기만 한다 —
   얼굴 그림(프로필 칸)도 같은 틈을 쓰므로 함께 적는다. */
.jsini-shell--bottomnav-nolabel .jsini-bottom-nav__icon,
.jsini-shell--bottomnav-nolabel .jsini-bottom-nav__avatar {
    margin-bottom: 0;
}

/* ── 띠가 꽉 찼을 때 무엇과 바꿀지 고르는 창 ─────────────────

   탭 메뉴의 「휴대폰 아래 띠에 넣기」가 다섯이 다 찬 채로 눌렸을 때 뜬다
   (`BottomNavSwapDialog`). 창은 휴대폰에서 열리는 일이 대부분이라 **줄을
   손가락으로 고르는 크기**로 짠다 — 44px 는 손가락 하나가 빗나가지 않는
   최소 높이다. */
.jsini-navswap__lead {
  margin-bottom: 0.75rem;
  line-height: 1.5;
}

.jsini-navswap__list {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

/* 줄 전체가 단추다. 아이콘만·글자만 눌리면 손가락으로는 자주 빗나간다. */
.jsini-navswap__row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  min-height: 44px;
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-md);
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.jsini-navswap__row:hover,
.jsini-navswap__row:focus-visible {
  background: color-mix(in srgb, currentcolor 8%, transparent);
}

/* 띠의 아이콘과 같은 규칙으로 크기를 맞춘다 — 집안이 둘이라
   (`jsini-icon-*` 와 `jsini-mi--*`) 한쪽만 맞추면 줄이 들쭉날쭉해진다. */
.jsini-navswap__icon {
  flex: 0 0 auto;
  font-size: 20px;
  --jsini-icon-size: 20px;
}

.jsini-navswap__icon.jsini-mi {
  width: 20px;
  height: 20px;
}

/* 이름이 길면 줄임표. 줄바꿈시키면 줄 높이가 제각각이 되어 다섯을
   한눈에 훑을 수 없다. */
.jsini-navswap__name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.jsini-navswap__slot {
  flex: 0 0 auto;
  font-size: var(--jsini-fs-2xs);
  color: var(--jsini-text-muted);
}

/* ── 떠다니는 단추를 오래 눌렀을 때 뜨는 창 (`FabHoldMenu`) ───

   그 단추의 자리와 감추기를 **그 자리에서** 고친다. 손가락으로 쓰는 창이라
   고르는 칸을 전부 44px 위로 잡는다(위 `navswap` 과 같은 까닭). */
.jsini-fabhold__lead {
  margin-bottom: 0.9rem;
  line-height: 1.5;
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-sm);
}

.jsini-fabhold__group {
  margin-bottom: 0.4rem;
  font-size: var(--jsini-fs-2xs);
  font-weight: 700;
  color: var(--jsini-text-muted);
}

/* 네모 넷을 **화면처럼** 벌여 놓는다 — 두 칸씩 두 줄이라 격자의 자리가
   곧 화면의 귀퉁이다. 목록으로 늘어놓으면 글자를 읽어야 어디인지 안다. */
.jsini-fabhold__corners {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.4rem;
}

.jsini-fabhold__corner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  min-height: 44px;
  padding: 0.5rem 0.3rem;
  border: 1px solid var(--jsini-border);
  border-radius: var(--jsini-radius-md);
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

.jsini-fabhold__corner:hover,
.jsini-fabhold__corner:focus-visible {
  background: color-mix(in srgb, currentcolor 8%, transparent);
}

/* 지금 서 있는 자리. **표시가 없으면 네 번 눌러 봐야** 어디인지 안다. */
.jsini-fabhold__corner.is-on {
  border-color: var(--jsini-accent);
  background: color-mix(in srgb, var(--jsini-accent) 12%, transparent);
  color: var(--jsini-accent);
}

/* 화면을 본뜬 작은 네모와 그 안의 점(= 단추). 가로세로 비가 휴대폰과
   비슷해야 「아래」가 아래로 읽힌다. */
.jsini-fabhold__box {
  position: relative;
  width: 2rem;
  height: 2.6rem;
  border: 1px solid currentcolor;
  border-radius: 0.25rem;
  opacity: 0.55;
}

.jsini-fabhold__dot {
  position: absolute;
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  background: currentcolor;
}

.jsini-fabhold__corner--top-left .jsini-fabhold__dot {
  top: 0.22rem;
  left: 0.22rem;
}

.jsini-fabhold__corner--top-right .jsini-fabhold__dot {
  top: 0.22rem;
  right: 0.22rem;
}

.jsini-fabhold__corner--bottom-left .jsini-fabhold__dot {
  bottom: 0.22rem;
  left: 0.22rem;
}

.jsini-fabhold__corner--bottom-right .jsini-fabhold__dot {
  bottom: 0.22rem;
  right: 0.22rem;
}

.jsini-fabhold__corner-name {
  font-size: var(--jsini-fs-2xs);
  white-space: nowrap;
}

/* 아래 두 줄(감추기 · 환경설정). 귀퉁이 넷과 선 하나로 가른다(자리를 고르는 일과 화면을 떠나는 일은 되돌리는 품이 다르다). */
.jsini-fabhold__acts {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  margin-top: 0.9rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--jsini-border);
}

.jsini-fabhold__act {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  min-height: 44px;
  padding: 0.4rem 0.5rem;
  border: 0;
  border-radius: var(--jsini-radius-md);
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.jsini-fabhold__act:hover,
.jsini-fabhold__act:focus-visible {
  background: color-mix(in srgb, currentcolor 8%, transparent);
}

.jsini-fabhold__act [class^='jsini-icon-'] {
  flex: 0 0 auto;
  --jsini-icon-size: 20px;
}

.jsini-fabhold__act-text {
  flex: 1;
  min-width: 0;
}

@media (max-width: 767px) {
    .jsini-bottom-nav {
        display: flex;
    }

/* 띠를 쓰지 않기로 한 사람(jsini-bottomnav-hidden). 띠가 없으면 띠가 비켜 달라고 만든 자리(본문 아래 여백, 떠다니는 단추, 사이드바 아래 여백, AI 대화 서랍 아래 끝)를 되돌려야 빈 높이가 남지 않는다. 여기 숫자는 --jsini-bottomnav-h 를 보지 않는다. */
    .jsini-shell--bottomnav-hidden .jsini-shell__content {
        padding-bottom: calc(0.75rem + env(safe-area-inset-bottom)) !important;
    }

    .jsini-shell--bottomnav-hidden .jsini-shell__fab--bottom-left,
    .jsini-shell--bottomnav-hidden .jsini-shell__fab--bottom-right,
    .jsini-shell--bottomnav-hidden .jsini-shell__fab--scroll-top {
        bottom: calc(0.75rem + env(safe-area-inset-bottom)) !important;
    }

    .jsini-shell--bottomnav-hidden .jsini-sidebar__tree .dxbl-scroll-viewer {
        padding-bottom: calc(60px + env(safe-area-inset-bottom)) !important;
    }

    /* 띠를 안 쓰면 서랍도 바닥까지 내려온다 — 위 셋과 같은 되돌림이다. */
    .jsini-shell--bottomnav-hidden .jsini-right-drawer {
        bottom: 0;
    }

/* 오른쪽 서랍이 띠를 피한다. 서랍은 bottom 0 이라 띠와 같은 자리를 쓰는데, 헤더가 제 쌓임 맥락을 만들어 z-index 로는 띠를 못 넘는다. 그래서 서랍 아래 끝을 띠 높이만큼 끌어올린다. */
    .jsini-right-drawer {
        bottom: calc(var(--jsini-bottomnav-h) + env(safe-area-inset-bottom));
    }

    
        /* 위 본문 여백 규칙과 같은 값이다. 뒤에 있어 실제로 쓰이는 쪽이라 숫자를 베끼지 않고 같은 토큰을 본다. */
    .jsini-shell__content {
        padding-bottom: calc(var(--jsini-bottomnav-h) + env(safe-area-inset-bottom) + var(--jsini-shell-pad-bottom)) !important;
    }
    
    .jsini-shell__fab--bottom-left, 
    .jsini-shell__fab--bottom-right,
    .jsini-shell__fab--scroll-top {
        bottom: calc(var(--jsini-bottomnav-h) + 16px) !important;
    }

/* 업무 화면의 단추 줄이 맨 아래 칸을 갖는다. 본문은 띠 몫만 비우고 숨 쉴 틈은 걷는다(틀이 두 겹이 되지 않게). 단추 줄은 bottom 0 으로 붙고, 떠다니는 단추는 그 줄 높이만큼 올라선다. :has() 로 그런 줄이 있는 화면에서만 건다. 위로 가기도 같은 층계를 쓴다. */
    .jsini-shell__content:has(.jsini-actbar) {
        padding-bottom: calc(var(--jsini-bottomnav-h) + env(safe-area-inset-bottom)) !important;
    }

/* 줄은 판의 아래 끝에 닿아야 한다. 판의 안쪽 여백이 밑에 남으면 짧은 화면에서 줄이 띠 위로 떠서 선다. 바닥 칸을 가진 동안에는 그 여백을 줄이 제 몫으로 갖고 판 아래 틈도 걷는다. */
    .commcont:has(> .commcont__body > .jsini-actbar) {
        padding-bottom: 0;
        margin-bottom: 0;
    }

    .jsini-shell:has(.jsini-actbar) .jsini-shell__fab--bottom-left,
    .jsini-shell:has(.jsini-actbar) .jsini-shell__fab--bottom-right,
    .jsini-shell:has(.jsini-actbar) .jsini-shell__fab--scroll-top {
        bottom: calc(var(--jsini-bottomnav-h) + 16px + var(--jsini-actbar-height)) !important;
    }

        /* 띠를 쓰지 않기로 한 사람. 띠가 없으면 단추 줄이 바닥까지 내려간다. 선택자 무게가 위 둘과 같아서 뒤에 둔 이것이 이긴다. */
    .jsini-shell--bottomnav-hidden .jsini-shell__content:has(.jsini-actbar) {
        padding-bottom: env(safe-area-inset-bottom) !important;
    }

    .jsini-shell--bottomnav-hidden:has(.jsini-actbar) .jsini-shell__fab--bottom-left,
    .jsini-shell--bottomnav-hidden:has(.jsini-actbar) .jsini-shell__fab--bottom-right,
    .jsini-shell--bottomnav-hidden:has(.jsini-actbar) .jsini-shell__fab--scroll-top {
        bottom: calc(0.75rem + env(safe-area-inset-bottom) + var(--jsini-actbar-height)) !important;
    }

    .jsini-sidebar__tree .dxbl-scroll-viewer {
        padding-bottom: calc(var(--jsini-bottomnav-h) + 60px + env(safe-area-inset-bottom)) !important;
    }
}

/* 알림 보내기 창(NotifySendPopup). 업무 화면 어디서나 뜨므로 모듈 CSS 가 아니라 여기 둔다(모듈마다 다르게 생기는 것을 막는다). 칸 이름은 jsini-notify__* 다. */

.jsini-notify__who {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
  margin-bottom: 0.8rem;
}

.jsini-notify__who-label {
  font-size: var(--jsini-fs-xs);
  font-weight: 600;
  color: var(--jsini-text-muted);
}

    /* 받는 사람 한 명. 얼굴과 이름을 한 알로 묶는다(이름만 쉼표로 이으면 여럿일 때 한 사람의 경계가 흐려진다). */
.jsini-notify__chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.15rem 0.5rem 0.15rem 0.15rem;
  border: 1px solid var(--jsini-border);
  border-radius: 999px;
  background: var(--jsini-bg);
  font-size: var(--jsini-fs-sm);
}

.jsini-notify__face {
  --jsini-face-size: 1.4rem;
}

.jsini-notify__empty {
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text-muted);
}

/* 보낼 길. 체크 둘이라 한 줄이면 충분하다. */
.jsini-notify__ways {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding-bottom: 0.6rem;
  margin-bottom: 0.6rem;
  border-bottom: 1px solid var(--jsini-border);
}

.jsini-notify__field {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  margin-bottom: 0.7rem;
}

.jsini-notify__label {
  font-size: var(--jsini-fs-xs);
  font-weight: 600;
  color: var(--jsini-text-muted);
}

    /* 입력 칸은 창 폭을 그대로 쓴다(DevExpress 가 내용만큼만 넓히므로 적어 두지 않으면 짧은 칸이 왼쪽에 붙는다). */
.jsini-notify__input {
  width: 100%;
}

/* 토스트는 글자 한 토막에 맞게 한 줄로 선다. 판의 기본 치수는 제목과 문단을 담는 크기라 글자 한 줄짜리가 300px 에 62px 로 섰다. 폭은 글자만큼(판의 Width 는 상한), 한 줄에 모두 세우고 제목 여백을 0 으로, 글자와 여백을 한 단 낮춘다. 닫기 단추의 누를 자리는 손가락 폭을 지킨다. */

/* 판을 화면 모서리에서 떼어 놓는 여백. 토스트가 작아진 만큼 함께 줄인다. */
.dxbl-toast-portal {
  --dxbl-toast-bound-margin: 0.375rem;
  --dxbl-toast-bound-padding-right: 0.375rem;
  --dxbl-toast-bound-padding-bottom: 0.375rem;
}

.dxbl-toast-portal .dxbl-toast {
  --dxbl-toast-margin-top: 0.1875rem;
  --dxbl-toast-accent-line-width: 0.125rem;
  --dxbl-toast-body-padding: 0.15rem 0.3rem 0.15rem 0.35rem;
  --dxbl-toast-content-gap: 0.25rem;
  --dxbl-toast-text-content-gap: 0.1rem;
  --dxbl-toast-icon-top-padding: 0;
  --dxbl-toast-text-content-top-padding: 0;
  --dxbl-toast-template-padding: 0;
  --dxbl-toast-template-gap: 0.25rem;
  --dxbl-toast-text-font-size: var(--jsini-fs-2xs);
  --dxbl-toast-text-line-height: 1.3;
  --dxbl-toast-header-font-size: var(--jsini-fs-xs);
  --dxbl-toast-header-line-height: 1.3;

    /* 판이 준 폭은 상한이다. max-content 는 줄바꿈 없이 재므로 긴 문구에서는 max-width 에 걸려 접힌다. */
  width: max-content;
  max-width: 100%;
}

   /* 고른 자리 쪽에 붙여 세운다(왼쪽은 블록 상자의 기본이라 적을 것이 없다). */
.dxbl-toast-portal.dxbl-toast-align-right .dxbl-toast {
  margin-left: auto;
}

.dxbl-toast-portal.dxbl-toast-align-center .dxbl-toast {
  margin-left: auto;
  margin-right: auto;
}

   /* 아이콘·글·닫기를 한 줄에 세운다(stretch 면 아이콘이 위에 붙고 글만 길면 높이가 어긋난다). */
.dxbl-toast-portal .dxbl-toast > .dxbl-toast-body {
  align-items: center;
}

   /* 아이콘 칸. 글자 줄높이만큼 부풀어 있던 빈 틈을 걷는다(안에 든 것이 svg 하나라 inline 으로 설 까닭이 없다). */
.dxbl-toast-portal .dxbl-toast .dxbl-toast-icon-column {
  display: flex;
  align-items: center;
  line-height: 0;
}

   /* 아이콘은 글자 사다리를 따른다(판의 기본 16px 은 글자보다 커서 혼자 눈에 띈다). */
.dxbl-toast-portal .dxbl-toast .dxbl-toast-icon-column .dxbl-toast-icon {
  width: calc(var(--jsini-fs-2xs) * 1.2);
  height: calc(var(--jsini-fs-2xs) * 1.2);
}

   /* 그림 둘레의 2px 를 걷는다. 이미지의 세로 여백이 flex 아이템에서 높이에 그대로 얹힌다. */
.dxbl-toast-portal .dxbl-toast .dxbl-toast-icon-column .dxbl-toast-icon,
.dxbl-toast-portal .dxbl-toast .dxbl-toast-close svg.dxbl-toast-close-btn {
  margin: 0;
}

   /* 닫기 단추. 그림만 줄이고 누를 자리는 남긴다(휴대폰에서 빗나가는 것이 쓸어 치우기를 넣은 까닭이다). 줄높이에서 생기는 빈 몫은 걷는다. */
.dxbl-toast-portal .dxbl-toast .dxbl-toast-close button.dxbl-toast-close-btn {
  --dxbl-btn-padding-x: 0.2rem;
  --dxbl-btn-padding-y: 0.2rem;

  display: flex;
  align-items: center;
  line-height: 0;
}

/* 손가락으로 쓰는 화면에서는 누를 자리를 되돌린다. 좁히는 것은 마우스 쪽뿐이고 늘어나는 것은 토스트 높이 2~3px 이다. */
@media (pointer: coarse) {
  .dxbl-toast-portal .dxbl-toast .dxbl-toast-close button.dxbl-toast-close-btn {
    --dxbl-btn-padding-x: 0.3rem;
    --dxbl-btn-padding-y: 0.3rem;
  }
}

.dxbl-toast-portal .dxbl-toast .dxbl-toast-close svg.dxbl-toast-close-btn {
  width: calc(var(--jsini-fs-2xs) * 1.1);
  height: calc(var(--jsini-fs-2xs) * 1.1);
}

/* 토스트의 남은 시간. 판은 그대로 두고 표시만 덧그린다. 토스트를 손으로 만들지 않고 Toasts 가 붙이는 클래스 하나에 기대어 가상 요소 둘로 얹는다(테마 색과 고른 크기를 따른다). 초는 Toasts 가 정하고, 시간표가 양쪽에 있는지는 ToastTests 가 대조한다. */

/* 남은 초를 세는 값. 등록해 두어야 정수 사이값이 이어진다(아니면 애니메이션이 0 과 60 사이를 건너뛴다). */
@property --jsini-toast-left {
  syntax: '<integer>';
  inherits: false;
  initial-value: 0;
}

.dxbl-toast.jsini-toast-time {
  position: relative;

  /* 막대가 둥근 모서리를 넘어가지 않게 한다. */
  overflow: hidden;
}

/* 줄어드는 막대. 2px 띠 하나라 width 로 줄여도 값이 싸다. 한 줄짜리 토스트의 높이를 늘리지 않도록 본문 아래에 겹쳐 깐다. */
.dxbl-toast.jsini-toast-time::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 2px;
  background: var(--dxbl-toast-accent-line-color, currentColor);
  animation: jsini-toast-drain var(--jsini-toast-span) linear forwards;
}

/* 남은 초. 실패의 빨강으로 적으면 문구보다 숫자가 먼저 읽히므로 색을 따로 둔다. 닫기 단추 칸에 붙여 이미 있는 줄 안에 든다(본문 아래를 비울 필요가 없다). */
.dxbl-toast.jsini-toast-time .dxbl-toast-close {
  display: flex;
  align-items: center;
  gap: 0.15rem;
}

.dxbl-toast.jsini-toast-time .dxbl-toast-close::before {
  content: counter(jsini-toast-left) '초';
  counter-reset: jsini-toast-left var(--jsini-toast-left);
  font-size: var(--jsini-fs-2xs);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  opacity: 0.6;
  animation: jsini-toast-count var(--jsini-toast-span)
             steps(var(--jsini-toast-secs), end) forwards;

  /* 「2」와 「초」가 갈라져 두 줄이 되지 않게 한다 — 닫기 칸은 좁다. */
  white-space: nowrap;
  flex: none;

     /* 자리를 미리 잡아 둔다. 토스트 폭이 글자만큼이라 숫자가 한 자리로 줄면 폭이 움찔한다. 가장 긴 숫자만큼 비워 오른쪽에 붙인다. */
  min-width: var(--jsini-toast-digits, 2.6em);
  text-align: right;
}

/* 짧은 토스트에는 숫자를 안 쓴다(2초를 세어 봐야 할 수 있는 일이 없고 그 두 글자가 폭의 6분의 1 이다). 줄어드는 막대는 그대로 둔다. */
.dxbl-toast.jsini-toast-time--2s .dxbl-toast-close::before {
  content: none;
}

/* 누른 동안은 멈춘다. DevExpress 가 누른 토스트를 닫지 않으므로 표시만 줄면 화면이 거짓말을 한다. 표시를 붙이는 것은 theme.js 다. */
.dxbl-toast.jsini-toast-time--held .dxbl-toast-close::before,
.dxbl-toast.jsini-toast-time--held::after {
  animation-play-state: paused;
}

@keyframes jsini-toast-drain {
  from {
    width: 100%;
  }

  to {
    width: 0;
  }
}

/* 마지막 1초를 1초로 보여 주려고 걸음으로 센다(고르게 이으면 절반쯤 지난 뒤 0초가 떠서 아직 남았는데 끝난 것처럼 보인다). */
@keyframes jsini-toast-count {
  from {
    --jsini-toast-left: var(--jsini-toast-secs);
  }

  to {
    --jsini-toast-left: 0;
  }
}

/* Toasts 가 붙이는 시간표. 없는 초를 붙이면 막대가 그려지지 않는다. --jsini-toast-digits 는 숫자가 설 자리이므로 자릿수가 늘면 함께 늘린다. */
.dxbl-toast.jsini-toast-time--2s {
  --jsini-toast-span: 2s;
  --jsini-toast-secs: 2;
}

.dxbl-toast.jsini-toast-time--60s {
  --jsini-toast-span: 60s;
  --jsini-toast-secs: 60;
  --jsini-toast-digits: 2.6em;
}

/* 토스트 쓸어 치우기(theme.js). 다 읽은 뒤의 시간은 쓸모가 없는데 닫기 단추는 손가락보다 작아 휴대폰에서 빗나간다. 옆으로 쓸면 손가락을 따라가다가 충분히 가면 날아가고 모자라면 돌아온다. 좌우 둘 다 받는다. 마우스 끌기는 건드리지 않는다(실패 문구를 끌어서 복사하는 것이 쓰임새다). */

/* 가로는 우리가 받고 세로는 브라우저에 남긴다(이 줄이 없으면 첫 움직임을 브라우저가 스크롤로 채 간다). */
.dxbl-toast {
  touch-action: pan-y;
}

/* 손을 뗀 뒤 날아가거나 돌아오는 동안만 곁들인다(끄는 동안 이 클래스가 있으면 손가락과 어긋난다). */
.dxbl-toast.jsini-toast-swipe--settling {
  transition: transform 0.18s ease-out, opacity 0.18s ease-out;
}

/* 판이 그리는 등장 애니메이션을 멈춘다. 안 멈추면 뜬 직후 쓸었을 때 그쪽 transform 이 이겨 토스트가 제자리에 붙는다. 판의 규칙보다 앞서도록 판 이름을 하나 더 붙였다. */
.dxbl-toast-portal .dxbl-toast.jsini-toast-swipe--dragging,
.dxbl-toast-portal .dxbl-toast.jsini-toast-swipe--settling {
  animation: none;
}

@media (prefers-reduced-motion: reduce) {
  .dxbl-toast.jsini-toast-swipe--settling {
    transition: none;
  }
}

/* 토스트는 보이는 화면 안에 선다. position fixed 는 레이아웃 뷰포트에 붙는데 사람이 보는 것은 비주얼 뷰포트다(확대·주소창·화면 자판에서 갈린다). theme.js 가 보이는 자리와 레이아웃 뷰포트의 차이를 네 방향 칸에 적고, 판은 그 칸을 읽는다. 확대했을 때는 크기도 1/확대배율 로 되돌린다. */

/* 줄일 때 붙잡을 모서리. 고른 자리 쪽을 붙잡아야 줄어드는 동안 그 모서리가 제자리에 남는다. */
.dxbl-toast-portal {
  --jsini-toast-origin-x: left;
  --jsini-toast-origin-y: top;
}

.dxbl-toast-portal.dxbl-toast-align-top {
  top: var(--jsini-toast-inset-top, 0px);
}

.dxbl-toast-portal.dxbl-toast-align-bottom {
  bottom: var(--jsini-toast-inset-bottom, 0px);
  --jsini-toast-origin-y: bottom;
}

.dxbl-toast-portal.dxbl-toast-align-left {
  left: var(--jsini-toast-inset-left, 0px);
}

.dxbl-toast-portal.dxbl-toast-align-right {
  right: var(--jsini-toast-inset-right, 0px);
  --jsini-toast-origin-x: right;
}

/* 가운데는 좌우를 둘 다 들여야 판의 margin auto 가 보이는 자리의 가운데를 잡는다. */
.dxbl-toast-portal.dxbl-toast-align-center {
  left: var(--jsini-toast-inset-left, 0px);
  right: var(--jsini-toast-inset-right, 0px);
  --jsini-toast-origin-x: center;
}

/* 확대한 동안에만 얹는다(늘 얹으면 scale 1 이라도 래스터 층이 올라가 글자가 흐려지는 브라우저가 있다). 표를 붙이고 떼는 일은 theme.js 가 한다. */
html.jsini-toast-zoomed .dxbl-toast-portal {
  transform: scale(var(--jsini-toast-scale, 1));
  transform-origin: var(--jsini-toast-origin-x) var(--jsini-toast-origin-y);
}

/* 쪽지(NoteWritePanel · NoteWritePopup). 상단 띠의 ✉ 가 어디서나 여는 판이라 모듈 CSS 가 아니라 여기 둔다. 이름을 jsini-notify__* 와 나눈다(한 벌을 나누면 한쪽을 손볼 때 다른 쪽이 따라 움직인다). */

.jsini-note__field {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  margin-bottom: 0.7rem;
}

.jsini-note__label {
  font-size: var(--jsini-fs-xs);
  font-weight: 600;
  color: var(--jsini-text-muted);
}

/* 입력 칸은 판 폭을 그대로 쓴다(DevExpress 가 내용만큼만 넓히므로 적어 두지 않으면 짧은 칸이 왼쪽에 붙는다). */
.jsini-note__input {
  width: 100%;
}

/* 받는 사람 딱지가 놓이는 자리. 비어 있어도 높이를 남겨 딱지가 처음 생길 때 아래 칸이 밀리지 않게 한다. */
.jsini-note__to {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
  min-height: 1.9rem;
}

/* 딱지 하나가 받는 사람 하나다(글자로 쉼표를 이으면 빠뜨렸을 때 두 사람이 한 사람으로 뭉친다). */
.jsini-note__chip {
  display: inline-flex;
  align-items: center;
  gap: 0.1rem;
  padding: 0.1rem 0.15rem 0.1rem 0.55rem;
  border: 1px solid var(--jsini-border);
  border-radius: 999px;
  background: var(--jsini-bg);
  font-size: var(--jsini-fs-sm);
}

.jsini-note__pick {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  margin-top: 0.35rem;
}

/* 찾기 칸이 추가 단추보다 훨씬 넓어야 한다(flex 기본이 내용 크기라 적어 두지 않으면 둘이 반반이 된다). */
.jsini-note__pick .jsini-note__input {
  flex: 1 1 auto;
  min-width: 0;
}

/* 단추는 안 줄어든다. 좁은 화면에서 눌려 두 줄이 되면 고장으로 읽힌다. */
.jsini-note__pick .dxbl-btn {
  flex: 0 0 auto;
  white-space: nowrap;
}

/* 찾은 사람들 목록이 길어지면 여기서 구른다(판이 늘어나 보내기 단추가 화면 밖으로 나가는 것을 막는다). */
.jsini-note__matches {
  max-height: 11rem;
  margin: 0.35rem 0 0;
  padding: 0;
  overflow-y: auto;
  border: 1px solid var(--jsini-border);
  border-radius: 0.3rem;
  list-style: none;
}

.jsini-note__match {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
  width: 100%;
  padding: 0.3rem 0.55rem;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.jsini-note__match:hover,
.jsini-note__match:focus-visible {
  background: var(--jsini-surface-muted, rgba(127, 127, 127, 0.12));
}

.jsini-note__match-name {
  font-size: var(--jsini-fs-sm);
}

/* 부서와 메일 주소. 이름보다 작고 흐리게 — 고르는 눈이 이름에 먼저 가야 한다. */
.jsini-note__match-sub {
  font-size: var(--jsini-fs-2xs);
  color: var(--jsini-text-muted);
}

.jsini-note__empty {
  margin: 0.2rem 0 0;
  font-size: var(--jsini-fs-sm);
  color: var(--jsini-text-muted);
}

/* 접어 둔 제목 칸을 펴는 줄. 단추와 안내가 한 줄에 나란히 선다. */
.jsini-note__more {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
  margin-bottom: 0.6rem;
}

/* 어떻게 전해지는지 알려 주는 줄. 고르는 자리가 아니라 읽는 자리다(두드림은 받는 사람이 자기 설정에서 정한다). */
.jsini-note__ways {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  padding-bottom: 0.6rem;
  margin-bottom: 0.6rem;
  border-bottom: 1px solid var(--jsini-border);
}

.jsini-note__send {
  display: flex;
  gap: 0.4rem;
}

/* 빠른 선택(NoteWritePanel). 아무것도 치지 않은 동안 최근 보낸 사람을 딱지로 늘어놓아 한 번 눌러 담게 한다. jsini-note__chip(담긴 사람)과 이름이 다른 것은 하는 일이 반대이기 때문이다. */

.jsini-note__quick {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem;
  margin-top: 0.45rem;
}

.jsini-note__quick-label {
  font-size: var(--jsini-fs-2xs);
  font-weight: 600;
  color: var(--jsini-text-muted);
}

/* 손가락으로 누르는 자리라 세로 여백을 넉넉히 둔다. 글자는 접지 않는다(두 줄로 갈리면 어디를 눌러야 할지 흐려진다). */
.jsini-note__quick-chip {
  padding: 0.25rem 0.6rem;
  border: 1px solid var(--jsini-border);
  border-radius: 999px;
  background: var(--jsini-bg);
  color: inherit;
  font: inherit;
  font-size: var(--jsini-fs-sm);
  line-height: 1.4;
  white-space: nowrap;
  cursor: pointer;
}

.jsini-note__quick-chip:hover,
.jsini-note__quick-chip:focus-visible {
  border-color: var(--bs-primary, #066fd1);
}

/* 담긴 사람은 목록에서 지우지 않는다(줄이 누를 때마다 흔들리면 옆 사람을 잘못 누른다). 한 번 더 누르면 빠진다. */
.jsini-note__quick-chip--on {
  border-color: var(--bs-primary, #066fd1);
  background: var(--bs-primary, #066fd1);
  color: #fff;
}

/* 화면을 채우는 모습(휴대폰). 상단 띠의 ✉ 가 휴대폰에서 여는 모습이다. 마크업은 창과 같고 모습만 클래스로 갈린다(폭으로만 가르면 답장 창까지 화면을 먹는데, 그쪽은 뒤의 목록이 보여야 한다). 바깥 상자가 이미 화면을 덮으므로 그 안의 판을 늘린다. 창의 max-width 는 !important 로 자릿수를 높여 이긴다. */

.dxbl-modal > .dxbl-modal-root > .dxbl-popup.jsini-note-popup--full {
  width: 100vw !important;
  max-width: 100vw !important;
  height: 100% !important;
  max-height: 100% !important;
  margin: 0 !important;
  border-radius: 0 !important;
}

/* 창 몸통이 남은 높이를 넘겨받아야 판이 바닥까지 닿는다. max-height 는 DevExpress 가 JS 로 재어 인라인으로 박으므로 !important 여야 이긴다(CSS 만 뒤져서는 범인이 안 보인다). */
.jsini-note-popup--full .dxbl-modal-content {
  height: 100% !important;
  max-height: none !important;
  border-radius: 0;
}

.jsini-note-popup--full .dxbl-modal-body {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1 1 auto;
}

.jsini-note--fill {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1 1 auto;
}

/* 내용 칸이 남은 높이를 먹는다. min-height 0 이 없으면 flex 항목의 기본 최소 높이 때문에 줄어들지 않아 보내기 줄이 밀려난다. */
.jsini-note--fill .jsini-note__field--grow {
  flex: 1 1 auto;
  min-height: 7rem;
}

/* 그 높이를 편집기까지 흘려보낸다. 칸만 늘리면 DevExpress 가 Rows 로 잡은 키를 그대로 써서 남은 자리가 빈다. 이름은 .dxbl-memo-edit 다. 끌어 늘리는 손잡이는 끈다(이미 남은 높이를 다 쓴다). */
.jsini-note--fill .jsini-note__field--grow .dxbl-memo-edit {
  flex: 1 1 auto;
  min-height: 0;
}

.jsini-note--fill .jsini-note__field--grow textarea,
.jsini-note--fill .jsini-note__field--grow textarea.dxbl-v-resize {
  height: 100%;
  resize: none;
}

/* 보내기 줄은 바닥에 붙는다. 받는 사람이 많아 스크롤 저편으로 가면 자판이 올라왔을 때 글을 다 쓰고도 보낼 수 없게 된다. 바탕을 칠해 아래 글이 비쳐 지나가지 않게 한다. */
.jsini-note--fill .jsini-note__send {
  position: sticky;
  bottom: 0;
  padding: 0.6rem 0 calc(0.6rem + env(safe-area-inset-bottom, 0px));
  margin-top: auto;
  background: var(--jsini-surface, #fff);
  border-top: 1px solid var(--jsini-border);
}

/* 채우는 모습에서는 단추가 가로를 채운다 — 엄지로 누르는 자리다. */
.jsini-note--fill .jsini-note__send .dxbl-btn {
  flex: 1 1 auto;
  justify-content: center;
}

/* 받을 사람 목록이 길어지면 판 전체가 아니라 목록만 구른다(창보다 넉넉히 잡는다). */
.jsini-note--fill .jsini-note__matches {
  max-height: 40vh;
}

/* 상단 띠의 숫자 배지(HeaderTools). ✉ 에 안 읽은 쪽지 수를 얹는다. 단추 안에 넣을 수 없어 단추를 감싼 자리에 절대 배치한다. 감싼 자리는 inline-flex 다(inline-block 이면 단추가 줄 바닥선에 앉아 처진다). */

/* 아래 띠에 고른 칸들이 넓은 화면에서 서는 자리(HeaderNavTools). 도구 단추와 같은 줄이지만 사이에 옅은 선을 세워 고른 바로가기와 늘 있는 도구가 한 덩어리로 읽히지 않게 한다. 휴대폰에서는 감춘다. */
.jsini-header__nav {
  display: inline-flex;
  align-items: center;
  margin-right: 0.25rem;
  padding-right: 0.25rem;
  border-right: 1px solid var(--jsini-border, rgb(0 0 0 / 12%));
}

.jsini-header__badged {
  position: relative;
  display: inline-flex;
}

.jsini-header__badge {
  position: absolute;
  top: 0;
  right: 0;
  min-width: 1rem;
  padding: 0 0.2rem;
  border-radius: 999px;
  background: var(--jsini-danger, #d13438);
  color: #fff;
  font-size: var(--jsini-fs-2xs);
  font-weight: 700;
  line-height: 1rem;
  text-align: center;

     /* 숫자를 누르면 단추가 아니라 배지가 먹는다. 그러면 ✉ 가 가끔 안 눌리는 단추가 된다. 배지의 포인터를 끈다. */
  pointer-events: none;
}

/* 빠른 지시 요청(노란 번개). 적어 두는 요청 단추다. 그림은 ⚡ 와 같아 색으로만 가른다. 권한이 둘 다 있는 사람에게는 흰 번개 옆에 나란히 선다(휴대폰에서도). 아이콘이 마스크와 currentColor 라 그림의 배경색을 바꾼다(글자색을 바꾸면 테마가 덮어쓴다). 노랑은 밝은 띠에서 흐려져 호박색으로 잡는다. */

.jsini-header__tool--request .jsini-icon-bolt {
  background-color: var(--jsini-request-bolt, #f2a900);
}

/* 글꼴 고르기(화면 설정 → 글꼴). theme.js 가 html 의 data-tb-font 를 세우면 --tb-font 를 갈아 끼운다. 기본(system)에는 표시를 세우지 않아 tabler.css 의 원래 값이 그대로 쓰인다. 파일은 고른 사람만 받는다. 뒤따르는 한글 글꼴을 꼭 적는다(Play 는 라틴 문자뿐이다). */

/* S-CoreDream 은 무게 둘만 가져왔다. 범위로 선언해 보통 글자는 Light, 500 이상은 Bold 로 그리게 한다(가짜 굵기는 획을 뭉갠다). */
@font-face {
  font-family: 'S-CoreDream';
  font-style: normal;
  font-weight: 100 450;
  font-display: swap;
  src: url('fonts/S-CoreDream-3Light.woff') format('woff');
}

@font-face {
  font-family: 'S-CoreDream';
  font-style: normal;
  font-weight: 451 900;
  font-display: swap;
  src: url('fonts/S-CoreDream-6Bold.woff') format('woff');
}

@font-face {
  font-family: 'Play';
  font-style: normal;
  font-weight: 100 550;
  font-display: swap;
  src: url('fonts/Play-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+2122;
}

@font-face {
  font-family: 'Play';
  font-style: normal;
  font-weight: 551 900;
  font-display: swap;
  src: url('fonts/Play-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+2122;
}

/* Play 의 한글 받침. 상용 한글 조각이라 드문 음절은 시스템 고딕으로 그려지고, 한글 범위로 묶어 라틴은 늘 Play 가 그린다. */
@font-face {
  font-family: 'Pretendard';
  font-style: normal;
  font-weight: 100 550;
  font-display: swap;
  src: url('fonts/Pretendard-Regular.subset.woff2') format('woff2');
  unicode-range: U+1100-11FF, U+3000-303F, U+3130-318F, U+A960-A97F, U+AC00-D7AF, U+D7B0-D7FF, U+FF00-FFEF;
}

@font-face {
  font-family: 'Pretendard';
  font-style: normal;
  font-weight: 551 900;
  font-display: swap;
  src: url('fonts/Pretendard-Bold.subset.woff2') format('woff2');
  unicode-range: U+1100-11FF, U+3000-303F, U+3130-318F, U+A960-A97F, U+AC00-D7AF, U+D7B0-D7FF, U+FF00-FFEF;
}

:root[data-tb-font='scoredream'] {
  --tb-font: 'S-CoreDream', 'Apple SD Gothic Neo', 'Malgun Gothic', 'Noto Sans KR',
    system-ui, -apple-system, 'Segoe UI', sans-serif;
}

:root[data-tb-font='play'] {
  --tb-font: 'Play', 'Pretendard', 'Apple SD Gothic Neo', 'Malgun Gothic', 'Noto Sans KR',
    system-ui, -apple-system, 'Segoe UI', sans-serif;
}

/* 서랍의 글꼴 단추는 자기 글꼴로 이름을 쓴다. 선택자를 두 겹으로 둔다(tabler.css 가 뒤에 실리며 단추에 font inherit 를 걸어 한 겹이면 진다). */
.tb-custom .tb-pill.tb-pill--font-system { font-family: system-ui, -apple-system, 'Segoe UI', 'Malgun Gothic', sans-serif; }
.tb-custom .tb-pill.tb-pill--font-scoredream { font-family: 'S-CoreDream', 'Malgun Gothic', sans-serif; }
.tb-custom .tb-pill.tb-pill--font-play { font-family: 'Play', 'Pretendard', 'Malgun Gothic', sans-serif; }

/* 로그인·가입 카드 맨 아래의 개인정보처리방침·이용약관. 조용히 두되 방침만 굵게 한다(법이 쉽게 확인할 수 있게 하라고 한다). */
.jsini-login__legal {
  display: flex;
  justify-content: center;
  gap: 0.5rem;
  margin: 1.25rem 0 0;
  font-size: 0.75rem;
  color: #86868b;
}

.jsini-login__legal a {
  color: #6e6e73;
  text-decoration: none;
}

.jsini-login__legal a:hover {
  color: #0a0a0a;
  text-decoration: underline;
}

/* 그림 미리보기(ImagePreviewHost). 화면을 통째로 덮는다(본문이 그림 한 장이라 창틀이 차지하는 만큼이 손해다). 확대·회전·색은 js/image-preview.js 가 인라인으로 그리고 이 파일은 창틀과 도구띠만 쥔다. 쌓임은 19000 으로 DevExpress 창보다 위, 잠금 덮개보다 아래다. 단추 선택자를 두 겹으로 적는 것은 tabler.css 가 뒤에 실리기 때문이다. */

.jsini-imgview {
  position: fixed;
  inset: 0;
  z-index: 19000;
  display: flex;
  flex-direction: column;
/* 96% 다. 완전한 검정이면 덮개가 아니라 다른 화면으로 옮겨 온 것으로 읽힌다. 88% 에서는 뒤 화면의 제목이 그대로 읽혔다. */
  background: rgb(0 0 0 / 96%);
  color: #f5f5f7;
  user-select: none;
}

/* 뒤 화면이 함께 구르지 않게 한다. 켜고 끄는 것은 JS 쪽이다. */
body.jsini-imgview-open {
  overflow: hidden;
}

.jsini-imgview__bar,
.jsini-imgview__tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem;
  padding: 0.5rem 0.75rem;
  background: rgb(0 0 0 / 35%);
}

.jsini-imgview__bar {
  border-bottom: 1px solid rgb(255 255 255 / 12%);
}

.jsini-imgview__tools {
  justify-content: center;
  border-top: 1px solid rgb(255 255 255 / 12%);
}

/* 이름이 길어도 도구가 밀려나지 않는다(닫기 단추가 화면 밖으로 나가면 창을 걷을 수 없다). */
.jsini-imgview__title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.875rem;
}

.jsini-imgview__count,
.jsini-imgview__read {
  flex: none;
  padding: 0 0.375rem;
  font-size: 0.8125rem;
  color: #c7c7cc;
  font-variant-numeric: tabular-nums;
}

.jsini-imgview__gap {
  flex: 1 1 auto;
}

.jsini-imgview .jsini-imgview__btn {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2rem;
  height: 2rem;
  padding: 0 0.5rem;
  border: 1px solid rgb(255 255 255 / 18%);
  border-radius: 4px;
  background: rgb(255 255 255 / 8%);
  color: #f5f5f7;
  font-size: 0.8125rem;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
}

.jsini-imgview .jsini-imgview__btn:hover {
  background: rgb(255 255 255 / 18%);
}

/* 켜진 것은 눌린 채로 둔다(흑백으로 보는 중인지 그림만 봐서는 헷갈린다). */
.jsini-imgview .jsini-imgview__btn--on {
  background: #f5f5f7;
  border-color: #f5f5f7;
  color: #1c1c1e;
}

/* 닫기만 그림이다. 이 띠의 단추는 고정 크기로 서 있고, 뿌리의 아이콘 크기는 사다리를 타므로 큰 글자 사용자 화면에서 그림이 단추를 꽉 채운다. 그래서 크기를 여기서 다시 잰다. */
.jsini-imgview .jsini-imgview__btn--close {
  --jsini-icon-size: 1rem;
}

.jsini-imgview__sep {
  flex: none;
  width: 1px;
  height: 1.25rem;
  margin: 0 0.25rem;
  background: rgb(255 255 255 / 18%);
}

/* 그림이 노는 칸. 남는 높이를 먹고 overflow hidden 이라야 키운 그림이 문서를 늘이지 않는다. touch-action none 은 두 손가락 확대를 우리가 받기 위해서다(안 걸면 브라우저가 화면 전체를 키운다). */
.jsini-imgview__stage {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  touch-action: none;
}

/* 처음에는 화면에 맞춰 선다. 그 크기가 확대율 100%다. 원래 크기(1:1)는 따로 있다. */
.jsini-imgview__img {
  max-width: 100%;
  max-height: 100%;
  transform-origin: center center;
  -webkit-user-drag: none;
}

.jsini-imgview .jsini-imgview__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 1;
  width: 2.75rem;
  height: 2.75rem;
  border: 0;
  border-radius: 50%;
  background: rgb(0 0 0 / 45%);
  color: #f5f5f7;
  font-size: 1.75rem;
  line-height: 1;
  cursor: pointer;
}

.jsini-imgview .jsini-imgview__nav:hover {
  background: rgb(0 0 0 / 70%);
}

.jsini-imgview .jsini-imgview__nav--prev {
  left: 0.75rem;
}

.jsini-imgview .jsini-imgview__nav--next {
  right: 0.75rem;
}

/* 눌러서 크게 볼 수 있는 그림이라는 표시. 본문에 박힌 그림은 여기서 한 번에 준다(data-imgview-scope). */
[data-imgview-scope] img {
  cursor: zoom-in;
}

/* 휴대폰에서는 도구가 열여섯 개라 두 줄이 되므로 글자를 줄이고 사이를 좁혀 한 줄에 담는다. */
@media (max-width: 575.98px) {
  .jsini-imgview__bar,
  .jsini-imgview__tools {
    padding: 0.375rem 0.5rem;
    gap: 0.1875rem;
  }

  .jsini-imgview .jsini-imgview__btn {
    min-width: 1.75rem;
    height: 1.75rem;
    padding: 0 0.375rem;
    font-size: 0.75rem;
  }

  .jsini-imgview__sep {
    margin: 0 0.125rem;
  }
}



/* 공통 오른쪽 서랍 */
:root {
  --jsini-drawer-width: min(26rem, 100vw);
}

.jsini-right-drawer {
  position: fixed;
  top: var(--jsini-header-height);
  right: 0;
  bottom: 0;
  width: var(--jsini-drawer-width, min(26rem, 100vw));
  max-width: 100vw;
  z-index: 1040;
  display: flex;
  flex-direction: column;
  background: var(--jsini-bg);
  border-left: 1px solid var(--jsini-border);
  box-shadow: -4px 0 16px rgba(0, 0, 0, 0.05);
  transform: translateX(100%);
  transition: transform 0.22s cubic-bezier(0.2, 0, 0, 1), visibility 0.22s;
  visibility: hidden;
}

.jsini-right-drawer--open {
  transform: translateX(0);
  visibility: visible;
}

.jsini-right-drawer--resizing {
  transition: none !important;
}

/* 오른쪽 서랍 좌측 리사이즈 핸들 */
.jsini-right-drawer__resizer {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  width: 10px;
  margin-left: -5px;
  cursor: col-resize;
  z-index: 20;
  touch-action: none;
  user-select: none;
}

.jsini-right-drawer__resizer::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 4px;
  width: 2px;
  background-color: transparent;
  transition: background-color 0.15s ease;
}

.jsini-right-drawer__resizer:hover::before,
.jsini-right-drawer--resizing .jsini-right-drawer__resizer::before {
  background-color: var(--jsini-accent);
}

@media (max-width: 767px) {
  .jsini-right-drawer__resizer {
    display: none !important;
  }
}

.jsini-right-drawer__head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.5rem 0.5rem 0.9rem;
  border-bottom: 1px solid var(--jsini-border);
}

/* 머리의 단추 둘(고정핀 · 닫기)은 링크 모양이라 속 글자에 밑줄이 그어진다. 변수를 none 으로 덮지 않고 돌려 둔다(머리에 글자가 들어오면 그 자리가 다시 살아나도록). 선택자로 이기려면 싸움이 길어지므로 변수를 쓴다. */
.jsini-right-drawer__head .dxbl-btn-link {
  --dxbl-btn-link-text-decoration: none;
}

.jsini-right-drawer__title {
  flex: 1;
  font-weight: 600;
}

.jsini-right-drawer__body {
  flex: 1;
  min-height: 0;
  padding: 0.75rem;
  overflow-y: auto;
}

.jsini-right-drawer__foot {
  margin: 0;
  padding: 0 0.9rem 0.75rem;
  color: var(--jsini-text-muted);
  font-size: var(--jsini-fs-xs);
  line-height: 1.5;
}

/* 휴대폰에서 표를 카드로 편다(CommGrd MobileCards). 칸이 예닐곱인 표는 360px 에 안 들어가 가로 스크롤 저편에 값이 숨는다. 표를 버리지 않고 DevExpress 가 그린 그대로 CSS 로 편다(줄 누르기·선택·오른쪽 클릭·엑셀이 한 코드로 돈다). 미디어 쿼리로 가르므로 회로가 폭을 몰라도 된다. 기대는 자리는 tr[data-visible-index] 와 td[role=gridcell] 과 data-caption 이다. */

@media (max-width: 767px) {

     /* 표의 뼈대를 푼다. colgroup 의 칸 너비가 살아 있으면 블록으로 바꿔도 칸이 폭을 주장해 가로 스크롤이 남는다. */
  .commgrd--cards .dxbl-grid-table > colgroup {
    display: none;
  }

  .commgrd--cards .dxbl-grid-table {
    display: block;
    width: 100% !important;
    min-width: 0 !important;
  }

     /* 머리줄은 카드에서 쓸모가 없다(이름은 값 옆에 붙는다). 칸별 검색 줄도 함께 감춘다. */
  .commgrd--cards .dxbl-grid-table > thead {
    display: none;
  }

  .commgrd--cards .dxbl-grid-table > tbody {
    display: block;
  }

  /* 가로 스크롤 막대는 더 이상 뜰 일이 없다. 자리만 먹는 것을 막는다. */
  .commgrd--cards .dxbl-scroll-viewer-hor-scroll-bar {
    display: none !important;
  }

  /* ── 카드 한 장 = 자료 줄 하나 ───────────────────────── */
  .commgrd--cards .dxbl-grid-table > tbody > tr[data-visible-index] {
    display: block;
    position: relative;
    margin: 0 0 0.5rem;
    padding: 0.5rem 0.7rem;
    border: 1px solid var(--jsini-border);
    border-radius: 10px;
    background: var(--jsini-bg);
  }

     /* 고른 카드. 표는 진한 강조색으로 줄을 칠하지만 카드는 글이 많아 흰 글자가 읽기 어려우므로 테와 옅은 바탕으로 말한다. */
  .commgrd--cards .dxbl-grid-table > tbody > tr.dxbl-grid-focused-row,
  .commgrd--cards .dxbl-grid-table > tbody > tr.dxbl-grid-selected-row {
    border-color: var(--jsini-accent);
    background: color-mix(in srgb, var(--jsini-accent) 7%, var(--jsini-bg)) !important;
    color: inherit !important;
  }

  .commgrd--cards .dxbl-grid-table > tbody > tr.dxbl-grid-focused-row > td,
  .commgrd--cards .dxbl-grid-table > tbody > tr.dxbl-grid-selected-row > td {
    color: inherit !important;
    background: transparent !important;
  }

     /* 줄무늬는 끈다. 카드는 테가 경계를 말하므로 바탕색까지 갈리면 반쯤 고른 것처럼 보인다. */
  .commgrd--cards .dxbl-grid-table > tbody > tr.commgrd__row--alt:not(.dxbl-grid-focused-row):not(.dxbl-grid-selected-row) {
    background: var(--jsini-bg);
  }

  /* ── 칸 하나 = 카드 안의 한 줄 ───────────────────────── */
  .commgrd--cards .dxbl-grid-table > tbody > tr > td {
    display: block;
    width: auto !important;
    max-width: none !important;
    min-width: 0 !important;
    border: 0 !important;
    padding: 0 !important;
    text-align: left !important;

       /* 붙박은 칸을 푼다. 가로로 구를 일이 없으니 붙박을 자리도 없다(안 풀면 카드 위에 떠서 글을 가린다). */
    position: static !important;
    box-shadow: none !important;

       /* 표는 한 줄을 지키지만 카드에서는 접히는 편이 낫다(긴 글이 … 로 잘리지 않는다). */
    white-space: normal !important;
    overflow: visible !important;
    text-overflow: clip !important;
  }

  /* 오른쪽 끝의 빈 채움 칸. 카드에서는 빈 줄 하나다. */
  .commgrd--cards .dxbl-grid-table > tbody > tr > td.dxbl-grid-empty-cell {
    display: none !important;
  }

     /* 칸 이름은 ::after 로 그린다(::before 는 DevExpress 가 고른 줄의 바탕을 칠하는 데 쓴다). 거기에 이름을 담으면 고른 카드에서만 이름이 사라진다. 카드에서는 칠할 것이 카드라 줄에 직접 준다. */
  .commgrd--cards .dxbl-grid-table > tbody > tr > td::before {
    content: none !important;
  }

  /* 이름이 적힌 칸 — 왼쪽에 이름, 오른쪽에 값. */
  .commgrd--cards .dxbl-grid-table > tbody > tr > td[data-caption] {
    display: flex;
    gap: 0.5rem;
    align-items: baseline;
    padding: 0.1rem 0 !important;
  }

  .commgrd--cards .dxbl-grid-table > tbody > tr > td[data-caption]::after {
    content: attr(data-caption);

    /* 뒤에 그려지는 것을 앞으로 돌린다. 가짜 요소도 flex 항목이라 듣는다. */
    order: -1;

    flex: 0 0 5rem;
    color: var(--jsini-text-muted);
    font-size: 0.78rem;
    line-height: 1.55;
    word-break: keep-all;
  }

     /* 값 쪽이 남은 폭을 다 쓴다. 셀 속에 감싸개 없이 글자 노드가 오는 경우가 있어 where 로 무엇이 와도 늘어나게 둔다. */
  .commgrd--cards .dxbl-grid-table > tbody > tr > td[data-caption] > * {
    min-width: 0;
  }

     /* 첫 값 칸이 제목이다(앞에 같은 것이 없는 하나). 순번·체크·관리 칸은 이름이 없어 이 셈에서 저절로 빠진다. */
  .commgrd--cards .dxbl-grid-table > tbody > tr > td[data-caption] {
    font-weight: 600;
    font-size: 0.95rem;
    padding-bottom: 0.25rem !important;
  }

  .commgrd--cards .dxbl-grid-table > tbody > tr > td[data-caption] ~ td[data-caption] {
    font-weight: normal;
    font-size: inherit;
    padding-bottom: 0.1rem !important;
  }

     /* 붙박았던 칸의 바탕과 그림자를 지운다. 표에서는 글을 가리려고 불투명했지만 카드에서는 그 사각형이 흰 판으로 떠 보인다. */
  .commgrd--cards .dxbl-grid .dxbl-grid-table > tbody > tr > td.dxbl-grid-fixed-cell,
  .commgrd--cards .dxbl-grid .dxbl-grid-table > tbody > tr.commgrd__row--alt > td.dxbl-grid-fixed-cell,
  .commgrd--cards .dxbl-grid .dxbl-grid-table > tbody > tr.dxbl-grid-focused-row > td.dxbl-grid-fixed-cell,
  .commgrd--cards .dxbl-grid .dxbl-grid-table > tbody > tr.dxbl-grid-selected-row > td.dxbl-grid-fixed-cell {
    background-color: transparent;
  }

  /* ── 이름이 없는 칸들 ────────────────────────────────── */

     /* 순번은 카드 오른쪽 위에 작게 앉는다(자리표다). 칸을 밀지 않고 속을 가운데로 둔다. */
  .commgrd--cards .dxbl-grid-table > tbody > tr > td.commgrd__cell--seq {
    font-size: 0.75rem;
    line-height: 1.2;
  }

  .commgrd--cards .dxbl-grid-table > tbody > tr > td.commgrd__cell--seq .commgrd__no {
    text-align: right;
  }

  /* 관리(수정·삭제). 카드 맨 아래에 선을 긋고 오른쪽으로 민다. */
  .commgrd--cards .dxbl-grid-table > tbody > tr > td.commgrd__cell--act {
    margin-top: 0.35rem;
    padding-top: 0.3rem !important;
    border-top: 1px dashed var(--jsini-border) !important;
  }

  .commgrd--cards .dxbl-grid-table > tbody > tr > td.commgrd__cell--act .commgrd__actions {
    justify-content: flex-end;
  }

  .commgrd--cards .dxbl-grid-table > tbody > tr > td.commgrd__cell--act .commgrd__more {
    margin-right: 0;
  }

     /* 체크 칸은 이름이 없어 제목 셈에 끼지 않고, 제목 줄 오른쪽 위에 떠서 카드를 고르는 손잡이가 된다. */
  .commgrd--cards .dxbl-grid-table > tbody > tr[data-visible-index]:has(> td.dxbl-grid-selection-cell) {
    padding-right: 2.6rem;
  }

  .commgrd--cards .dxbl-grid-table > tbody > tr > td.dxbl-grid-selection-cell {
    position: absolute !important;
    top: 0.45rem;
    right: 0.6rem;
    width: 1.8rem !important;
  }

     /* 펼침 단추도 손잡이다. 체크 칸과 같은 위쪽 귀퉁이에 두되 체크가 오른쪽을 쓰므로 왼쪽에 선다. */
  .commgrd--cards .dxbl-grid-table > tbody > tr > td.dxbl-grid-expand-button-cell {
    position: absolute !important;
    top: 0.3rem;
    left: 0.4rem;
  }

  /* 그 단추가 선 카드는 제목 줄이 그 자리를 비켜 준다. */
  .commgrd--cards .dxbl-grid-table > tbody > tr[data-visible-index]:has(> td.dxbl-grid-expand-button-cell) {
    padding-top: 1.9rem;
  }

  /* 펼친 속줄은 카드가 아니다 — 테와 여백만 카드에 맞춘다. */
  .commgrd--cards .dxbl-grid-table > tbody > tr.dxbl-grid-detail-row > td {
    padding: 0 0 0.5rem !important;
  }

     /* 쪽 넘김 줄. 카드에서는 가상 스크롤 대신 쪽 넘김이 선다. 넓은 화면용 규칙이 그대로 오면 둘이 걸린다. */

     /* 왼쪽 10rem 은 아이콘 자리를 비우려는 값인데 342px 띠에서 그만큼 빼면 쪽 단추가 잘린다. */
  .commgrd--cards:has(.dxbl-grid-bottom-panel) .dxbl-grid-bottom-panel {
    padding-left: 0.5rem;
    justify-content: center;
  }

     /* 쪽 요약 글은 건수를 머리줄이 이미 말하므로 여기서는 쪽 단추를 밀어낼 뿐이다. */
  .commgrd--cards .dxbl-pager-summary {
    display: none !important;
  }

     /* 카드 목록은 쪽이 구른다. 업무 화면의 기본(조건은 위, 구르는 것은 자료)과 달리 카드 한 장이 290px 라 보이는 상자 안에서 또 굴리면 손가락에 어긋난다. 곧장 매달린 자료 판만 그렇게 한다(좌우 분할·지도처럼 높이를 받아야 사는 판은 제외). */
  .jsini-shell__content:has(> .commcont > .commcont__body > .commgrd--cards) {
    overflow: auto;
  }

  .jsini-shell__content:has(> .commcont > .commcont__body > .commgrd--cards) > .commcont {
    flex: 0 0 auto;
    min-height: 0;
  }

  .jsini-shell__content:has(> .commcont > .commcont__body > .commgrd--cards) > .commcont > .commcont__body {
    display: block;
  }

  /* 판이 제 높이를 갖게 되었으니 표도 안에서 굴릴 것이 없다. */
  .jsini-shell__content:has(> .commcont > .commcont__body > .commgrd--cards) .commgrd--cards > .dxbl-grid {
    height: auto;
    max-height: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .jsini-shell,
  .jsini-header,
  .jsini-shell__sidebar {
    transition: none !important;
  }
}
