/* NeoCannBio ControlCenter — 디자인 토큰
   ────────────────────────────────────────────────────────────────────────
   같은 회사의 LUKA(헴프 마켓 인텔리전스) 대시보드와 **같은 시각 언어**를 쓴다.
   두 제품이 따로 놀면 둘 다 작아 보인다. LUKA의 실제 토큰(app/globals.css)에서
   캔버스·강조색·상태색 체계를 그대로 가져왔다.

   핵심: **거의 검은 캔버스 + 형광 민트 강조**. 어두운 바탕에서 값이 스스로 빛나 보이게
   하는 방식이라, 데이터가 많은 관제 화면에서 숫자가 먼저 읽힌다.

   색을 바꿔야 하면 이 블록만 고친다 — 아래 모든 규칙이 이 변수를 참조한다. */
:root {
    /* ── 브랜드 ────────────────────────────────────────────────────────
       neocannbio.com 실제 로고에서 추출한 4색(CLAUDE.md — 임의로 다시 고르지 말 것).
       로고 마크 안에서만 쓰고 화면 상태색으로는 쓰지 않는다. */
    --brand-green: #009A00;
    --brand-sky: #00B0EF;
    --brand-blue: #0070C0;
    --brand-purple: #7130A2;

    /* ── 캔버스 ────────────────────────────────────────────────────────
       완전한 검정이 아니라 초록 기운이 아주 옅게 도는 검정이다. 순검정은 화면이
       뚫린 것처럼 보이고, 이 색은 깊이가 생긴다. */
    --canvas: #07110e;
    --canvas-deep: #050c0a;

    --bg: var(--canvas);
    --surface: #0d1815;
    --surface-alt: #10201b;
    --surface-raised: #13251f;
    --border: #21352e;
    --border-strong: #2d453c;
    --border-soft: rgba(126, 166, 148, 0.15);

    --text: #f2f7f4;
    --text-muted: #95a69f;
    --text-dim: #667871;

    /* ── 강조색: 형광 민트 ─────────────────────────────────────────────
       어두운 바탕에서 이 색만 튀어야 한다. 강조색 위 글자는 어두워야 읽힌다. */
    --accent: #31e697;
    --accent-dark: #24b878;
    --accent-light: #6cf0b8;
    --accent-bg: rgba(49, 230, 151, 0.12);
    --accent-border: rgba(49, 230, 151, 0.28);
    --accent-on: #05100c;

    /* ── 상태색 4종 ────────────────────────────────────────────────────
       LUKA와 같은 값. 의미가 같으면 색도 같아야 한다. */
    --success: #54d99a;
    --success-bg: rgba(84, 217, 154, 0.12);
    --success-border: rgba(84, 217, 154, 0.30);
    --danger: #ff6f7d;
    --danger-bg: rgba(255, 111, 125, 0.12);
    --danger-border: rgba(255, 111, 125, 0.32);
    --warn: #f7c65b;
    --warn-bg: rgba(247, 198, 91, 0.12);
    --warn-border: rgba(247, 198, 91, 0.30);
    --info: #65c8ff;
    --info-bg: rgba(101, 200, 255, 0.12);
    --info-border: rgba(101, 200, 255, 0.30);

    /* ── 생육 단계 색 ──────────────────────────────────────────────────
       단계는 7개지만 색은 4개다. 7색을 다 다르게 주면 무지개가 되어 무엇이 급한지
       읽히지 않는다 — 성격이 같은 단계끼리 묶는다(서버 ZoneGrowthStages.Tone과 같은 규칙).
       개화만 다른 계열(보라)을 주는 이유: 가장 중요한 단계라 한눈에 갈라져야 한다.
       상태색(빨강=문제, 파랑=사람)과 겹치지 않게 골랐다 — 겹치면 화면을 두 번 배워야 한다. */
    --grow-start: #6fb6ff;
    --grow-grow: var(--accent);
    --grow-flower: #c88bff;
    --grow-post: var(--warn);
    --grow-idle: var(--text-dim);

    /* 단계 색이 정해지기 전의 기본값. .tone-* 클래스가 이 값을 덮어쓴다 —
       클래스가 빠져도 글자가 사라지지 않게 하려고 기본값을 둔다. */
    --grow-tone: var(--text-muted);

    /* 설비 상태 색도 같은 방식이다(.tone-on/off/fault/maint가 덮어쓴다).
       가동은 초록이 아니라 강조 민트 — 정상은 눈에 안 걸려야 한다.
       정지가 회색인 이유: 겨울에 냉방기를 꺼두는 것은 문제가 아니다. */
    --equip-tone: var(--text-muted);

    /* 도면·상황판의 구역 상태 — 위 상태색과 같은 체계를 쓴다.
       두 화면이 다른 색으로 같은 상태를 말하면 화면을 두 번 배워야 한다. */
    --status-normal: var(--text-dim);
    --status-occupied: var(--info);
    --status-pending: var(--warn);
    --status-incident: var(--danger);

    /* ── 어두운 화면 별칭 ──────────────────────────────────────────────
       상황판·현장 단말이 쓰던 --dark-* 는 이제 본 테마와 같은 값을 가리킨다.
       앱 전체가 어두워졌으므로 별도 팔레트를 유지할 이유가 없어졌다. */
    --dark-bg: var(--canvas);
    --dark-surface: var(--surface);
    --dark-surface-2: var(--surface-raised);
    --dark-hover: #1a2f27;
    --dark-accent-bg: var(--accent-bg);
    --dark-border: var(--border);
    --dark-border-2: var(--border-strong);
    --dark-border-3: #3a5348;
    --dark-rule: #16261f;
    --dark-text: var(--text);
    --dark-text-2: #dce7e2;
    --dark-text-3: #bccbc5;
    --dark-muted: var(--text-muted);
    --dark-muted-2: #84968f;
    --dark-faint: #7a8d86;
    --dark-blue: var(--info);
    --dark-blue-soft: #9adcff;
    --dark-green: var(--success);
    --dark-red: var(--danger);
    --dark-red-soft: #ff9ba5;
    --dark-amber: var(--warn);
    --dark-amber-strong: #c9832f;
    --dark-amber-bg: rgba(247, 198, 91, 0.16);
    --dark-amber-soft: #fadfa4;

    /* ── 간격: 4의 배수 ────────────────────────────────────────────────── */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 24px;
    --space-6: 32px;
    --space-7: 48px;

    /* ── 타이포 ────────────────────────────────────────────────────────
       숫자는 모노스페이스로 — 관제 화면에서 자릿수가 흔들리면 비교가 안 된다. */
    --font-sans: Pretendard, "Pretendard Variable", Inter, -apple-system,
                 "Segoe UI", "Malgun Gothic", "맑은 고딕", sans-serif;
    --font-mono: "SFMono-Regular", "Cascadia Code", "Roboto Mono", Consolas, monospace;
    --text-xs: 0.78rem;
    --text-sm: 0.875rem;
    --text-lg: 1.125rem;
    --text-xl: 1.5rem;
    --text-2xl: 2rem;

    --radius: 10px;
    --radius-sm: 6px;
    --radius-md: 12px; /* 상황판 패널·통계·램프 — 예전엔 12px 하드코딩이 흩어져 있었다 */
    --radius-lg: 14px;
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
    --shadow-lg: 0 18px 40px rgba(0, 0, 0, 0.45);

    /* 좌측 메뉴 폭 — 항목이 20개라 가로 배치로는 감당이 안 된다. */
    --sidebar: 244px;
}

* {
    box-sizing: border-box;
}

body {
    margin: 0;
    font-family: var(--font-sans);
    color: var(--text);
    background: var(--bg);
    line-height: 1.5;
}

main, .page-content {
    max-width: 1024px;
    margin: 0 auto;
    padding: var(--space-5) 20px 60px;
}

/* ── 타이포 위계 ─────────────────────────────────────────────────────
   한글은 자간을 아주 조금 좁히면(-0.01em) 훨씬 정돈돼 보인다. 제목 색을 파랑에서
   본문색으로 바꾼 이유: 제목까지 강조색이면 정작 강조할 것이 눈에 안 띈다.
   위계는 색이 아니라 크기·굵기·여백으로 만든다. */
h1, h2, h3, h4 {
    color: var(--text);
    letter-spacing: -0.01em;
    line-height: 1.3;
    font-weight: 700;
}

h1 { font-size: var(--text-2xl); margin: 0 0 var(--space-4); }
h2 { font-size: var(--text-xl); margin: var(--space-6) 0 var(--space-3); }
h3 { font-size: var(--text-lg); margin: var(--space-5) 0 var(--space-2); }

/* 화면 맨 위 제목 아래에 붙는 한 줄 설명 — 무엇을 하는 화면인지 먼저 말한다. */
.page-lead {
    margin: calc(var(--space-4) * -1) 0 var(--space-5);
    color: var(--text-muted);
    max-width: 78ch;
}

/* 숫자는 자리폭을 고정한다 — 표에서 자릿수가 흔들리면 비교가 어렵고 지저분해 보인다. */
table, .stat-value, .number-cell, time {
    font-variant-numeric: tabular-nums;
}

/* 키보드 이동 시 지금 어디에 있는지 항상 보이게 — 접근성이자 완성도의 문제다. */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 2px;
}

/* ── 앱 껍데기: 좌측 메뉴 + 본문 ──────────────────────────────────────
   TopNav는 로그인 전에는 아무것도 렌더링하지 않는다. 형제로 두고 flex로 나누면
   사이드바가 없을 때 본문이 알아서 전체 폭을 쓴다(고정 위치 + 여백 방식이면
   로그인 화면에서 빈 여백만 남는다). */
.app-shell {
    display: flex;
    align-items: flex-start;
    min-height: 100vh;
}

.appnav {
    position: sticky;
    top: 0;
    flex: none;
    display: flex;
    flex-direction: column;
    width: var(--sidebar);
    height: 100vh;
    padding: 22px 14px 14px;
    border-right: 1px solid var(--border);
    /* 위쪽에 강조색이 아주 옅게 깔린다 — 평면적인 검정에 깊이를 준다(LUKA와 같은 방식). */
    background:
        linear-gradient(180deg, rgba(49, 230, 151, 0.03), transparent 13rem),
        var(--canvas-deep);
    overflow-y: auto;
}

.appnav-brand {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 0 6px var(--space-4);
    text-decoration: none;
    color: var(--text);
    border-bottom: 1px solid var(--border);
}

.appnav-logo { width: 26px; height: 26px; flex: none; }
.appnav-brand span { display: flex; flex-direction: column; min-width: 0; }
.appnav-brand b { font-size: 0.95rem; letter-spacing: -0.01em; }
.appnav-brand small {
    color: var(--text-dim);
    font-size: var(--text-xs);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.appnav-groups {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    margin-top: var(--space-4);
    flex: 1 1 auto;
}

/* 묶음 제목이 곧 설명이다 — 항목 20개를 나열하는 대신 "무슨 일을 하는 화면인가"로 묶는다. */
.appnav-group-title {
    margin: 0 0 var(--space-1);
    padding: 0 8px;
    color: var(--text-dim);
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: 0.08em;
}

.appnav-item {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 38px;
    padding: 6px 8px;
    border: 1px solid transparent;
    border-radius: 8px;
    color: var(--text-muted);
    text-decoration: none;
    font-size: 0.9rem;
}

.appnav-item:hover { background: rgba(255, 255, 255, 0.03); color: var(--text); }

/* 지금 보고 있는 화면 — 강조색은 여기에만 쓴다. */
.appnav-item.is-active {
    position: relative;
    border-color: var(--accent-border);
    background: var(--accent-bg);
    color: var(--text);
    font-weight: 600;
}

.appnav-item.is-active::before {
    position: absolute;
    left: -15px;
    width: 3px;
    height: 20px;
    border-radius: 0 3px 3px 0;
    background: var(--accent);
    content: "";
}

.appnav-glyph {
    display: grid;
    place-items: center;
    width: 22px;
    height: 22px;
    flex: none;
    color: var(--text-dim);
    font-family: var(--font-mono);
    font-size: 15px;
}

.appnav-item.is-active .appnav-glyph { color: var(--accent); }

.appnav-user {
    margin-top: var(--space-4);
    padding-top: var(--space-3);
    border-top: 1px solid var(--border);
}

.appnav-user-name {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 8px;
    border-radius: 8px;
    color: var(--text);
    text-decoration: none;
}

.appnav-user-name:hover { background: rgba(255, 255, 255, 0.03); }
.appnav-user-name span { display: flex; flex-direction: column; min-width: 0; }
.appnav-user-name b { font-size: 0.9rem; }
.appnav-user-name small { color: var(--text-dim); font-size: var(--text-xs); }

.appnav-logout {
    display: block;
    margin-top: var(--space-1);
    padding: 6px 8px 6px 40px;
    color: var(--text-dim);
    text-decoration: none;
    font-size: var(--text-xs);
}

.appnav-logout:hover { color: var(--danger); }

/* 지금 떠 있는 빌드 표시. 눈에 띄면 안 되지만 물어보면 답할 수 있어야 하는 정보라
   가장 낮은 대비로 맨 아래 둔다. 전역 p 여백이 남으면 로그아웃과 붙어 보인다. */
.appnav-version {
    margin: var(--space-2) 0 0;
    padding: 0 8px 0 40px;
    color: var(--text-dim);
    font-size: var(--text-xs);
    font-variant-numeric: tabular-nums;
}

/* 좁은 화면에서는 세로 메뉴가 자리를 다 먹는다 — 가로로 눕히고 스크롤시킨다. */
@media (max-width: 900px) {
    .app-shell { flex-direction: column; }
    .appnav {
        position: static;
        width: 100%;
        height: auto;
        border-right: none;
        border-bottom: 1px solid var(--border);
    }
    .appnav-groups { flex-direction: row; overflow-x: auto; gap: var(--space-5); }
    .appnav-group { flex: none; }
    .appnav-item.is-active::before { display: none; }
}


/* ── 로딩 / 빈 상태 / 에러 3종 (전 화면 공통) ───────────────────────── */
.state-loading,
.state-empty {
    margin: var(--space-4) 0;
    padding: var(--space-4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text-muted);
    text-align: center;
}

.state-error {
    margin: var(--space-4) 0;
    padding: var(--space-3) var(--space-4);
    background: var(--danger-bg);
    border: 1px solid var(--danger-border);
    border-radius: var(--radius);
    color: var(--danger);
}

.state-success {
    margin: var(--space-4) 0;
    padding: var(--space-3) var(--space-4);
    background: var(--success-bg);
    border: 1px solid var(--success-border);
    border-radius: var(--radius);
    color: var(--success);
}

/* ── 공개 랜딩 화면 ─────────────────────────────────────────────────── */
.landing section {
    margin-bottom: var(--space-6);
}

/* ── 공개 상단 바 ────────────────────────────────────────────────────
   로그인 전 화면에 상단 바가 없어서 "제품"이 아니라 "문서"로 보였다. */
.publicnav {
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 20;
}

.publicnav-inner {
    max-width: 1024px;
    margin: 0 auto;
    padding: var(--space-3) 20px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
}

.publicnav-brand {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    text-decoration: none;
    color: var(--text);
}

.publicnav-logo { width: 24px; height: 24px; flex: none; }
.publicnav-name { font-size: var(--text-sm); letter-spacing: 0.01em; color: var(--text-muted); }
.publicnav-name b { color: var(--text); font-weight: 700; }

.publicnav-links {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    font-size: var(--text-sm);
}

.publicnav-links a { color: var(--text-muted); text-decoration: none; }
.publicnav-links a:hover { color: var(--text); }

.publicnav-login {
    padding: 6px 14px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    color: var(--text) !important;
}

.publicnav-login:hover { background: var(--surface-alt); }

/* ── 랜딩 도입부 ─────────────────────────────────────────────────────
   여백을 크게 늘렸다. 규제 담당자가 보는 화면에서 빽빽함은 "급조"로 읽힌다 —
   여백은 자신감의 표현이고, 실제로 읽는 속도를 올린다. */
.landing-hero {
    padding: var(--space-7) 0 var(--space-6);
    border-bottom: 1px solid var(--border);
}

/* 제목 위 한 줄 — 어떤 영역의 제품인지 먼저 말한다. */
.landing-eyebrow {
    margin: 0 0 var(--space-3);
    color: var(--accent);
    font-size: var(--text-sm);
    font-weight: 600;
    letter-spacing: 0.04em;
}

.landing-hero h1 {
    font-size: clamp(1.9rem, 3.4vw, 2.75rem);
    line-height: 1.25;
    letter-spacing: -0.02em;
    max-width: 20ch;
}

.landing-lead {
    font-size: 1.0625rem;
    line-height: 1.7;
    color: var(--text-muted);
    max-width: 62ch;
}

.landing-cta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    margin: var(--space-5) 0 var(--space-3);
}

.btn-primary,
.btn-secondary,
.btn-plain {
    display: inline-block;
    padding: 10px 20px;
    border-radius: var(--radius);
    text-decoration: none;
    font-weight: 600;
}

/* 기본 button이 테두리형이 되면서, 채움이 필요한 곳은 테두리까지 같이 지정해야 한다
   (안 그러면 파란 바탕에 회색 테두리가 남는다). */
.btn-primary {
    background: var(--accent);
    color: var(--accent-on);
    border: 1px solid var(--accent);
}

.btn-primary:hover {
    background: var(--accent-dark);
    border-color: var(--accent-dark);
    color: var(--accent-on);
}

.btn-secondary {
    background: var(--surface);
    color: var(--accent);
    border: 1px solid var(--border-strong);
}

.btn-secondary:hover {
    background: var(--accent-bg);
    border-color: var(--accent-light);
}

.btn-plain {
    padding: 10px 0;
    font-weight: normal;
    color: var(--text-muted);
    text-decoration: underline;
}

.landing-note {
    color: var(--text-muted);
    font-size: var(--text-sm);
    max-width: 70ch;
}

.landing-features {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: var(--space-4);
}

/* 카드에서 그림자를 뺐다 — 떠 있는 카드는 소비자 앱의 문법이다. 대신 위쪽에 얇은 선을
   그어 "항목"임을 표시한다. 계측 장비 패널의 인상에 가깝다. */
.landing-feature {
    background: var(--surface);
    border: 1px solid var(--border);
    border-top: 2px solid var(--accent);
    border-radius: var(--radius);
    padding: var(--space-5) var(--space-4) var(--space-4);
}

.landing-feature h3 {
    margin: 0 0 var(--space-2);
    font-size: 1rem;
    color: var(--text);
}

.landing-feature p {
    margin: 0;
    color: var(--text-muted);
    font-size: var(--text-sm);
}

/* (여기 있던 `.landing-plan-note`는 걷어냈다 — 공개 정가표를 없애면서 그 표 자체가
   사라졌다. 쓰는 곳 없는 규칙을 남겨두면 다음 사람이 "어디에 쓰이나" 찾느라 시간을 버린다.) */

.landing-footer {
    border-top: 1px solid var(--border);
    padding-top: var(--space-4);
    color: var(--text-muted);
    font-size: var(--text-sm);
}

.landing-copyright { color: var(--text-dim); font-size: var(--text-xs); }

/* 화면 모음 — 예전에는 화면마다 한 절씩 설계 근거를 적었다. 그게 제품의 값인데 읽는
   것만으로 가져갈 수 있었고, 방문자는 다 읽고 나면 문의할 이유가 없었다.
   지금은 넉 장을 나란히 보여주고 설명은 한 줄로 줄인다. */
.landing-gallery {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: var(--space-4);
    margin: var(--space-4) 0;
}

.landing-gallery .landing-shot { margin: 0; }
.landing-gallery figcaption { font-size: var(--text-sm); }

/* ── 랜딩: 제품 화면 ─────────────────────────────────────────────────
   전부 실제로 동작하는 화면을 찍은 것이다(목업 아님). 로딩이 페이지를 붙잡지 않도록
   loading="lazy"를 붙이고, 폰에서 가로로 넘치지 않게 max-width로 묶는다. */
.landing-shot {
    margin: var(--space-6) 0 0;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg);
    overflow: hidden;
    background: var(--surface);
}

.landing-shot img {
    display: block;
    width: 100%;
    height: auto;
}

.landing-shot figcaption {
    padding: var(--space-3) var(--space-4);
    border-top: 1px solid var(--border);
    color: var(--text-muted);
    font-size: var(--text-sm);
}

/* 기능 하나 = 설명 + 그 기능의 실제 화면. 좁은 화면에서는 세로로 쌓인다. */
.landing-split {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: var(--space-5);
    align-items: start;
    margin-bottom: var(--space-6);
}

.landing-split h3 {
    margin-top: 0;
    color: var(--text);
}

/* 도입 절차 — 순서가 실제로 의미를 갖는 유일한 목록이라 번호를 쓴다. */
.landing-steps {
    list-style: none;
    counter-reset: step;
    padding: 0;
    margin: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: var(--space-4);
}

.landing-steps li {
    counter-increment: step;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: var(--space-4);
}

.landing-steps li::before {
    content: counter(step);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    margin-bottom: var(--space-2);
    border-radius: 50%;
    background: var(--accent);
    color: var(--accent-on); /* "표면색을 글자색으로"는 의미 오용 — 표면색이 바뀌면 같이 깨진다 */
    font-weight: 700;
    font-size: var(--text-sm);
}

.landing-steps b {
    display: block;
    margin-bottom: var(--space-2);
}

.landing-steps p {
    margin: 0;
    color: var(--text-muted);
    font-size: var(--text-sm);
}

/* ── 약관/개인정보처리방침(/terms) ──────────────────────────────────────
   Terms.md를 TermsDocument가 HTML로 바꿔 넣는 자리라 개별 요소에 클래스를 붙일 수
   없다. 그래서 .terms-body 하위 선택자로만 스타일을 준다. */
.terms-page {
    max-width: 820px;
    margin: 0 auto;
    padding: var(--space-5) var(--space-4);
}

.terms-back {
    font-size: var(--text-sm);
    margin-bottom: var(--space-5);
}

.terms-body {
    line-height: 1.75;
}

.terms-body h1 {
    margin-top: 0;
}

.terms-body h2 {
    margin-top: var(--space-6);
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--border);
}

.terms-body h3 {
    margin-top: var(--space-5);
    font-size: 1.05rem;
}

/* 문서 최상단 "변호사 검토 전 초안" 경고 — 놓치면 안 되는 내용이라 눈에 띄게 한다. */
.terms-notice {
    margin: 0 0 var(--space-5);
    padding: var(--space-4);
    background: var(--warn-bg);
    border-left: 4px solid var(--warn); /* 경고 배경에 민트 선은 상태색 의미 위반이었다 */
    border-radius: var(--radius);
}

.terms-notice p {
    margin: 0 0 var(--space-3);
}

.terms-notice p:last-child {
    margin-bottom: 0;
}

/* 표 전역 규칙(nowrap·min-width 480px)은 수량 같은 짧은 값을 전제로 한 것이라,
   문장이 들어가는 약관 표에는 맞지 않는다 — 여기서만 줄바꿈을 허용한다. */
.terms-body table {
    min-width: 0;
    font-size: var(--text-sm);
}

.terms-body th,
.terms-body td {
    white-space: normal;
    vertical-align: top;
}

/* ── 현장 QR 체크인(/checkin/{token}) ────────────────────────────────
   폰에서 한 손으로 쓰는 화면이라 글자와 버튼을 크게 잡는다. */
.checkin-page {
    max-width: 480px;
    margin: 0 auto;
    padding: var(--space-4);
}

.checkin-zone {
    text-align: center;
    padding: var(--space-4) 0 var(--space-5);
    border-bottom: 1px solid var(--border);
}

.checkin-facility {
    margin: 0;
    color: var(--text-muted);
    font-size: var(--text-sm);
}

.checkin-zone h1 {
    margin: var(--space-2) 0;
    font-size: 1.8rem;
}

.checkin-meta {
    margin: 0;
    color: var(--text-muted);
}

.badge-controlled {
    display: inline-block;
    margin-left: var(--space-2);
    padding: 0 var(--space-2);
    background: var(--danger-bg);
    border: 1px solid var(--danger-border);
    border-radius: var(--radius);
    color: var(--danger);
    font-size: var(--text-sm);
}

.checkin-field {
    margin: var(--space-4) 0;
}

.checkin-field label {
    display: block;
    margin-bottom: var(--space-2);
    font-weight: 600;
}

.checkin-field select,
.checkin-field input[type=date] {
    width: 100%;
    padding: var(--space-3);
    font-size: 1.1rem;
}

.checkin-status {
    color: var(--text-muted);
    font-size: var(--text-sm);
}

.checkin-actions {
    display: flex;
    gap: var(--space-3);
    margin: var(--space-4) 0;
}

/* 장갑 낀 손으로도 누를 수 있게 충분히 크게 — 현장에서 쓰는 버튼이다. */
.checkin-button {
    flex: 1;
    padding: var(--space-5) 0;
    font-size: 1.3rem;
    font-weight: 600;
}

.checkin-result {
    font-size: 1.05rem;
}

/* ── 구역 QR 인쇄(/zone-qr) ──────────────────────────────────────── */
.qr-sheet {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    margin-top: var(--space-4);
}

.qr-card {
    width: 260px;
    padding: var(--space-4);
    /* 이 카드는 배경이 항상 흰색(인쇄물)이다 — 다크 테마 토큰(--text=밝은 회백)을 쓰면
       화면 미리보기에서 테두리·글자가 흰 바탕에 묻힌다. 밝은 표면 전용 색을 직접 준다. */
    border: 2px solid #333;
    border-radius: var(--radius);
    text-align: center;
    background: #fff;
    color: #111;
    /* 인쇄할 때 카드가 페이지 경계에서 잘리지 않게 한다. */
    break-inside: avoid;
    page-break-inside: avoid;
}

.qr-card-facility {
    color: #555; /* 흰 카드 위 — 다크 토큰(--text-muted)은 2.4:1로 흐렸다 */
    font-size: var(--text-sm);
}

.qr-card-zone {
    margin: var(--space-1) 0 var(--space-3);
    font-size: 1.3rem;
    font-weight: 700;
}

.qr-card-image {
    width: 200px;
    height: 200px;
    image-rendering: pixelated; /* 확대해도 QR 모듈 경계가 뭉개지지 않게 */
}

.qr-card-code {
    margin-top: var(--space-2);
    font-family: monospace;
    color: var(--text-muted);
}

.qr-card-hint {
    margin-top: var(--space-2);
    font-size: var(--text-sm);
    color: var(--text-muted);
}

@media print {
    /* 인쇄물에는 네비·설명·버튼이 필요 없다 — QR 카드만 남긴다. */
    .no-print, .topnav {
        display: none !important;
    }
    .qr-card {
        border-color: #000;
    }
}

/* 표 안에서 쓰는 짧은 상태 표시 — 문단용 .state-success/.state-error와 달리 칸을 안 먹는다. */
.state-inline-success {
    color: var(--success);
    font-weight: 600;
}

.state-inline-error {
    color: var(--danger);
    font-weight: 600;
}

/* 언어 전환(한국어 ↔ English) — 메뉴 항목과 헷갈리지 않게 작은 알약 모양으로 둔다. */
.lang-switch {
    display: inline-block;
    padding: 2px 10px;
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    color: var(--text-muted);
    font-size: 0.8rem;
    line-height: 1.6;
    text-decoration: none;
    white-space: nowrap;
}

.lang-switch:hover,
.lang-switch:focus-visible {
    color: var(--text);
    border-color: var(--accent-border);
}

.appnav-user .lang-switch {
    align-self: flex-start;
    margin-top: 6px;
}

/* 조직 보안 정책 스위치(사용자 관리) — 설명이 길어 체크박스와 글을 나란히 둔다. */
.policy-list {
    display: grid;
    gap: 12px;
    max-width: 760px;
}

.policy-item {
    display: flex;
    gap: 12px;
    align-items: flex-start;
    padding: 12px 14px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface);
    cursor: pointer;
}

.policy-item input[type="checkbox"] {
    margin-top: 4px;
    flex-shrink: 0;
}

/* 개체 태그 — 스캐너로 연달아 찍는 칸은 한 줄 전체를 쓴다. */
.plant-scan-field {
    grid-column: 1 / -1;
}

.plant-scan-field textarea {
    width: 100%;
    font-family: var(--font-mono, ui-monospace, Consolas, monospace);
}

/* 폼 격자에서 한 줄 전체를 쓰는 칸(긴 설명·원인·효과 확인). */
.wide-field {
    grid-column: 1 / -1;
}

.wide-field textarea,
.wide-field input {
    width: 100%;
}

.scan-camera {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 12px;
    align-items: center;
    margin-bottom: 8px;
}

.scan-video {
    display: block;
    width: 100%;
    max-width: 420px;
    margin-bottom: 12px;
    border-radius: 8px;
    background: #000;
}

.scan-video.is-off {
    display: none;
}

.link-button {
    padding: 0;
    border: 0;
    background: none;
    color: var(--accent);
    font: inherit;
    text-decoration: underline;
    cursor: pointer;
}

.login-lang {
    margin: 0 0 8px;
    text-align: right;
}

.failure-reason {
    display: block;
    color: var(--text-muted);
    font-size: var(--text-sm);
    white-space: normal;
}

/* ── 설계도(/floorplan) ──────────────────────────────────────────────── */
.floorplan-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: center;
    margin: var(--space-4) 0;
}

/* InputFile은 기본 모양이 제각각이라 라벨로 감싸 버튼처럼 보이게 한다. */
.file-button {
    position: relative;
    overflow: hidden;
    display: inline-block;
    padding: var(--space-2) var(--space-4);
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    cursor: pointer;
}

.file-button input[type=file] {
    position: absolute;
    inset: 0;
    opacity: 0;
    cursor: pointer;
}

/* ── 표시 영역(확대·이동) ─────────────────────────────────────────────
   바깥에 창을 두고 안쪽을 확대·이동시킨다. **이미지를 자르지 않는다** — 자르면 되돌릴 수
   없고 구역 좌표(0~1 비율)의 기준도 함께 틀어진다. 창 밖으로 나간 부분만 안 보일 뿐이다. */
.floorplan-viewport {
    position: relative;
    overflow: hidden;
    max-width: 1100px;
    border-radius: var(--radius);
    background: var(--canvas-deep);
    /* 확대해 놓고 손가락으로 밀어 볼 수 있게 — 전자칠판은 터치 화면이다. */
    touch-action: none;
}

.floorplan-stage {
    transform-origin: 50% 50%;
    /* 휠 한 칸마다 뚝뚝 끊기면 어디를 보고 있었는지 놓친다. */
    transition: transform 0.12s ease-out;
    will-change: transform;
}

.floorplan-canvas {
    position: relative;
    width: 100%;
    max-width: 1100px;
    /* 도면이 밝은 배경이라 경계가 보이게 테두리를 준다. */
    border: 1px solid var(--border);
    background: var(--surface);
    user-select: none;
}

.floorplan-canvas img {
    width: 100%;
    height: auto;
    display: block;
}

/* 편집 중에는 커서로 "그릴 수 있다"를 알린다. */
.floorplan-canvas.editing {
    cursor: crosshair;
}

.region-box {
    /* 한 방의 첫 조각은 <button>(키보드·스크린리더 진입점), 나머지 조각은 <div>다.
       두 태그가 픽셀 단위로 같아야 병합된 방(⧉)의 접합부에 이음매가 안 생긴다.
       전역 button 규칙은 위에서 :not(.region-box)로 이미 제외했고, 여기서는 브라우저
       기본 버튼 외형(UA 스타일시트)만 눌러 div와 동일하게 맞춘다. */
    appearance: none;
    -webkit-appearance: none;
    font: inherit;
    text-align: inherit;
    margin: 0;
    padding: 0;
    background: none;
    border-radius: 0;
    position: absolute;
    border: 2px solid;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

/* ── 3D 보기(WebGL) ───────────────────────────────────────────────────
   캔버스 하나에 전부 그린다. 예전에는 CSS 3D로 면마다 div를 세웠는데 ① 겹칠 때 앞뒤가
   실제 깊이가 아니라 DOM 순서로 정해져 방이 서로를 뚫고 나오고 ② 원근이 화면 전체에 한
   번만 걸려 카메라를 낮추면 형태가 무너지며 ③ 방이 20개만 넘어도 합성 비용이 급격히 늘어
   갈아탔다. 그리는 코드는 floorplan-3d.js(외부 라이브러리 없음 — 이 앱 CSP가 막는다). */
.floorplan-3d {
    position: relative;
    width: 100%;
    max-width: 1100px;
    /* 궤도를 돌리면 방이 위아래로 움직이므로 넉넉한 높이가 필요하다. */
    height: clamp(320px, 52vh, 620px);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background:
        radial-gradient(ellipse at 50% 42%, rgba(49, 230, 151, 0.07), transparent 62%),
        var(--canvas-deep);
    overflow: hidden;
    cursor: grab;
    /* 손가락으로 돌릴 때 브라우저가 페이지를 스크롤해 버리면 조작이 안 된다. */
    touch-action: none;
}

.floorplan-3d:active { cursor: grabbing; }

.floorplan-3d canvas {
    display: block;
    width: 100%;
    height: 100%;
}

/* 이름표 층 — 자바스크립트가 매 프레임 위치만 옮긴다. 클릭은 캔버스가 받아야 하므로
   이 층은 마우스를 통과시킨다. */
.floorplan-3d-labels {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

.floorplan-3d-label {
    position: absolute;
    top: 0;
    left: 0;
    padding: 2px 8px;
    background: rgba(5, 12, 10, 0.82);
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    color: var(--text);
    font-size: var(--text-xs);
    font-weight: 600;
    white-space: nowrap;
    /* 이름이 빈 조각(같은 방의 두 번째 사각형)은 자리를 차지하지 않게 한다. */
    line-height: 1.4;
}

.floorplan-3d-label:empty { display: none; }

/* 3D를 못 여는 환경(원격 데스크톱 등)에서 검은 화면만 남기지 않는다. */
.floorplan-3d.is-broken { cursor: default; }

.floorplan-3d-fallback {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0;
    padding: var(--space-5);
    color: var(--warn);
    text-align: center;
}

/* 구역명은 **반드시 검은 글씨**여야 한다.
   도면 이미지는 흰 바탕 도면이라 라벨 배경도 흰색인데, 앱이 다크 테마가 되면서 글자색이
   본문색(거의 흰색)을 물려받아 **흰 바탕에 흰 글씨**가 됐다. 화면에서는 "뿌옇게 안 보이는"
   것으로만 나타나서 원인을 찾기 어려운 종류다 — 색을 명시적으로 고정한다. */
.region-label {
    font-size: var(--text-sm);
    font-weight: 700;
    color: #0b1210;
    background: rgba(255, 255, 255, 0.92);
    border: 1px solid rgba(11, 18, 16, 0.18);
    padding: 1px 6px;
    border-radius: 3px;
    white-space: nowrap;
    pointer-events: none;
    letter-spacing: -0.01em;
}

.region-merged { color: #0b1210; }

.region-box.selected {
    outline: 3px solid var(--accent-dark);
    outline-offset: -1px;
}

/* 도면 위 포커스 표시는 **이중 링**으로 준다. 이 요소는 흰 설계도 이미지 위에 얹히는데,
   민트 단색 링은 흰 바탕에서 휘도 대비가 1.6:1 수준이라 저시력 사용자에게 색상 차이로만
   읽힌다(WCAG 1.4.11 미달). 어두운 링을 바깥에 하나 더 둘러 배경이 희든 어둡든 최소
   한 겹은 3:1을 넘게 만든다. */
.region-box:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 0;
    box-shadow: 0 0 0 6px var(--canvas-deep);
    border-radius: 0; /* 전역 focus-visible의 2px 모서리가 이겨 방 모양이 튀던 것 방지 */
}

/* 이미 선택된 구역에 포커스가 와도 "방금 포커스가 여기 왔다"가 구분되게 — 선택은
   안쪽 진한 아웃라인, 포커스는 바깥 이중 링으로 역할을 나눈다.
   :where로 준 전역 focus-visible(특이도 0)이 .selected에 지던 것을 이긴다. */
.region-box.selected:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 0;
    box-shadow: inset 0 0 0 3px var(--accent-dark), 0 0 0 6px var(--canvas-deep);
    border-radius: 0;
}

/* 편집 중에는 옮길 수 있음을 커서로 알리고, 오른쪽 아래에 크기조절 손잡이를 보여준다. */
.floorplan-canvas.editing .region-box {
    cursor: move;
}

.floorplan-canvas.editing .region-box::after {
    content: "";
    position: absolute;
    right: 0;
    bottom: 0;
    width: 12px;
    height: 12px;
    background: var(--accent-dark);
    cursor: nwse-resize;
}

/* 여러 조각이 한 방으로 묶여 있음을 알리는 표시. */
.region-merged {
    margin-left: 3px;
    color: var(--accent-dark);
}

/* ── 격벽·출입문 ─────────────────────────────────────────────────────
   SVG로 그린다 — 대각선이 필요해서 div로는 안 된다. viewBox 0~100에 비율 좌표를
   그대로 넣고 preserveAspectRatio=none으로 늘려 도면에 정확히 겹친다. */
.wall-layer, .wall-preview-layer {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    /* 레이어 전체가 클릭을 먹으면 그 아래 구역을 못 누른다 — 선에만 클릭을 준다. */
    pointer-events: none;
}

.wall-line {
    pointer-events: stroke;
    cursor: pointer;
    /* 얇은 선은 정확히 클릭하기 어려워서, 보이는 굵기와 별개로 클릭 영역을 넓힌다. */
    stroke-linecap: round;
    vector-effect: non-scaling-stroke;
}

.wall-partition {
    stroke: var(--text-muted);
    stroke-width: 5;
}

.wall-door {
    stroke: var(--brand-green);
    stroke-width: 5;
    stroke-dasharray: 8 5;
}

.wall-preview {
    stroke: var(--accent-dark);
    stroke-width: 5;
    stroke-dasharray: 6 4;
    vector-effect: non-scaling-stroke;
}

/* 범례의 선 표시 — 색 사각형(.swatch)과 달리 선처럼 보여야 한다. */
.swatch-line {
    display: inline-block;
    width: 20px;
    height: 0;
    margin-right: 6px;
    vertical-align: 4px;
    border-top: 3px solid;
}

.swatch-line.wall-partition { border-color: var(--text-muted); }
.swatch-line.wall-door { border-color: var(--brand-green); border-top-style: dashed; }

/* ── 처음 시작하기 안내(대시보드) ───────────────────────────────────── */
.onboarding {
    margin: var(--space-4) 0 var(--space-5);
    padding: var(--space-4) var(--space-5);
    background: var(--surface-alt);
    border: 1px solid var(--accent-light);
    border-radius: var(--radius);
}

.onboarding h2 {
    margin-top: 0;
}

.onboarding-steps {
    line-height: 1.9;
    padding-left: var(--space-5);
}

/* 끝낸 단계는 톤을 낮춰서 "남은 것"이 눈에 먼저 들어오게 한다. */
.onboarding-steps li.done {
    color: var(--text-muted);
}

.onboarding-check {
    display: inline-block;
    margin-left: var(--space-2);
    padding: 0 var(--space-2);
    background: var(--success-bg);
    border: 1px solid var(--success-border);
    border-radius: var(--radius);
    color: var(--success);
    font-size: var(--text-sm);
}

/* ── 층 선택줄 ──────────────────────────────────────────────────────── */
.floor-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin: var(--space-3) 0;
    padding: var(--space-3);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

/* 층 버튼에 붙는 구역 수 — 어느 층에 방이 몇 개인지 바로 보인다. */
.floor-zone-count {
    display: inline-block;
    margin-left: 6px;
    padding: 0 6px;
    background: rgba(0, 0, 0, 0.12);
    border-radius: 10px;
    font-size: var(--text-sm);
}

/* ── 도면 실측 치수 입력줄 ───────────────────────────────────────────── */
.scale-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin: var(--space-3) 0;
    padding: var(--space-3);
    background: var(--surface-alt);
    border-radius: var(--radius);
    font-size: var(--text-sm);
}

.scale-label {
    font-weight: 600;
}

.scale-bar input[type=number] {
    width: 110px;
    padding: var(--space-2);
}

.scale-x {
    color: var(--text-muted);
}

.scale-info {
    color: var(--accent-dark);
    font-weight: 600;
}

.scale-info-missing {
    color: var(--text-muted);
    font-weight: 400;
}

.toolbar-divider {
    width: 1px;
    height: 24px;
    background: var(--border-strong);
    margin: 0 var(--space-2);
}

/* 끌어 그리는 동안의 미리보기(자바스크립트가 만든다). */
.region-preview {
    position: absolute;
    border: 2px dashed var(--accent-dark);
    background: color-mix(in srgb, var(--accent) 18%, transparent);
    pointer-events: none;
}

/* 상태별 색 — 급한 것일수록 강한 색. 색만으로 구분하지 않도록 클릭 시 숫자도 보여준다.
   :root의 --status-* 를 쓴다. 예전에는 여기서만 쓰는 hex를 직접 박아놨는데, 그러면
   같은 "이상"이 화면마다 다른 빨강으로 나온다. */
/* color도 같이 정한다 — 3D 보기의 벽·윗면이 currentColor로 이 색을 물려받는다.
   2D와 3D가 다른 색으로 같은 상태를 말하면 화면을 두 번 배워야 한다. */
.status-normal   { border-color: var(--status-normal);   color: var(--status-normal);   background: color-mix(in srgb, var(--status-normal) 16%, transparent); }
.status-occupied { border-color: var(--status-occupied); color: var(--status-occupied); background: color-mix(in srgb, var(--status-occupied) 16%, transparent); }
.status-pending  { border-color: var(--status-pending);  color: var(--status-pending);  background: color-mix(in srgb, var(--status-pending) 16%, transparent); }
.status-incident { border-color: var(--status-incident); color: var(--status-incident); background: color-mix(in srgb, var(--status-incident) 20%, transparent); }
.status-inactive { border-color: var(--border-strong);   background: color-mix(in srgb, var(--border-strong) 8%, transparent); }

.floorplan-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    margin: var(--space-3) 0;
    color: var(--text-muted);
    font-size: var(--text-sm);
}

.floorplan-legend .swatch {
    display: inline-block;
    width: 14px;
    height: 14px;
    margin-right: 6px;
    border: 2px solid;
    vertical-align: -2px;
}

.zone-detail {
    margin-top: var(--space-5);
    padding: var(--space-4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.zone-detail h2 small {
    color: var(--text-muted);
    font-size: var(--text-sm);
    font-weight: 400;
}

.stat-card-alert {
    border-color: var(--danger-border);
    background: var(--danger-bg);
}

.stat-value-sm {
    font-size: 1.1rem;
}

/* ── 기준정보 관리(/settings) ────────────────────────────────────────── */
.tab-bar {
    display: flex;
    gap: var(--space-1);
    margin: var(--space-4) 0;
    border-bottom: 2px solid var(--border);
}

.tab {
    padding: var(--space-2) var(--space-4);
    background: transparent;
    border: none;
    border-bottom: 3px solid transparent;
    cursor: pointer;
    font-size: 1rem;
    color: var(--text-muted);
}

.tab.active {
    color: var(--accent-dark);
    border-bottom-color: var(--accent);
    font-weight: 600;
}

/* 표 안에서 바로 편집하는 화면이라 입력칸이 칸을 꽉 채우게 한다. */
.tab-bar + * table input[type=text],
table input:not([type]) {
    width: 100%;
    min-width: 90px;
}

.row-actions {
    white-space: nowrap;
}

.row-actions button {
    margin-right: var(--space-1);
}

/* 맨 아래 "새로 추가" 줄 — 기존 행과 구분되게 한다. */
.row-new {
    background: var(--surface-alt);
}

/* 삭제는 되돌릴 수 없는 동작이라 눈에는 띄되, 평소에 화면을 붉게 물들이지는 않는다.
   테두리로 두고 hover에서만 채운다 — 누르려는 순간에 확실히 알린다. */
.btn-danger {
    background: var(--surface);
    color: var(--danger);
    border: 1px solid var(--danger-border);
}

.btn-danger:hover {
    /* var(--danger) 위 흰 글자는 2.7:1 — 삭제를 누르려는 바로 그 순간 안 읽혔다. */
    background: #c93248;
    color: #fff;
    border-color: #c93248;
}

/* 제목 옆에 붙는 작은 버튼(도면에서 바로 이름 변경 등). */
.btn-inline {
    margin-left: var(--space-2);
    padding: 2px var(--space-2);
    font-size: var(--text-sm);
    font-weight: 400;
    vertical-align: middle;
}

.zone-rename {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: center;
    margin-bottom: var(--space-3);
}

.zone-rename input {
    padding: var(--space-2);
}

/* 직접 추가한 점검 항목 — 자동 생성분(정기/사건)과 구분한다. */
.badge-manual {
    background: var(--accent-light);
    color: var(--accent-on); /* 민트 위 민트(대비 1.8:1)라 글자가 안 보이던 결함 */
}

.account-info {
    display: grid;
    grid-template-columns: 120px 1fr;
    gap: var(--space-2) var(--space-4);
    max-width: 420px;
}

.account-info dt {
    color: var(--text-muted);
}

.account-info dd {
    margin: 0;
    font-weight: 600;
}

.audit-package-form {
    display: flex;
    gap: var(--space-4);
    flex-wrap: wrap;
    max-width: 480px;
}

.audit-package-list {
    line-height: 1.8;
}

/* 입력란 바로 아래 붙는 형식 안내(예: 회사 코드 NCB001). */
.field-hint {
    display: block;
    margin-top: var(--space-1);
    color: var(--text-muted);
    font-size: var(--text-sm);
}

/* ── 운영 지표 카드(/operator-organizations) ─────────────────────────── */
.stat-cards {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin: var(--space-4) 0;
}

.stat-card {
    flex: 1 1 150px;
    padding: var(--space-3) var(--space-4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

/* 테스트 조직 수는 "집계에서 뺀 값"이라 나머지 지표와 시각적으로 구분한다. */
.stat-card-muted {
    background: var(--surface-alt);
    box-shadow: none;
}

.stat-label {
    display: block;
    color: var(--text-muted);
    font-size: var(--text-sm);
}

.stat-value {
    display: block;
    margin-top: var(--space-1);
    font-size: var(--text-xl);
    font-weight: 600;
}

/* 테스트·데모로 표시된 조직 행 — 실제 고객과 한눈에 구분되게 톤을 낮춘다. */
.row-test-data {
    background: var(--surface-alt);
    color: var(--text-muted);
}

/* 권한이 없어 막힌 화면 — 오류처럼 붉게 칠하지 않는다. 사용자가 뭘 잘못한 게 아니라
   그냥 권한이 없는 것이고, 붉은 화면은 "고장"으로 읽힌다. */
.access-denied {
    max-width: 520px;
    margin: var(--space-6) auto;
    text-align: center;
}

/* 표 안의 금액 입력칸 — 기본 폭이면 한 칸이 표를 통째로 밀어낸다. 숫자는 오른쪽 정렬해야
   자릿수가 눈으로 비교된다(9,900과 99,000을 왼쪽 정렬로 두면 한참 봐야 구분된다). */
.input-price {
    width: 9ch;
    margin: 0;
    text-align: right;
    font-family: var(--font-mono);
}

.badge-test {
    display: inline-block;
    margin-left: var(--space-2);
    padding: 0 var(--space-2);
    background: var(--warn-bg);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    color: var(--text-muted);
    font-size: var(--text-sm);
    white-space: nowrap;
}

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

/* 입력 양식 아래 붙는 짧은 안내문(예: /interest의 개인정보 수집 안내). */
.form-note {
    margin: var(--space-4) 0;
    color: var(--text-muted);
    font-size: var(--text-sm);
    line-height: 1.6;
}

/* 오류가 아니라 "알아두셔야 할 것" — 빨강(state-error)과 구분한다. */
.state-notice {
    margin: var(--space-4) 0;
    padding: var(--space-3) var(--space-4);
    background: var(--warn-bg);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    color: var(--text-muted);
}

@media (max-width: 640px) {
    .topnav-inner {
        gap: var(--space-2);
    }

    .topnav-links {
        order: 3;
        width: 100%;
        gap: var(--space-2) var(--space-3);
        font-size: var(--text-sm);
    }

    .topnav-user {
        margin-left: auto;
        font-size: 0.78rem;
    }
}

/* 제목은 토큰이 정한다 — 예전에는 여기 하드코딩이 :root의 --text-* 위계를 통째로
   덮어써서, 제목 크기가 토큰 통제 밖에 있었다(2026-09-05 디자인 감사). 기존 실측
   크기를 유지하되 출처를 토큰으로 옮긴다. */
h1 {
    font-size: calc(var(--text-2xl) * 0.8); /* 1.6rem — 2rem 토큰은 화면 제목으론 과했다 */
    margin: 0 0 var(--space-4);
}

h2 {
    font-size: calc(var(--text-xl) * 0.77); /* 1.15rem */
    margin: var(--space-6) 0 var(--space-2); /* 4px 그리드에 맞춤(24/8) */
}

/* 제목 옆 "바로 가기" 링크 — 제목의 크기·두께를 물려받지 않는다.
   (Home의 "설계도 편집 →"이 미정의 클래스라 제목 크기 민트 글자로 렌더되던 결함) */
h1 .section-link, h2 .section-link {
    font-size: var(--text-sm);
    font-weight: 400;
    margin-left: var(--space-2);
    vertical-align: middle;
}

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

a:hover {
    color: var(--accent-dark);
}

.table-responsive {
    width: 100%;
    overflow-x: auto;
}

/* ── 표 ──────────────────────────────────────────────────────────────
   격자 전체에 선을 긋고 머리글을 파랗게 채우던 것을 걷어냈다. 그건 스프레드시트의
   문법이고, 규제 대응 자료를 다루는 화면에서는 "급하게 만든 표"처럼 읽힌다.
   가로줄만 남기고 머리글은 색 대신 굵기·자간·아래선으로 구분한다 — 선이 줄면
   숫자 자체가 먼저 보인다. */
table {
    border-collapse: collapse;
    width: 100%;
    min-width: 480px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

th, td {
    white-space: nowrap;
    padding: 10px 14px;
    text-align: left;
    border-bottom: 1px solid var(--border);
}

thead th {
    background: var(--surface-alt);
    color: var(--text-muted);
    font-weight: 600;
    font-size: var(--text-xs);
    letter-spacing: 0.02em;
    border-bottom: 1px solid var(--border-strong);
}

/* 마지막 줄 아래 선은 표 테두리와 겹쳐 두 겹으로 보인다. */
tbody tr:last-child td { border-bottom: none; }

/* 얼룩무늬(zebra) 대신 hover로 줄을 짚는다 — 가로줄이 있으면 얼룩은 소음이다. */
tbody tr:hover { background: var(--surface-alt); }

tr.row-warn td {
    background: var(--warn-bg);
}

label {
    display: block;
    margin: 12px 0 4px;
    font-weight: 600;
    color: var(--text);
    font-size: var(--text-sm);
}

/* Blazor의 <InputText>는 type 속성을 아예 안 붙인 <input>으로 렌더링된다 —
   input[type=text]만 지정하면 로그인 폼의 회사코드/아이디 칸이 스타일을 못 받아
   비밀번호 칸보다 좁게 나오는 실제 불일치가 있었다(브라우저로 확인 후 수정).
   그래서 type이 없는 input도 함께 지정한다. */
input[type=text], input[type=password], input[type=email], input:not([type]) {
    width: 100%;
    max-width: 320px;
    padding: 8px 10px;
    border: 1px solid var(--border-strong);
    border-radius: 4px;
    font-size: 1rem;
}

input[type=text]:focus, input[type=password]:focus, input[type=email]:focus, input:not([type]):focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 30%, transparent); /* 구 팔레트 파랑 잔재였다 — 테두리(민트)와 같은 색 계열로 */
}

/* ── 버튼 ────────────────────────────────────────────────────────────
   **기본 버튼을 채운 파랑에서 테두리형으로 바꿨다.** 예전에는 모든 <button>이 강조색으로
   꽉 차서, 기준정보 표처럼 줄마다 버튼이 있는 화면이 파란 벽이 됐다. 다 강조하면
   아무것도 강조되지 않는다.

   이제 위계가 셋이다:
     · 기본(테두리)  — 저장·발급처럼 줄마다 반복되는 동작
     · 제출/btn-primary(채움) — 그 화면의 주 동작 하나
     · btn-danger(테두리 빨강) — 삭제. 누르기 전까지는 조용히 있는다. */
button:not(.region-box), input[type=submit] {
    /* margin-top 하드코딩을 제거한다 — 이게 버튼이 들어가는 컨텍스트마다 세로 정렬을
       깨서, 곳곳에서 margin:0으로 되돌리는 땜질을 낳았다(스스로 "전역 여백 함정"이라
       주석 단 자리들). 간격은 부모의 gap/여백이 통제한다는 이 파일의 원칙에 맞춘다.
       폼의 제출 버튼만 위 여백이 필요하므로 그쪽에 국소적으로 준다(아래 .card 규칙 등).

       **:not(.region-box)로 도면 구역을 아예 제외한다.** 도면 구역은 한 방의 첫 조각만
       <button>이고 나머지 조각은 <div>다(키보드 진입점을 하나로 두기 위해서다). 전역
       버튼 규칙이 button 조각에만 걸리면 같은 방이 두 모양으로 갈라진다 — 모서리·패딩·
       호버 배경·전환이 조각마다 달라져 접합부에 이음매가 보인다. 개별 리셋을 쌓는 대신
       여기서 제외하는 것이 근본 해결이다(리셋 누락은 계속 새 결함을 만든다). */
    padding: 8px 16px;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    font-size: var(--text-sm);
    font-weight: 500;
    font-family: inherit;
    cursor: pointer;
    transition: background 120ms ease, border-color 120ms ease;
}

button:not(.region-box):hover, input[type=submit]:hover {
    background: var(--surface-alt);
    border-color: var(--text-dim);
}

button:disabled {
    opacity: 0.45;
    cursor: default;
}

button:disabled:hover {
    background: var(--surface);
    border-color: var(--border-strong);
}

/* 화면의 주 동작 하나만 채운다. */
button[type=submit], input[type=submit] {
    background: var(--accent);
    color: var(--accent-on);
    border-color: var(--accent);
    font-weight: 600;
}

button[type=submit]:hover, input[type=submit]:hover {
    background: var(--accent-dark);
    border-color: var(--accent-dark);
}

.login-page {
    max-width: 360px;
    margin: 60px auto;
    padding: 28px;
    background: var(--surface);
    border-radius: var(--radius);
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

/* 전역 button margin-top을 걷어낸 뒤, 폼 흐름에서 제출 버튼 위 간격이 필요한 곳에만
   국소로 준다(예전엔 전역 규칙이 우연히 채워주던 자리). */
form button[type=submit], .card button[type=submit] { margin-top: var(--space-4); }

.login-error {
    color: var(--danger);
    background: var(--danger-bg);
    border: 1px solid var(--danger-border);
    border-radius: 4px;
    padding: 8px 12px;
}

.facility-selector {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 16px;
    padding: 10px 12px;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: 6px;
}

.facility-selector label {
    margin: 0;
    font-weight: 600;
}

.facility-selector select {
    padding: 6px 8px;
    border: 1px solid var(--border-strong);
    border-radius: 4px;
}

.checklist-toolbar {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 16px;
}

.checklist-toolbar label {
    margin: 0;
}

.checklist-toolbar input[type="date"] {
    padding: 6px 8px;
    border: 1px solid var(--border-strong);
    border-radius: 4px;
}

.checklist-summary {
    margin: 0 0 16px;
    padding: 10px 12px;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: 6px;
}

.zone-progress {
    font-size: 0.8em;
    font-weight: normal;
    color: var(--text-muted);
    margin-left: 8px;
}

.checklist-toolbar select {
    padding: 6px 8px;
    border: 1px solid var(--border-strong);
    border-radius: 4px;
}

ul.checklist {
    list-style: none;
    margin: 0 0 20px;
    padding: 0;
}

ul.checklist li {
    display: flex;
    align-items: center;
    gap: 10px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 10px 14px;
    margin-bottom: 8px;
}

ul.checklist li.checklist-done {
    background: var(--success-bg);
    border-color: var(--success-border);
}

ul.checklist li.checklist-done .checklist-title {
    text-decoration: line-through;
    color: var(--text-muted);
}

.checklist-badge {
    font-size: 0.75rem;
    font-weight: 600;
    padding: 2px 8px;
    border-radius: 10px;
    color: var(--accent-on);
    white-space: nowrap;
}

.badge-routine {
    background: var(--accent);
}

.badge-incident {
    background: var(--danger);
}

.checklist-title {
    flex: 1;
}

.checklist-status {
    color: var(--text-muted);
    font-size: 0.9rem;
}

ul.checklist button {
    margin-top: 0;
    padding: 4px 12px;
    font-size: 0.9rem;
}

/* (.zone-detail 중복 정의 제거 — 위쪽 토큰 기반 정의(--space-*, --radius)가 유일본이다) */

.validation-errors {
    list-style: none;
    margin: 0 0 8px;
    padding: 0;
}

.validation-message {
    display: block;
    color: var(--danger);
    margin: 2px 0 4px;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid var(--success);
}

.invalid {
    outline: 1px solid var(--danger);
}

/* .validation-message는 위에서 이미 정의했다 — 템플릿 기본 규칙이 뒤에서 덮어쓰던
   중복을 제거했다(같은 "오류" 의미에 빨강이 3종류였던 것을 --danger로 통일). */

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, var(--danger);
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: var(--border-strong);
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}
/* ── 입력 카드 ─────────────────────────────────────────────────────────
   작업일지·폐기·재고대사 화면의 "등록" 영역. 목록과 입력폼이 시각적으로 붙어 있으면
   지금 보고 있는 것이 기록인지 입력칸인지 헷갈린다. */
.card {
    border: 1px solid var(--border);
    border-radius: var(--radius); /* 카드류 8/10/12px 제각각이던 것을 토큰으로 통일 */
    padding: 16px 18px;
    margin: 16px 0 24px;
    background: var(--surface); /* #fff 폴백은 다크 캔버스에서 사고다 — 토큰이 항상 있다 */
}

.card > h2 {
    margin-top: 0;
}

/* ── 감사 로그 화면 ───────────────────────────────────────────────────
   변경 전/후를 한 줄에 나란히 놓는다. 두 값을 위아래로 두면 무엇이 무엇으로 바뀌었는지
   눈이 따라가지 못한다. */
.audit-filters {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    align-items: end;
    margin-bottom: var(--space-4);
}

.audit-filters > div { display: flex; flex-direction: column; gap: 4px; }
.audit-filters label { font-size: var(--text-xs); color: var(--text-muted); }
.audit-filters input, .audit-filters select { margin: 0; }
.audit-filter-actions { flex-direction: row !important; align-items: center; gap: var(--space-3) !important; }
/* 전역 button 여백이 이 줄에서 버튼만 아래로 밀어 놓는다(설계도 뷰바에서 겪은 것과 같은 함정). */
.audit-filter-actions button { margin: 0; }

.audit-verify {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-bottom: var(--space-4);
}
.audit-verify button { margin: 0; align-self: flex-start; }

/* 검증 결과의 한계 설명 — 빼면 "위변조 불가"로 읽힌다. 흐리게 두되 반드시 보이게 한다. */
.audit-limitation { margin: var(--space-2) 0 0; font-size: var(--text-xs); opacity: 0.85; }

.audit-count { color: var(--text-muted); font-size: var(--text-sm); }
.audit-entity-id { color: var(--text-dim); font-size: var(--text-xs); }

.audit-toggle {
    margin: 4px 0 0;
    padding: 2px 8px;
    font-size: var(--text-xs);
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text-muted);
}
.audit-toggle:hover { color: var(--text); border-color: var(--border-strong); }

.audit-diff {
    margin-top: var(--space-2);
    padding-left: var(--space-3);
    border-left: 2px solid var(--accent-border);
}

.audit-diff-target { margin: 0 0 4px; font-size: var(--text-xs); color: var(--text-dim); }

.audit-diff-row {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-2);
    font-size: var(--text-sm);
    padding: 2px 0;
}

.audit-diff-field { color: var(--text-muted); min-width: 110px; }
.audit-diff-before { color: var(--text-dim); text-decoration: line-through; }
.audit-diff-arrow { color: var(--text-dim); }
.audit-diff-after { color: var(--text); font-weight: 600; }

.audit-pager {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-top: var(--space-4);
}
.audit-pager button { margin: 0; }
.audit-pager span { color: var(--text-muted); font-size: var(--text-sm); }

/* ── 환경 구분 띠(운영이 아닐 때만) ───────────────────────────────────
   검증 환경과 운영 환경은 화면이 똑같아서, 색으로 갈라놓지 않으면 검증인 줄 알고 실제
   기록을 건드린다. 흐르는 배치(sticky 아님)라 본문을 가리지 않는다. */
.env-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-2);
    padding: 6px var(--space-4);
    background: var(--danger);
    color: #23060b; /* 흰 글자는 이 빨강 위에서 2.7:1 — 사고 방지 문구가 AA 미달이었다 */
    font-size: var(--text-sm);
}

.env-banner b { font-weight: 700; }
.env-banner span { opacity: 0.9; font-size: var(--text-xs); }

/* ── 시스템 정보 화면 ─────────────────────────────────────────────────
   이름을 sysinfo-로 묶는다. 전역 이름(.kv, .hint 같은 것)을 새로 만들면 나중에 같은
   이름의 규칙이 또 생겨 조용히 덮어쓴다 — 이 파일에서 실제로 두 번 겪은 사고다. */
.sysinfo-kv {
    display: grid;
    grid-template-columns: minmax(120px, max-content) 1fr;
    gap: var(--space-2) var(--space-4);
    margin: 0;
}

.sysinfo-kv dt { color: var(--text-muted); font-size: var(--text-sm); }
.sysinfo-kv dd { margin: 0; }

.sysinfo-note { color: var(--text-dim); font-size: var(--text-xs); }

.sysinfo-env {
    display: inline-block;
    padding: 2px 10px;
    border-radius: 999px;
    font-size: var(--text-xs);
    font-weight: 700;
    border: 1px solid currentColor;
}

/* 운영과 그 밖(개발·검증)을 색으로 갈라 놓는다 — 검증 환경을 운영으로 착각하고
   시험 데이터를 넣는 사고를 막는 게 목적이다. */
.sysinfo-env.is-prod { color: var(--danger); }
.sysinfo-env.is-nonprod { color: var(--accent); }

@media (max-width: 560px) {
    .sysinfo-kv { grid-template-columns: 1fr; gap: 2px; }
    .sysinfo-kv dd { margin-bottom: var(--space-2); }
}

/* 되돌릴 수 없는 동작(조직 삭제)은 색으로도 구분한다 — 확인 문구만으로는 부족하다. */
.card-danger {
    border-color: var(--danger);
    border-left-width: 4px;
    background: var(--danger-bg);
}

/* 입력칸을 화면 너비에 맞춰 자동으로 접는다. 항목이 8개쯤 되는 폼에서
   한 줄에 하나씩 쌓이면 등록 버튼이 화면 밖으로 밀린다. */
.form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 12px 16px;
    margin-bottom: 14px;
}

.form-grid .checkin-field {
    margin: 0;
}

/* ── 로트 라벨 인쇄 ──────────────────────────────────────────────────────
   화면에서는 격자로 늘어놓고, 인쇄할 때는 라벨이 페이지 경계에서 잘리지 않게 한다. */
.label-sheet {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 10px;
}

.lot-label {
    border: 1px solid #333;
    border-radius: 4px;
    padding: 8px 10px;
    background: #fff;
    color: #000;
    /* 인쇄 시 라벨이 페이지 중간에서 잘리면 못 쓴다. */
    break-inside: avoid;
    page-break-inside: avoid;
}

.lot-label-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    border-bottom: 1px solid #999;
    padding-bottom: 4px;
    margin-bottom: 6px;
}

.lot-label-code {
    font-family: Consolas, "D2Coding", monospace;
    font-size: 1.1rem;
    font-weight: 700;
    letter-spacing: 0.5px;
}

.lot-label-body {
    display: flex;
    gap: 10px;
    align-items: center;
}

.lot-label-qr {
    width: 96px;
    height: 96px;
    flex: 0 0 auto;
    image-rendering: pixelated;   /* QR을 흐릿하게 보간하면 인식률이 떨어진다 */
}

.lot-label-info {
    font-size: 0.85rem;
    line-height: 1.45;
}

.lot-label-date {
    color: #555;
    font-size: 0.78rem;
}

.lot-label-barcode {
    margin-top: 6px;
    text-align: center;
}

.lot-label-barcode svg {
    max-width: 100%;
    height: auto;
}

.lot-label-barcode-missing {
    margin-top: 6px;
    font-size: 0.78rem;
    color: var(--danger);
}

@media print {
    /* 인쇄에는 라벨만 남긴다 — 메뉴·버튼이 같이 찍히면 라벨 한 장이 낭비된다. */
    .no-print, .appnav { display: none !important; }
    .label-sheet { grid-template-columns: repeat(2, 1fr); }
    .lot-label { border-color: #000; }
}

/* 설정 예시 블록 — 사용자가 그대로 복사해 넣을 수 있어야 한다. */
.config-sample {
    background: #1e1e1e;
    color: #d4d4d4;
    padding: 12px 14px;
    border-radius: 6px;
    font-family: Consolas, "D2Coding", monospace;
    font-size: 0.85rem;
    line-height: 1.6;
    overflow-x: auto;
    white-space: pre;
}

/* ── 상황판(/display) ─────────────────────────────────────────────────
   전자칠판·빔프로젝터에 띄우는 화면이라 다른 화면과 전제가 정반대다 — 조작하지 않고,
   멀리서 보고, 며칠씩 켜둔다.

   **어두운 배경을 쓰는 이유**: 밝은 흰 화면을 회의실 빔으로 크게 쏘면 눈이 부시고,
   같은 화면을 오래 켜두는 패널에서는 흰 바탕이 번인에도 더 불리하다.

   **글자 크기를 clamp()로 잡는 이유**: 같은 화면이 24인치 모니터에도, 85인치 전자칠판에도
   뜬다. 고정 px로 잡으면 한쪽에서는 글자가 깨알같고 다른 쪽에서는 넘친다. */
/* 화면 전체를 세로 3단으로 나눈다: 헤더(얇게) · 본문(남는 자리 전부) · 아래 띠.
   본문에 minmax(0,1fr)을 주는 이유 — 그냥 1fr이면 안쪽 내용(도면·표)이 자기 크기만큼
   격자를 밀어내서 아래 띠가 화면 밖으로 밀려난다(범례가 잘려 보이던 원인). */
/* **한 화면에 다 들어와야 하는 화면이다.** 벽에 걸어두고 멀리서 보는 용도라 페이지 전체가
   스크롤되면 아래쪽(통계·환경표)은 아무도 못 본다. 그래서 판 자체를 화면 높이에 묶고,
   넘치는 것은 안쪽 영역(도면 칸·우측 패널)만 스크롤한다.
   100dvh는 모바일 주소창이 접힐 때 높이가 튀는 것을 막는다 — 100vh만 쓰면 폰에서 잘린다. */
.display-board {
    min-height: 100vh;
    height: 100dvh;
    overflow: hidden;
    display: grid;
    grid-template-rows: auto minmax(0, 1fr) auto;
    gap: clamp(10px, 1.2vw, 20px);
    background: var(--dark-bg);
    color: var(--dark-text);
    padding: clamp(12px, 1.6vw, 28px);
    font-size: clamp(1rem, 1.1vw, 1.35rem);
}

/* ── 상황판에서의 도면 크기 ───────────────────────────────────────────
   기본값(52vh)은 상황판에서 너무 컸다 — 도면이 화면 절반을 넘게 잡아서 구역 목록·생육·설비가
   아래로 밀려 스크롤해야 보였다(실제 보고). 도면은 "어느 방이 문제인가"를 위치로 알려주는
   역할이라 그만한 크기가 필요하지 않다. 남는 자리는 숫자와 목록이 쓴다. */
.display-board .floorplan-3d {
    height: clamp(220px, 34vh, 400px);
    /* 상황판 칸은 1100px보다 넓다 — 여기서 막으면 옆에 검은 여백만 남는다. */
    max-width: none;
    /* **줄어들지 않게 못 박는다.** 세로 flex 안에서 기본값(shrink:1)으로 두면 아래 내용이
       많을 때 도면부터 쥐어짜여서 1440×900에서 99px까지 납작해졌다(실측) — 넘치는 것은
       도면을 눌러서가 아니라 이 칸을 스크롤해서 해결해야 한다. */
    flex: 0 0 auto;
}

.display-board .floorplan-viewport {
    max-height: clamp(220px, 34vh, 400px);
    max-width: none;
    flex: 0 0 auto;
}

.display-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-4);
    border-bottom: 2px solid var(--dark-border);
    padding-bottom: var(--space-2);
    /* 홈 버튼이 화면 왼쪽 위에 떠 있으므로(fixed) 제목이 그 밑에 깔리지 않게 비켜준다. */
    padding-left: 92px;
}

/* 제목을 한 단계 줄였다 — 실제 화면에서 위쪽이 잘려 보였고, 상황판에서 제목은 가장
   덜 중요한 정보다(어느 현장인지는 한 번 보면 안다). line-height를 1.15로 고정해
   글자 위아래가 잘리지 않게 한다. */
.display-header h1 {
    margin: 0;
    color: #fff;
    font-size: clamp(1.3rem, 1.9vw, 2.2rem);
    line-height: 1.15;
}

.display-stamp {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

/* 시각·숫자는 자리폭을 고정한다 — 안 그러면 1초마다 글자가 좌우로 흔들려 거슬린다. */
.display-time {
    color: var(--dark-muted);
    font-variant-numeric: tabular-nums;
    font-size: clamp(1rem, 1.4vw, 1.6rem);
}

/* 데이터가 언제 들어왔는지 — 시계와 다른 정보다. 시계만 움직이고 숫자는 멈춰 있는
   상황을 사용자가 구분할 수 있어야 한다. */
.display-since {
    color: var(--dark-muted-2);
    font-size: clamp(0.75rem, 0.9vw, 1rem);
}

/* ── 구역 상세(도면에서 누른 방) ──────────────────────────────────────
   벽에 걸린 화면이라도 사람이 다가가 누르는 순간이 있다. 그때 아무 반응이 없으면
   고장으로 읽힌다(개편 과정에서 이 패널을 빠뜨렸다가 되살렸다). */
.display-zonedetail {
    margin-top: var(--space-3);
    background: var(--dark-surface-2);
    border: 1px solid var(--dark-border-2);
    border-left: 3px solid var(--accent-light);
    border-radius: 10px;
    padding: clamp(10px, 1.1vw, 18px);
}

.display-zonedetail.alert { border-left-color: var(--dark-red); }

.display-zonedetail-head {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    margin-bottom: var(--space-3);
}

.display-zonedetail-head b { color: #fff; font-size: clamp(1.1rem, 1.5vw, 1.6rem); }
.display-zonedetail-head span { color: var(--dark-muted); font-size: 0.85em; }

/* 전자칠판을 손으로 만지는 경우가 있어 닫기 버튼도 터치 크기를 지킨다. */
.display-zonedetail-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    margin: 0 0 0 auto;
    padding: 0;
    background: none;
    border: 1px solid var(--dark-border-3);
    border-radius: 999px;
    color: var(--dark-muted);
    font-size: 0.9em;
    cursor: pointer;
}

.display-zonedetail-close:hover { background: var(--dark-hover); border-color: var(--dark-faint); color: #fff; }

.display-zonedetail-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
    gap: clamp(8px, 1vw, 16px);
    margin: 0;
}

.display-zonedetail-stats div { display: flex; flex-direction: column; gap: 2px; }
.display-zonedetail-stats dt { color: var(--dark-muted); font-size: 0.8em; }
.display-zonedetail-stats dd {
    margin: 0;
    color: #fff;
    font-size: clamp(1.1rem, 1.6vw, 1.7rem);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}
.display-zonedetail-stats div.bad dd { color: var(--dark-red-soft); }

/* 그 방의 측정값을 같이 보여준다 — 구역을 눌렀는데 환경을 보려고 표를 다시 찾게 하면 안 된다. */
.display-zonedetail-env {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-3);
    padding-top: var(--space-3);
    border-top: 1px solid var(--dark-border);
}

.display-zonedetail-env span {
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
    padding: 3px 10px;
    border: 1px solid var(--dark-border-2);
    border-radius: 999px;
    color: var(--dark-text-2);
    font-variant-numeric: tabular-nums;
}

.display-zonedetail-env em { color: var(--dark-muted); font-style: normal; font-size: 0.8em; }
.display-zonedetail-env .env-high { border-color: var(--danger); color: var(--dark-red-soft); }
.display-zonedetail-env .env-low { border-color: var(--dark-blue); color: var(--dark-blue-soft); }

/* 숫자가 멈춰 있는데 멈춘 줄 모르는 것이 가장 위험하다. */
.display-stale {
    background: var(--dark-amber-bg);
    color: var(--dark-amber-soft);
    padding: 4px 14px;
    border-radius: 999px;
    font-weight: 600;
    white-space: nowrap;
}

/* ── 본문: 도면(왼쪽, 가장 큼) + 감시 램프·세부(오른쪽) ────────────────
   처음엔 큰 숫자 타일을 위에 늘어놓았는데, 실제 대형 화면에서 보니 숫자가 화면 절반을
   먹고 정작 도면이 작았다. 상황판에서 먼저 봐야 하는 것은 "어느 방인가"다. */
.display-main {
    display: grid;
    grid-template-columns: minmax(0, 2.2fr) minmax(320px, 0.85fr);
    gap: clamp(12px, 1.4vw, 24px);
    min-height: 0;
}

.display-plan {
    display: flex;
    flex-direction: column;
    /* **가운데 정렬하지 않는다.** center로 두면 내용이 세로로 가운데 몰려서 위아래에
       똑같이 큰 빈 자리가 생긴다 — 도면은 화면에서 가장 큰 자리를 차지해야 하는데,
       정작 위쪽 여백이 도면만큼 커 보였다(실제 보고). 위에서부터 채운다. */
    justify-content: flex-start;
    gap: var(--space-2);
    min-width: 0;
    min-height: 0;
    background: var(--dark-surface);
    border: 1px solid var(--dark-border);
    border-radius: var(--radius-md);
    padding: clamp(10px, 1.1vw, 20px);
    overflow: auto;
}

/* 오른쪽 세로 스택 — 램프가 맨 위(멀리서 색만 보고 판단), 세부는 아래. */
.display-side {
    display: flex;
    flex-direction: column;
    gap: clamp(10px, 1.1vw, 16px);
    min-height: 0;
    overflow: auto;
}

/* 재실 명단은 인원이 늘면 길어지므로 남는 세로 공간을 이 패널이 먹게 한다. */
.display-side-grow { flex: 1 1 auto; }

/* ── 감시 램프 ────────────────────────────────────────────────────────
   멀리서 **색만 보고** 상태를 알 수 있어야 한다. 색 의미는 도면 위 구역 색과 같게
   맞췄다(파랑=사람 있음, 빨강=문제) — 두 개가 다른 뜻이면 화면을 두 번 배워야 한다.

   꺼진 램프는 지우지 않고 어둡게만 둔다. 자리가 사라지면 "그 항목이 없는 것"인지
   "정상이라 안 보이는 것"인지 구분이 안 된다. */
.display-lamps {
    display: flex;
    flex-direction: column;
    gap: clamp(8px, 0.9vw, 14px);
}

.display-lamp {
    display: flex;
    align-items: center;
    gap: clamp(10px, 1vw, 16px);
    background: var(--dark-surface);
    border: 1px solid var(--dark-border);
    border-radius: var(--radius-md);
    padding: clamp(10px, 1.1vw, 18px);
    transition: background 240ms ease, border-color 240ms ease;
}

.lamp-dot {
    flex: none;
    width: clamp(18px, 1.6vw, 26px);
    height: clamp(18px, 1.6vw, 26px);
    border-radius: 50%;
    background: var(--dark-border-2);
    border: 1px solid var(--dark-border-3);
}

.lamp-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.lamp-text b {
    color: var(--dark-text-2);
    font-size: clamp(1rem, 1.25vw, 1.45rem);
}

.lamp-text span {
    color: var(--dark-muted-2);
    font-size: clamp(0.85rem, 1vw, 1.1rem);
}

/* 켜진 램프: 색이 들어오고 은은하게 맥동한다. */
.display-lamp.on .lamp-text span { color: var(--dark-text-3); }

.lamp-blue.on { border-color: var(--brand-blue); background: var(--dark-accent-bg); }
.lamp-blue.on .lamp-dot {
    background: var(--dark-blue);
    border-color: var(--dark-blue);
    animation: lamp-breathe 3s ease-in-out infinite;
}

.lamp-green.on { border-color: var(--dark-green); background: color-mix(in srgb, var(--dark-green) 10%, transparent); }
.lamp-green.on .lamp-dot {
    background: var(--dark-green);
    border-color: var(--dark-green);
    animation: lamp-breathe 3s ease-in-out infinite;
}

/* 문제 램프만 빠르고 강하게 점멸한다 — 나머지와 같은 속도면 눈에 걸리지 않는다. */
.lamp-red.on { border-color: var(--danger); background: color-mix(in srgb, var(--danger) 12%, transparent); }
.lamp-red.on .lamp-dot {
    background: var(--dark-red);
    border-color: var(--dark-red);
    animation: lamp-alarm 1.1s ease-in-out infinite;
}

.lamp-red.on .lamp-text b { color: var(--dark-red-soft); }

@keyframes lamp-breathe {
    0%, 100% { box-shadow: 0 0 0 0 currentColor; opacity: 1; }
    50% { box-shadow: 0 0 14px 3px currentColor; opacity: 0.75; }
}

@keyframes lamp-alarm {
    0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--danger) 85%, transparent); opacity: 1; }
    50% { box-shadow: 0 0 20px 6px color-mix(in srgb, var(--danger) 55%, transparent); opacity: 0.45; }
}

/* 며칠씩 켜두는 화면이라 깜빡임이 피로를 준다는 사람도 있다 — 시스템 설정을 존중한다.
   색(켜짐/꺼짐)만으로도 상태는 그대로 읽힌다. */
@media (prefers-reduced-motion: reduce) {
    .lamp-blue.on .lamp-dot,
    .lamp-green.on .lamp-dot,
    .lamp-red.on .lamp-dot { animation: none; }
}

/* ── 아래 띠: 숫자와 환경 표를 여기로 몰았다 ─────────────────────────── */
/* 아래 띠가 화면을 다 잡아먹지 않게 높이를 묶는다 — 구역이 많으면 환경표가 길어져서
   판이 화면 높이에 묶인 상태에서는 도면 칸을 0으로 눌러 버린다. */
.display-footer {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: clamp(12px, 1.4vw, 24px);
    align-items: stretch;
    min-height: 0;
    max-height: 32vh;
}

@media (max-width: 1200px) {
    .display-footer { grid-template-columns: 1fr; }
}

.display-stats {
    display: flex;
    gap: clamp(8px, 1vw, 18px);
    flex-wrap: wrap;
}

.display-stat {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-width: clamp(76px, 7vw, 120px);
    background: var(--dark-surface);
    border: 1px solid var(--dark-border);
    border-radius: var(--radius-md);
    padding: clamp(8px, 0.9vw, 14px);
}

.display-stat-value {
    color: #fff;
    font-size: clamp(1.4rem, 2.1vw, 3.4rem); /* 4K 상한 확장 */
    font-weight: 700;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.display-stat-value small {
    font-size: 0.45em;
    font-weight: 500;
    color: var(--dark-muted);
}

.display-stat-label {
    color: var(--dark-muted);
    font-size: clamp(0.75rem, 0.9vw, 1rem);
}

/* 도면 위 구역 색과 같은 의미(주황=점검 미완료, 빨강=이상). */
.display-stat.alert { border-color: var(--danger); background: color-mix(in srgb, var(--danger) 12%, transparent); }
.display-stat.alert .display-stat-value { color: var(--dark-red-soft); }
.display-stat.warn { border-color: var(--dark-amber-strong); background: color-mix(in srgb, var(--dark-amber-strong) 14%, transparent); }
.display-stat.warn .display-stat-value { color: var(--dark-amber); }

.display-env-empty {
    background: var(--dark-surface);
    border: 1px solid var(--dark-border);
    border-radius: var(--radius-md);
    padding: clamp(10px, 1.1vw, 18px);
    margin: 0;
}

/* 오른쪽 세로 스택 — 급한 것(환경 이탈)이 위, 참고(최근 이상)가 아래. */
/* .display-side는 위 본문 격자 쪽에서 한 번만 정의한다 — 여기에도 같은 선택자가 있어서
   나중 것이 min-height/overflow를 덮어쓰고 있었다. */

.display-panel {
    background: var(--dark-surface);
    border: 1px solid var(--dark-border);
    border-radius: var(--radius-md);
    padding: clamp(10px, 1.1vw, 20px);
}

.display-panel h2 {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    margin: 0 0 var(--space-3);
    font-size: clamp(1rem, 1.3vw, 1.5rem);
    color: var(--dark-text-2);
}

.display-count {
    font-size: 0.75em;
    font-weight: 400;
    color: var(--dark-muted);
}

.display-title {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    flex-wrap: wrap;
}

.display-subtitle {
    color: var(--dark-muted);
    font-size: clamp(1rem, 1.3vw, 1.5rem);
}

/* 화면이 살아 있다는 표시 — 시각만 있으면 "멈춘 건지 같은 값인 건지"를 알 수 없다. */
.display-live {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--dark-green);
    font-size: clamp(0.85rem, 1vw, 1.15rem);
}

.display-live i {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--dark-green);
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--dark-green) 70%, transparent);
    animation: display-pulse 2.5s infinite;
}

@keyframes display-pulse {
    70% { box-shadow: 0 0 0 9px transparent; }
    100% { box-shadow: 0 0 0 0 transparent; }
}

/* 움직임에 민감한 사람에게는 깜빡임 자체가 방해가 된다. */
@media (prefers-reduced-motion: reduce) {
    .display-live i { animation: none; }
}

/* ── 재배 환경 표 ─────────────────────────────────────────────────────
   지표가 6개라 좁은 화면에서는 넘칠 수 있다 — **표만** 가로 스크롤되게 하고
   페이지 자체는 절대 가로로 밀리지 않게 한다(이 앱의 .table-responsive와 같은 원칙). */
/* 표 자체만 스크롤한다 — 판 전체가 스크롤되면 벽 화면에서 아래쪽을 아무도 못 본다.
   (모바일에서 표가 가로로 넘칠 때 페이지가 아니라 표만 스크롤되는 것과 같은 규칙.) */
.display-envwrap {
    overflow: auto;
    min-height: 0;
}

.display-envtable {
    width: 100%;
    border-collapse: collapse;
    font-variant-numeric: tabular-nums;
}

.display-envtable th,
.display-envtable td {
    padding: 6px 8px;
    text-align: right;
    white-space: nowrap;
    border-bottom: 1px solid var(--dark-rule);
}

.display-envtable thead th {
    color: var(--dark-muted);
    font-weight: 500;
    font-size: 0.85em;
    border-bottom-color: var(--dark-border-2);
}

.display-envtable th[scope="row"],
.display-envtable thead th:first-child {
    text-align: left;
    color: #fff;
}

.display-env-value {
    display: block;
    font-size: clamp(0.95rem, 1.15vw, 1.3rem);
    font-weight: 600;
}

.display-env-target {
    display: block;
    font-size: 0.72em;
    color: var(--dark-muted-2);
}

/* 계산값(VPD) 표시 — 어느 센서에서 온 값인지 묻지 않게 한다. */
.display-env-calc {
    color: var(--accent-light);
    font-size: 0.7em;
    margin-left: 3px;
}

.display-env-none { color: var(--dark-faint); }

/* 목표를 벗어난 칸만 색을 준다 — 정상까지 칠하면 어디가 문제인지 안 보인다. */
.display-envtable td.env-high {
    background: color-mix(in srgb, var(--danger) 22%, transparent);
    box-shadow: inset 3px 0 0 var(--danger);
}

.display-envtable td.env-high .display-env-value { color: var(--dark-red-soft); }

.display-envtable td.env-low {
    background: color-mix(in srgb, var(--brand-sky) 20%, transparent);
    box-shadow: inset 3px 0 0 var(--brand-sky);
}

.display-envtable td.env-low .display-env-value { color: var(--dark-blue-soft); }

/* 끊긴 값 — 숫자는 흐리게, 칸은 주의색. 멈춘 숫자를 지금 숫자로 읽으면 안 된다. */
.display-envtable td.env-stale {
    background: color-mix(in srgb, var(--warn) 12%, transparent);
    box-shadow: inset 3px 0 0 var(--warn);
}

.display-envtable td.env-stale .display-env-value { color: var(--text-dim); }

.display-env-stale {
    display: block;
    font-size: 0.72em;
    font-weight: 600;
    color: var(--warn);
}

.display-zonecard-env .env-stale,
.display-zonedetail-env .env-stale,
.display-targets tr.env-stale .display-targets-now,
.number-cell.env-stale { color: var(--warn); }

/* 폐기 승인 줄의 승인자 PIN — 4자리라 넓을 이유가 없다. */
.disposal-pin {
    width: 7.5rem;
    letter-spacing: 0.3em;
}

/* 2단계 인증 등록 — QR은 흰 바탕이어야 휴대폰 카메라가 읽는다(어두운 테마에서도). */
.mfa-qr {
    display: block;
    margin: 12px 0;
    padding: 12px;
    background: #ffffff;
    border-radius: 8px;
    max-width: 100%;
    height: auto;
}

.mfa-secret {
    font-family: var(--font-mono);
    letter-spacing: 0.08em;
    word-break: break-all;
}

.mfa-steps {
    margin: 0 0 12px;
    padding-left: 1.2em;
    line-height: 1.7;
}

.mfa-recovery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
    gap: 6px 16px;
    margin: 10px 0;
    padding: 0;
    list-style: none;
    font-family: var(--font-mono);
}

.display-hint {
    margin: var(--space-2) 0 0;
    color: var(--dark-muted-2);
    font-size: 0.8em;
}

/* ── 재실 현황 ──────────────────────────────────────────────────────── */
.display-occupancy {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.display-occupancy li {
    display: flex;
    gap: var(--space-3);
    align-items: baseline;
    flex-wrap: wrap;
}

.display-occ-zone {
    min-width: 5.5em;
    color: var(--dark-muted);
}

.display-occ-people {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.display-occ-person {
    background: var(--dark-surface-2);
    border: 1px solid var(--dark-border-2);
    border-radius: 999px;
    padding: 2px 10px;
    color: #fff;
}

.display-occ-person small {
    color: var(--dark-muted);
    margin-left: 5px;
    font-variant-numeric: tabular-nums;
}

/* 좁은 화면(세로형 패널·노트북)에서는 도면 아래로 내린다.
   그리고 판을 화면 높이에 가두지 않는다 — 1열로 쌓인 내용이 100dvh를 넘는 순간,
   가둬두면 아래(감시 램프·환경표·통계 띠)가 어떤 스크롤로도 닿지 않는 죽은 영역이
   된다(세로형 패널·폰 실측 결함, 2026-09-05 감사). */
@media (max-width: 1100px) {
    .display-main { grid-template-columns: 1fr; }
    .display-board { height: auto; min-height: 100dvh; overflow: visible; }
}

/* 좁은 화면에서 고정 92px 들여쓰기는 화면 1/4을 버린다 — 홈 버튼을 피해 위로 비킨다. */
@media (max-width: 720px) {
    .display-header { padding-left: var(--space-4); padding-top: 44px; }
}

/* 공용 도면 컴포넌트는 조작 화면 기준(최대 1100px)이라 상황판에서는 풀어준다 —
   벽에 거는 화면에서 도면이 가운데 조그맣게 뜨면 의미가 없다. */
.display-board .floorplan-canvas {
    max-width: none;
    border-color: var(--dark-border);
}

/* 방 이름도 멀리서 읽혀야 한다. */
.display-board .region-label {
    /* 상한이 1.2rem이면 4K(1vw=38px)에서 스케일이 멈춰 몇 미터 뒤에서 안 읽힌다. */
    font-size: clamp(0.8rem, 1vw, 2.2rem);
}

.display-legend {
    display: flex;
    flex-wrap: wrap;
    gap: clamp(10px, 1.4vw, 24px);
    margin-top: var(--space-3);
    color: var(--dark-text-3);
}

/* .swatch는 .floorplan-legend 안에서만 스타일이 잡혀 있어서 여기서 따로 준다. */
.display-legend .swatch {
    display: inline-block;
    width: 16px;
    height: 16px;
    margin-right: 8px;
    border: 2px solid;
    vertical-align: -3px;
}

/* 도면을 아직 안 올린 시설도 상황판은 쓸 수 있어야 한다 — 구역 카드로 대신한다. */
/* ── 구역 블록 ────────────────────────────────────────────────────────
   각 방을 검은 블록으로 두고 상태에 따라 테두리가 빛나게 한다. 어두운 화면에서 색이
   번져 나오면(box-shadow) 멀리서도 "어느 방이 문제인지"가 먼저 눈에 걸린다 —
   전자칠판을 회의실 뒤에서 보는 게 이 화면의 전제다. */
.display-zonegrid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: clamp(8px, 1vw, 14px);
    margin-top: clamp(10px, 1.2vw, 18px);
}

.display-zonecard {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
    /* 카드 바탕은 캔버스보다도 어둡다 — 방이 "파여 있는" 느낌을 주고, 그래야 테두리
       빛이 도드라진다. */
    background: var(--canvas-deep);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: clamp(10px, 1.1vw, 16px);
    margin: 0;
    text-align: left;
    color: var(--text-muted);
    font-family: inherit;
    cursor: pointer;
    transition: box-shadow 160ms ease, border-color 160ms ease, transform 120ms ease;
}

.display-zonecard:hover { transform: translateY(-1px); }

.display-zonecard-head {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.display-zonecard b {
    flex: 1;
    color: var(--text);
    font-size: clamp(1rem, 1.35vw, 1.5rem);
    letter-spacing: -0.01em;
}

/* 통제구역 표시 — 규제상 출입 기록이 필수인 방이라 한눈에 구분돼야 한다. */
.display-zonecard-lock {
    padding: 2px 7px;
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    color: var(--text-muted);
    font-size: var(--text-xs);
    font-style: normal;
}

/* 누가 있는지 — 숫자만 있으면 "3명"까지는 알아도 누구인지 모른다. */
.display-zonecard-occ {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    min-height: 24px;
}

.display-zonecard-occ em {
    padding: 2px 8px;
    background: var(--info-bg);
    border: 1px solid var(--info-border);
    border-radius: 999px;
    color: var(--info);
    font-style: normal;
    font-size: var(--text-xs);
}

.display-zonecard-empty { color: var(--text-muted); font-size: clamp(0.75rem, 0.85vw, 1.05rem); }

.display-zonecard-env {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    font-variant-numeric: tabular-nums;
    font-family: var(--font-mono);
    /* 벽걸이 전제의 다른 요소는 전부 clamp인데 이 정보만 12.5px 고정이라 판독 불가였다. */
    font-size: clamp(0.75rem, 0.85vw, 1.05rem);
}

.display-zonecard-env span { color: var(--text-muted); }
.display-zonecard-env .env-high { color: var(--danger); }
.display-zonecard-env .env-low { color: var(--info); }

.display-zonecard-foot {
    display: flex;
    justify-content: space-between;
    gap: var(--space-2);
    padding-top: 6px;
    border-top: 1px solid var(--border);
    color: var(--text-muted); /* --text-dim은 소형 글자에서 AA 미달(약 4:1) */
    font-size: clamp(0.75rem, 0.85vw, 1.05rem);
    font-variant-numeric: tabular-nums;
}

.display-zonecard-alert { color: var(--danger); font-weight: 600; }

/* ── 상태별 발광 ──────────────────────────────────────────────────────
   테두리 색 + 바깥으로 번지는 빛. 정상인 방은 빛나지 않는다 — 평소가 조용해야
   문제가 있는 방만 눈에 들어온다. */
.display-zonecard.status-normal { border-color: var(--border); background: var(--canvas-deep); }

.display-zonecard.status-occupied {
    border-color: var(--info);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--info) 25%, transparent), 0 0 18px color-mix(in srgb, var(--info) 18%, transparent);
}

.display-zonecard.status-pending {
    border-color: var(--warn);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--warn) 25%, transparent), 0 0 18px color-mix(in srgb, var(--warn) 16%, transparent);
}

.display-zonecard.status-incident {
    border-color: var(--danger);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--danger) 35%, transparent), 0 0 24px color-mix(in srgb, var(--danger) 28%, transparent);
    animation: zone-alarm 2.2s ease-in-out infinite;
}

.display-zonecard.status-inactive { opacity: 0.45; }

.display-zonecard.selected {
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent-border), 0 0 22px color-mix(in srgb, var(--accent) 22%, transparent);
}

/* 이상이 난 방만 천천히 숨쉰다 — 전부 깜빡이면 아무것도 급해 보이지 않는다. */
@keyframes zone-alarm {
    0%, 100% { box-shadow: 0 0 0 1px color-mix(in srgb, var(--danger) 35%, transparent), 0 0 24px color-mix(in srgb, var(--danger) 28%, transparent); }
    50% { box-shadow: 0 0 0 1px color-mix(in srgb, var(--danger) 50%, transparent), 0 0 34px color-mix(in srgb, var(--danger) 45%, transparent); }
}

@media (prefers-reduced-motion: reduce) {
    .display-zonecard.status-incident { animation: none; }
}


/* 패널 제목은 .display-panel h2 한 곳에서만 정의한다 — 예전에 여기에도 같은 선택자가
   있어서 크기가 두 군데서 갈렸다. */

.display-alerts {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.display-alerts li {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: clamp(8px, 0.9vw, 14px);
    background: var(--dark-surface-2);
    border-left: 4px solid var(--dark-border-2);
    border-radius: 0 6px 6px 0;
}

.display-alerts li.high { border-left-color: var(--danger); background: color-mix(in srgb, var(--danger) 12%, transparent); }
.display-alerts li b { color: #fff; }
.display-alert-when { color: var(--dark-muted); font-variant-numeric: tabular-nums; font-size: 0.85em; }
.display-alert-msg { color: var(--dark-text-3); }

.display-loading,
.display-empty {
    color: var(--dark-muted);
    padding: var(--space-4) 0;
}

.display-fatal {
    padding: clamp(24px, 5vw, 80px);
    text-align: center;
}

.display-fatal h1 { color: var(--dark-red-soft); }
.display-fatal-note { color: var(--dark-muted); }

/* ── 현장 터치 단말(/kiosk) ───────────────────────────────────────────
   서서, 장갑 낀 손으로, 지나가면서 누른다. 그래서 상황판보다도 버튼이 크고
   한 화면에 한 가지만 묻는다. 44px는 터치 최소 크기 권고인데 여기서는 훨씬 크게 잡는다 —
   현장에서 잘못 눌러 남의 이름으로 기록이 남는 것이 가장 나쁜 결과다. */
.kiosk {
    min-height: 100vh;  /* dvh 미지원 폴백 */
    min-height: 100dvh; /* 주소창 수축 시 하단 확인 버튼이 크롬에 깔리던 것 방지 */
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: clamp(14px, 2vh, 28px);
    background: var(--dark-bg);
    color: var(--dark-text);
    padding: clamp(16px, 3vh, 40px);
    font-size: clamp(1rem, 1.2vw, 1.4rem);
}

.kiosk-header {
    width: 100%;
    max-width: 720px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    border-bottom: 1px solid var(--dark-border);
    padding-bottom: var(--space-3);
}

.kiosk-label {
    display: block;
    color: var(--dark-muted);
    font-size: 0.8em;
}

.kiosk-zone {
    color: #fff;
    font-size: clamp(1.2rem, 1.8vw, 1.8rem);
}

.kiosk-question {
    margin: 0;
    color: #fff;
    font-size: clamp(1.5rem, 2.6vw, 2.6rem);
    text-align: center;
}

.kiosk-input {
    width: min(100%, 560px);
    padding: clamp(12px, 1.6vh, 20px);
    font-size: clamp(1.4rem, 2.2vw, 2rem);
    text-align: center;
    background: var(--dark-surface-2);
    color: #fff;
    border: 2px solid var(--dark-border-2);
    border-radius: var(--radius-md);
}

.kiosk-input:focus {
    outline: none;
    border-color: var(--accent-light);
}

.kiosk-zones {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: clamp(10px, 1.4vh, 18px);
    width: min(100%, 900px);
}

.kiosk-btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    min-height: clamp(72px, 10vh, 110px);
    padding: clamp(10px, 1.4vh, 18px);
    background: var(--dark-surface-2);
    color: #fff;
    border: 2px solid var(--dark-border-2);
    border-radius: var(--radius-lg);
    font-size: clamp(1.1rem, 1.6vw, 1.6rem);
    font-weight: 600;
    cursor: pointer;
}

.kiosk-btn small { color: var(--dark-muted); font-size: 0.62em; font-weight: 400; }
.kiosk-btn.on { border-color: var(--brand-blue); background: var(--dark-accent-bg); }
.kiosk-btn.on::after { content: " ✓"; font-weight: 700; } /* 색 변화만으론 색맹에게 안 보인다 */
.kiosk-btn:disabled { opacity: 0.4; cursor: default; }

.kiosk-btn-wide {
    width: min(100%, 560px);
    background: var(--accent);
    border-color: var(--accent);
    /* 부모(.kiosk-btn)의 흰 글자를 그대로 물려받으면 형광 민트 위 흰 글자(대비 1.6:1)가
       된다 — 키오스크에서 가장 중요한 버튼이 가장 안 읽히는 결함(2026-09-05 감사).
       btn-primary와 같은 규칙: 강조색 배경엔 어두운 글자. */
    color: var(--accent-on);
}

/* 터치 대상은 최소 44px — 장갑 낀 손으로 서서 누르는 화면이라 권고치를 그대로 지킨다.
   "구역 변경"·"이름 다시 입력"이 이 크기 미만이면 옆 버튼을 잘못 누른다. */
.kiosk-btn-ghost {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    background: none;
    border: 1px solid var(--dark-border-2);
    color: var(--dark-muted);
    border-radius: 999px;
    padding: 8px 20px;
    font-size: clamp(0.85rem, 1vw, 1.1rem);
    cursor: pointer;
}

/* 입력한 자리수만 점으로 보여준다 — 어깨너머로 숫자가 보이면 PIN의 의미가 없다. */
.kiosk-pin-dots {
    display: flex;
    gap: clamp(12px, 1.6vw, 22px);
}

.kiosk-pin-dots span {
    width: clamp(18px, 2vw, 26px);
    height: clamp(18px, 2vw, 26px);
    border-radius: 50%;
    border: 2px solid var(--dark-border-3);
}

.kiosk-pin-dots span.filled { background: var(--accent-light); border-color: var(--accent-light); }

.kiosk-keypad {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: clamp(8px, 1.2vh, 16px);
    width: min(100%, 460px);
}

.kiosk-key {
    min-height: clamp(64px, 9vh, 96px);
    background: var(--dark-surface-2);
    color: #fff;
    border: 2px solid var(--dark-border-2);
    border-radius: var(--radius-lg);
    font-size: clamp(1.5rem, 2.4vw, 2.2rem);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    cursor: pointer;
}

.kiosk-key:active { background: var(--dark-hover); }
.kiosk-key-fn { color: var(--dark-muted); }
.kiosk-key-go { background: var(--accent); border-color: var(--accent); color: var(--accent-on); font-size: clamp(1.1rem, 1.5vw, 1.5rem); }
.kiosk-key-go:disabled { opacity: 0.35; cursor: default; }

/* ── 결과 ─────────────────────────────────────────────────────────────
   멀리서도 성공/실패가 즉시 읽혀야 한다. 입실=파랑, 퇴실=회색, 실패=빨강으로
   도면·상황판과 같은 색 의미를 유지한다. */
.kiosk-result {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: clamp(12px, 2vh, 24px);
    text-align: center;
    width: 100%;
}

.kiosk-result h1 { margin: 0; font-size: clamp(1.5rem, 3vw, 3rem); color: #fff; }

.kiosk-result-mark {
    width: clamp(80px, 12vh, 140px);
    height: clamp(80px, 12vh, 140px);
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    font-size: clamp(2.5rem, 6vh, 4.5rem);
    font-weight: 700;
}

.kiosk-result.in .kiosk-result-mark { background: color-mix(in srgb, var(--brand-blue) 25%, transparent); color: var(--dark-blue); border: 3px solid var(--brand-blue); }
.kiosk-result.out .kiosk-result-mark { background: color-mix(in srgb, var(--status-normal) 20%, transparent); color: var(--dark-text-2); border: 3px solid var(--status-normal); }
.kiosk-result.fail .kiosk-result-mark { background: color-mix(in srgb, var(--danger) 25%, transparent); color: var(--dark-red-soft); border: 3px solid var(--danger); }
.kiosk-result.fail h1 { color: var(--dark-red-soft); }

.kiosk-result-time { color: var(--dark-muted); font-variant-numeric: tabular-nums; font-size: clamp(1.1rem, 1.6vw, 1.6rem); }
.kiosk-hint { color: var(--dark-muted-2); font-size: 0.85em; }
.kiosk-warn { color: var(--dark-amber); }

/* ── 스크롤바 ────────────────────────────────────────────────────────
   기본 스크롤바는 밝은 회색이라 어두운 화면에서 유일하게 튀는 밝은 요소가 된다.
   강조색(민트)으로 맞추고 굵기를 키웠다 — 관제 화면은 서서 보거나 멀리서 보는 일이
   많아 얇은 손잡이는 잡기 어렵다. */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--accent-dark) transparent;
}

::-webkit-scrollbar {
    width: 14px;
    height: 14px;
}

::-webkit-scrollbar-track {
    background: var(--canvas-deep);
}

::-webkit-scrollbar-thumb {
    background: linear-gradient(180deg, var(--accent), var(--accent-dark));
    border: 3px solid var(--canvas-deep);
    border-radius: 999px;
}

::-webkit-scrollbar-thumb:hover {
    background: var(--accent-light);
}

::-webkit-scrollbar-corner {
    background: var(--canvas-deep);
}

/* ── 우측 환경 목표 대비 표 ───────────────────────────────────────────
   "지금 6.1"만으로는 판단이 안 되고 "5.8~6.3 안"이어야 판단이 된다 —
   현재값과 목표를 반드시 나란히 둔다. */
.display-targets {
    width: 100%;
    min-width: 0;
    border: none;
    background: none;
    font-variant-numeric: tabular-nums;
}

.display-targets th,
.display-targets td {
    padding: 5px 0;
    border-bottom: 1px solid var(--dark-rule);
    white-space: nowrap;
}

.display-targets tr:last-child th,
.display-targets tr:last-child td { border-bottom: none; }

.display-targets th[scope="row"] {
    color: var(--text-muted);
    font-weight: 500;
    font-size: var(--text-sm);
    text-align: left;
}

.display-targets th i {
    color: var(--info);
    font-style: normal;
    font-size: 0.75em;
    margin-left: 3px;
}

.display-targets-now {
    text-align: right;
    color: var(--text);
    font-family: var(--font-mono);
    font-size: clamp(0.95rem, 1.15vw, 1.25rem);
    font-weight: 600;
}

.display-targets-goal {
    text-align: right;
    padding-left: var(--space-3) !important;
    color: var(--text-dim);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
}

/* 목표를 벗어난 줄만 색이 든다 — 정상까지 칠하면 어디가 문제인지 안 보인다. */
.display-targets tr.env-high .display-targets-now { color: var(--danger); }
.display-targets tr.env-low .display-targets-now { color: var(--info); }
.display-targets tr.env-none .display-targets-now { color: var(--text-dim); }

/* ── 생육 단계 표시 ───────────────────────────────────────────────────
   색은 최상단 :root의 --grow-* 에서 온다. 여기서는 어느 단계가 어느 색인지만 잇는다.
     start  시작(파종·클로닝)   grow  자라는 중(영양·생식)
     flower 결실(개화)          post  후처리(수확·건조) */
.tone-start { --grow-tone: var(--grow-start); }
.tone-grow { --grow-tone: var(--grow-grow); }
.tone-flower { --grow-tone: var(--grow-flower); }
.tone-post { --grow-tone: var(--grow-post); }
.tone-idle { --grow-tone: var(--grow-idle); }

/* 구역 블록 안의 단계 배지 */
.display-zonecard-grow {
    display: flex;
    align-items: baseline;
    gap: 6px;
    margin-top: 2px;
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.display-zonecard-grow em {
    padding: 1px 7px;
    border: 1px solid var(--grow-tone);
    border-radius: 999px;
    color: var(--grow-tone);
    font-style: normal;
    font-weight: 600;
}

.display-zonecard-grow b {
    color: var(--text);
    font-family: var(--font-mono);
}

.display-zonecard-grow i {
    color: var(--text-dim);
    font-style: normal;
    font-family: var(--font-mono);
}

/* 도면에서 구역을 눌렀을 때의 상세 줄 */
.display-zonedetail-grow {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: var(--space-2);
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.display-zonedetail-grow .grow-chip {
    padding: 2px 10px;
    border: 1px solid var(--grow-tone);
    border-radius: 999px;
    color: var(--grow-tone);
    font-weight: 600;
}

.display-zonedetail-grow b {
    color: var(--text);
    font-family: var(--font-mono);
}

/* 우측 생육 현황 패널 — 단계별로 묶는다 */
.display-grow {
    list-style: none;
    margin: 0;
    padding: 0;
}

.display-grow li {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: baseline;
    gap: var(--space-2);
    padding: 6px 0;
    border-bottom: 1px solid var(--dark-rule);
}

.display-grow li:last-child { border-bottom: none; }

.display-grow-stage {
    padding: 1px 9px;
    border: 1px solid var(--grow-tone);
    border-radius: 999px;
    color: var(--grow-tone);
    font-size: var(--text-xs);
    font-weight: 600;
    white-space: nowrap;
}

.display-grow-zones {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 10px;
    min-width: 0;
}

.display-grow-zone {
    color: var(--text);
    font-size: var(--text-sm);
}

.display-grow-zone small {
    margin-left: 3px;
    color: var(--text-dim);
    font-family: var(--font-mono);
}

.display-grow-total {
    color: var(--grow-tone);
    font-family: var(--font-mono);
    font-weight: 600;
    white-space: nowrap;
}

/* ── 뒤로가기 ─────────────────────────────────────────────────────────
   전자칠판·현장 단말은 브라우저를 `--app` 모드로 띄워 주소창도 뒤로가기 버튼도 없다.
   그래서 앱이 직접 준다. 화면 왼쪽 위 고정 — 사람이 뒤로가기를 찾는 자리가 거기다.

   색은 강조 민트(--accent)를 쓴다. 이 화면에서 "누를 수 있는 것"은 전부 이 색이라
   뒤로가기만 다른 색을 주면 그 규칙이 깨진다. 평소에는 테두리만, 손을 올리면 채운다. */
.backbtn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-bottom: var(--space-4);
    padding: 7px 15px 7px 12px;
    background: var(--surface);
    border: 1px solid var(--accent-border);
    border-radius: 999px;
    color: var(--accent);
    font-size: var(--text-sm);
    font-weight: 600;
    text-decoration: none;
    /* 손가락으로 누르는 화면이라 최소 크기를 지킨다(모바일 접근성 기준). */
    min-height: 36px;
}

.backbtn:hover,
.backbtn:focus-visible {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-on);
}

.backbtn-arrow {
    font-size: 1.05em;
    line-height: 1;
}

/* 인쇄물에 뒤로가기 버튼이 찍힐 이유가 없다(로트 라벨·구역 QR을 인쇄한다). */
@media print {
    .backbtn { display: none; }
}

/* 상황판·현장 단말용 — 화면 위에 띄운다.
   absolute가 아니라 fixed인 이유: 현장 단말(키오스크)은 세로로 스크롤되는데,
   absolute면 스크롤을 올려야만 보인다.

   **흐리게 두지 않는다.** 처음에는 벽 화면이라고 opacity 0.55 + 회색 테두리 + 화살표만
   남겼는데, 거의 검은 배경에서 사실상 보이지 않아 "버튼이 없다"로 읽혔다(실제 보고).
   전자칠판은 몇 미터 떨어져서 보는 화면이라 오히려 여기서 더 또렷해야 한다. */
.backbtn-quiet {
    position: fixed;
    top: var(--space-3);
    left: var(--space-3);
    z-index: 20;
    margin-bottom: 0;
    /* 배경을 깔아야 도면·구역 블록 위에 얹혔을 때도 글자가 읽힌다. */
    background: var(--surface-raised);
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
}

/* 상황판 제목이 이 버튼 밑으로 들어가지 않게 자리를 비운다 —
   버튼이 화면에 떠 있으므로(fixed) 제목 쪽에서 피해줘야 한다. */
/* 재배 환경 화면의 측정값 입력 — 값 칸과 기록 버튼을 한 줄로 붙인다 */
.env-measure {
    display: flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
}

.env-measure input {
    width: 96px;
    margin: 0;
}

.env-measure .form-note { margin: 0; }

/* CCTV 패널 — 영상이 여기 뜨지 않는다는 것을 화면이 분명히 말해야 한다 */
.display-cctv-open {
    display: block;
    padding: var(--space-3);
    background: var(--accent-bg);
    border: 1px solid var(--accent-border);
    border-radius: var(--radius);
    color: var(--accent);
    font-weight: 600;
    text-align: center;
    text-decoration: none;
}

.display-cctv-open:hover {
    background: var(--accent);
    color: var(--accent-on);
}

/* ── 재배 자리 배치 ───────────────────────────────────────────────────
   8홀 1세트를 점으로 그린다. 세트는 한 덩어리로 묶어 그리고 덩어리 사이는 띄운다 —
   점을 균등하게 늘어놓으면 "세트"라는 단위가 사라져 현장 말과 그림이 안 맞는다. */
.planting-blocks {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
}

.planting-block {
    min-width: 0;
    padding: var(--space-3);
    background: var(--canvas-deep);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.planting-block-head {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
}

.planting-block-head b { color: var(--text); }

.planting-block-head span {
    color: var(--text-dim);
    font-size: var(--text-xs);
    font-family: var(--font-mono);
}

.planting-block-head em {
    margin-left: auto;
    color: var(--accent);
    font-style: normal;
    font-family: var(--font-mono);
    font-weight: 600;
}

.planting-tier-label {
    margin: var(--space-2) 0 3px;
    color: var(--text-dim);
    font-size: var(--text-xs);
}

.planting-sets {
    display: flex;
    gap: 7px;
}

/* 세로 배치: 세트가 위에서 아래로 늘어서고, 한 세트 안의 홀은 가로로 놓인다.
   가로 배치: 그 반대. 도면과 눈으로 맞춰봐야 해서 방향이 실제와 같아야 한다. */
.planting-sets.is-vertical { flex-direction: column; }
.planting-sets.is-vertical .planting-set { flex-direction: row; }
.planting-sets.is-horizontal { flex-direction: row; flex-wrap: wrap; }
.planting-sets.is-horizontal .planting-set { flex-direction: column; }

.planting-set {
    display: flex;
    gap: 2px;
    padding: 2px;
    background: var(--surface);
    border-radius: 4px;
}

.hole {
    width: 8px;
    height: 8px;
    background: transparent;
    border: 1px solid var(--border-strong);
    border-radius: 50%;
}

/* 채워진 자리 — 어두운 바탕에서 스스로 빛나 보이게 한다. */
.hole.filled {
    background: var(--accent);
    border-color: var(--accent);
    box-shadow: 0 0 4px var(--accent-border);
}

.planting-block-note {
    margin: var(--space-2) 0 0;
    color: var(--text-dim);
    font-size: var(--text-xs);
}

.planting-summary {
    margin-top: var(--space-3);
    color: var(--text-muted);
    font-size: var(--text-sm);
}

.planting-summary b {
    color: var(--text);
    font-family: var(--font-mono);
}

/* 자리보다 주수가 많으면 둘 중 하나가 틀린 것이다 — 조용히 넘어가면 안 된다. */
.planting-over { color: var(--warn); }

/* 상황판에서는 자리가 좁아 점을 조금 줄인다 */
.display-zonedetail-layout {
    margin-top: var(--space-3);
    padding-top: var(--space-3);
    border-top: 1px solid var(--dark-rule);
}

.display-zonedetail-layout .hole { width: 6px; height: 6px; }

.display-zonedetail-layout .planting-block {
    padding: var(--space-2);
    background: transparent;
}

/* ── 2D / 3D 전환 ─────────────────────────────────────────────────────
   두 화면(설계도·상황판)이 같은 모양을 써야 해서 규칙을 공유한다. */
.floorplan-viewtabs,
/* 2D/3D 알약과 그 옆의 버튼·안내를 한 줄에 세운다. 설계도 화면의 .floorplan-viewbar와
   같은 규칙이다 — 같은 컨트롤이 화면마다 다르게 보이지 않게 한다. */
.display-viewbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
}

/* **전역 여백을 이 줄에서 걷어낸다.** 앱 전역 규칙이 버튼에 margin-top 16px, 알약에
   margin-bottom 8px을 주고 있어서 두 컨트롤의 높이와 위치가 어긋났다(실측: 시점 초기화가
   16px 아래로 밀리고 stretch도 남은 44px에만 걸렸다). 줄 간격은 위 gap이 준다. */
.display-viewbar > * {
    margin: 0;
}

.display-viewtabs {
    display: inline-flex;
    /* 이게 없으면 알약 안의 글자가 위로 떠오른다(stretch 기본값 때문). */
    align-items: center;
    gap: 2px;
    padding: 2px;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: 999px;
}

.floorplan-viewtabs button,
.display-viewtabs button {
    /* **알약 안쪽 버튼의 전역 여백은 여기서 걷어낸다.** 바깥 줄의 `> * { margin: 0 }`은
       직계 자식만 잡는데 2D/3D 버튼은 알약의 자식(=줄의 손자)이라 전역 규칙
       `button { margin-top: 16px }`이 그대로 걸려 있었다. 그 16px 때문에 **글자가
       알약 중심보다 8px 아래로 내려앉고**(실측) 알약 높이도 36px이어야 할 것이
       52px로 불어서, 옆 버튼이 아무리 높이를 맞춰도 글자 줄이 어긋났다. */
    margin: 0;
    min-height: 44px; /* 벽 앞에 서서 팔 뻗어 누른다 — 마우스보다 정확도가 낮다 */
    padding: 2px 14px;
    background: transparent;
    border: none;
    border-radius: 999px;
    color: var(--text-muted);
    font-size: var(--text-xs);
    font-weight: 600;
    cursor: pointer;
}

.floorplan-viewtabs button.on,
.display-viewtabs button.on {
    background: var(--accent);
    color: var(--accent-on);
}

.floorplan-viewbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
}

/* 상황판의 `.display-viewbar > *`와 같은 이유다 — 전역 규칙이 버튼에만 margin-top 16px을
   주기 때문에, 그냥 두면 버튼 글자만 스팬(배율·안내)보다 8~9px 아래로 내려간다(실측).
   줄 간격은 위 gap이 준다. */
.floorplan-viewbar > * { margin: 0; }

.floorplan-viewzoom {
    color: var(--text-dim);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
}

/* (여기 있던 `.display-viewtabs { margin-bottom }`은 걷어냈다 — 알약이 홀로 서 있던
   시절의 여백인데, 지금은 `.display-viewbar`의 gap이 간격을 준다. 같은 선택자가 두 벌로
   갈려 있어서 위쪽 규칙을 덮고 있었고, 그 8px 때문에 줄 높이가 알약보다 커져서 옆 버튼이
   아무리 stretch해도 높이가 안 맞았다 — `.display-header`가 두 벌로 갈렸던 것과 같은 사고다.) */

/* 전체 보기로 되돌리는 버튼은 알약 밖에 둔다 — 2D/3D와 성격이 다르다.
   2D/3D 알약과 나란히 서는 버튼이다. **높이를 숫자로 박지 않고 늘린다** — 상황판 글자
   크기가 화면 폭에 따라 바뀌어서(.display-board의 clamp) 알약 높이도 같이 변하는데,
   여기에 52px 같은 값을 박으면 다른 해상도에서 다시 어긋난다. align-self: stretch면
   줄에서 가장 큰 것(=알약)과 항상 같은 높이가 된다. */
.display-viewreset {
    align-self: stretch;
    padding: 2px 18px;
    border: 1px solid var(--border-strong) !important;
    border-radius: 999px;
    color: var(--text-muted);
}

/* 시계 옆 날짜·요일 — 벽에 걸어두는 화면이라 "오늘이 며칠인지"를 여기서 보게 된다. */
.display-date {
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
}

.display-date b {
    color: var(--text-muted);
    font-weight: 600;
}

.display-weekday {
    color: var(--text-dim);
    font-size: var(--text-sm);
}

/* 주말은 색을 달리한다 — 기록을 볼 때 "이 날이 주말이었나"가 바로 읽혀야 한다. */
.display-weekday.weekend { color: var(--danger); }

/* 갱신 루프가 한 바퀴 실패한 이유. 조용히 죽는 대신 여기에 남는다. */
.display-looperror {
    margin: var(--space-1) 0 0;
    color: var(--warn);
    font-size: var(--text-xs);
    text-align: right;
}

/* 면적 고정 — 자동 계산과 다른 값이라는 것이 보여야 한다 */
.zone-area-lock {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin: var(--space-3) 0;
}

.zone-area-lock label {
    margin: 0;
    color: var(--text-muted);
    font-size: var(--text-sm);
}

.zone-area-lock input {
    width: 130px;
    margin: 0;
}

.zone-area-unit {
    color: var(--text-dim);
    font-family: var(--font-mono);
}

.stat-card-manual {
    border-color: var(--accent-border);
}

.stat-card-manual .stat-label { color: var(--accent); }

/* ── 설비 상태 색 ─────────────────────────────────────────────────────
   가동은 초록이 아니라 차분한 민트다 — 정상은 눈에 안 걸려야 한다.
   정지는 회색: 겨울에 냉방기를 꺼두는 것은 문제가 아니다. */
.tone-on { --equip-tone: var(--accent); }
.tone-off { --equip-tone: var(--text-dim); }
.tone-fault { --equip-tone: var(--danger); }
.tone-maint { --equip-tone: var(--warn); }

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

.display-equip li {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: baseline;
    gap: var(--space-2);
    padding: 5px 0;
    border-bottom: 1px solid var(--dark-rule);
}

.display-equip li:last-child { border-bottom: none; }

.display-equip-zone {
    color: var(--text-muted);
    font-size: var(--text-xs);
    white-space: nowrap;
}

.display-equip-name {
    min-width: 0;
    color: var(--text);
    font-size: var(--text-sm);
}

.display-equip-status {
    color: var(--equip-tone);
    font-size: var(--text-xs);
    font-weight: 600;
    white-space: nowrap;
}

/* 도면에서 구역을 눌렀을 때의 설비 줄 */
.display-zonedetail-equip {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: var(--space-2);
}

.display-zonedetail-equip span {
    display: inline-flex;
    align-items: baseline;
    gap: 5px;
    padding: 2px 9px;
    background: var(--canvas-deep);
    border: 1px solid var(--equip-tone);
    border-radius: 999px;
    color: var(--text);
    font-size: var(--text-xs);
}

.display-zonedetail-equip em {
    color: var(--text-dim);
    font-style: normal;
}

.display-zonedetail-equip i {
    color: var(--equip-tone);
    font-style: normal;
    font-family: var(--font-mono);
}

/* 설비 화면(/equipment) */
.equip-setpoint {
    display: flex;
    align-items: center;
    gap: 6px;
}

.equip-setpoint small {
    color: var(--text-dim);
    font-family: var(--font-mono);
}

.equip-actions {
    display: flex;
    gap: 6px;
    white-space: nowrap;
}

/* (tr.row-warn 중복 정의 제거 — 표 공통 구역의 정의가 유일본이다) */

tr.row-new td { background: var(--surface-alt); }

/* 생육 입력 화면(/cultivation)의 요약 칩 */
.cultivation-summary {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: var(--space-4);
}

.cultivation-chip {
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
    padding: 5px 14px;
    background: var(--surface);
    border: 1px solid var(--grow-tone);
    border-radius: 999px;
    color: var(--grow-tone);
    font-size: var(--text-sm);
}

.cultivation-chip b {
    color: var(--text);
    font-family: var(--font-mono);
}

.cultivation-chip small {
    color: var(--text-muted);
    font-family: var(--font-mono);
}

.cultivation-chip.tone-total {
    border-color: var(--border-strong);
    color: var(--text-muted);
}

/* ── 상황판: 도면을 어두운 청사진처럼 ─────────────────────────────────
   업로드하는 도면은 흰 바탕에 검은 선이다. 어두운 관제 화면에 그대로 얹으면 도면만
   하얗게 떠서 화면이 두 동강 난다. 색을 반전시키면 **검은 바탕에 밝은 선**이 되어
   청사진처럼 읽히고, 화면 전체와 톤이 맞는다.
   hue-rotate를 같이 거는 이유: invert만 하면 도면 안의 색(구역 채색 등)이 보색으로
   뒤집혀 이상해진다 — 180도 돌려 원래 색조로 되돌린다. */
.display-board .floorplan-canvas {
    background: var(--canvas-deep);
}

.display-board .floorplan-canvas img {
    filter: invert(0.92) hue-rotate(180deg) contrast(1.05) brightness(0.95);
}

/* 구역 종류 켜고 끄기 */
.display-filters {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: var(--space-3);
}

.display-filter {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 44px; /* 터치 최소 크기 — 키오스크에선 지키면서 여기만 미달이었다 */
    margin: 0;
    padding: 4px 12px;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    color: var(--text);
    font-size: var(--text-xs);
    cursor: pointer;
}

.display-filter small {
    color: var(--text-dim);
    font-family: var(--font-mono);
}

.display-filter:hover { border-color: var(--accent-border); }

/* 꺼둔 종류 — 지우지 않고 흐리게 남긴다. 사라지면 "그런 구역이 없는 것"인지
   "꺼둔 것"인지 구분이 안 된다. */
.display-filter.off {
    background: transparent;
    border-color: var(--border);
    color: var(--text-dim);
    text-decoration: line-through;
}

/* ── 커뮤니티 뉴스피드 ─────────────────────────────────────────────────
   스크롤로 내려가며 읽는 화면이다. 카드가 곧 상세라 목록/상세를 나누지 않는다.

   시각 언어는 인스타그램(좁은 기둥·아바타·전폭 사진·하트)에 싸이월드의 손맛(방명록 점선,
   끝맺음 장식)을 얹었다 — 단, 색은 이 앱의 다크·민트 토큰만 쓴다. 감성을 위해 팔레트를
   새로 여는 순간 관제 화면과 다른 제품처럼 보인다. */

/* 피드는 좁은 가운데 기둥이다 — 인스타그램이 데스크톱에서도 470px을 고집하는 이유와 같다.
   카드를 화면 폭대로 늘이면 게시판이 되고, 좁히면 이야기가 된다. */
.feed-shell {
    max-width: 560px;
    margin: 0 auto;
}

.feed-tabs {
    display: flex;
    align-items: center;
    gap: 2px;
    padding: 2px;
    margin-bottom: var(--space-3);
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: 999px;
}

.feed-tabs button {
    /* 모바일 엄지 조작 — 다른 탭바(44px)와 일관되게 44px로. */
    flex: 1;
    min-height: 44px;
    margin: 0;
    padding: 2px 18px;
    background: transparent;
    border: none;
    border-radius: 999px;
    color: var(--text-muted);
    font-size: var(--text-sm);
    font-weight: 600;
    cursor: pointer;
}

.feed-tabs button.on {
    background: var(--accent);
    color: var(--accent-on);
}

.feed-notice {
    margin: 0 0 var(--space-3);
    padding: var(--space-2) var(--space-3);
    background: var(--info-bg);
    border: 1px solid var(--info-border);
    border-radius: var(--radius-sm);
    color: var(--text-muted);
    font-size: var(--text-sm);
}

/* 전시장 탭은 밖으로 나가는 글이라 경고색으로 구분한다 — 사내인 줄 알고 쓰면 사고다. */
.feed-notice.warn {
    background: var(--warn-bg);
    border-color: var(--warn-border);
}

/* ── 아바타 ──────────────────────────────────────────────────────────
   사진이 없는 서비스라 이름 첫 글자로 얼굴을 만든다. 배경 그라디언트는 실제 브랜드
   4색(--brand-*)에서 나온다 — 같은 사람은 언제나 같은 색이라 아바타가 얼굴처럼 기능하고,
   무채색 관제 화면 속에서 커뮤니티만 사람 냄새가 나는 이유가 이 색이다.
   바깥의 민트 링은 인스타그램 스토리 링의 인용 — 테두리가 아니라 배경 레이어로 그린다. */
.feed-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 2px solid var(--canvas);
    box-shadow: 0 0 0 2px var(--accent-border);
    color: #fff;
    font-size: var(--text-sm);
    font-weight: 700;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
    user-select: none;
}

.feed-avatar.g0 { background: linear-gradient(135deg, var(--brand-green), var(--brand-sky)); }
.feed-avatar.g1 { background: linear-gradient(135deg, var(--brand-sky), var(--brand-blue)); }
.feed-avatar.g2 { background: linear-gradient(135deg, var(--brand-blue), var(--brand-purple)); }
.feed-avatar.g3 { background: linear-gradient(135deg, var(--brand-purple), var(--brand-green)); }

/* ── 글쓰기 ────────────────────────────────────────────────────────── */
.feed-composer {
    margin-bottom: var(--space-5);
    padding: var(--space-4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
}

.feed-composer-row {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    margin-bottom: var(--space-2);
}

.feed-composer textarea {
    flex: 1;
    min-width: 0;
    margin: 0;
    resize: vertical;
    border-radius: var(--radius);
}

.feed-composer-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    flex-wrap: wrap;
}

/* 파일 선택칸 기본 모양은 브라우저마다 제각각이라 감추고 라벨을 버튼처럼 쓴다. */
.feed-photo-pick {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--text-muted);
    font-size: var(--text-sm);
    cursor: pointer;
}

.feed-photo-pick input[type="file"] {
    max-width: 200px;
    font-size: var(--text-xs);
}

/* "공유" 버튼 — 이 화면의 주 동작이라 채운다. 전역 버튼 여백은 이 줄에서 걷어낸다
   (뷰바에서 배운 그 함정 — 전역 margin-top 16px이 줄 정렬을 깬다). */
.feed-post-btn {
    margin: 0;
    padding: 6px 22px;
    background: var(--accent);
    border-color: var(--accent);
    border-radius: 999px;
    color: var(--accent-on);
    font-weight: 700;
}

.feed-picked {
    margin: 0 0 var(--space-2);
    color: var(--text-dim);
    font-size: var(--text-xs);
}

.feed-error {
    margin: var(--space-2) 0 0;
    color: var(--danger);
    font-size: var(--text-sm);
}

/* ── 빈 피드 ─────────────────────────────────────────────────────────
   "글이 없습니다" 한 줄은 고장으로 읽힌다 — 비어 있음도 화면이다. */
.feed-empty {
    padding: var(--space-7) 0;
    color: var(--text-dim);
    text-align: center;
}

.feed-empty-mark {
    display: block;
    margin-bottom: var(--space-2);
    font-size: 44px;
    line-height: 1;
    opacity: 0.5;
}

.feed {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}

.feed-card {
    padding: var(--space-4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
}

.feed-card-head {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-bottom: var(--space-3);
}

/* 이름 블록이 남는 폭을 다 가져가고 배지가 오른쪽 끝에 선다. */
.feed-card-who {
    flex: 1;
    min-width: 0;
}

.feed-card-who b {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.feed-card-who small {
    display: block;
    color: var(--text-dim);
    font-size: var(--text-xs);
}

.feed-badge {
    padding: 1px 10px;
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    color: var(--text-muted);
    font-size: var(--text-xs);
    white-space: nowrap;
}

.feed-badge.all {
    background: var(--warn-bg);
    border-color: var(--warn-border);
    color: var(--warn);
}

.feed-card-title {
    margin: 0 0 var(--space-1);
    font-size: var(--text-lg);
}

/* 줄바꿈을 그대로 살린다 — 사람이 엔터로 나눈 문단이 한 줄로 붙으면 읽기 어렵다. */
.feed-card-body {
    margin: 0 0 var(--space-3);
    white-space: pre-wrap;
    word-break: break-word;
    line-height: 1.6;
}

/* ── 사진: 카드 여백을 뚫고 전폭으로 ─────────────────────────────────
   사진 둘레에 카드 패딩이 남아 있으면 "첨부파일"로 읽히고, 가장자리까지 밀면
   "게시물"로 읽힌다 — 인스타그램 카드의 문법. 음수 여백은 카드 패딩(--space-4)과
   짝이다. 카드 패딩을 바꾸면 여기도 같이 바꿔야 한다. */
.feed-images {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 2px;
    margin: 0 calc(-1 * var(--space-4)) var(--space-3);
    background: var(--canvas-deep);
}

.feed-images a {
    display: block;
    line-height: 0;
}

.feed-images img {
    width: 100%;
    aspect-ratio: 1;
    object-fit: cover;
    display: block;
}

/* 한 장짜리 글은 잘라내지 않는다 — 현장 사진은 세로 계기판·가로 도면이 섞여 있어서
   정사각형으로 자르면 정보가 날아간다. 여러 장일 때만 격자 정합을 위해 자른다. */
.feed-images.single {
    grid-template-columns: 1fr;
}

.feed-images.single img {
    aspect-ratio: auto;
    height: auto;
    max-height: 600px;
    object-fit: contain;
}

/* ── 하트 줄 ───────────────────────────────────────────────────────── */
.feed-card-actions {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding-top: var(--space-2);
    border-top: 1px solid var(--border-soft);
}

/* 하트는 알약이 아니라 맨 아이콘이다 — 테두리를 두르는 순간 "버튼"이 되고,
   아이콘만 두면 "반응"이 된다. 누르는 맛은 색과 팝 애니메이션이 준다. */
.feed-like {
    margin: 0;
    padding: 2px 6px;
    background: transparent;
    border: none;
    color: var(--text-muted);
    font-size: 22px;
    line-height: 1;
    cursor: pointer;
}

.feed-like:hover {
    background: transparent;
    color: var(--text);
}

.feed-like.on {
    color: var(--danger);
}

.feed-like-heart {
    display: inline-block;
}

/* 누른 순간 한 번 튀어오른다 — .on이 붙을 때만 재생되고 되돌릴 땐 조용하다. */
.feed-like.on .feed-like-heart {
    animation: feed-heart-pop 0.35s cubic-bezier(0.2, 1.6, 0.4, 1);
}

@keyframes feed-heart-pop {
    0% { transform: scale(0.6); }
    55% { transform: scale(1.35); }
    100% { transform: scale(1); }
}

.feed-like-count {
    font-size: var(--text-sm);
}

.feed-count {
    color: var(--text-dim);
    font-size: var(--text-sm);
}

.feed-more-comments {
    margin: var(--space-2) 0 0;
    color: var(--text-dim);
    font-size: var(--text-xs);
}

/* ── 댓글: 방명록 ────────────────────────────────────────────────────
   점선이 싸이월드 방명록의 그 줄이다 — 실선보다 한 뼘 가볍고, 본문과 댓글의
   위계가 선 굵기 없이도 갈라진다. */
.feed-comments {
    margin: var(--space-2) 0 0;
    padding: 0;
    list-style: none;
}

.feed-comments li {
    padding: var(--space-2) 0;
    border-bottom: 1px dashed var(--border-strong);
    font-size: var(--text-sm);
    line-height: 1.5;
}

.feed-comments li b {
    margin-right: var(--space-2);
}

.feed-comments li small {
    margin-left: var(--space-2);
    color: var(--text-dim);
    font-size: var(--text-xs);
}

/* 댓글 입력은 카드에 붙은 마지막 줄이다 — 입력칸 테두리를 걷어내면 "댓글 달기..."가
   카드의 일부로 읽힌다(인스타그램 문법). 포커스가 가면 밑줄만 살아난다. */
.feed-comment-write {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-top: var(--space-2);
    padding-top: var(--space-2);
    border-top: 1px solid var(--border-soft);
}

.feed-comment-write input {
    flex: 1;
    min-width: 0;
    margin: 0;
    padding: 6px 2px;
    background: transparent;
    border: none;
    border-bottom: 1px solid transparent;
    border-radius: 0;
}

.feed-comment-write input:focus {
    outline: none;
    border-bottom-color: var(--accent-border);
}

/* "게시"는 글자 버튼이다 — 내용이 있을 때만 민트로 살아난다. */
.feed-comment-write button {
    margin: 0;
    padding: 4px 10px;
    background: transparent;
    border: none;
    color: var(--accent);
    font-size: var(--text-sm);
    font-weight: 700;
    cursor: pointer;
}

.feed-comment-write button:hover {
    background: transparent;
    color: var(--accent-light);
}

.feed-comment-write button:disabled {
    color: var(--text-dim);
}

.feed-sentinel {
    height: 1px;
}

.feed-loadmore {
    margin-top: var(--space-3);
    text-align: center;
}

/* 끝맺음 장식 — 미니홈피 하단의 그 마침표. 피드가 뚝 끊기지 않고 닫힌다. */
.feed-end {
    margin-top: var(--space-4);
    color: var(--text-dim);
    font-size: var(--text-xs);
    letter-spacing: 0.2em;
    text-align: center;
}

/* ── 로트 계보 사슬 ──────────────────────────────────────────────────────
   "종자 → 묘 → 생체 → 건조물"을 한 줄로 보여준다. 표로 나열하면 단계가 나란히
   놓일 뿐이고, 화살표로 이어야 "이것이 저것에서 나왔다"가 읽힌다.
   좁은 화면에서는 가로로 스크롤한다 — 줄바꿈하면 사슬이 끊겨 보인다. */
.lot-lineage {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    overflow-x: auto;
    padding: var(--space-3) 0;
    margin-bottom: var(--space-3);
    border-bottom: 1px solid var(--border);
}

.lot-lineage-node {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 0 0 auto;
    padding: var(--space-2) var(--space-3);
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    cursor: pointer;
    min-height: 44px; /* 터치 최소 크기 — 발표 중 태블릿으로 짚을 수 있어야 한다 */
    justify-content: center;
}

/* 지금 보고 있는 로트는 사슬 위에서 즉시 찾을 수 있어야 한다. */
.lot-lineage-node.on {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 12%, transparent);
}

.lot-lineage-node b { font-size: var(--text-sm); }
.lot-lineage-node small { color: var(--text-muted); font-size: var(--text-xs); white-space: nowrap; }
.lot-lineage-rate { color: var(--accent); font-weight: 600; margin-left: 4px; }
.lot-lineage-arrow { color: var(--text-dim); flex: 0 0 auto; font-size: var(--text-lg); }

/* ── 일일 기록 검토 ─────────────────────────────────────────────────────
   심사자가 보는 화면이라 "무엇을 봤는가"와 "무슨 근거로 보는가"가 같은 자리에 있어야 한다.
   근거 조항 블록(.review-clause)을 항목마다 붙이는 이유가 이것이다. */

/* 어느 판의 지침인지 — 판과 시행일이 없는 인용은 심사 자리에서 근거가 되지 못한다. */
.gacp-cover {
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: var(--radius);
    padding: var(--space-3);
    margin-bottom: var(--space-3);
    font-size: var(--text-sm);
}

.gacp-cover-line { display: flex; gap: var(--space-2); padding: 2px 0; flex-wrap: wrap; }
.gacp-cover-key { color: var(--text-muted); min-width: 92px; flex: 0 0 auto; }

.review-toolbar {
    display: flex;
    gap: var(--space-3);
    align-items: flex-end;
    flex-wrap: wrap;
    margin-bottom: var(--space-3);
}

.review-toolbar > * { margin: 0; }

.review-headline {
    border: 1px solid var(--border);
    border-left: 3px solid var(--text-dim);
    border-radius: var(--radius);
    padding: var(--space-3);
    margin-bottom: var(--space-3);
    background: var(--surface);
}

.review-headline-approved { border-left-color: var(--success); }
.review-headline-reviewed { border-left-color: var(--warn); }
.review-headline-pending { border-left-color: var(--danger); }

.review-headline-main { display: flex; gap: var(--space-3); flex-wrap: wrap; align-items: baseline; }
.review-headline-sub { color: var(--text-muted); font-size: var(--text-sm); margin-top: 4px; }
.review-attention { color: var(--warn); }

.review-section {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: var(--space-3);
    margin-bottom: var(--space-3);
    background: var(--surface);
}

.review-section-head { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; margin-top: 0; }
.review-section-summary { color: var(--text-muted); font-size: var(--text-sm); margin: 4px 0 var(--space-2); }

.review-badge {
    font-size: var(--text-xs);
    padding: 2px 8px;
    border-radius: 999px;
    border: 1px solid var(--border);
    flex: 0 0 auto;
}

.review-badge-normal { color: var(--success); background: var(--success-bg); border-color: var(--success-border); }
.review-badge-attention { color: var(--warn); background: var(--warn-bg); border-color: var(--warn-border); }
.review-badge-nodata { color: var(--text-dim); background: var(--surface-alt); }

/* 근거 조항. 원문(영문)을 그대로 싣는다 — 우리가 옮긴 한국어만 있으면 심사자가 대조할 수 없다. */
.review-clause {
    background: var(--surface-alt);
    border-left: 2px solid var(--info);
    border-radius: var(--radius);
    padding: var(--space-2) var(--space-3);
    margin-bottom: var(--space-2);
}

.review-clause-ref { color: var(--info); font-size: var(--text-xs); font-weight: 600; }
.review-clause-meaning { font-size: var(--text-sm); margin-top: 2px; }

.review-clause-quote {
    color: var(--text-muted);
    font-size: var(--text-xs);
    margin: 6px 0 0;
    padding: 0;
    border: 0;
    font-style: italic;
}

.review-lines { list-style: none; margin: 0; padding: 0; }

.review-lines li {
    display: flex;
    gap: var(--space-3);
    padding: 6px 0;
    border-bottom: 1px solid var(--border-soft);
    flex-wrap: wrap;
}

.review-lines li:last-child { border-bottom: 0; }
.review-line-label { color: var(--text-muted); flex: 0 0 auto; min-width: 150px; font-size: var(--text-sm); }
.review-line-value { flex: 1 1 260px; font-size: var(--text-sm); }
.review-line-flagged .review-line-label { color: var(--warn); font-weight: 600; }

.review-note { color: var(--text-muted); font-size: var(--text-xs); margin-top: var(--space-2); }
.review-verdict { border-left: 3px solid var(--accent); }
