/* 전역 보조 스타일 — 색상 값은 넣지 않는다 (themes.css 토큰을 참조만 한다) */

[x-cloak] {
  display: none !important;
}

/* 페이지 헤더 아이콘 타일 — 사이드바 그룹과 같은 액센트 매핑으로 화면의 소속을 색으로 알린다
   (미디어 테스트=primary, 콘텐츠=secondary, 관리=accent, 개발 테스트=warning, 계정=info — 사이드바 그룹 아이콘과 동일하며
   그 매핑의 원본은 router.js 의 Router.groupMeta 다).
   색은 전부 테마 토큰이라 다크 모드 자동 대응 */
.page-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 2.5rem;
  block-size: 2.5rem;
  flex-shrink: 0;
  border-radius: var(--radius-box);
  font-size: 1.25rem;
  background-color: color-mix(in oklab, var(--pi-color, var(--color-primary)) 14%, transparent);
  color: var(--pi-color, var(--color-primary));
}
.page-icon-primary { --pi-color: var(--color-primary); }
.page-icon-secondary { --pi-color: var(--color-secondary); }
.page-icon-accent { --pi-color: var(--color-accent); }
.page-icon-info { --pi-color: var(--color-info); }
.page-icon-warning { --pi-color: var(--color-warning); }

/* daisyUI CDN 번들 보정: .input/.select의 기본 크기 규칙이 같은 레이어에서
   -xs/-sm 모디파이어보다 뒤에 선언되어 있어 크기 클래스가 무시된다.
   레이어 밖(unlayered) 규칙은 레이어 규칙보다 항상 우선하므로 여기서 다시 선언한다. */
.input-xs { --in-size-mul: 6; --font-size-min: 0.6875rem; }
.input-sm { --in-size-mul: 8; --font-size-min: 0.75rem; }
.select-xs { --sl-size-mul: 6; --font-size-min: 0.6875rem; }
.select-sm { --sl-size-mul: 8; --font-size-min: 0.75rem; }

/* 테이블 헤더: 연한 배경 + 상단 보더로 본문 행과 구분 (테마 토큰이라 다크 모드 자동 대응) */
.table thead tr {
  background-color: color-mix(in oklab, var(--color-base-content) 5%, var(--color-base-100));
}
.table thead th {
  border-top: 1px solid var(--color-base-300);
  padding-block: 0.5rem;
  font-size: 0.75rem;
}

/* 카드 안 테이블: 카드 가장자리까지 확장(full-bleed) */
.card-body .table-flush {
  margin-inline: calc(var(--card-p, 1.5rem) * -1);
}
.card-body .table-flush .table :where(th, td):first-child {
  padding-inline-start: var(--card-p, 1.5rem);
}
.card-body .table-flush .table :where(th, td):last-child {
  padding-inline-end: var(--card-p, 1.5rem);
}

/* 테이블 푸터(총 개수·페이지네이션): 헤더처럼 구분 보더를 카드 가장자리까지 확장 */
.card-body .table-footer {
  margin-inline: calc(var(--card-p, 1.5rem) * -1);
  padding-inline: var(--card-p, 1.5rem);
}

/* 페이지네이션: 버튼 배경색 없이 보더(그룹 구분선)는 유지, 활성 번호만 primary 배경 */
.pagination.join > .btn:not(.btn-primary) {
  background-color: transparent;
  box-shadow: none;
}
.pagination.join > .btn:disabled,
.pagination.join > .btn[disabled] {
  background-color: transparent !important;
  border-color: var(--color-base-300) !important; /* 비활성화돼도 보더 유지 (아이콘만 흐려짐) */
}
/* daisyUI join은 가로 그룹에서 각 항목의 오른쪽 보더 폭을 0으로 두고 다음 항목의 왼쪽
   보더로 구분선을 대신한다 — 마지막 항목은 다음 항목이 없어 오른쪽 변이 비므로 복원 */
.pagination.join > :last-child {
  border-inline-end-width: var(--border, 1px);
}

/* 옅은 구분선·경계·호버·선택 — daisyUI 표의 행 구분선과 같은 값(base-content 5%).
   ⚠ Tailwind 브라우저 빌드는 daisyUI 색 이름을 모른다 — divide-base-content/5·border-base-200·bg-primary/10 같은
   유틸리티는 **생성되지 않고**, 그러면 border-color 기본값(currentColor)으로 글자색 선이 진하게 그려진다(2026-09-05
   TMDB 매칭 모달에서 실제로 겪음). 목록·모달의 선은 이 클래스로 그린다. */
.divide-soft > :not(:first-child) {
  border-top: var(--border, 1px) solid color-mix(in oklab, var(--color-base-content) 5%, transparent);
}
.border-soft {
  border-color: color-mix(in oklab, var(--color-base-content) 5%, transparent);
}
.hover-soft:hover {
  background-color: color-mix(in oklab, var(--color-base-content) 5%, transparent);
}
.row-selected {
  background-color: color-mix(in oklab, var(--color-primary) 10%, transparent);
}

/* 아이콘 자리(1em)를 항상 확보 — 레이아웃 밀림 방지.
   :not(:defined) 만으로는 부족하다: 컴포넌트 정의 후에도 SVG 는 비동기로 그려져서
   그 사이 폭이 0 이 되어 옆 글자가 먼저 붙었다가 밀려난다. 프로젝트의 아이콘은 전부
   1em(폰트 크기 연동)이라 고정 예약이 최종 크기와 일치한다. */
iconify-icon {
  display: inline-block;
  width: 1em;
  height: 1em;
}

/* 첫 렌더 동안 전환 효과 잠금 — 새로고침 시 사이드바가 슬라이드로 열리는 것 방지.
   초기 렌더가 끝나면 app.js가 preload 클래스를 제거해 이후 애니메이션은 정상 동작 */
html.preload *,
html.preload *::before,
html.preload *::after {
  transition: none !important;
}

/* 사이드바 메뉴: 선택/호버 시 애니메이션 없이 즉시 반영 */
.drawer-side .menu li > *,
.drawer-side .menu li > *:active,
.drawer-side .menu li > *:hover {
  transition: none !important;
  animation: none !important;
}
.drawer-side .menu li > *:active {
  transform: none !important;
  scale: none !important;
  translate: none !important;
}

/* 사이드바 활성 메뉴: 은은한 primary 톤 */
.drawer-side .menu {
  --menu-active-bg: color-mix(in oklab, var(--color-primary) 12%, transparent);
  --menu-active-fg: var(--color-primary);
}
.drawer-side .menu .menu-active {
  font-weight: 600;
}
.drawer-side .menu li > *:active:not(.menu-active) {
  background-color: transparent !important;
  color: inherit !important;
}

/* 라우트 전환 로딩 인디케이터 */
.route-progress {
  position: fixed;
  inset: 0 0 auto 0;
  height: 3px;
  z-index: 100;
  overflow: hidden;
  pointer-events: none;
}
.route-progress::after {
  content: "";
  display: block;
  height: 100%;
  width: 40%;
  background: var(--color-primary);
  animation: route-progress 1s ease-in-out infinite;
}
@keyframes route-progress {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(350%); }
}

/* 키보드 사용자용 스킵 링크 */
.skip-link {
  position: fixed;
  top: -100px;
  left: 8px;
  z-index: 200;
  padding: 0.5rem 1rem;
  border-radius: var(--radius-field);
  background: var(--color-primary);
  color: var(--color-primary-content);
}
.skip-link:focus {
  top: 8px;
}

/* 서버 설정 탭 — 카드를 넓은 화면에서 2열로 흘린다.
   flex 한 열(max-w-2xl)로 쌓으면 탭마다 4~5장을 세로로 훑어야 하고 오른쪽 절반이 빈다.
   grid 2열은 같은 행의 카드 높이를 맞추느라 긴 카드 옆에 빈칸이 생기므로 multi-column 을 쓴다 —
   브라우저가 카드 높이를 보고 두 열을 균형 있게 나눈다(HTML 순서는 위→아래, 왼쪽 열→오른쪽 열).
   카드는 열 사이에서 잘리지 않게 break-inside 를 막고, gap 대신 아래 여백으로 간격을 준다
   (multi-column 은 flex gap 이 안 먹는다). 폭 상한은 두 열이 각각 읽기 좋은 너비(≈40rem)가 되는 선. */
.settings-grid {
  max-width: 84rem;
}
.settings-grid > * {
  break-inside: avoid;
  margin-bottom: 1.5rem;
}
@media (min-width: 1024px) {
  .settings-grid {
    columns: 2;
    column-gap: 1.5rem;
  }
  /* 첫 카드 위 여백이 열 첫머리에서 어긋나지 않게 — 카드 자체의 상단 여백은 0 */
  .settings-grid > * {
    margin-top: 0;
  }
}

/* 라이브 채널 표의 드래그 정렬 — 끼워 넣을 자리 표시선.
   border 가 아니라 box-shadow(inset)라 행 높이가 변하지 않아 표 전체 레이아웃이 다시 계산되지 않고,
   Alpine 상태가 아니라 대상 행 하나에 DOM 으로 직접 붙여(pages.js onDragOver) 다른 행이 다시
   그려지지 않는다 — 드래그 중 매 이동마다 가벼워야 한다(실사용 "선 따라오는 게 느려"). */
tr.drop-before > td { box-shadow: inset 0 2px 0 0 var(--color-primary); }
tr.drop-after > td { box-shadow: inset 0 -2px 0 0 var(--color-primary); }

/* 로그인 카드 상단 브랜드 띠.
   Tailwind 의 그라데이션 유틸리티(bg-gradient-to-r, from-, via-, to-)는 브라우저 CDN
   빌드(@tailwindcss/browser@4)에서 생성되지 않아 background-image 가 none 으로 남는다 —
   띠가 아예 안 그려지고 있었다. 그래서 테마 토큰으로 직접 만든다(색은 themes.css 만 참조). */
.brand-band {
  block-size: 3px;
  background: linear-gradient(
    90deg,
    var(--color-primary),
    var(--color-secondary),
    var(--color-accent)
  );
}

/* ── 라이브 > 편성표 탭의 편성 격자(채널 × 시간) ──
   왜 표(table)가 아닌가: 편성 블록의 폭이 **길이(분)** 를 나타내야 하는데, 표의 칸은 내용에 따라
   폭이 정해진다. 그래서 채널마다 한 줄(.guide-row)을 두고 그 안의 시간축(.guide-track)에 블록을
   절대 배치한다 — 좌표는 pages.js 의 liveGuide 가 분 → px 로 미리 계산해 인라인 style 로 준다
   (행 안에서 계산하면 채널 200줄 × 편성 10건마다 다시 계산된다 — alpine 큰 표 성능 규칙).

   가로·세로 스크롤은 컨테이너(.guide) 하나가 맡고, 채널 칸은 왼쪽에·시간 눈금은 위에 sticky 로
   붙는다. 겹침 순서: 현재 시각 선(5) < 채널 칸(10) < 눈금 줄(20) < 눈금 줄의 채널 칸(30) —
   선이 채널 칸이나 눈금을 가로지르지 않아야 격자가 읽힌다. */
.guide {
  overflow: auto;
  max-block-size: min(70vh, 42rem);
  border: 1px solid var(--color-base-300);
  border-radius: var(--radius-box);
  background-color: var(--color-base-100);
}
/* max-content: 시간축 전체 폭을 잡아 가로 스크롤이 생기게 한다(부모가 자르지 않는다) */
.guide-grid {
  position: relative;
  inline-size: max-content;
  min-inline-size: 100%;
}
.guide-row {
  display: flex;
  align-items: stretch;
  background-color: var(--color-base-100);
  border-block-end: 1px solid var(--color-base-200);
}
.guide-row:nth-child(even) {
  background-color: color-mix(in oklab, var(--color-base-content) 3%, var(--color-base-100));
}
/* 채널 칸 — 배경이 불투명해야 시간축이 뒤로 지나간다(inherit 로 줄무늬를 따라간다) */
.guide-ch {
  position: sticky;
  inset-inline-start: 0;
  z-index: 10;
  inline-size: var(--guide-ch-w, 13rem);
  flex: 0 0 var(--guide-ch-w, 13rem);
  background-color: inherit;
  border-inline-end: 1px solid var(--color-base-300);
  padding: 0.375rem 0.5rem;
  overflow: hidden;
}
/* 시간축 — 30분마다 세로 눈금선을 배경으로 그린다(선마다 DOM 을 두면 창 6시간에 12개 × 채널 수다).
   --guide-slot 은 30분의 px 폭으로, liveGuide 가 .guide-grid 에 인라인으로 심는다. */
.guide-track {
  position: relative;
  flex: 1 0 auto;
  inline-size: var(--guide-w, 0);
  block-size: 2.75rem;
  background-image: repeating-linear-gradient(
    to right,
    var(--color-base-200) 0 1px,
    transparent 1px var(--guide-slot, 120px)
  );
}
.guide-head {
  position: sticky;
  inset-block-start: 0;
  z-index: 20;
  background-color: var(--color-base-100);
  border-block-end: 1px solid var(--color-base-300);
}
.guide-head .guide-ch {
  z-index: 30;
}
.guide-head .guide-track {
  block-size: 2.25rem;
}
/* 눈금 글자 — 선 바로 오른쪽에 붙인다(선이 곧 그 시각의 시작이다) */
.guide-tick {
  position: absolute;
  inset-block-start: 0.5rem;
  padding-inline-start: 0.375rem;
  font-size: 0.6875rem;
  line-height: 1;
  white-space: nowrap;
  color: color-mix(in oklab, var(--color-base-content) 55%, transparent);
}
.guide-tick-hour {
  font-weight: 600;
  color: var(--color-base-content);
}
/* 편성 블록 — 폭이 곧 길이다. 짧은 편성(5분)도 눌러 볼 수 있게 최소 폭은 pages.js 가 보장한다. */
.guide-block {
  position: absolute;
  inset-block: 0.25rem;
  overflow: hidden;
  border: 1px solid var(--color-base-300);
  border-radius: var(--radius-field, 0.375rem);
  background-color: color-mix(in oklab, var(--color-base-content) 7%, var(--color-base-100));
  padding: 0.25rem 0.375rem;
  text-align: start;
  line-height: 1.2;
  cursor: pointer;
}
.guide-block:hover {
  border-color: var(--color-primary);
}
/* 지금 방송 중 — 격자에서 가장 먼저 찾는 것이라 색과 굵기를 함께 준다(색만으로 말하지 않는다) */
.guide-block-live {
  background-color: color-mix(in oklab, var(--color-primary) 16%, var(--color-base-100));
  border-color: var(--color-primary);
  font-weight: 600;
}
/* 이미 끝난 편성 — 지우지 않고 흐리게 둔다. 창 왼쪽이 비면 "편성이 없다"로 오해된다. */
.guide-block-past {
  opacity: 0.5;
}
.guide-block-title {
  font-size: 0.75rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.guide-block-time {
  font-size: 0.625rem;
  white-space: nowrap;
  color: color-mix(in oklab, var(--color-base-content) 55%, transparent);
}
/* 현재 시각 선 — 격자 전체를 세로로 가른다. 채널 칸·눈금 줄보다 낮게 깔아 그 아래로 지나간다. */
.guide-now {
  position: absolute;
  inset-block: 0;
  z-index: 5;
  inline-size: 2px;
  background-color: var(--color-error);
  pointer-events: none;
}

/* ── 표 행의 스캔 메뉴 (라이브러리 > 섹션의 ⋯) ──
   표 안의 메뉴는 popover 로 띄운다 — 보통의 dropdown 은 표를 감싼 overflow-x-auto 에 잘린다
   (overflow-x 가 visible 이 아니면 overflow-y 도 잘린다). popover 는 top layer 라 잘리지 않는다.
   다만 daisyUI 의 .dropdown 은 anchor 로 붙이기만 하고 넘침은 다루지 않아, 표 아래쪽 행에서는
   메뉴 아래가 뷰포트 밖으로 나갔다(2026-09-06 실측) — 공간이 모자라면 위로 뒤집는다.
   ⚠ anchor positioning 을 모르는 브라우저(Firefox)에서는 메뉴가 화면 가운데에 뜬다(기능은 동작). */
[popover].dropdown {
  position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
  max-block-size: min(70vh, 32rem);
  overflow-y: auto;
}

/* 스캔 화면(라이브러리 > 섹션 > 스캔, 페이지) — 표 머리글이 창 스크롤에 붙는데 상단 바(.navbar, 4rem, sticky)가
   위를 덮으므로 그만큼 내린다. 모달일 때는 상자 안에서 붙어 0 이면 됐다(2026-09-09 페이지 전환). */
.scan-page .table-pin-rows thead tr {
  top: 4rem;
}

