/* =====================================================================
   동원산업 선박 모니터링 — 공통 스타일
   모던 다크 테마(포인트색: 동원 블루). 회사 UI 가이드에서는 서체(Pretendard),
   위젯 단위 구성, 표 행 높이 40, 버튼 높이 40만 따른다.
   색·모서리·그림자는 아래 변수로만 쓴다(테마를 바꿀 때 이 블록만 바꾼다).
   ===================================================================== */

/* ---------- 글꼴: Pretendard (SIL OFL, Content/fonts/Pretendard-LICENSE.txt) ---------- */
@font-face {
    font-family: "Pretendard";
    font-weight: 400;
    font-style: normal;
    font-display: swap;
    src: url("fonts/Pretendard-Regular.woff2") format("woff2");
}

@font-face {
    font-family: "Pretendard";
    font-weight: 500;
    font-style: normal;
    font-display: swap;
    src: url("fonts/Pretendard-Medium.woff2") format("woff2");
}

@font-face {
    font-family: "Pretendard";
    font-weight: 700;
    font-style: normal;
    font-display: swap;
    src: url("fonts/Pretendard-Bold.woff2") format("woff2");
}

/* ---------- 테마 토큰 (모던 다크) ---------- */
:root {
    /* 면: 페이지 배경 → 위젯 → 위젯 안 상자(버튼·입력·블록) 순으로 한 단계씩 밝아진다 */
    --color-bg: #0b1220;
    --color-surface: #121b2d;
    --color-button-box: #1b2639;
    --color-button-box-hover: #233049;
    --color-popup: #162034;
    --color-hover-surface: rgba(255, 255, 255, 0.04);

    /* 글자: 본문은 위젯 면 대비 13:1 이상, 보조는 6:1 이상(명암비 계산값은 설계 기록에 둔다) */
    --color-title: #f1f5f9;
    --color-text: #e2e8f0;
    --color-text-sub: #94a3b8;
    --color-table-text: #d6dde8;
    --color-popup-text: #f1f5f9;

    /* 경계선: 굵은 테두리 대신 흰색을 옅게 깔아 면과 면을 구분한다. 입력칸은 클릭 대상이라 조금 더 진하게 */
    --color-border-subtle: rgba(255, 255, 255, 0.06);
    --color-divider: rgba(255, 255, 255, 0.08);
    --color-input-border: rgba(255, 255, 255, 0.12);
    --color-scrollbar: rgba(255, 255, 255, 0.18);

    /* 동원 블루(파비콘 #0065bd). 어두운 면 위의 글자·선은 #0065bd가 묻혀서(대비 2:1 안팎) 밝은 톤을 쓰고, 채움에만 #0065bd를 쓴다 */
    --color-point: #4da3ff;
    --color-point-fill: #0065bd;
    --color-point-fill-hover: #0a74d1;
    --color-point-soft: rgba(77, 163, 255, 0.12);
    --color-focus-ring: rgba(77, 163, 255, 0.35);

    --color-nav-active: var(--color-point);
    --color-nav-active-bg: var(--color-point-soft);
    --color-nav-inactive: #aeb9c9;
    --color-button-active-bg: var(--color-point-fill);
    --color-button-active-hover: var(--color-point-fill-hover);
    --color-button-active-text: #ffffff;
    --color-button-border: var(--color-border-subtle);

    /* 숫자 아님("--", 기관)·태그 없음 표시용 회색. 위젯 안 상자(#1b2639) 위에서도 4:1 이상 */
    --color-muted: #7c879a;

    /* 상태색: 채도를 낮춘 톤으로 통일한다(오류·알람 / 정상·운전 / 경고).
       기관 화면에서 정상은 운전·ON 램프, 경고는 게이지·바의 알람 설정치 마커에 쓴다 */
    --color-error: #ef6f7a;
    --color-success: #4fbf8b;
    --color-warning: #e9a25b;
    /* 정상색을 옅게 깐 면. point-soft와 같은 방식·같은 투명도(0.12) */
    --color-success-soft: rgba(79, 191, 139, 0.12);

    /* 이력 차트 선 색(fishhold.js가 읽는다). 두 계열이 겹쳐도 구분되도록 블루·주황 */
    --color-chart-1: #4da3ff;
    --color-chart-2: #f2a65a;

    /* 모달 뒤 배경. 페이지 배경(#0b1220)을 70% 불투명으로 덮어 뒤 화면을 가린다 */
    --color-modal-backdrop: rgba(11, 18, 32, 0.7);

    /* 평면도·장비 그림의 선색. 어두운 배경에서 보이면서 값보다 튀지 않게 회색(muted)을 쓴다 */
    --color-drawing-line: var(--color-muted);

    /* 어창 콘솔(EawLog)의 주황 주요 측정값 색(용도 별칭). 콘솔과 같은 색으로 보이도록 주황(경고색과 같은 값)을 가리킨다 */
    --color-console-primary: var(--color-warning);

    /* 모서리: 위젯·모달은 크게, 버튼·입력은 중간, 위젯 안 작은 상자는 작게 */
    --radius-widget: 16px;
    --radius-control: 10px;
    --radius-small: 8px;
    /* 램프(14px)·탱크 칸(높이 20)처럼 아주 작은 상자. small(8)이면 원에 가까워져 네모로 보이지 않는다 */
    --radius-tiny: 4px;
    --radius-pill: 999px;

    /* 그림자: 테두리 대신 면을 띄워 구분한다. 배경이 어두워 그림자도 검정을 진하게 둔다 */
    --shadow-widget: 0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 24px rgba(0, 0, 0, 0.24);
    --shadow-modal: 0 24px 64px rgba(0, 0, 0, 0.5);
    --shadow-focus: 0 0 0 3px var(--color-focus-ring);

    /* 호버·탭 전환. 값 갱신(자동 새로고침) 때는 상태가 바뀌지 않아 전환이 일어나지 않는다 */
    --transition-fast: 0.15s ease;

    /* 어창 평면도 최소 폭. 23칸 안에 수치 2줄이 읽히는 폭이고, 이보다 좁으면 가로 스크롤 */
    --hull-plan-min-width: 1000px;

    /* 어창 상세 블록 줄의 "S (우현)" 라벨 폭과 블록 사이 간격(6–1 구역 블록 5개를 한 줄에 두려고 위젯 간격 20보다 좁게).
       가운데 평면도 줄은 이 둘을 더한 만큼 들여 블록과 맞춘다 */
    --detail-side-width: 80px;
    --detail-block-gap: 8px;
    /* 4칸 구역(7–10)의 블록 한 칸 폭. 블록 줄 안에서 쓰므로 %는 블록 줄 폭 기준이다
       (줄 = 좌/우현 라벨 + 간격 4개 + 블록 4개) */
    --detail-narrow-block-width: calc((100% - var(--detail-side-width) - 4 * var(--detail-block-gap)) / 4);

    /* 어창 화면 표 행 높이. 가이드(40) 대신 줄인다: 1920×1080 크롬 화면 영역(1920×950)에 세 탭이
       세로 스크롤 없이 들어가야 하고, 어창 상세는 7행 표가 위·가운데·아래 3단이라 40이면 넘친다.
       세로 예산: 950 - 제목 줄(88 + 20) - 아래 여백 30 - 위젯 테두리·여백 52 - 위젯 제목 줄(40 + 12) - 범례(12 + 21)
                  = 탭 안 내용 약 675px. 어창 상세 3단(7행 × 3 + 간격·여백 20)이 30이면 650px로 들어간다 */
    --console-row-height: 30px;
    /* 어창 화면 탭 안 묶음 사이 간격(제목 줄 ↔ 내용, 설명 줄 ↔ 평면도, 내용 ↔ 범례, 냉동기 위·아래 단).
       위젯 간격(20)보다 좁혀 세로 공간을 아낀다 */
    --console-section-gap: 12px;
    /* 어창 상세 가운데 평면도 높이(원래 SVG 높이 280). 6–1 구역의 1번 어창 블록(7행 × 30 = 210)과 같게 맞춰
       가운데 줄이 블록 줄보다 높아지지 않게 한다 */
    --detail-plan-height: 210px;
    /* 위 평면도의 칸 번호 글자(viewBox 단위). 평면도를 210/280로 줄인 만큼 키워 화면에서 원래처럼 약 16px로 보이게 한다 */
    --detail-plan-label-size: 21px;
    /* 냉동기 장비 그림 높이(원래 SVG 140). 값 표 6행(lc)과 그림이 위 단 한 칸에 들어가도록 줄인다. 폭은 비율대로 따라간다.
       두 단 = 그림 2개 + 표 9행 + 이름·여백이라 탭 안 675px에 들어가는 값(129 + 2 × 116 + 9 × 30 = 631) */
    --plant-figure-height: 116px;

    /* 기관 화면 전용 크기. 세로 예산은 아래 "기관 화면" 섹션 머리 주석에 있다(탭 안 내용 약 656px) */
    /* 표 행 높이: 어창(30)보다 줄인다. AUX 2단 23행(머리글 포함 24 × 26 = 624)이 탭 안에 들어가는 값 */
    --engine-row-height: 26px;
    /* 원형 게이지: 작은 게이지 원판 폭과 이름까지 포함한 칸 폭. GRAPHS·GEAR BOX 4줄(줄당 약 147)이 들어가는 크기 */
    --engine-gauge-small-width: 128px;
    --engine-gauge-cell-width: 160px;
    --engine-gauge-large-width: 270px;
    /* 세로 바 높이(SVG 원래 높이 104)와 바 한 칸 폭(바를 키운 만큼 SVG 폭 64 × 112/104이 들어가게) */
    --engine-bar-height: 112px;
    --engine-bar-width: 72px;
    /* 발전기 바 높이: 3대 × (제목 27 + 바 + 값·태그 36 + 이름 2줄 28)과 줄 사이 여백 34가 탭 안 656px에 들어가는 값
       (3 × 199 + 34 = 631). 오른쪽 램프 11개(줄 18 × 11 = 198)와 줄 높이가 맞는다 */
    --engine-generator-bar-height: 108px;
    --engine-generator-lamp-line: 18px;
    --engine-lamp-size: 14px;
    /* 탭 안 묶음 사이 세로 간격. 위젯 간격(20)보다 좁혀 세로 공간을 아낀다(어창 상세 블록 간격과 같은 값) */
    --engine-gap: 8px;
    /* 게이지·바 묶음 사이 가로 간격(GRAPHS 격자, SYSTEM 묶음, GEAR BOX 게이지 줄) */
    --engine-item-gap: 16px;
    /* 탱크 박스 폭: 사진처럼 한 줄 5칸 + 선수 쪽 여백이 콘텐츠 폭(약 1,580px) 안에 들어가는 값 */
    --engine-tank-width: 220px;
    --engine-hull-bow-space: 176px;

    /* 간격 */
    --space-page-x: 30px;
    --space-widget-gap: 20px;
    --space-widget-y: 25px;
    --space-widget-x: 30px;

    /* 네비: 로고 묶음(로고 + "동원산업 / 선박 모니터링")이 한 줄에 들어가는 폭. 항목은 네비 안쪽으로 들여 둥근 면으로 강조한다 */
    --nav-width: 220px;
    --nav-gutter: 12px;
    --nav-item-padding: 12px;
    /* 로고 영역 높이 = 본문 페이지 제목 줄(위 여백 30 + 제목)과 아래 경계를 맞춘 값 */
    --nav-brand-height: 88px;
    /* 로고 원본(dongwon-logo.png) 114×47 비율 그대로 줄인 높이. 메뉴 폭 220 안에 서비스명과 한 줄로 들어간다 */
    --nav-logo-height: 36px;
    /* 장비 화면 카드 1장(어탐기) 폭: 1920 화면에서 Radar 카드 한 장(약 779px)과 같은 크기 */
    --capture-single-width: 780px;

    /* 항해 화면: 지도(1) : 오른쪽 묶음(1.25) 비율과 위젯 안쪽 여백. 오른쪽 세 줄(GPS / 나침반 / 속도 · 바람)이
       본문 최소 높이(--page-body-min-height)에 들어가도록 위젯 여백을 기본(25 30)보다 줄인다 */
    --navigation-col-right: 1.25fr;
    --navigation-widget-pad: 20px;
    /* 항해 그림 색(용도 별칭. 상태 의미가 아니라 구분용으로 기존 색을 가리킨다):
       침로(COG)는 포인트 블루, 진풍은 주황, Beaufort 등급은 초록 */
    --color-cog: var(--color-point);
    --color-wind: var(--color-warning);
    --color-beaufort: var(--color-success);
    --color-beaufort-soft: var(--color-success-soft);
    /* 나침반 북쪽(N) 글자: 방위 기준점이 눈에 띄도록 주황. 진풍 색과 값은 같지만 용도가 달라 따로 둔다 */
    --color-compass-north: var(--color-warning);
    /* 나침반 크기: 오른쪽 가운데 위젯 높이에 맞춘 값. 격자 칸 폭과 SVG 크기가 같이 쓴다 */
    --navigation-compass-size: 300px;
    /* 항해 오프라인 지도(해안선): 바다는 페이지 배경, 육지는 위젯 안 상자 면, 해안선은 회색. navigation.js가 읽어 Canvas에 그린다 */
    --navigation-map-sea: var(--color-bg);
    --navigation-map-land: var(--color-button-box);
    --navigation-map-coast: var(--color-muted);

    /* 화면 높이에 맞추는 본문 높이: 화면 높이에서 제목 줄(88)·제목 줄 아래 간격(20)·페이지 아래 여백(30)을 뺀 값.
       1920×950 화면 영역이면 812px이고 위젯 아래 끝이 920에 온다(관리 · 항해 화면) */
    --page-body-height: calc(100vh - var(--nav-brand-height) - var(--space-widget-gap) - var(--space-page-x));
    /* 위 본문 높이의 기준값(1920×950 화면 영역 = 950 - 88 - 20 - 30). 항해 화면은 이보다 작아지지 않게 둔다(작은 창에서는 문서가 스크롤) */
    --page-body-min-height: 812px;

    /* 관리 화면 높이: 본문 높이 그대로. 사용자 수가 늘어도 문서 전체는 늘지 않고 표만 위젯 안에서 스크롤한다 */
    --admin-page-height: var(--page-body-height);
    /* 스크롤바 폭: 회사 UI 가이드 4px. 모든 스크롤 영역(관리 표 포함)이 같은 폭을 쓴다 */
    --scrollbar-size: 4px;
}

/* ---------- 기본 ---------- */
*,
*::before,
*::after {
    box-sizing: border-box;
}

html,
body {
    margin: 0;
    padding: 0;
}

body {
    font-family: "Pretendard", sans-serif;
    font-size: 15px;
    line-height: 1.5;
    color: var(--color-text);
    background: var(--color-bg);
}

button,
input,
select {
    font-family: inherit;
}

a {
    color: var(--color-point);
}

/* 스크롤바: 폭 --scrollbar-size(가이드 4px), 트랙 투명, 손잡이 둥글게 */
::-webkit-scrollbar {
    width: var(--scrollbar-size);
    height: var(--scrollbar-size);
}

::-webkit-scrollbar-track {
    background: transparent;
}

::-webkit-scrollbar-thumb {
    background: var(--color-scrollbar);
    border-radius: var(--radius-pill);
}

/* ---------- 레이아웃: 좌측 고정 네비 + 상단 헤더 + 콘텐츠 ---------- */
.app {
    display: flex;
    min-height: 100vh;
}

.side-nav {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    width: var(--nav-width);
    background: var(--color-surface);
    border-right: 1px solid var(--color-border-subtle);
    overflow-y: auto;
}

/* 로고와 서비스명을 가로 한 줄로 묶는다. 로고 그림 안에 "동원산업" 글자가 있어
   세로로 쌓으면 글자 줄이 세 겹이 되어 산만하다 */
.side-nav__brand {
    display: flex;
    align-items: center;
    gap: 12px;
    height: var(--nav-brand-height);
    /* 좌: 네비 항목 글자 시작선(들여쓰기 + 항목 padding)에 맞춘다 */
    padding: 0 var(--nav-gutter) 0 calc(var(--nav-gutter) + var(--nav-item-padding));
    margin-bottom: 16px;
    border-bottom: 1px solid var(--color-border-subtle);
    /* 첫 화면 링크(<a>)라 기본 링크 밑줄을 없앤다. 글자색은 .side-nav__brand-sub가 정한다 */
    text-decoration: none;
}

/* 로고 영역이 메뉴 폭을 꽉 채우고 .side-nav(overflow-y: auto)가 바깥을 잘라, 공통 --shadow-focus(바깥 3px)는
   아래 변만 보인다. 같은 색·굵기의 테두리를 안쪽(inset)에 그린다 */
.side-nav__brand:focus-visible {
    outline: none;
    border-radius: var(--radius-tiny);
    box-shadow: inset 0 0 0 3px var(--color-focus-ring);
}

.side-nav__logo {
    display: block;
    flex: none;
    width: auto;
    height: var(--nav-logo-height);
}

/* "동원산업 / 선박 모니터링" 두 줄. 한 줄이면 메뉴 폭 220을 넘는다 */
.side-nav__brand-sub {
    display: flex;
    flex-direction: column;
    justify-content: center;
    height: var(--nav-logo-height);
    padding-left: 12px;
    border-left: 1px solid var(--color-divider);
    font-size: 13px;
    font-weight: 700;
    line-height: 1.35;
    letter-spacing: -0.01em;
    color: var(--color-title);
    white-space: nowrap;
}

.side-nav__list {
    display: flex;
    flex-direction: column;
    gap: 4px;
    list-style: none;
    margin: 0;
    padding: 0 var(--nav-gutter);
}

.side-nav__link {
    display: block;
    padding: 10px var(--nav-item-padding);
    font-size: 15px;
    line-height: 1.3em;
    color: var(--color-nav-inactive);
    text-decoration: none;
    border-radius: var(--radius-control);
    transition: background-color var(--transition-fast), color var(--transition-fast);
}

.side-nav__link:hover {
    color: var(--color-title);
    background: var(--color-hover-surface);
}

.side-nav__link.is-active {
    font-weight: 500;
    color: var(--color-nav-active);
    background: var(--color-nav-active-bg);
}

/* 제목 줄 · 콘텐츠를 세로로 쌓고 화면 높이(100vh)를 최소 높이로 둔다. 콘텐츠가 남는 높이를 받아
   화면을 채우는 위젯(.widget--fill)이 화면 아래 여백까지 늘어난다. 내용이 더 길면 그대로 늘어나 문서가 스크롤된다 */
.main {
    display: flex;
    flex: 1;
    flex-direction: column;
    min-width: 0;
    min-height: 100vh;
    margin-left: var(--nav-width);
    padding: 0 var(--space-page-x) var(--space-page-x);
}

.content {
    display: flex;
    flex: 1 0 auto;
    flex-direction: column;
}

/* 화면 감싸개(어창 #fishhold-page, 기관 #engine-page): 콘텐츠의 남는 높이를 받아 안쪽 위젯에 넘긴다 */
.page-fill {
    display: flex;
    flex: 1 0 auto;
    flex-direction: column;
}

/* 제목 줄 높이를 좌측 로고 영역과 같게 두고 그 안에서 세로 가운데 정렬한다(두 영역의 아래 경계가 맞는다).
   화면별 도구가 오류 문구로 두 줄이 되면 늘어나도록 고정 높이가 아니라 최소 높이로 둔다 */
.page-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    min-height: var(--nav-brand-height);
    margin-bottom: var(--space-widget-gap);
}

/* Pretendard SemiBold(600) 파일이 없어 600을 지정해도 Bold로 그려진다. 굵기 대신 크기를 줄여 부드럽게 한다 */
.page-title {
    margin: 0;
    font-size: 22px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--color-title);
}

/* 줄바꿈하지 않는다: 줄바꿈을 허용하면 창이 좁을 때(1366px 안팎) 왼쪽 제목 줄(선박 선택·기록 시각)에 밀려 "로그아웃" 글자가
   여러 줄이 되고, 웹 글꼴이 늦게 도착하면 그 줄 수가 바뀌어 헤더 높이와 그 아래 본문 전체가 움직인다(레이아웃 흔들림) */
.page-header__right {
    display: flex;
    align-items: center;
    gap: 20px;
    font-size: 14px;
    color: var(--color-text-sub);
    white-space: nowrap;
}

.breadcrumb {
    display: flex;
    margin: 0;
    padding: 0;
    list-style: none;
}

.breadcrumb li + li::before {
    content: "/";
    padding: 0 8px;
    color: var(--color-muted);
}

.breadcrumb li:last-child {
    color: var(--color-point);
}

.page-header__logout {
    margin: 0;
}

/* 사용자 이름 옆 "비밀번호 변경". 버튼(로그아웃)보다 약하게 보이도록 보조 글자색 링크로 둔다 */
.page-header__link {
    color: var(--color-text-sub);
    text-decoration: none;
    transition: color var(--transition-fast);
}

.page-header__link:hover {
    color: var(--color-point);
}

.page-header__link:focus-visible {
    outline: none;
    border-radius: var(--radius-tiny);
    box-shadow: var(--shadow-focus);
}

/* ---------- 위젯(카드) ---------- */
.widget {
    margin-bottom: var(--space-widget-gap);
    padding: var(--space-widget-y) var(--space-widget-x);
    background: var(--color-surface);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-widget);
    box-shadow: var(--shadow-widget);
}

/* 화면 아래까지 채우는 메인 위젯. 아래 여백은 페이지 아래 여백(30)과 겹쳐 세로 공간만 쓰므로 두지 않는다.
   안쪽을 세로 flex로 두어 탭 패널이 남는 높이를 받는다 */
.widget--fill {
    display: flex;
    flex: 1 0 auto;
    flex-direction: column;
    margin-bottom: 0;
}

.widget__title {
    margin: 0 0 20px;
    font-size: 18px;
    font-weight: 700;
    color: var(--color-title);
}

.widget__note {
    margin-left: 10px;
    font-size: 14px;
    font-weight: 400;
    color: var(--color-text-sub);
}

/* ---------- 버튼: 높이 40, 14px ---------- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 40px;
    padding: 8px 18px;
    font-size: 14px;
    font-weight: 500;
    text-align: center;
    text-decoration: none;
    border: 1px solid var(--color-button-border);
    border-radius: var(--radius-control);
    cursor: pointer;
    transition: background-color var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}

.btn:focus-visible {
    outline: none;
    box-shadow: var(--shadow-focus);
}

.btn--primary {
    color: var(--color-button-active-text);
    background: var(--color-button-active-bg);
    border-color: transparent;
}

.btn--primary:hover:not(:disabled) {
    background: var(--color-button-active-hover);
}

.btn--outline {
    color: var(--color-text);
    background: var(--color-button-box);
}

.btn--outline:hover:not(:disabled) {
    background: var(--color-button-box-hover);
}

.btn--outline.is-selected,
.btn--outline.is-selected:hover:not(:disabled) {
    color: var(--color-button-active-text);
    background: var(--color-button-active-bg);
    border-color: transparent;
}

.btn:disabled {
    opacity: 0.5; /* 비활성 버튼 색 토큰을 따로 두지 않고 반투명으로 구분한다 */
    cursor: default;
}

/* ---------- 폼: 라벨 14px, 입력 기본 폭 365 ---------- */
.form-field {
    margin-bottom: 20px;
}

.form-field__label {
    display: block;
    margin-bottom: 8px;
    font-size: 14px;
    color: var(--color-text-sub);
}

.input {
    width: 365px;
    max-width: 100%;
    height: 40px;
    padding: 0 12px;
    font-size: 15px;
    color: var(--color-text);
    background: var(--color-button-box);
    border: 1px solid var(--color-input-border);
    border-radius: var(--radius-control);
    /* 어두운 배경에서 select 펼침 목록·달력 아이콘이 밝은 기본 테마로 뜨지 않게 한다 */
    color-scheme: dark;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.input:focus {
    outline: none;
    border-color: var(--color-point);
    box-shadow: var(--shadow-focus);
}

.input--select {
    /* 상단 도구줄에서 선박명 길이만큼만 차지하게 한다(기본 365는 도구줄 한 줄 배치를 넘김). 최소 폭은 "다누리호 (HK2401)"가 들어가는 값 */
    width: auto;
    min-width: 200px;
}

.input--datetime {
    /* 모달 한 줄에 프리셋·시작~종료·조회를 함께 두려고 365보다 줄였다. "yyyy-mm-dd hh:mm" + 달력 아이콘이 잘리지 않는 폭 */
    width: 230px;
}

.form-error {
    margin: 0 0 20px;
    font-size: 14px;
    color: var(--color-error);
}

/* ---------- 표: 행 높이 40, 14px ---------- */
.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
    color: var(--color-table-text);
}

.data-table th,
.data-table td {
    height: 40px;
    padding: 0 12px;
    text-align: right;
    white-space: nowrap;
    border-bottom: 1px solid var(--color-divider);
}

.data-table th {
    font-weight: 500;
    color: var(--color-text-sub);
}

.data-table tbody tr {
    transition: background-color var(--transition-fast);
}

.data-table tbody tr:hover {
    background: var(--color-hover-surface);
}

/* 어창 화면의 표 행은 아직 클릭 동작이 없어, 위 공통 행 호버 강조를 #fishhold-page 특이도로 덮어 끈다.
   행에 클릭 동작을 붙일 때 이 블록을 지우면 공통 호버가 되살아난다 */
#fishhold-page .data-table tbody tr:hover {
    background: transparent;
}

.data-table th:first-child,
.data-table td:first-child {
    text-align: left;
}

/* 첫 열 외의 글자 열(태그명 등)도 왼쪽 정렬 */
.data-table th.data-table__text,
.data-table td.data-table__text {
    text-align: left;
}

/* ---------- 값 표시 상태 ---------- */
.value--invalid {
    color: var(--color-muted);
    cursor: help;
}

.value--missing {
    color: var(--color-muted);
}

/* ---------- 로그인 ---------- */
.login-page {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 28px;
    min-height: 100vh;
}

.login-page__logo {
    display: block;
    width: 114px;
    height: auto;
}

.login-box {
    width: calc(365px + 2 * var(--space-widget-x));
    margin: 0;
    padding: var(--space-widget-x);
}

.login-box__title {
    margin: 0 0 28px;
    font-size: 20px;
    font-weight: 700;
    text-align: center;
    color: var(--color-title);
}

.login-box__actions {
    display: flex;
    justify-content: center;
}

/* ---------- 오류 화면 ---------- */
.error-page__text {
    margin: 0;
    color: var(--color-text-sub);
}

/* ---------- 어창: 상단 도구줄 ---------- */
.toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 20px 40px;
}

/* 제목 줄 안에 둔 도구줄: 제목과 오른쪽 묶음(위치·사용자·로그아웃) 사이 남는 폭을 모두 쓴다.
   position: relative는 장비 화면 장애 문구(.toolbar__status--below)의 위치 기준이다 */
.toolbar--header {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
}

.toolbar__item {
    display: flex;
    align-items: center;
    gap: 12px;
}

.toolbar__label {
    font-size: 16px;
    color: var(--color-text-sub);
}

.toolbar__time {
    font-size: 16px;
    font-weight: 500;
    color: var(--color-point);
}

.toolbar__status {
    flex-basis: 100%;
    margin: 0;
    font-size: 14px;
    color: var(--color-error);
}

.toolbar__status:empty {
    display: none;
}

/* ---------- 어창: 콘솔형 탭 (전체 개요 / 어창 상세 / 냉동기) ---------- */
.console-tabs,
.console-zones {
    display: flex;
    flex-wrap: wrap;
    gap: 8px; /* 기간 프리셋 버튼 묶음과 같은 간격 */
    margin-bottom: 20px;
}

/* 화면 구분 탭과 구역 버튼은 알약 모양으로 둬 일반 버튼(모서리 10px)과 구분한다 */
.console-tabs .btn,
.console-zones .btn {
    border-radius: var(--radius-pill);
}

/* 위젯 제목과 화면 구분 탭을 한 줄에 둔다(제목 아래 탭 줄을 따로 두면 세 탭이 한 화면에 들어가지 않는다) */
.console-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 24px; /* 가이드에 없는 값: 범례 항목 간격(가로 24, 줄바꿈 시 세로 8)과 같다 */
    margin-bottom: var(--console-section-gap);
}

.console-head .widget__title,
.console-head .console-tabs {
    margin: 0;
}

/* 탭 패널은 위젯(.widget--fill)의 남는 높이를 받아 범례를 위젯 아래 끝으로 민다 */
.console-panel {
    flex: 1 0 auto;
}

/* 전체 개요(평면도 폭에 높이가 묶여 남는 높이가 크다)·냉동기: 내용 묶음을 늘어난 패널의 세로 가운데에 둔다 */
.console-panel--center {
    display: flex;
    flex-direction: column;
    justify-content: center;
}

/* 위 display:flex가 hidden 속성의 기본 display:none을 덮으므로 탭 전환 때 숨김을 되살린다 */
.console-panel--center[hidden] {
    display: none;
}

.console-panel__note {
    margin: 0;
    font-size: 14px;
    color: var(--color-text-sub);
}

/* 어창 상세: 구역 버튼을 내용 왼쪽에 세로로 세워 구역 버튼 줄 높이만큼 세로 공간을 아낀다 */
.console-panel--detail {
    display: flex;
    align-items: flex-start;
    gap: var(--detail-block-gap);
}

/* 위 display:flex가 hidden 속성의 기본 display:none을 덮으므로 탭 전환 때 숨김을 되살린다 */
.console-panel--detail[hidden] {
    display: none;
}

.console-panel--detail .console-zones {
    flex: none;
    flex-direction: column;
    margin-bottom: 0;
}

.console-panel--detail .console-scroll {
    flex: 1 1 auto;
    min-width: 0; /* flex 항목이 내용 폭만큼 늘어나지 않고 좁은 화면에서 안쪽 가로 스크롤이 생기게 한다 */
}

/* 전체 개요: 설명(왼쪽)과 브라인쿨러 out 표(오른쪽 끝, 선수 쪽)를 평면도 위 한 줄에 둔다 */
.overview-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-widget-gap);
    margin-bottom: var(--console-section-gap);
}

/* 좁은 화면에서는 위젯 안에서 가로 스크롤(스크롤바 간격 4px) */
.console-scroll {
    overflow-x: auto;
    padding-bottom: 4px;
}

/* 콘솔의 주황(주요 측정값)/흰색(보조값) 구분을 그대로 쓴다: 주요값은 콘솔 주황, 보조값은 기본색 */
.console-value--primary {
    color: var(--color-console-primary);
}

/* 위 규칙이 뒤에 있어 .value--missing의 회색을 덮으므로, 태그 없음은 회색으로 되돌린다 */
.console-value--primary.value--missing {
    color: var(--color-muted);
}

/* ---------- 어창: 선체 평면도 (SVG) ---------- */
.hull-plan {
    min-width: var(--hull-plan-min-width);
}

.hull-plan__svg {
    display: block;
    width: 100%;
    height: auto;
}

/* 상세 구역 평면도는 해당 칸만 그려 폭이 작으므로 늘리지 않고 원래 크기로 둔다 */
.hull-plan--zone {
    display: flex;
    flex: 1 1 auto;
    justify-content: center;
    min-width: 0;
}

/* 높이만 정하고 폭은 SVG 비율대로 따라가게 둔다 */
.hull-plan--zone .hull-plan__svg {
    width: auto;
    max-width: 100%;
    height: var(--detail-plan-height);
}

.hull-plan--zone .hull-plan__label {
    font-size: var(--detail-plan-label-size);
}

.hull-plan__shape {
    fill: var(--color-button-box);
    stroke: var(--color-drawing-line);
    stroke-width: 1.5;
    transition: stroke var(--transition-fast), fill var(--transition-fast);
}

/* 값이 있는 칸(is-clickable, fishhold.js가 붙인다)은 칸 모양 전체가 클릭 영역이다 */
.hull-plan__cell.is-clickable {
    cursor: pointer;
}

.hull-plan__cell.is-clickable:hover .hull-plan__shape {
    fill: var(--color-button-box-hover);
    stroke: var(--color-point);
    stroke-width: 2;
}

/* 포커스는 칸 테두리로 보인다. :has 미지원 브라우저에서는 이 블록 전체가 빠져 버튼 기본 outline이 남는다 */
@supports selector(:has(*)) {
    .hull-plan__cell:has(.hull-cell:focus-visible) .hull-plan__shape {
        stroke: var(--color-point);
        stroke-width: 2;
    }

    .hull-cell:focus-visible {
        outline: none;
    }
}

/* 콘솔 평면도의 빨간 중심선을 테마 포인트색 점선으로 옮긴다 */
.hull-plan__centerline {
    stroke: var(--color-point);
    stroke-width: 1;
    stroke-dasharray: 8 4;
}

.hull-plan__side {
    font-size: 16px;
    font-weight: 700;
    fill: var(--color-point);
}

.hull-plan__label {
    font-size: 16px;
    font-weight: 700;
    fill: var(--color-text);
}

/* 개요 칸 안 버튼(foreignObject). 글자 크기는 viewBox 단위라 평면도 폭에 따라 함께 커진다 */
.hull-cell {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: center;
    width: 100%;
    height: 100%;
    /* 가이드에 없는 값(viewBox 단위): 가장 좁은 칸(2번, 높이 약 61)에 번호 + 수치 2줄이 들어가도록 위아래 2, 칸 선과 글자가 붙지 않게 좌우 6 */
    padding: 2px 6px;
    line-height: 1.1;
    color: var(--color-text);
    background: transparent;
    border: 0;
    cursor: pointer;
}

.hull-cell:disabled {
    cursor: default;
}

/* 가이드에 없는 값(viewBox 단위): 1920 화면에서 평면도가 콘텐츠 폭(약 1,580px)으로 늘어나면 1.4배쯤 커져 약 17px로 보인다 */
.hull-cell__title {
    align-self: flex-start;
    font-size: 12px;
    font-weight: 700;
    color: var(--color-text-sub);
}

/* 가이드에 없는 값(viewBox 단위): 칸 폭 74 안에 "-45.1"이 들어가는 크기. 화면에서는 약 24px로 보인다 */
.hull-cell__value {
    font-size: 17px;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
}

/* ---------- 어창: 전체 개요 브라인쿨러 out 짝 ---------- */
/* 콘솔 _10처럼 선수 쪽(오른쪽) 위에 둔다. 소제목을 표 왼쪽에 붙여 세로 공간을 줄인다 */
.brine-pairs {
    display: flex;
    flex: none;
    align-items: center;
    gap: 12px; /* 가이드에 없는 값: 소제목과 표 사이(도구줄 라벨과 값 사이 12와 같게) */
}

.brine-pairs__title {
    margin: 0;
    font-size: 15px;
    font-weight: 500;
    color: var(--color-title);
}

.brine-pairs__table {
    width: auto;
}

.brine-pairs__table th,
.brine-pairs__table td {
    height: var(--console-row-height);
}

.brine-pairs__table th[scope="row"] {
    font-weight: 400;
    text-align: left;
    color: var(--color-text-sub);
}

/* 칸 안 장비 번호: 값보다 작고 흐리게 두어 값이 먼저 읽히게 한다 */
.brine-pairs__unit {
    margin-right: 8px;
    font-size: 13px;
    color: var(--color-text-sub);
}

/* ---------- 어창: 상세 구역 ---------- */
.detail-zone {
    display: flex;
    flex-direction: column;
    gap: var(--detail-block-gap); /* 좌현 블록 줄 · 평면도 · 우현 블록 줄 사이. 3단이 한 화면에 들어가도록 블록 사이 간격과 같게 좁힌다 */
}

.detail-zone[hidden] {
    display: none;
}

.detail-zone__row {
    display: flex;
    align-items: flex-start;
    gap: var(--detail-block-gap);
}

.detail-zone__side {
    flex: 0 0 var(--detail-side-width);
    /* 블록 제목 줄 글자 위치에 맞춘다: (표 행 높이 - 글자 줄 높이 1.5em) / 2 */
    padding-top: calc((var(--console-row-height) - 1.5em) / 2);
    font-size: 14px;
    color: var(--color-text-sub);
}

/* 어창 2개 구역(11–12): 블록을 4칸 구역(7–10)의 한 칸 폭으로 고정하고, 한 칸 들여
   9·8번 블록 자리(가운데 두 칸)에 놓는다. 늘리면 표가 옆으로 너무 길어진다 */
.detail-zone--narrow .hold-block {
    flex: 0 0 var(--detail-narrow-block-width);
}

.detail-zone--narrow .detail-zone__side + .hold-block {
    margin-left: calc(var(--detail-narrow-block-width) + var(--detail-block-gap));
}

.detail-zone__middle {
    display: flex;
    align-items: center;
    gap: var(--space-widget-gap);
    /* 위·아래 블록 줄의 라벨 폭 + 간격만큼 들여 블록과 맞춘다 */
    padding-left: calc(var(--detail-side-width) + var(--detail-block-gap));
}

.hold-block {
    flex: 1 1 0;
    min-width: 150px; /* 가이드에 없는 값: "s2 akv1" 라벨과 "-45.1"이 줄바꿈 없이 들어가는 폭 */
    background: var(--color-button-box);
    /* collapse 표에는 border-radius가 적용되지 않아 separate로 바꾼다(간격 0이라 줄 모양은 같다) */
    border-collapse: separate;
    border-spacing: 0;
    border-radius: var(--radius-small);
    overflow: hidden;
}

.hold-block th,
.hold-block td {
    height: var(--console-row-height);
}

.hold-block tbody tr:last-child td {
    border-bottom: 0;
}

.hold-block--first {
    flex: 0 0 auto;
    width: auto;
}

.data-table th.hold-block__title {
    font-weight: 700;
}

/* ---------- 어창: 냉동기 ---------- */
.plant {
    display: flex;
    flex-direction: column;
    gap: var(--console-section-gap); /* 위 단과 아래 단 사이 */
}

.plant__row {
    display: grid;
    grid-auto-flow: column;
    /* 가이드에 없는 값: 장비 11칸이 1080p 콘텐츠 폭에 한 줄로 들어가는 최소 폭. 더 좁으면 가로 스크롤 */
    grid-auto-columns: minmax(130px, 1fr);
    gap: 8px; /* 가이드에 없는 값: 11칸을 한 줄에 두려고 위젯 간격(20)보다 좁게. 상세 블록 간격과 같은 값 */
}

.plant-unit {
    display: flex;
    flex-direction: column;
    gap: 8px; /* 가이드에 없는 값: 그림 · 이름 · 값 표 사이 */
    /* 위아래·좌우 8: 좌우는 130px 칸에 값 표가 들어가도록, 위아래는 두 단이 한 화면에 들어가도록 좁힘(그림·이름·표 사이 간격과 같은 값) */
    padding: 8px;
    background: var(--color-button-box);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-small);
}

.plant-unit--empty {
    background: transparent;
    border-color: transparent;
}

/* 위 단은 그림을 칸 아래 끝에 붙여 두 단의 그림이 가운데에서 마주 보게 한다 */
.plant-unit--figure-below .plant-unit__figure {
    margin-top: auto;
}

.plant-unit__figure {
    display: flex;
    justify-content: center;
}

/* 높이만 정하고 폭은 SVG 비율(60×140)대로 따라가게 둔다 */
.plant-unit__figure .equipment-figure {
    width: auto;
    height: var(--plant-figure-height);
}

.plant-unit__name {
    font-size: 15px;
    font-weight: 700;
    color: var(--color-point);
}

/* 가이드 표 셀 padding(12)보다 좁힌다: 장비 11칸을 한 화면 폭에 두려고 */
.plant-unit__table th,
.plant-unit__table td {
    height: var(--console-row-height);
    padding: 0 6px;
}

.equipment-figure__line {
    fill: none;
    stroke: var(--color-drawing-line);
    stroke-width: 1.5;
}

.equipment-figure__axis {
    fill: none;
    stroke: var(--color-point);
    stroke-width: 1;
}

/* ---------- 어창: 미배치 항목 ---------- */
.unplaced {
    margin-top: 30px; /* 가이드에 없는 값: 같은 위젯 안 다른 묶음이라 위젯 간격(20)보다 넓게 둬 장비 배치와 구분한다 */
}

.unplaced__title {
    margin: 0 0 8px; /* 가이드에 없는 값: 소제목과 표 사이(브라인쿨러 소제목과 같은 값) */
    font-size: 15px;
    font-weight: 500;
    color: var(--color-title);
}

.unplaced__table-wrap {
    overflow-x: auto;
}

.data-table td .value {
    font-variant-numeric: tabular-nums;
}

/* ---------- 어창: 범례 ---------- */
.legend {
    display: flex;
    flex-wrap: wrap;
    /* 가이드에 없는 값: 범례 항목 사이(가로 24, 줄바꿈 시 세로 8). 탭 내용과의 간격은 탭 안 묶음 간격과 같다 */
    gap: 8px 24px;
    margin: var(--console-section-gap) 0 0;
    font-size: 14px;
    color: var(--color-text-sub);
}

/* ---------- 모달 (팝업) ---------- */
.modal {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-page-x);
    background: var(--color-modal-backdrop);
}

.modal[hidden] {
    display: none;
}

.modal__dialog {
    display: flex;
    flex-direction: column;
    /* 가이드에 없는 값: 이력 차트가 1주일(2,016점)을 구분해 보일 만큼 넓게, 화면보다 크지 않게 */
    width: min(1200px, 100%);
    max-height: 100%;
    padding: 30px; /* 가이드 팝업(캘린더) 내부 여백 30 */
    color: var(--color-popup-text);
    background: var(--color-popup);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-widget);
    box-shadow: var(--shadow-modal);
    overflow-y: auto;
}

.modal__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 20px;
}

.modal__title {
    margin: 0;
    font-size: 20px;
    font-weight: 700;
}

.modal__close {
    /* 크기는 padding으로 맞춘다(가이드 버튼 규칙). 24px 글자 + 위아래 7px + 테두리 = 높이 40 */
    padding: 7px 12px;
    line-height: 1;
    font-size: 24px; /* × 기호가 14px로는 너무 작다 */
    color: var(--color-text-sub);
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-control);
    cursor: pointer;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}

.modal__close:hover {
    color: var(--color-popup-text);
    background: var(--color-hover-surface);
}

.modal__close:focus-visible {
    outline: none;
    box-shadow: var(--shadow-focus);
}

.history-filter {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 20px;
    margin-bottom: 20px;
}

.history-filter__presets {
    display: flex;
    gap: 8px;
}

.history-filter__range {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
}

/* 기간 입력줄에서는 라벨을 입력칸 옆에 둔다(기본은 라벨이 입력칸 위) */
.history-filter__range .form-field__label {
    margin-bottom: 0;
}

.history-meta,
.history-message {
    margin: 0 0 10px;
    font-size: 14px;
    color: var(--color-text-sub);
}

.history-message:empty {
    display: none;
}

.history-chart {
    position: relative;
    height: 420px; /* 가이드에 없는 값: Chart.js는 부모 높이를 따르므로 고정 높이가 필요하다. 모달이 1080p 화면에 스크롤 없이 들어가는 값 */
}

/* =====================================================================
   기관 화면 (Views/Machinery/*, Scripts/engine.js)
   선박 에머슨 AMS 화면과 같은 배치를 1920×950 화면 영역에 세로 스크롤 없이 넣는다.
   세로 예산: 화면 950 - 제목 줄(88 + 20) - 아래 여백 30 - 화면 메뉴 줄(40 + 12) - 위젯 테두리·여백 52
              - 위젯 제목 줄(40 + 12) = 탭 안 내용 약 656px. 크기 값(:root --engine-*)은 이 안에 들어가게 정했고,
              남는 높이는 패널이 받아 묶음 사이에 고르게 나눈다(위젯은 화면 아래 여백까지 늘어난다).
   색은 :root 테마 변수만 쓴다(의미 색은 알람 빨강 --color-error, 운전 초록 --color-success, 설정치 마커 --color-warning).
   ===================================================================== */

/* ---------- 기관: 제목 줄 도구(선박 · 기록 시각) + 상시 값 줄 ---------- */
/* 기록 시각이 "UTC · KST" 두 시각이라 길어 가로 간격을 어창 도구줄보다 줄인다 */
.toolbar--engine {
    gap: 8px 24px;
}

/* 상시 값 3개(ENGINE SPEED 등): 제목 줄 아래, 화면 메뉴 위 한 줄 */
.engine-readings {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 32px; /* 가이드에 없는 값: 값 묶음끼리는 라벨-값 간격(8)보다 확실히 떨어져 보이게 */
    margin-bottom: var(--console-section-gap);
}

/* 값 자리는 서버가 빈 칸으로 내려 보내고 engine.js가 첫 그리기 뒤에 채운다. 빈 칸일 때도 채운 뒤와 같은 높이(값 16px × 줄 높이 1.5)를
   잡아 두어, 값이 들어오는 순간 이 줄과 그 아래 화면 메뉴·본문이 아래로 밀리지 않게 한다 */
.engine-reading {
    gap: 8px;
    min-height: 24px;
}

.engine-reading__label {
    font-size: 14px;
    color: var(--color-text-sub);
    white-space: nowrap;
}

.engine-reading__slot {
    font-size: 16px;
    font-weight: 500;
    color: var(--color-title);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.engine-reading__unit {
    margin-left: 4px;
    font-size: 14px;
    font-weight: 400;
    color: var(--color-text-sub);
}

/* ---------- 기관: 화면 메뉴 + 알람 배너 ---------- */
.engine-nav-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--engine-gap) var(--space-widget-gap);
    margin-bottom: var(--console-section-gap);
}

.engine-menu {
    display: flex;
    flex-wrap: wrap;
    gap: 8px; /* 화면 구분 탭 묶음과 같은 간격 */
}

/* 화면 메뉴는 알약 모양(어창 화면 구분 탭과 같은 모양) */
.engine-menu__link {
    gap: 6px;
    border-radius: var(--radius-pill);
}

/* 선박 하단 메뉴의 빨간 점과 같은 역할: 그 화면에 활성 알람이 있다 */
.engine-menu__dot {
    flex: none;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--color-error);
}

.engine-alarm {
    display: flex;
    flex: 1 1 360px;
    align-items: center;
    gap: 12px;
    min-width: 0;
    max-width: 640px;
    min-height: 40px; /* 메뉴 버튼과 같은 높이 */
    padding: 0 8px 0 16px;
    font-size: 14px;
    background: var(--color-button-box);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-control);
}

.engine-alarm--active {
    border-color: var(--color-error);
}

.engine-alarm__count {
    flex: none;
    font-weight: 700;
    color: var(--color-text-sub);
}

.engine-alarm--active .engine-alarm__count {
    color: var(--color-error);
}

/* 긴 설명은 한 줄에서 말줄임(배너 높이를 메뉴 줄과 같게 유지) */
.engine-alarm__text {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--color-text);
}

.engine-alarm__steps {
    display: flex;
    flex: none;
    gap: 4px;
}

.engine-alarm__step {
    width: 28px;
    height: 28px;
    padding: 0;
    font-size: 12px;
    color: var(--color-text);
    background: transparent;
    border: 1px solid var(--color-input-border);
    border-radius: var(--radius-small);
    cursor: pointer;
    transition: background-color var(--transition-fast);
}

.engine-alarm__step:hover:not(:disabled) {
    background: var(--color-hover-surface);
}

.engine-alarm__step:focus-visible {
    outline: none;
    box-shadow: var(--shadow-focus);
}

.engine-alarm__step:disabled {
    opacity: 0.5; /* 비활성 버튼(.btn:disabled)과 같은 방식 */
    cursor: default;
}

/* ---------- 기관: 위젯 · 탭 · 범례 ---------- */
/* 탭 패널: 위젯(.widget--fill)의 남는 높이를 받는다. 표 화면(ALARM·AUX·HYD)은 위에서부터 읽으므로 위에 붙이고,
   그림 화면은 아래 수정자로 남는 높이를 나눈다 */
.engine-panel {
    display: flex;
    flex: 1 0 auto;
    flex-direction: column;
}

/* 위 display:flex가 hidden 속성의 기본 display:none을 덮으므로 탭 전환 때 숨김을 되살린다 */
.engine-panel[hidden] {
    display: none;
}

/* 묶음이 여러 줄인 화면(GEAR BOX 게이지 4줄): 줄 사이·위아래에 남는 높이를 고르게 나눈다 */
.engine-panel--spread {
    justify-content: space-evenly;
}

/* 내용이 적은 화면(STERN TUBE, TANK & BILGE): 묶음 하나를 패널 가운데에 둔다 */
.engine-panel--center {
    justify-content: center;
}

/* .console-tabs의 display:flex가 hidden 속성의 기본 display:none을 덮으므로 ALARM 하위 탭 숨김을 되살린다 */
.engine-subtabs[hidden] {
    display: none;
}

/* 범례는 위젯 제목 줄 오른쪽 끝에 둔다(세로 공간을 쓰지 않게) */
.engine-legend {
    margin: 0 0 0 auto;
    gap: 4px 16px;
    font-size: 13px;
}

.engine-legend .legend__item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

/* ---------- 기관: 램프 · 상태 박스 ---------- */
.engine-lamp {
    display: inline-block;
    flex: none;
    width: var(--engine-lamp-size);
    height: var(--engine-lamp-size);
    vertical-align: middle;
    border: 1px solid transparent;
    border-radius: 50%;
}

.engine-lamp--off {
    background: var(--color-button-box-hover);
    border-color: var(--color-scrollbar);
}

/* 값이 없는(태그 없음·숫자 아님) 램프: 꺼짐과 구분되게 속을 비운 점선 */
.engine-lamp--unknown {
    background: transparent;
    border: 1px dashed var(--color-muted);
}

.engine-lamp--alarm {
    background: var(--color-error);
}

.engine-lamp--run {
    background: var(--color-success);
}

/* 빌지 램프는 사진처럼 네모 */
.engine-lamp--square {
    border-radius: var(--radius-tiny);
}

.engine-lamps {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: 14px;
}

.engine-lamps__item {
    display: flex;
    align-items: center;
    gap: 10px;
    line-height: 1.3;
    white-space: nowrap;
}

.engine-lamps__name {
    color: var(--color-text);
}

/* MAIN ENGINE SYSTEM OVERVIEW 상태 박스: 켜짐(운전 초록)·알람(빨강) 때 면 전체를 칠한다(사진의 상태 박스) */
.engine-status {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 32px;
    padding: 4px 8px;
    font-size: 13px;
    font-weight: 500;
    line-height: 1.2;
    text-align: center;
    color: var(--color-text-sub);
    background: var(--color-button-box);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-small);
}

/* 칠한 면 위 글자는 페이지 배경색(진한 남색)으로 둬 밝은 초록·빨강 위에서 읽히게 한다 */
.engine-status--run {
    color: var(--color-bg);
    background: var(--color-success);
}

.engine-status--alarm {
    color: var(--color-bg);
    background: var(--color-error);
}

.engine-status--unknown {
    border-style: dashed;
    border-color: var(--color-muted);
}

/* ---------- 기관: 원형 게이지 ---------- */
.engine-gauge {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    width: var(--engine-gauge-cell-width);
}

.engine-gauge--large {
    width: auto;
}

.engine-gauge__dial {
    position: relative;
    width: var(--engine-gauge-small-width);
}

.engine-gauge--large .engine-gauge__dial {
    width: var(--engine-gauge-large-width);
}

.engine-gauge__svg {
    display: block;
    width: 100%;
    height: auto;
}

.engine-gauge__track,
.engine-gauge__fill {
    fill: none;
    stroke-width: 7;
}

.engine-gauge__track {
    stroke: var(--color-button-box-hover);
}

.engine-gauge__fill {
    stroke: var(--color-point);
}

.engine-gauge__tick {
    stroke: var(--color-drawing-line);
    stroke-width: 1;
}

/* 알람 설정치 마커(사진의 눈금 바깥 주황·빨강 짧은 선) */
.engine-gauge__marker {
    stroke: var(--color-warning);
    stroke-width: 2.5;
}

/* SVG 글자 크기는 viewBox 단위(작은 게이지 --engine-gauge-small-width/120 배, 대형 --engine-gauge-large-width/120 배로 보인다) */
.engine-gauge__scale {
    font-size: 9px;
    fill: var(--color-text-sub);
}

.engine-gauge__tag {
    font-size: 10px;
    font-weight: 700;
    fill: var(--color-text-sub);
}

/* 값·단위는 원호 중심(viewBox 58/108 ≈ 54%)에 겹친다 */
.engine-gauge__reading {
    position: absolute;
    top: 54%;
    left: 0;
    right: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    line-height: 1.15;
    transform: translateY(-50%);
}

.engine-gauge__value {
    font-size: 20px;
    font-weight: 700;
    color: var(--color-title);
    font-variant-numeric: tabular-nums;
}

.engine-gauge__unit {
    font-size: 12px;
    color: var(--color-point);
}

.engine-gauge--large .engine-gauge__value {
    font-size: 40px;
}

.engine-gauge--large .engine-gauge__unit {
    font-size: 16px;
}

.engine-gauge__caption {
    font-size: 12px;
    line-height: 1.25;
    text-align: center;
    color: var(--color-text-sub);
}

.engine-gauge--large .engine-gauge__caption {
    font-size: 14px;
}

/* ---------- 기관: 세로 바 ---------- */
.engine-bar {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: var(--engine-bar-width);
}

.engine-bar__label {
    font-size: 12px;
    color: var(--color-text-sub);
}

/* 높이만 정하고 폭은 SVG 비율(64×104)대로 따라가게 둔다 */
.engine-bar__svg {
    display: block;
    width: auto;
    height: var(--engine-bar-height);
}

.engine-bar__track {
    fill: var(--color-button-box-hover);
}

.engine-bar__fill {
    fill: var(--color-point);
}

.engine-bar__tick {
    stroke: var(--color-drawing-line);
    stroke-width: 1;
}

.engine-bar__marker {
    stroke: var(--color-warning);
    stroke-width: 2;
}

.engine-bar__scale {
    font-size: 9px;
    fill: var(--color-text-sub);
}

.engine-bar__reading {
    font-size: 13px;
    white-space: nowrap;
}

.engine-bar__value {
    font-weight: 700;
    color: var(--color-title);
    font-variant-numeric: tabular-nums;
}

.engine-bar__unit {
    margin-left: 2px;
    font-size: 11px;
    color: var(--color-text-sub);
}

.engine-bar__tag {
    font-size: 11px;
    font-weight: 700;
    color: var(--color-text-sub);
}

.engine-bar__caption {
    font-size: 11px;
    line-height: 1.25;
    text-align: center;
    color: var(--color-text-sub);
}

/* ---------- 기관: 묶음 제목 · 수치 한 줄 ---------- */
.engine-group__title {
    margin: 0 0 6px;
    font-size: 14px;
    font-weight: 700;
    text-align: center;
    color: var(--color-title);
}

.engine-number {
    display: flex;
    align-items: baseline;
    justify-content: center;
    gap: 6px;
    font-size: 14px;
    white-space: nowrap;
}

.engine-number__label {
    color: var(--color-text-sub);
}

.engine-number__label::after {
    content: " :";
}

.engine-number__value {
    font-weight: 700;
    color: var(--color-title);
    font-variant-numeric: tabular-nums;
}

.engine-number__unit {
    color: var(--color-text-sub);
}

/* 위 게이지·바·수치 숫자 규칙이 .value--invalid/.value--missing보다 뒤에 있어 회색을 덮으므로,
   숫자 아님(--)·태그 없음(–)은 회색으로 되돌린다(어창 .console-value--primary와 같은 방식) */
.engine-gauge__value.value--invalid,
.engine-gauge__value.value--missing,
.engine-bar__value.value--invalid,
.engine-bar__value.value--missing,
.engine-number__value.value--invalid,
.engine-number__value.value--missing {
    color: var(--color-muted);
}

/* ---------- 기관: MAIN ENGINE › GRAPHS (왼쪽 2열 · 가운데 · 오른쪽 2열, 4줄) ---------- */
.engine-graphs {
    display: grid;
    grid-template-columns:
        repeat(2, var(--engine-gauge-cell-width))
        minmax(0, 1fr)
        repeat(2, var(--engine-gauge-cell-width));
    /* 패널 높이를 모두 받아 4줄 사이·위아래에 남는 높이를 고르게 나눈다 */
    flex: 1 0 auto;
    align-content: space-evenly;
    gap: var(--engine-gap) var(--engine-item-gap);
    justify-items: center;
    align-items: start;
}

.engine-graphs__center {
    display: flex;
    grid-column: 3;
    grid-row: 1 / 4;
    align-self: center;
    justify-content: space-evenly;
    width: 100%;
}

.engine-graphs__large {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--engine-gap);
}

.engine-overview {
    grid-column: 2 / 5;
    grid-row: 4;
    width: 100%;
}

.engine-overview__grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 6px 8px;
    align-items: center;
}

/* ---------- 기관: MAIN ENGINE › SYSTEM (위 베어링, 아래 두 줄 = 배기 좌·우 + WARNING) ---------- */
.engine-system {
    display: grid;
    grid-template-columns: repeat(3, auto);
    grid-template-areas:
        "bearing bearing bearing"
        "exhaust-left pre-turbine post-turbo"
        "exhaust-right turbocharger manifold";
    /* 패널 높이를 모두 받아 3줄 사이·위아래에 남는 높이를 고르게 나눈다 */
    flex: 1 0 auto;
    align-content: space-evenly;
    gap: var(--engine-gap) var(--engine-item-gap);
    justify-content: center;
}

.engine-group {
    padding: var(--engine-gap) 12px;
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-small);
}

.engine-group__bars {
    display: flex;
    justify-content: center;
    gap: 4px;
}

.engine-group--bearing { grid-area: bearing; justify-self: center; }
.engine-group--exhaust-left { grid-area: exhaust-left; }
.engine-group--exhaust-right { grid-area: exhaust-right; }
.engine-group--pre-turbine { grid-area: pre-turbine; }
.engine-group--post-turbo { grid-area: post-turbo; }
.engine-group--turbocharger { grid-area: turbocharger; }
.engine-group--manifold { grid-area: manifold; }

/* ---------- 기관: 램프 · 값 표 (ALARM 목록 · AUX · HYD) ---------- */
.engine-columns {
    display: grid;
    /* 단 폭은 고르게 나누되 표의 가장 긴 설명(max-content)보다 좁아지지 않게 한다. WARNING1은 3단이라
       똑같이 나누면 셋째 단(ME45·ME48 설명, 약 393px)이 잘리므로 그 단만 넓어지고 나머지 단이 줄어든다 */
    grid-template-columns: repeat(var(--engine-column-count, 2), minmax(max-content, 1fr));
    gap: 0 var(--space-widget-gap);
    align-items: start;
}

/* 열 폭은 내용에 맞춘다(자동 배치). 설명이 잘리지 않고, 단 폭은 위 격자가 내용 기준으로 정한다 */
.engine-table th,
.engine-table td {
    height: var(--engine-row-height);
    padding: 0 8px;
}

.data-table .engine-table__number {
    width: 48px;
    text-align: left;
}

/* 머리글 "STATUS"(약 54px) + 좌우 여백 16이 들어가는 폭 */
.data-table .engine-table__lamp {
    width: 76px;
    text-align: center;
}

/* 머리글 "SENSOR TAG"(약 86px) + 좌우 여백 16이 들어가는 폭 */
.data-table .engine-table__tag {
    width: 112px;
}

.data-table .engine-table__value {
    width: 130px;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.engine-table__unit {
    margin-left: 6px;
    color: var(--color-text-sub);
}

/* ---------- 기관: GEAR BOX (게이지 줄) ---------- */
.engine-gauge-row {
    display: flex;
    align-items: center;
    justify-content: space-around;
    gap: var(--engine-item-gap);
}

.engine-gauge-row + .engine-gauge-row {
    margin-top: var(--engine-gap);
}

/* 진동 게이지 두 줄은 사진처럼 바탕을 깔아 묶는다 */
.engine-gauge-row--labeled {
    padding: 0 12px;
    background: var(--color-hover-surface);
    border-radius: var(--radius-small);
}

.engine-gauge-row__label {
    flex: 0 0 140px;
    font-size: 14px;
    font-weight: 700;
    color: var(--color-text-sub);
}

/* ---------- 기관: GENERATOR ENGINE (엔진 3대 × 바 9개 + 램프 11개) ---------- */
/* 엔진 3대가 패널 높이를 똑같이 나눠 갖고, 남는 높이 안에서 바 묶음과 램프 목록을 세로 가운데에 둔다 */
.engine-generator {
    display: flex;
    flex: 1 0 auto;
    align-items: center;
    gap: 24px;
    padding: var(--engine-gap) 0;
    border-bottom: 1px solid var(--color-divider);
}

.engine-generator:first-child {
    padding-top: 0;
}

.engine-generator:last-child {
    padding-bottom: 0;
    border-bottom: 0;
}

.engine-generator__main {
    flex: 1 1 auto;
    min-width: 0;
}

.engine-generator .engine-group__title {
    text-align: left;
}

.engine-generator__bars {
    display: grid;
    grid-template-columns: repeat(9, minmax(0, 1fr));
    gap: 4px;
}

.engine-generator .engine-bar {
    width: auto;
}

.engine-generator .engine-bar__svg {
    height: var(--engine-generator-bar-height);
}

/* 눈금 글자(viewBox 단위)를 다른 화면(9)보다 키워 화면에서 약 11px로 읽히게 한다(108/104 배로 보인다) */
.engine-generator .engine-bar__scale {
    font-size: 10.5px;
}

/* 램프 11개 목록 높이를 바 묶음 높이(약 199)에 맞추려고 목록 간격 대신 줄 높이로 정한다 */
.engine-generator .engine-lamps {
    flex: none;
    gap: 0;
    font-size: 13px;
}

.engine-generator .engine-lamps__item {
    line-height: var(--engine-generator-lamp-line);
}

/* ---------- 기관: STERN TUBE ---------- */
.engine-stern__gauges {
    display: flex;
    justify-content: center;
    gap: 48px;
}

.engine-stern__card {
    padding: 16px 24px;
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-small);
}

.engine-stern__lamps {
    width: max-content;
    margin: 24px auto 0;
}

/* ---------- 기관: TANK & BILGE (선체 평면 위 탱크 박스) ---------- */
/* 선체 + BILGE 목록 묶음. 사진 _06처럼 위젯 가로 가운데에 두고, BILGE 목록은 묶음 왼쪽 끝(선체 왼쪽 끝)에 맞춘다 */
.engine-tank-layout {
    align-self: center;
    width: max-content;
    max-width: 100%;
}

/* 선체 그림이 탱크 격자 + 선수 쪽 여백을 덮도록 내용 폭만큼만 차지한다 */
.engine-hull {
    position: relative;
    width: max-content;
    max-width: 100%;
    padding-right: var(--engine-hull-bow-space);
}

.engine-hull__outline {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}

.engine-hull__shape {
    fill: var(--color-hover-surface);
    stroke: var(--color-drawing-line);
    stroke-width: 1.5;
}

.engine-hull__grid {
    position: relative; /* 선체 그림(absolute) 위에 그린다 */
    display: grid;
    grid-template-columns: repeat(5, var(--engine-tank-width));
    gap: var(--engine-gap);
    padding: 12px 0 12px 12px;
}

.engine-tank {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 6px 8px;
    font-size: 12px;
    text-align: center;
    background: var(--color-button-box);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-small);
}

.engine-tank__name {
    font-weight: 700;
    color: var(--color-title);
}

.engine-tank__tag {
    color: var(--color-text-sub);
}

.engine-tank-cell {
    height: 20px;
    line-height: 18px;
    font-weight: 500;
    color: var(--color-text-sub);
    background: var(--color-surface);
    border: 1px solid var(--color-input-border);
    border-radius: var(--radius-tiny);
}

.engine-tank-cell--alarm {
    color: var(--color-bg);
    background: var(--color-error);
    border-color: transparent;
}

.engine-tank-cell--run {
    color: var(--color-bg);
    background: var(--color-success);
    border-color: transparent;
}

.engine-tank-cell--unknown {
    border-style: dashed;
}

.engine-orientation {
    align-self: center;
    justify-self: center;
    width: 220px;
    height: 90px;
}

.engine-orientation__shape {
    fill: none;
    stroke: var(--color-drawing-line);
    stroke-width: 1.5;
}

.engine-orientation__text {
    font-size: 12px;
    fill: var(--color-text-sub);
}

.engine-bilge {
    margin-top: var(--console-section-gap);
}

.engine-bilge .engine-group__title {
    text-align: left;
}

/* ---------- 기관: 알람 조건 활성 숫자 ---------- */
/* 게이지·바·표 숫자 기본색(같은 클래스 1개 특이도)보다 뒤에 두어 빨강이 이긴다.
   숫자 아님·태그 없음 회색 되돌림(클래스 2개 특이도)보다도 알람을 먼저 보이도록 같은 특이도 규칙을 뒤에 둔다 */
.engine-value--alarm,
.engine-value--alarm.value--invalid,
.engine-value--alarm.value--missing {
    color: var(--color-error);
}

/* =====================================================================
   장비 화면 (Views/Capture/*, Scripts/capture.js: Sonar · 어탐기 · Radar)
   위젯 하나에 가로형 이미지 카드를 나란히 둔다. 어창·기관과 달리 위젯을 화면 아래까지 늘리지 않는다
   (사용자 요청: 영역을 가득 채우지 않고 카드를 가로형으로).
   이미지는 잘리지 않게 카드 안에 전부 보인다(object-fit: contain). 원래 화면 비율을 아는 칸(CaptureSlot)은
   카드를 그 비율로 두고 채워 그린다(object-fit: fill, 늘어난 캡처를 되돌림). 색은 :root 테마 변수만 쓴다.
   ===================================================================== */

/* ---------- 장비 화면: 이미지 서버 장애 문구 ---------- */
/* 장애 문구는 장애가 생기면 나타나고 다음 정상 응답에서 사라진다. 흐름 안에 두면 그때마다 제목 줄 높이가 바뀌어
   선박 선택·기록 시각과 아래 이미지 카드가 움직이므로, 흐름에서 빼서 선박 선택 아래 ~ 이미지 카드 위 빈 공간 가운데에 띄운다.
   기준 상자는 도구줄(.toolbar--header, 높이 h)이고 제목 줄(최소 높이 H = --nav-brand-height) 세로 가운데에 있다.
   도구줄 아래에서 카드 위까지 빈 공간 D = max((H - h) / 2, 0) + --space-widget-gap 이다
   (창이 좁아 도구줄이 두 줄이 되면 h가 H 이상이 되어 제목 줄이 도구줄 높이만큼 늘어난다).
   그 가운데 = 100% + D/2 (top의 %는 h 기준)에 translateY(-50%)로 글자 줄 가운데를 맞춘다.
   두 줄일 때 D가 gap(20px)까지 줄어드므로 줄 높이를 gap으로 둬서 기록 시각·카드와 겹치지 않게 한다. 넘치면 말줄임한다 */
.toolbar__status--below {
    position: absolute;
    top: calc(100% + (max((var(--nav-brand-height) - 100%) / 2, 0px) + var(--space-widget-gap)) / 2);
    left: 0;
    right: 0;
    transform: translateY(-50%);
    font-size: 16px;
    line-height: var(--space-widget-gap);
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* ---------- 장비 화면: 칸 배치 ---------- */
/* 카드를 한 줄에 나란히 두고 한 줄의 카드 높이를 맞춘다: 칸 폭을 카드 비율(가로/세로)에 비례해 나누면
   (flex-grow = 비율, 기준 폭 0) 높이 = 폭 ÷ 비율이 모든 칸에서 같아진다. 5:4 칸은 16:9 칸보다 좁다.
   1장(어탐기)·2장(Radar)은 아래 규칙으로 고정 높이: 위젯 폭 전체를 쓰면 카드가 너무 높아지기 때문이다
   (어탐기 16:9는 화면 1920×950을 넘고, Radar 5:4 두 장은 623px) */
.capture-grid {
    display: flex;
    gap: var(--space-widget-gap);
}

/* 1장(어탐기)·2장(Radar): 위젯을 화면 폭으로 펼치지 않고 카드 폭에 맞춰 왼쪽에 둔다(사용자 요청).
   카드 높이는 16:9 기준 카드(--capture-single-width, 780px → 높이 438.75px)와 같게 두고 폭은 카드 비율로 정한다.
   Radar는 5:4라 16:9 카드보다 좁다(사용자 요청: 높이는 16:9 때와 같게, 폭만 좁게) */
.capture-widget--count-1,
.capture-widget--count-2 {
    align-self: flex-start;
    width: fit-content;
    max-width: 100%;
}

/* 위젯이 내용 폭을 따르므로 %가 아니라 화면 폭(본문 = 화면 - 네비 - 본문 여백 - 위젯 여백)으로 상한을 잡는다 */
.capture-grid--count-1 {
    display: grid;
    grid-template-columns: min(var(--capture-single-width), calc(100vw - var(--nav-width) - 2 * var(--space-page-x) - 2 * var(--space-widget-x)));
}

/* 비율 변수는 원래 화면 비율을 아는 칸에만 있다(_CaptureBoard). 없으면 16:9 */
.capture-slot {
    display: flex;
    flex: calc(var(--capture-aspect-width, 16) / var(--capture-aspect-height, 9)) 1 0;
    flex-direction: column;
    min-width: 0;
}

/* 2장(Radar): 칸 폭 = 16:9 기준 카드 높이 × 카드 비율. 좁은 화면에서는 본문을 반씩 나눈 폭을 넘지 않는다(count-1과 같은 화면 폭 계산) */
.capture-grid--count-2 .capture-slot {
    flex: 0 0 min(
        var(--capture-single-width) * 9 / 16 * var(--capture-aspect-width, 16) / var(--capture-aspect-height, 9),
        (100vw - var(--nav-width) - 2 * var(--space-page-x) - 2 * var(--space-widget-x) - var(--space-widget-gap)) / 2);
}

.capture-slot__name {
    margin: 0 0 var(--console-section-gap);
    font-size: 16px;
    font-weight: 700;
    color: var(--color-title);
}

/* 카드 전체가 버튼(팝업 열기). 가로형 16:9(장비 화면 캡처가 가로 화면이라). 면은 위젯 안 상자색 */
.capture-slot__view {
    position: relative;
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
    padding: 0;
    background: var(--color-button-box);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-small);
    overflow: hidden;
    cursor: pointer;
    transition: background-color var(--transition-fast);
}

/* 원래 화면 비율을 아는 칸(레이다·소나 고주파·저주파, CaptureSlot): 카드 자체를 그 비율로 둬 좌우 여백 없이 꽉 채운다.
   칸 폭이 비율에 비례하므로(.capture-slot) 같은 줄의 16:9 카드와 높이가 같다 */
.capture-slot__view--device-aspect {
    aspect-ratio: var(--capture-aspect-width) / var(--capture-aspect-height);
}

.capture-slot__view:hover {
    background: var(--color-button-box-hover);
}

.capture-slot__view:focus-visible {
    outline: none;
    box-shadow: var(--shadow-focus);
}

/* 카드·팝업 이미지는 잘리지 않게 전부 보인다. 장비마다 캡처 비율이 달라 상자 비율과 다르면 남는 곳은 상자색 여백이 된다 */
.capture-image {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

/* 캡처 파일은 장비 화면(5:4 등)을 16:9로 가로로 늘려 저장돼 있어, 원래 비율 상자에 채워 그려 늘어난 것을 되돌린다 */
.capture-image--device-aspect {
    object-fit: fill;
}

/* ---------- 장비 화면: 이미지 없음 ---------- */
/* "No Image Found" 그림(원본 360×250)도 잘리지 않게 전부 보인다. 카드(16:9)에서는 좌우, 5:4 카드에서는 위아래에 상자색 여백이 생긴다 */
.capture-empty {
    position: absolute;
    inset: 0;
    display: block;
}

/* 위 display:block이 hidden 속성의 기본 display:none을 덮으므로 이미지를 보일 때 숨김을 되살린다 */
.capture-empty[hidden] {
    display: none;
}

.capture-empty__image {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

/* ---------- 장비 화면: 크게 보기 팝업 ---------- */
/* 팝업은 이미지 크기에 맞춰 줄어든다: 고정 크기 상자에 비율이 다른 이미지를 넣으면 여백이나 잘림이 생기기 때문이다.
   이미지 최대 크기 = 화면에서 팝업 바깥 여백(.modal, 30 × 2) · 팝업 안쪽 여백(30 × 2) · 테두리(1 × 2)를 뺀 값,
   높이는 머리 줄(닫기 버튼 40 + 아래 간격 20)도 뺀다. 폭은 공통 팝업 최대 폭(1200px)을 넘지 않는다 */
.capture-modal__dialog {
    --capture-modal-image-max-width: calc(min(1200px, 100vw - 2 * var(--space-page-x)) - 2 * 30px - 2px);
    --capture-modal-image-max-height: calc(100vh - 2 * var(--space-page-x) - 2 * 30px - 2px - 40px - 20px);
    width: auto;
    max-width: min(1200px, 100%);
}

.capture-modal__view {
    position: relative;
    display: flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    background: var(--color-button-box);
    border-radius: var(--radius-small);
    overflow: hidden;
}

/* 카드용 .capture-image(상자를 채우는 절대 위치)를 풀고 파일 비율 그대로 최대 크기 안에서 줄인다.
   원본보다 키우지 않는다(장비 캡처는 1920×1080 등 팝업보다 크다) */
.capture-modal__view .capture-image {
    position: static;
    width: auto;
    height: auto;
    max-width: var(--capture-modal-image-max-width);
    max-height: var(--capture-modal-image-max-height);
}

/* 원래 화면 비율을 아는 칸의 팝업: 파일 비율 대신 그 비율로 최대 크기 안에서 가장 크게 그린다.
   폭 = 최대 폭과 (최대 높이 × 비율) 중 작은 값, 높이는 비율로 정해진다. 비율 변수는 capture.js가 팝업을 열 때 넣는다 */
.capture-modal__view .capture-image--device-aspect {
    width: min(var(--capture-modal-image-max-width), var(--capture-modal-image-max-height) * var(--capture-aspect-width) / var(--capture-aspect-height));
    height: auto;
    aspect-ratio: var(--capture-aspect-width) / var(--capture-aspect-height);
}

/* 팝업의 이미지 없음 그림은 원본 비율(360×250) 그대로 두 배 크기로 보인다 */
.capture-modal__view .capture-empty {
    position: static;
}

.capture-modal__view .capture-empty__image {
    width: 720px;
    height: auto;
    max-width: var(--capture-modal-image-max-width);
}

/* =====================================================================
   항해 화면 (Views/Navigation/Index.cshtml, Scripts/navigation.js)
   왼쪽 지도(Map) 1 : 오른쪽 1.25. 오른쪽은 GPS / Heading · Course / Speed · Wind 세 줄.
   높이는 본문 높이(--page-body-height)를 따르고 1920×950 기준 본문(--page-body-min-height, 812px)보다 작아지지 않는다.
   색은 :root 테마 변수만 쓴다. 나침반 · 선박 마커(SVG)도 클래스로 색을 준다(오프라인 지도 Canvas 색만 navigation.js가 변수에서 읽는다).
   값 칸은 "값 묶음(__value) > 값 글자(data-nav-field) + 단위(__unit)"이고, 값이 없을 때 붙는 .value--missing은
   값 글자에 붙는다. 값 글자에는 색 규칙이 없어(묶음 색을 물려받는다) .value--missing 회색이 그대로 적용된다.
   ===================================================================== */

/* ---------- 항해: 배치 ---------- */
.nav-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, var(--navigation-col-right));
    gap: var(--space-widget-gap);
    height: var(--page-body-height);
    min-height: var(--page-body-min-height);
}

/* 항해 위젯(.widget과 함께 쓰는 modifier): 위젯 사이 간격은 격자 gap이 맡으므로 위젯 아래 여백을 없애고,
   안쪽을 세로 flex로 두어 지도·나침반 칸이 남는 높이를 받게 한다. .widget보다 뒤에 있어 같은 특이도로 이긴다 */
.nav-widget {
    display: flex;
    flex-direction: column;
    min-height: 0;
    margin: 0;
    padding: var(--navigation-widget-pad);
}

.nav-side {
    display: grid;
    grid-template-rows: auto minmax(0, 1fr) auto;
    gap: var(--space-widget-gap);
    min-height: 0;
}

/* 위젯 제목 + 오른쪽 보조 글자(.widget__note: 범례 · 단위) */
.nav-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 14px;
}

.nav-head .widget__title {
    margin: 0;
}

/* 위젯 제목 줄의 단위 전환 버튼(Wind kn · m/s). 공통 .btn(높이 40)은 제목 줄을 높여 1920×950 본문을 넘기므로 작게 둔다 */
.nav-unit-toggle {
    display: flex;
    gap: 4px;
}

.nav-unit-toggle .btn {
    min-height: 28px;
    padding: 4px 10px;
    font-size: 13px;
}

/* ---------- 항해: 지도 (Leaflet) ---------- */
/* leaflet.css를 site.css보다 먼저 읽으므로(_Layout styles 섹션) Leaflet과 같은 선택자로 쓰면 뒤에 있는 이 규칙이 이긴다 */
.nav-map-wrap {
    position: relative;
    flex: 1;
    min-height: 0;
    border-radius: var(--radius-small);
    overflow: hidden;
    background: var(--navigation-map-sea);
}

.nav-map {
    position: absolute;
    inset: 0;
}

.leaflet-container {
    background: var(--navigation-map-sea);
    font-family: inherit;
}

/* 확대 · 축소 버튼: Leaflet 기본 흰 버튼을 위젯 안 상자 면으로 */
.leaflet-bar a {
    color: var(--color-title);
    background-color: var(--color-button-box);
    border-bottom-color: var(--color-divider);
}

.leaflet-bar a:hover,
.leaflet-bar a:focus {
    background-color: var(--color-button-box-hover);
}

.leaflet-bar a.leaflet-disabled {
    color: var(--color-muted);
    background-color: var(--color-button-box);
}

.leaflet-touch .leaflet-bar {
    border-color: var(--color-input-border);
}

/* 선박 이름 라벨(오른쪽 고정 툴팁): Leaflet 기본 흰 상자를 팝업 면으로. 꼬리(:before)도 같은 색 */
.leaflet-tooltip {
    color: var(--color-popup-text);
    background-color: var(--color-popup);
    border-color: var(--color-input-border);
}

.leaflet-tooltip-right:before {
    border-right-color: var(--color-popup);
}

/* 선박 마커: 선체(선수방위 회전) 또는 선수방위가 없을 때의 원. 회전한 선체가 아이콘 상자 밖으로 나가도 보이게 한다 */
.nav-ship-icon__svg {
    overflow: visible;
}

.nav-ship-icon__shape {
    fill: var(--color-point);
    stroke: var(--color-title);
    stroke-width: 2;
}

/* ---------- 항해: GPS ---------- */
.nav-pos-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}

.nav-pos-box {
    padding: 12px 16px;
    border-radius: var(--radius-small);
    background: var(--color-button-box);
}

.nav-pos-box__label {
    display: block;
    margin-bottom: 4px;
    font-size: 13px;
    color: var(--color-text-sub);
}

.nav-pos-box__value {
    font-size: 24px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--color-title);
    font-variant-numeric: tabular-nums;
}

/* ---------- 항해: 선수방위 · 침로 ---------- */
.nav-course {
    display: grid;
    flex: 1;
    grid-template-columns: var(--navigation-compass-size) minmax(0, 1fr);
    gap: 20px;
    align-items: center;
    min-height: 0;
}

.nav-compass {
    width: var(--navigation-compass-size);
    height: var(--navigation-compass-size);
}

/* 나침반 그림(navigation.js가 그린다): 바깥 원 · 안쪽 원 · 눈금(30° 굵게) · 방위 글자(N만 주황) */
.nav-compass__ring {
    fill: none;
    stroke: var(--color-divider);
    stroke-width: 2;
}

.nav-compass__inner {
    fill: var(--color-hover-surface);
    stroke: var(--color-border-subtle);
}

.nav-compass__tick {
    stroke: var(--color-muted);
    stroke-width: 1;
}

.nav-compass__tick--major {
    stroke: var(--color-text-sub);
    stroke-width: 2;
}

.nav-compass__label {
    font-family: inherit;
    font-size: 17px;
    font-weight: 700;
    fill: var(--color-title);
}

.nav-compass__label--north {
    fill: var(--color-compass-north);
}

/* 바늘: 진풍향(바깥 → 중심 화살표), 침로(중심 → 바깥 화살표), 선수방위(선체 + 점선) */
.nav-compass__wind,
.nav-compass__course {
    fill: none;
    stroke-linecap: round;
}

.nav-compass__wind {
    stroke: var(--color-wind);
    stroke-width: 4;
}

.nav-compass__course {
    stroke: var(--color-cog);
    stroke-width: 3;
}

.nav-compass__hull {
    fill: var(--color-divider);
    stroke: var(--color-title);
    stroke-width: 2;
}

.nav-compass__bow {
    stroke: var(--color-title);
    stroke-width: 1.5;
    stroke-dasharray: 4 4;
}

.nav-readouts {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.nav-readout {
    display: grid;
    grid-template-columns: 56px 1fr;
    align-items: baseline;
    padding: 10px 14px;
    border-radius: var(--radius-small);
    background: var(--color-button-box);
}

.nav-readout__label {
    font-size: 14px;
    color: var(--color-text-sub);
}

.nav-readout__value {
    font-size: 28px;
    font-weight: 700;
    text-align: right;
    color: var(--color-title);
    font-variant-numeric: tabular-nums;
}

.nav-readout__unit {
    margin-left: 4px;
    font-size: 14px;
    font-weight: 400;
    color: var(--color-text-sub);
}

.nav-readout--cog .nav-readout__value {
    color: var(--color-cog);
}

/* 위젯 제목 줄 범례의 색 점 */
.nav-swatch {
    display: inline-block;
    width: 10px;
    height: 10px;
    margin-right: 6px;
    border-radius: 50%;
    vertical-align: 1px;
}

.nav-swatch--cog {
    background: var(--color-cog);
}

.nav-swatch--wind {
    background: var(--color-wind);
}

/* ---------- 항해: 속도 · 바람 ---------- */
.nav-bottom-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
    gap: var(--space-widget-gap);
}

.nav-speed-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.nav-speed {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    padding: 8px 14px;
    border-radius: var(--radius-small);
    background: var(--color-button-box);
}

.nav-speed__label {
    font-size: 14px;
    color: var(--color-text-sub);
}

.nav-speed__value {
    font-size: 24px;
    font-weight: 700;
    color: var(--color-title);
    font-variant-numeric: tabular-nums;
}

.nav-speed__unit {
    margin-left: 4px;
    font-size: 13px;
    font-weight: 400;
    color: var(--color-text-sub);
}

.nav-wind {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
}

.nav-wind__cell {
    padding: 8px 12px;
    border-radius: var(--radius-small);
    background: var(--color-button-box);
}

.nav-wind__label {
    display: block;
    font-size: 13px;
    color: var(--color-text-sub);
}

/* 풍속과 방향은 같은 서식(크기 · 굵기 · 색 · 단위)으로 두고, 사이를 띄우고 세로선으로 나눈다 */
/* 풍속 · 방향 칸을 반씩 고정한다: 단위(kn · m/s)나 자릿수가 바뀌어도 방향 위치가 밀리지 않게.
   풍속은 kn · m/s 중 보이는 하나만 첫 칸에 들어간다 */
.nav-wind__values {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: baseline;
    column-gap: 16px;
}

.nav-wind__values > .nav-wind__value {
    grid-column: 1;
    grid-row: 1;
}

.nav-wind__values > .nav-wind__value--direction {
    grid-column: 2;
}

.nav-wind__value {
    font-size: 22px;
    font-weight: 700;
    color: var(--color-title);
    font-variant-numeric: tabular-nums;
}

.nav-wind__value--direction {
    padding-left: 16px;
    border-left: 1px solid var(--color-divider);
}

.nav-wind__unit {
    margin-left: 3px;
    font-size: 13px;
    font-weight: 400;
    color: var(--color-text-sub);
}

/* 각도 기호(°): 숫자와 같은 크기로 두면 글꼴이 숫자 윗선에 붙여 그린다(072.4°처럼).
   작은 단위 글자 크기로 두면 기준선 근처로 내려가 떠 보이므로, 크기만 숫자를 따르고 색 · 굵기는 단위 글자와 같게 둔다.
   .nav-readout__unit · .nav-wind__unit보다 뒤에 있어 같은 특이도로 크기 · 간격을 덮는다 */
.nav-degree {
    margin-left: 1px;
    font-size: inherit;
}

.nav-wind__cell--true .nav-wind__value {
    color: var(--color-wind);
}

.nav-bf {
    display: flex;
    grid-column: span 2;
    align-items: center;
    justify-content: space-between;
    padding: 8px 12px;
    font-size: 14px;
    color: var(--color-text-sub);
    border-radius: var(--radius-small);
    background: var(--color-button-box);
}

.nav-bf__badge {
    padding: 2px 10px;
    font-weight: 500;
    color: var(--color-beaufort);
    border-radius: var(--radius-pill);
    background: var(--color-beaufort-soft);
}

/* =====================================================================
   관리 화면 (Views/Admin/Index, Scripts/admin.js) · 비밀번호 변경 (Views/Account/ChangePassword)
   표·버튼·팝업은 공통 컴포넌트(.data-table, .btn, .modal)를 그대로 쓰고 배치만 더한다.
   색은 :root 테마 변수만 쓴다(성공 --color-success, 오류·주의 --color-error).
   ===================================================================== */

/* ---------- 관리: 작업 결과 문구 ---------- */
.admin-message {
    margin: 0 0 var(--space-widget-gap);
    padding: 12px 20px; /* 가이드에 없는 값: 한 줄 문구라 위젯 여백(25 30)보다 얇게, 높이가 버튼(40)보다 조금 큰 띠가 되게 */
    font-size: 14px;
    background: var(--color-surface);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-control);
}

.admin-message--success {
    color: var(--color-success);
}

.admin-message--error {
    color: var(--color-error);
}

/* ---------- 관리: 화면 높이 고정 (문서 전체 스크롤 없음) ---------- */
/* .page-fill(남는 높이를 받는 세로 flex)을 쓰되 높이를 화면 높이로 묶는다. .page-fill은 내용만큼 늘어나므로(flex 1 0 auto)
   그대로 두면 사용자가 많을 때 문서 전체가 길어진다 */
.admin-page {
    height: var(--admin-page-height);
}

.admin-page .admin-message {
    flex: none;
}

/* .widget--fill은 줄어들지 않는다(flex-shrink 0). 관리 화면은 위 높이 안에 들어가야 하므로 줄어들게 하고,
   min-height 0으로 내용(표) 높이보다 작아질 수 있게 해 표 영역이 스크롤되게 한다 */
.admin-page .admin-widget {
    flex: 1 1 auto;
    min-height: 0;
}

/* ---------- 관리: 위젯 제목 줄(제목 + 등록 버튼) ---------- */
.admin-head {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-widget-gap);
    margin-bottom: var(--space-widget-gap);
}

.admin-head .widget__title {
    margin: 0;
}

/* ---------- 관리: 찾기 · 필터 도구 줄 ---------- */
/* 어창 상단 도구줄(.toolbar)을 그대로 쓰고, 위젯 안 세로 flex에서 줄어들지 않게만 한다. 계정 수와 관계없이 항상 보인다 */
.admin-filter {
    flex: none;
    margin-bottom: var(--space-widget-gap);
}

/* 필터 결과 0건 안내. 표 첫 열 글자 시작선(칸 여백 12)에 맞춘다 */
.admin-empty {
    margin: var(--space-widget-gap) 0 0;
    padding: 0 12px;
    font-size: 14px;
    color: var(--color-text-sub);
}

/* ---------- 관리: 사용자 표 ---------- */
/* 위젯의 남는 높이를 받아 표만 세로 스크롤한다(좁은 화면에서는 가로 스크롤도). 계정이 적으면 표 아래가 빈 채로 둔다 */
.admin-table-wrap {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
}

/* 머리글 고정. 아래로 지나가는 행이 비치지 않게 위젯 면 색으로 가린다.
   border-collapse 표에서는 sticky 칸의 아래 테두리가 스크롤 중 사라지므로 같은 색 선을 안쪽 그림자로 그린다 */
.admin-table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--color-surface);
    box-shadow: inset 0 -1px 0 var(--color-divider);
}

/* 작업 버튼(높이 40)이 들어가는 행이라 위아래로 조금 띄워 버튼이 행 경계선에 붙지 않게 한다 */
.admin-table td {
    padding-top: 6px;
    padding-bottom: 6px;
}

.admin-table__row--inactive td {
    color: var(--color-muted);
}

.admin-table__self {
    margin-left: 6px; /* 가이드에 없는 값: 아이디와 "(나)"가 한 덩어리로 읽히게 낱말 간격 정도만 띄운다 */
    color: var(--color-text-sub);
}

/* 잠김 · 변경 필요: 관리자가 조치할 상태. 사용 안 함 행에서는 위 회색(.admin-table__row--inactive td, 특이도가 더 높다)이 이긴다 */
.admin-table__alert {
    color: var(--color-error);
}

.admin-actions {
    display: flex;
    gap: 8px; /* 가이드에 없는 값: 기간 프리셋·화면 탭 버튼 묶음과 같은 버튼 간격 */
}

.admin-actions form {
    margin: 0;
}

.admin-actions__danger {
    color: var(--color-error);
}

/* ---------- 관리: 등록 · 확인 팝업 ---------- */
/* 공통 .modal__dialog 폭(최대 1200px, 이력 차트용)은 입력 3칸·문구 한 줄에는 너무 넓어 입력칸 폭(365) + 여백으로 줄인다 */
.admin-modal {
    width: min(calc(365px + 2 * 30px), 100%);
}

.admin-modal__note,
.admin-modal__message {
    margin: 0 0 20px;
    font-size: 14px;
    color: var(--color-text-sub);
}

.admin-modal__message {
    font-size: 15px; /* 확인 팝업의 본문이라 보조 안내(14px)보다 한 단계 크게, 본문 기본 크기(body 15px)와 같게 */
    color: var(--color-popup-text);
}

/* 완전 삭제 팝업의 "되돌릴 수 없습니다": 다른 확인 창과 구분되게 오류색 굵은 글자로 둔다 */
.admin-modal__warning {
    font-weight: 700;
    color: var(--color-error);
}

.admin-modal__actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px; /* 가이드에 없는 값: 취소·확인 버튼 사이. 위 작업 버튼 묶음과 같은 간격 */
}

/* ---------- 비밀번호 변경 ---------- */
.password-change {
    max-width: calc(365px + 2 * var(--space-widget-x));
}

.password-change__notice {
    margin: 0 0 20px;
    font-size: 14px;
    color: var(--color-warning);
}

.password-change__rule {
    margin: 0 0 20px;
    font-size: 14px;
    color: var(--color-text-sub);
}
