@charset "UTF-8";
/*
 * CommonAdmin.css — 관리자 화면 커스텀 레이어 (KRDS HTML Component Kit 1.1.0 원본은 resources/admin/** 에 두고 수정하지 않는다)
 * 규약: .claude/skills/krds/references/project/custom-layer.md
 * 여러 화면이 함께 쓰는 것만 둔다. 한 화면만 쓰는 규칙은 그 화면 JSP 의 customHead <style> 에 둔다(custom-layer.md 2.2절).
 * 구역 1 palette   : 확장형(KEITI) 프리미티브 primary 11단계 재선언. 값은 scripts/contrast.py --palette "#073867" 산출(매직넘버 40/50/70 검증)
 *                   + 레이아웃 콘텐츠 폭 토큰 2개(전체 폭, institution.md 9절)
 * 구역 2 override  : 원본 선택자 재정의. 토큰 재선언 우선, 규칙마다 대상 원본과 이유를 주석으로
 * 구역 3 extension : nf- 접두사 신규 클래스(이름은 역할). 값은 var(--krds-…) 토큰만, 컴포넌트 토큰은 클래스 안에 정의
 */

/* ===== 1. palette ===== */
:root {
  /* light */
  --krds-color-light-primary-5: #ecf5fe; /* 흰 1.10:1 · 검정 19.06:1 */
  --krds-color-light-primary-10: #d9ebfc; /* 흰 1.22:1 · 검정 17.24:1 */
  --krds-color-light-primary-20: #b3d7fa; /* 흰 1.50:1 · 검정 14.02:1 */
  --krds-color-light-primary-30: #88c0f7; /* 흰 1.92:1 · 검정 10.93:1 */
  --krds-color-light-primary-40: #3b98f2; /* 흰 3.02:1 · 검정 6.96:1 */
  --krds-color-light-primary-50: #0f75d7; /* 흰 4.62:1 · 검정 4.55:1 */
  --krds-color-light-primary-60: #0e70cd; /* 흰 4.98:1 · 검정 4.22:1 */
  --krds-color-light-primary-70: #0a4e8f; /* 흰 8.40:1 · 검정 2.50:1 */
  --krds-color-light-primary-80: #063460; /* 흰 12.59:1 · 검정 1.67:1 */
  --krds-color-light-primary-90: #041f39; /* 흰 16.66:1 · 검정 1.26:1 */
  --krds-color-light-primary-95: #031526; /* 흰 18.44:1 · 검정 1.14:1 */
  /* high-contrast */
  --krds-color-high-contrast-primary-5: #ecf5fe; /* 흰 1.10:1 · 검정 19.06:1 */
  --krds-color-high-contrast-primary-10: #d9ebfc; /* 흰 1.22:1 · 검정 17.24:1 */
  --krds-color-high-contrast-primary-20: #b3d7fa; /* 흰 1.50:1 · 검정 14.02:1 */
  --krds-color-high-contrast-primary-30: #88c0f7; /* 흰 1.92:1 · 검정 10.93:1 */
  --krds-color-high-contrast-primary-40: #3b98f2; /* 흰 3.02:1 · 검정 6.96:1 */
  --krds-color-high-contrast-primary-50: #0f75d7; /* 흰 4.62:1 · 검정 4.55:1 */
  --krds-color-high-contrast-primary-60: #0e70cd; /* 흰 4.98:1 · 검정 4.22:1 */
  --krds-color-high-contrast-primary-70: #0a4e8f; /* 흰 8.40:1 · 검정 2.50:1 */
  --krds-color-high-contrast-primary-80: #063460; /* 흰 12.59:1 · 검정 1.67:1 */
  --krds-color-high-contrast-primary-90: #041f39; /* 흰 16.66:1 · 검정 1.26:1 */
  --krds-color-high-contrast-primary-95: #031526; /* 흰 18.44:1 · 검정 1.14:1 */
  /* layout: 콘텐츠 영역 가변형(전체 폭). 원본 admin/css/pattern/output.css:17,19 의 1200px/1248px(표준형 고정 폭)을 대체.
     좌우 스크린 마진은 .inner 의 --krds-contents-padding-x(24px, 1023px 이하 16px)가 그대로 지킨다(KRDS style_05 최소 스크린 마진) */
  --krds-contents-size: 100%;
  --krds-contents-wrap-size: 100%;
}

/* ===== 2. override ===== */
/* 대상: pattern/output.css:19530 `.conts-wrap .conts-wrap h3.sec-tit { margin-bottom: … }`
   이유: 본문 섹션 제목을 h2 로 쓸 때(layout.md §4 2항, h1 은 레이아웃) 같은 제목–본문 간격 규칙이 적용되도록 h2 에도 재선언 */
.conts-wrap .conts-wrap h2.sec-tit {
  margin-bottom: var(--krds-pc-gap-layout-title-body-large);
}

/* 대상: pattern/output.css:21465-21469 `.search-top-box .sch-filter-box .filter-form .label { white-space: nowrap; min-width: 6.3rem }`
   이유: 줄바꿈을 막고 최소 폭만 줄 뿐 flex-shrink 는 기본값 1 이라, 6.3rem 보다 긴 라벨("메시지 포맷" 등)이 폭 100% 셀렉트(:21471)에 눌려
         글자가 셀렉트 위로 겹친다. 라벨이 줄지 않게만 하고 배치·간격은 키트 그대로 둔다(관리자 목록 화면 검색 영역 전체) */
.search-top-box .sch-filter-box .filter-form .label {
  flex-shrink: 0;
}

/* --- 헤더 서비스 로고: 환경정보공개시스템 (포털 로그인 화면, 사용자 결정 2026-10-29) --- */
/* 대상: admin/css/pattern/output.css:5870-5891 `#krds-header .header-container .logo`(13.7rem×4.8rem) · `.logo a`(배경 contain·가운데), :15880-15887 `.logo.sample`
   이유: 포털 로그인(views/portal/login.jsp)은 관리자 레이아웃을 쓰므로(adminLayout headerLayout="portal") 포털 헤더와 같은 로고 규칙을 여기에도 둔다.
         값·근거는 CommonPortal.css 2구역 '아이덴티티' 블록과 같다(1배 PNG 원본 크기 24.7rem×5rem, 1023px 이하 0.8배, 자산 resources/img/org/README.md) */
#krds-header .header-container .logo.nf-logo-envinfo {
  width: 24.7rem;
  height: 5rem;
}

#krds-header .header-container .logo.nf-logo-envinfo a {
  background-image: url(../img/org/logo_envinfo.png);
}

@media (max-width: 1023px) {
  #krds-header .header-container .logo.nf-logo-envinfo {
    width: 19.76rem;
    height: 4rem;
  }
}

/* --- 입력창·선택 상자: 포커스 표시를 외곽선으로 (사용자 결정 2026-10-16) --- */
/* 대상: pattern/output.css:12613-12619 `.krds-input:focus, .krds-input.focus { border-width: 2px }`,
         :12967-12973 `.krds-form-select:focus { border-width: 2px; color: var(--krds-form-select--color-text-active) }`
   이유: 키트는 포커스 때 테두리를 0.1rem(:12607, :12959)에서 2px 로 굵히고 선택 상자 글자색을 회색(color-text)에서 검정(color-text-active)으로 바꾼다.
         폭이 내용에 맞춰지는 선택 상자(검색 영역의 지역·검색어 구분 등)는 굵어진 테두리만큼 폭이 늘어 옆 요소를 밀고,
         포커스가 빠질 때는 키트 전환 효과(transition: var(--krds-transition-base))로 색이 서서히 돌아와 번갈아 누르면 흔들려 보인다.
         사용자 결정(정부24 방식)으로 테두리 색·두께·여백·글자색은 평소 그대로 두고(테두리 색도 바꾸면 포커스가 빠질 때 서서히 돌아오며 옅은 파란 테두리가 남는다), 같은 활성 테두리 색의 외곽선을 테두리 위에 겹쳐 그린다.
         outline 은 배치에 영향을 주지 않으며, 굵기 0.2rem 을 안쪽으로 겹쳐(offset -0.2rem) 키트 2px 포커스 테두리와 같은 굵기로 보인다 */
.krds-input:focus,
.krds-input.focus {
  border-color: var(--krds-input--color-border);
  border-width: 0.1rem;
  outline: 0.2rem solid var(--krds-input--color-border-active);
  outline-offset: -0.2rem;
}

.krds-form-select:focus {
  border-color: var(--krds-form-select--color-border);
  border-width: var(--krds-light-border-width-variable-regular);
  outline: 0.2rem solid var(--krds-form-select--color-border-active);
  outline-offset: -0.2rem;
  color: var(--krds-form-select--color-text);
}

/* 값을 고른 상자(키트 .completed, :12975)는 포커스 때도 그 글자색을 유지한다(위 규칙과 특이도가 같아 덮이므로 한 단계 높인다) */
.krds-form-select.completed:focus {
  color: var(--krds-form-select--color-text-completed);
}

/* --- 입력창·선택 상자: 글자색을 기본 본문색으로 (사용자 결정 2026-10-16) --- */
/* 대상: pattern/output.css:12515 `--krds-input--color-text: text-subtle`, :12853·:12855 `--krds-form-select--color-text: text-disabled`,
         `--krds-form-select--color-text-completed: text-subtle` (고대비 :12787·:13024·:13026, 다크 :12820·:13083·:13085)
   이유: 키트는 값을 고르기 전 선택 상자 글자를 비활성 회색(text-disabled)으로, 입력값·선택값을 보조색(text-subtle)으로 그려
         "전체" 같은 기본값이 흐리게 보인다. 사용자 결정(정부24 방식)으로 기본 본문색(text-basic)으로 맞춘다.
         속성을 덮지 않고 컴포넌트 토큰만 재선언하며(custom-layer.md 6절 1항), 입력창 안내문(placeholder) 색은 그대로 둔다 */
.krds-input {
  --krds-input--color-text: var(--krds-light-color-text-basic);
}

.krds-form-select {
  --krds-form-select--color-text: var(--krds-light-color-text-basic);
  --krds-form-select--color-text-completed: var(--krds-light-color-text-basic);
}

[data-krds-mode=high-contrast] .krds-input {
  --krds-input--color-text: var(--krds-high-contrast-color-text-basic);
}

[data-krds-mode=high-contrast] .krds-form-select {
  --krds-form-select--color-text: var(--krds-high-contrast-color-text-basic);
  --krds-form-select--color-text-completed: var(--krds-high-contrast-color-text-basic);
}

@media (prefers-color-scheme: dark) {
  [data-krds-mode=theme] .krds-input {
    --krds-input--color-text: var(--krds-high-contrast-color-text-basic);
  }

  [data-krds-mode=theme] .krds-form-select {
    --krds-form-select--color-text: var(--krds-high-contrast-color-text-basic);
    --krds-form-select--color-text-completed: var(--krds-high-contrast-color-text-basic);
  }
}

/* --- 목록 검색 영역: 모바일에서도 펼쳐 보이기 (사용자 결정 2026-10-16) --- */
/* 대상: pattern/output.css:21553 `@media (max-width: 767px) { .search-top-box .sch-filter-box { display: none } }`,
         :21430-21468 `.filter-form { flex-flow: wrap }`, `.filter-form > div { flex-direction: row; max-width: 26.5rem }`,
         `> div:nth-child(4) { max-width: fit-content } .krds-input { width: 37rem }`, `.label { min-width: 6.3rem }`
   이유: 키트는 767px 이하에서 필터 상자를 숨기고 별도 필터 버튼·모달(공식 샘플 sample_search_02 의 .m-sort-btn)로 여는 것을 전제하는데,
         관리자 목록에는 그 버튼·모달이 없어 모바일에서 검색을 할 수 없다. 사용자 결정(정부24 방식)으로 필터 상자를 펼쳐 두고,
         각 항목을 라벨 위·입력 요소 아래(전체 폭)로 세로로 쌓는다. 여백·간격은 키트 모바일 카드 여백과 간격 토큰을 쓰고,
         세로로 쌓인 테두리 상자(선택 상자·입력창·버튼)가 붙어 보이지 않게 항목 안 간격은 gap-4 로 둔다.
         사용: 관리자 목록 검색 영역 전체(views/admin 의 .sch-filter-box 12화면) */
@media (max-width: 767px) {
  .search-top-box .sch-filter-box {
    display: block;
    padding: var(--krds-mobile-padding-card-large);
  }

  .search-top-box .sch-filter-box .filter-form {
    flex-direction: column;
    flex-wrap: nowrap;
  }

  .search-top-box .sch-filter-box .filter-form > div:nth-child(n) {
    flex-direction: column;
    align-items: stretch;
    gap: var(--krds-gap-4);
    max-width: none;
  }

  .search-top-box .sch-filter-box .filter-form > div:nth-child(4) .krds-input {
    width: 100%;
  }

  .search-top-box .sch-filter-box .filter-form .label {
    min-width: 0;
  }
}

/* --- 버튼이 많은 버튼 줄: 줄바꿈 (.page-btn-wrap.nf-btn-rows) --- */
/* 대상: pattern/output.css:15976-15982 `.page-btn-wrap { display: flex; gap: var(--krds-gap-5); margin-top: var(--krds-gap-9) }`
   이유: 키트 버튼 줄은 줄바꿈이 없어 엑셀 다운로드·업로드 버튼 5개 이상이 한 줄에 눌린다. 넘치면 다음 줄로 넘기고,
         연달아 놓인 버튼 줄 사이에 키트 버튼 간격(gap-5)을 둔다. (두 화면에 복사돼 있던 `#formList .page-btn-wrap` 규칙을 올림)
   사용: views/admin/colStdAplcnPrst/list.jsp, wordStdAplcnPrst/list.jsp */
.page-btn-wrap.nf-btn-rows {
  flex-wrap: wrap;
  margin-bottom: var(--krds-gap-5);
}

/* --- 입력 칸 도움말·오류 문구: hidden 이면 숨기기 --- */
/* 대상: pattern/output.css:12071 `.form-group [class^=form-hint] { display: flex }`
   이유: 키트가 도움말·오류 문구(p.form-hint·form-hint-invalid 등)를 display: flex 로 그려 hidden 속성이 덮인다.
         등록·수정 화면은 칸마다 오류 문구 자리(p.form-hint-invalid hidden)를 태그가 미리 그리고 CommonForm 이 보이고 숨기므로, hidden 일 때는 자리도 차지하지 않게 한다.
   사용: WEB-INF/tags/form/text.tag·textarea.tag·radio.tag(views/admin/comStdWord/regist.jsp·modify.jsp) */
.form-group [class^=form-hint][hidden] {
  display: none;
}

/* --- 라디오 점: 어떤 화면 배율에서도 원의 정가운데 (사용자 결정 2026-10-20) --- */
/* 대상: pattern/output.css:13603-13626 라디오 원 `label:before`(20px, 테두리 1px)와 점 `label:after`(10px, position absolute, top·left 계산값),
         :13432-13440·:13507-13515 medium·large 변형
   이유: 원과 점이 각각 화면 픽셀에 맞춰 반올림돼, 배율이 정수가 아닌 모니터(125% 등)에서는 원 25px·점 12.5px 가 되어 점이 0.5px 한쪽으로 쏠린다
         (125% 캡처 실측: 원 중심 (882.0, 59.0), 점 중심 (881.5, 58.5)). 점을 원 자신의 배경(radial-gradient)으로 그려 같은 상자 안에서 동심원이 되게 하고,
         키트 점(label:after)은 숨긴다. 점 색은 키트 상태 변수(기본·체크·비활성 체크, 고대비 모드는 같은 변수를 다시 선언) 그대로,
         점 크기는 medium·large 변수 그대로다. 가장자리 1px(±0.05rem)를 부드럽게 넘겨 키트 점과 같은 크기(지름 10px)로 보이게 한다.
   사용: 관리자 화면의 모든 라디오(.krds-form-check input[type=radio]) */
.krds-form-check input[type=radio] ~ label {
  --nf-radio-dot-color: var(--krds-form-check--radio-check-color-element);
  --nf-radio-dot-size: var(--krds-form-check--radio-check-size-medium);
}

.krds-form-check.large > input[type=radio] ~ label {
  --nf-radio-dot-size: var(--krds-form-check--radio-check-size-large);
}

.krds-form-check input[type=radio]:checked ~ label {
  --nf-radio-dot-color: var(--krds-form-check--radio-check-color-element-checked);
}

.krds-form-check input[type=radio]:checked:disabled ~ label {
  --nf-radio-dot-color: var(--krds-form-check--radio-check-color-element-disabled);
}

.krds-form-check input[type=radio] ~ label::before {
  background-image: radial-gradient(circle,
    var(--nf-radio-dot-color) calc(var(--nf-radio-dot-size) / 2 - 0.05rem),
    transparent calc(var(--nf-radio-dot-size) / 2 + 0.05rem));
}

.krds-form-check input[type=radio] ~ label::after {
  display: none;
}

/* ===== 3. extension ===== */
/* --- 목록 검색 조회 버튼 칸 (nf-search-submit) --- */
/* 키트 검색 폼 항목(.filter-form > div)은 폭 100%·최대 26.5rem(pattern/output.css:21447-21463)이라 버튼만 든 칸도 그 폭을 차지한다.
   조회 버튼 칸은 내용 크기로 두고 검색 상자 마지막 줄 오른쪽 끝에 붙인다(사용자 결정 2026-10-20). 검색어 태그(search:keyword)를 없애
   모든 목록 화면이 '조건 칸들 + 마지막 줄 끝 조회 버튼' 구조라 화면 CSS 가 아닌 여기 둔다(키트 선택자보다 특이도를 높이려고 같은 조상을 쓴다).
   마크업: 마지막 .filter-form 끝에 div.nf-search-submit > button[type=submit].krds-btn.medium.primary 조회하기
   사용: views/admin/{api,apiLog,comCd,comCd/group,comLog,comStdDmn,comStdTrm,comStdWord,conts,conts/group,file,user}/page.jsp, colStdAplcnPrst/list.jsp, wordStdAplcnPrst/list.jsp(14화면) */
.search-top-box .sch-filter-box .filter-form > .nf-search-submit {
  max-width: fit-content;
  margin-left: auto;
}

/* 모바일(767px 이하)은 2구역 규칙대로 세로로 쌓으므로 버튼을 전체 폭으로 두고 오른쪽 붙임을 푼다(같은 특이도라 여기서 다시 둔다) */
@media (max-width: 767px) {
  .search-top-box .sch-filter-box .filter-form > .nf-search-submit {
    max-width: none;
    margin-left: 0;
  }
}

/* --- 목록 검색 영역: 여러 줄 필터 (nf-filter-rows) --- */
/* 키트 검색 폼(.search-top-box .sch-filter-box .filter-form, pattern/output.css:21437-21445)은 한 줄 흐름(flex-wrap)만 정의하고
   여러 줄을 쌓을 때의 줄 간격·라벨 열 폭·줄 높이 규칙이 없다(예전 화면은 빈 dl.filter-chip 을 구분선으로 끼워 줄을 나눴다).
   검색 상자 안을 이 래퍼로 감싸고 줄마다 .filter-form 하나를 둔다. 한 줄에 항목(라벨 + 입력)이 여럿이면 키트 흐름대로 나란히 놓인다.
   - 줄 간격: 키트 항목 간격(gap-7)   - 줄 높이: 입력·선택 상자 medium 높이(size-height-7, 라디오 줄도 같은 높이)
   - 라벨 폭: 모든 화면·모든 칸의 라벨을 한 값(11rem)으로 맞춘다(사용자 결정 2026-10-20, 키트 .label min-width 6.3rem, :21465 대신).
     항목 칸 폭(키트 최대 26.5rem)이 같으므로 줄마다 첫 칸·둘째 칸 입력이 같은 세로선에 놓인다. 값은 관리자 검색 라벨 중 가장 긴
     '콘텐츠그룹번호'(103px, 17px 굵은 글꼴)보다 넉넉히 잡았다(2026-10-23 목록 열 맞춤으로 10rem → 11rem). 더 긴 라벨이 생기면 화면에서 따로 늘리지 말고 이 값을 고친다.
   - 줄 구성: 같은 계열끼리 한 줄에 묶고 한 줄에 3개까지(사용자 결정 2026-10-23). 항목은 목록 열에 맞춘다(common-api.md 2절).
   사용: views/admin/{api,apiLog,comCd,comCd/group,comLog,comStdDmn,comStdTrm,comStdWord,conts,conts/group,file,user}/page.jsp, comLog/summary.jsp, colStdAplcnPrst/list.jsp, wordStdAplcnPrst/list.jsp(15화면) */
.nf-filter-rows {
  --nf-filter-rows--gap-y: var(--krds-gap-7);
  --nf-filter-rows--size-height: var(--krds-size-height-7);
  --nf-filter-rows--label-width: 11rem;
  display: flex;
  flex-direction: column;
  gap: var(--nf-filter-rows--gap-y);
}

.nf-filter-rows .filter-form {
  min-height: var(--nf-filter-rows--size-height);
}

.search-top-box .sch-filter-box .nf-filter-rows .filter-form .label {
  min-width: var(--nf-filter-rows--label-width);
}

/* 모바일(767px 이하)은 2구역 규칙대로 라벨 위·입력 아래로 쌓으므로 라벨 폭을 푼다 */
@media (max-width: 767px) {
  .search-top-box .sch-filter-box .nf-filter-rows .filter-form .label {
    min-width: 0;
  }
}

/* --- 목록 검색 영역: 라디오·체크박스 묶음 항목 (nf-filter-group) --- */
/* 라디오 그룹은 fieldset + legend 로 묶는다(components/radio_button.md 접근성). 키트는 legend 를 화면에서 숨기므로(pattern/output.css .blind, legend)
   보이는 제목은 .label 로 두고 aria-hidden 으로 중복 낭독을 막는다. 제목·라디오 배치와 간격은 키트 .filter-form > div(:21447, gap-4)와 같다.
   키트 항목 폭(width 100%, 최대 26.5rem)은 셀렉트 하나 기준이라 라벨 + 라디오 3개가 넘치므로 이 항목만 내용 크기로 둔다(한 줄에 묶음이 여럿이어도 나란히 놓인다).
   마크업: .filter-form > div > fieldset.nf-filter-group > legend + span.label[aria-hidden] + .krds-check-area
   사용: views/admin/user/page.jsp(사용여부), comCd/group/page.jsp(발급여부·공개여부 등), comStdWord/page.jsp(형식단어·폐기 여부), comStdDmn·comStdTrm/page.jsp(폐기 여부), colStdAplcnPrst/list.jsp(NULL 허용) */
.search-top-box .sch-filter-box .filter-form > div:has(> .nf-filter-group) {
  width: auto;
  max-width: none;
}

.nf-filter-group {
  display: flex;
  align-items: center;
  gap: var(--krds-gap-4);
}

/* 라디오·체크박스 칸(.krds-form-check)은 키트에서 block 이라, 그 안의 inline-flex 라벨이 글자 기준선에 앉아 칸 아래에 4px 가 남는다(칸 29.5px, 라벨 25.5px).
   묶음을 가운데 정렬해도 라벨은 칸 위쪽에 붙어 줄 중심보다 2px 위에 놓인다(1366px 실측: 줄·셀렉트·버튼 중심 384.5px, 라디오 라벨 중심 382.5px).
   칸을 flex 로 두어 라벨 높이에 맞춘다. 폼 화면의 라디오(라벨 아래 .krds-form-check-cnt 보조 설명이 붙는 경우)는 그대로 둔다.
   원(label:before)의 위 여백 토큰(--krds-form-check--button-margin-top 0.3rem, pattern/output.css:13390)은 여러 줄 라벨의 첫 줄에 맞춘 값이라
   한 줄 라벨에서는 원 중심이 라벨 중심보다 0.25px 아래다(원 중심 384.75px). 검색 항목 라벨은 한 줄이므로 (줄 높이 - 원 크기) / 2 로 다시 선언해
   원·라디오 점·체크박스 체크 표시(같은 토큰으로 top 을 계산, :13608·:13633)를 라벨 정중앙에 둔다. 검색 영역은 medium 크기만 쓴다 */
.nf-filter-group .krds-form-check {
  --krds-form-check--button-margin-top: calc((1lh - var(--krds-form-check--button-size-medium)) / 2);
  display: flex;
}

/* 모바일(767px 이하): 제목 아래 왼쪽 정렬로 쌓는다 */
@media (max-width: 767px) {
  .nf-filter-group {
    flex-direction: column;
    align-items: flex-start;
  }
}

/* --- 표 셀 링크 (nf-table-link) --- */
/* 목록 표의 상세 이동 링크. 키트에는 표 셀 링크 스타일이 없고 전역 a 는 색·밑줄을 상속·제거한다(output.css:1330-1334).
   마우스를 올리면 링크임을 알 수 있게 키트 카드 제목 링크와 같은 밑줄(a:hover .d-card .c-tit, output.css:18267-18270)과 시맨틱 link-hover 색을 준다.
   사용: 목록 태그 list:col 에 href 를 주면 출력한다(WEB-INF/tags/list/col.tag, 목록 화면 전체) */
.nf-table-link {
  --nf-table-link--color-hover: var(--krds-light-color-link-hover);
}

.nf-table-link:hover {
  color: var(--nf-table-link--color-hover);
  text-decoration: underline;
  text-underline-offset: var(--krds-font-underline-offset);
}

[data-krds-mode=high-contrast] .nf-table-link {
  --nf-table-link--color-hover: var(--krds-high-contrast-color-link-hover);
}

@media (prefers-color-scheme: dark) {
  [data-krds-mode=theme] .nf-table-link {
    --nf-table-link--color-hover: var(--krds-high-contrast-color-link-hover);
  }
}

/* --- 필수 입력 표시 (nf-required) --- */
/* 키트 CSS 에는 필수 표시 클래스가 없다. KRDS 입력폼 패턴은 별표 표시를 허용한다(patterns/global_08.md 93행 "필수 입력 항목을 별표를 사용하여 표시").
   보조 기술에는 label 안의 .sr-only " 필수 입력" 텍스트가 전달하고(global_08.md 217행), 별표는 화면 표시용이라 대체 텍스트를 비운다(content 의 "/ """).
   색은 시맨틱 text-danger, 간격은 gap-1.
   사용: views/admin/rsrc/menuInfo.jsp·apiInfo.jsp 마크업. 등록·수정 화면은 구조를 다시 정할 때 이 클래스를 쓴다 */
.nf-required {
  --nf-required--color-mark: var(--krds-light-color-text-danger);
}

.nf-required::after {
  content: "*";
  content: "*" / "";
  margin-left: var(--krds-gap-1);
  color: var(--nf-required--color-mark);
}

[data-krds-mode=high-contrast] .nf-required {
  --nf-required--color-mark: var(--krds-high-contrast-color-text-danger);
}

@media (prefers-color-scheme: dark) {
  [data-krds-mode=theme] .nf-required {
    --nf-required--color-mark: var(--krds-high-contrast-color-text-danger);
  }
}

/* --- 좌우 분할 영역 (nf-split) --- */
/* 목록(트리)과 편집 폼을 나란히 두는 2열 영역. 키트 .conts-area 는 세로 flex(pattern/output.css:16011-16018)라 좌우 배치가 없다.
   .conts-area 안쪽에 두고 자식은 키트 .txt-box.bg-white 상자다. 열 간격은 키트가 상자를 세로로 쌓을 때 쓰는 간격과 같은 gap-7.
   키트 .txt-box:not(:first-of-type) { margin-top: gap-7 }(output.css:19707-19709)는 세로로 쌓을 때의 간격이라, 나란히 둘 때는 grid gap 이 대신하고
   두 상자 윗선이 맞도록 0 으로 둔다. 1023px 이하(키트 태블릿 경계, output.css:47)에서는 1열로 쌓는다.
   사용: views/admin/rsrc/menuInfo.jsp(메뉴 목록), views/admin/rsrc/apiInfo.jsp(API 목록) */
.nf-split {
  --nf-split--gap: var(--krds-gap-7);
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
  gap: var(--nf-split--gap);
  width: 100%;
}

.nf-split > .txt-box {
  min-width: 0;
  margin-top: 0;
}

@media (max-width: 1023px) {
  .nf-split {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* --- 계층 트리 (nf-tree) --- */
/* KRDS 에는 트리 컴포넌트가 없다(폴백 3단, decision-table.md 4절). 계층 탐색인 사이드 메뉴(.krds-side-navigation)의 상태 토큰 구성을 따른다:
   항목 hover·pressed·selected 배경 = action-secondary-hover/-pressed/-selected, 선택 글자 = text-secondary(output.css:8336-8341),
   목록 안 초점은 잘리지 않게 안쪽 링(--krds-box-shadow-outline-inset + outline-offset 0.4rem 안쪽, output.css:8388-8391).
   선택 상태는 색에 더해 굵기로도 구분한다. 접기·펼치기 아이콘은 키트 .svg-icon.ico-toggle(아래 꺾쇠, output.css:418)을 접힘일 때 -90° 돌린다.
   크기: 한 줄 높이 size-height-6(4rem), 토글 폭 size-height-5(3.2rem), 단계 들여쓰기 gap-8(3.2rem). 목록은 14줄(1023px 이하 9줄) 높이에서 스크롤한다.
   연결선은 하위 목록 항목마다 부모 토글 중심에서 그린다(세로: 형제 연결, 가로: 자기 줄 중심까지). 장식이라 계층은 들여쓰기·aria-level 로도 전달된다.
   마크업(화면 JS 가 생성): nav.nf-tree > ul.nf-tree-list[role=tree] > li.nf-tree-item > (.nf-tree-row > button.nf-tree-toggle | span.nf-tree-spacer, button.nf-tree-node > .nf-tree-name + .krds-badge) + ul.nf-tree-list[role=group]
   사용: views/admin/rsrc/menuInfo.jsp(메뉴 목록), views/admin/rsrc/apiInfo.jsp(API 목록) */
.nf-tree {
  --nf-tree--row-height: var(--krds-size-height-6);
  --nf-tree--toggle-width: var(--krds-size-height-5);
  --nf-tree--indent: var(--krds-gap-8);
  --nf-tree--item-gap: var(--krds-gap-1);
  --nf-tree--row-gap: var(--krds-gap-1);
  --nf-tree--node-gap: var(--krds-gap-3);
  --nf-tree--node-padding-x: var(--krds-padding-4);
  --nf-tree--node-padding-y: var(--krds-padding-3);
  --nf-tree--radius: var(--krds-radius-medium2);
  --nf-tree--focus-offset: calc(var(--krds-padding-2) * -1);
  --nf-tree--line-width: var(--krds-light-border-width-static-regular);
  --nf-tree--color-line: var(--krds-light-color-border-gray-light);
  --nf-tree--color-action-hover: var(--krds-light-color-action-secondary-hover);
  --nf-tree--color-action-pressed: var(--krds-light-color-action-secondary-pressed);
  --nf-tree--color-action-selected: var(--krds-light-color-action-secondary-selected);
  --nf-tree--color-text-selected: var(--krds-light-color-text-secondary);
  max-height: calc(var(--nf-tree--row-height) * 14);
  overflow: auto;
  scrollbar-gutter: stable;
}

[data-krds-mode=high-contrast] .nf-tree {
  --nf-tree--line-width: var(--krds-high-contrast-border-width-static-regular);
  --nf-tree--color-line: var(--krds-high-contrast-color-border-gray-light);
  --nf-tree--color-action-hover: var(--krds-high-contrast-color-action-secondary-hover);
  --nf-tree--color-action-pressed: var(--krds-high-contrast-color-action-secondary-pressed);
  --nf-tree--color-action-selected: var(--krds-high-contrast-color-action-secondary-selected);
  --nf-tree--color-text-selected: var(--krds-high-contrast-color-text-secondary);
}

@media (prefers-color-scheme: dark) {
  [data-krds-mode=theme] .nf-tree {
    --nf-tree--line-width: var(--krds-high-contrast-border-width-static-regular);
    --nf-tree--color-line: var(--krds-high-contrast-color-border-gray-light);
    --nf-tree--color-action-hover: var(--krds-high-contrast-color-action-secondary-hover);
    --nf-tree--color-action-pressed: var(--krds-high-contrast-color-action-secondary-pressed);
    --nf-tree--color-action-selected: var(--krds-high-contrast-color-action-secondary-selected);
    --nf-tree--color-text-selected: var(--krds-high-contrast-color-text-secondary);
  }
}

@media (max-width: 1023px) {
  .nf-tree {
    max-height: calc(var(--nf-tree--row-height) * 9);
  }
}

.nf-tree-list .nf-tree-list {
  padding-left: var(--nf-tree--indent);
}

.nf-tree-item + .nf-tree-item {
  margin-top: var(--nf-tree--item-gap);
}

.nf-tree-row {
  display: flex;
  align-items: flex-start;
  gap: var(--nf-tree--row-gap);
}

.nf-tree-toggle,
.nf-tree-spacer {
  flex: none;
  width: var(--nf-tree--toggle-width);
  height: var(--nf-tree--row-height);
}

.nf-tree-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--nf-tree--radius);
}

.nf-tree-toggle[aria-expanded="false"] .svg-icon {
  transform: rotate(-90deg);
}

.nf-tree-node {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex: 1;
  gap: var(--nf-tree--node-gap);
  min-width: 0;
  min-height: var(--nf-tree--row-height);
  padding: var(--nf-tree--node-padding-y) var(--nf-tree--node-padding-x);
  border-radius: var(--nf-tree--radius);
  text-align: left;
}

.nf-tree-toggle:hover,
.nf-tree-node:hover {
  background-color: var(--nf-tree--color-action-hover);
}

.nf-tree-toggle:active,
.nf-tree-node:active {
  background-color: var(--nf-tree--color-action-pressed);
}

.nf-tree-toggle:focus,
.nf-tree-node:focus {
  box-shadow: var(--krds-box-shadow-outline-inset);
  outline-offset: var(--nf-tree--focus-offset);
}

.nf-tree-node.is-active {
  background-color: var(--nf-tree--color-action-selected);
  color: var(--nf-tree--color-text-selected);
  font-weight: var(--krds-font-weight-bold);
}

.nf-tree-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.nf-tree-node .krds-badge {
  flex: none;
}

/* 연결선: 하위 목록 항목(li)의 왼쪽 -들여쓰기 위치 + 토글 폭 절반 = 부모 토글 중심 */
.nf-tree-list .nf-tree-list > .nf-tree-item {
  position: relative;
}

.nf-tree-list .nf-tree-list > .nf-tree-item::before,
.nf-tree-list .nf-tree-list > .nf-tree-item::after {
  content: "";
  position: absolute;
  left: calc(var(--nf-tree--toggle-width) / 2 - var(--nf-tree--indent));
  border: 0 solid var(--nf-tree--color-line);
}

.nf-tree-list .nf-tree-list > .nf-tree-item::before {
  top: 0;
  bottom: 0;
  border-left-width: var(--nf-tree--line-width);
}

.nf-tree-list .nf-tree-list > .nf-tree-item:last-child::before {
  bottom: auto;
  height: calc(var(--nf-tree--row-height) / 2);
}

.nf-tree-list .nf-tree-list > .nf-tree-item::after {
  top: calc(var(--nf-tree--row-height) / 2);
  width: var(--nf-tree--indent);
  border-top-width: var(--nf-tree--line-width);
}

.nf-tree-list .nf-tree-list > .nf-tree-item:has(> .nf-tree-row > .nf-tree-toggle)::after {
  width: calc(var(--nf-tree--indent) - var(--nf-tree--toggle-width) / 2);
}

/* --- 요청 중 표시 (nf-loading) --- */
/* 모든 요청(CommonApi.js)에 공통인 로딩 표시. 레이아웃 태그가 화면에 덮개(nf-loading) 하나를 두고, 요청 중에만 보인다.
   요청을 보내는 즉시 화면 전체를 옅게 덮고 정가운데에 KRDS 스피너를 보인다. 덮개가 클릭을 받아 두 번째 클릭을 막는다(button.md "두 번째 인터랙션이 무시되도록").
   빠른 요청도 똑같이 띄운다(spinner.md '1초 미만 작업에는 스피너를 쓰지 않음'은 권고라 미적용, 사용자 결정 2026-10-19: 어떤 요청이든 바로 같은 반응).
   위치는 화면 중앙(spinner.md 용례 "화면 중앙 또는 맥락이 변경되는 콘텐츠 영역 중앙"). 검정 음영(background-dim)은 모달용이라 쓰지 않고
   바탕색 반투명(alpha-base75)으로 덮어 화면 맥락을 남긴다. 층은 모달(pattern/output.css:3706 z-index 1020) 위라 모달 안 요청도 덮는다.
   키트 .krds-spinner 는 감싼 상자를 가득 채우고 가운데에 고리를 두므로(pattern/output.css:4625 width·height 100%) 덮개 안에 바로 넣으면 화면 정가운데에 놓인다.
   고리는 큰 크기(spinner.md "공간 제약이 없으면 큰 스피너").
   사용: tags/adminLayout.tag · tags/portalLayout.tag(모든 화면) */
.nf-loading {
  --nf-loading--color-cover: var(--krds-light-color-alpha-base75);
  position: fixed;
  inset: 0;
  z-index: 1030;
  background-color: var(--nf-loading--color-cover);
}

.nf-loading[hidden] {
  display: none;
}

.nf-loading .krds-spinner {
  --krds-spinner--size: var(--krds-size-height-7);
}

[data-krds-mode=high-contrast] .nf-loading {
  --nf-loading--color-cover: var(--krds-high-contrast-color-alpha-base75);
}

@media (prefers-color-scheme: dark) {
  [data-krds-mode=theme] .nf-loading {
    --nf-loading--color-cover: var(--krds-high-contrast-color-alpha-base75);
  }
}
