/* 헤더 · 푸터 · 로그인 · 대시보드 크롬 — 화면 공통 껍데기 */

:root{ --nav-h:80px }   /* 시안 nav 높이 (2026-09-10) */

/* ── 헤더 ── */
/* ~~★ 시안은 흰 바입니다 — 히어로가 어두워지므로 투명으로 두면 글자가 안 보입니다~~
   -> 2026-09-14 사용자 결정으로 뒤집었습니다. 흰 바가 어두운 히어로 위에 얹혀
      떠 보인다는 지적입니다. 투명으로 두되 **글자를 같이 밝은 쪽으로** 바꾸면
      위 주석이 걱정한 "안 보인다" 는 생기지 않습니다.
      히어로가 있는 화면만 .nav-over 를 답니다 — 지금은 index.html 하나입니다 */
.nav{position:fixed;top:0;left:0;right:0;z-index:90;height:var(--nav-h);
  display:flex;align-items:center;border-bottom:1px solid transparent;
  background:var(--surface);
  transition:border-color .3s ease, background-color .3s ease}
/* 굴린 뒤 — 불투명 흰 바(.nav 기본값 그대로). 테두리만 켭니다.
   ~~반투명 + 배경 흐림(유리 바)~~ -> 2026-09-14 만들었다가 되돌렸습니다.
     ① 어두운 히어로 위에서 바탕이 rgb(205,209,218) 까지밖에 안 밝아져
        메뉴 글자(--muted)의 대비가 **4.01:1** 로 떨어집니다. AA 기준 4.5 미달이고,
        흰 바에서는 같은 글자가 6.08:1 입니다. 기준을 맞추려면 불투명도를 0.88 까지
        올려야 하는데 그건 이미 유리가 아닙니다
     ② 애초에 앵커 전환 구멍을 고친 것은 유리가 아니라 **작은 문턱** 이었습니다 */
.nav.solid{border-bottom-color:var(--line)}

/* 맨 위 — 완전 투명 + 밝은 글자. 어두운 히어로가 있는 화면만 답니다
   (로그인 버튼은 파란 알약이라 어두운 바탕에서도 그대로 읽힙니다) */
.nav-over:not(.solid){background:transparent}
.nav-over:not(.solid) .brand{color:#fff}
.nav-over:not(.solid) .nav-links a{color:rgba(255,255,255,.78)}
.nav-over:not(.solid) .nav-links a:hover,
.nav-over:not(.solid) .nav-links a.on{color:#fff;background:rgba(255,255,255,.14)}
/* 컨테이너 1200 (padding 32 포함 1264).
   ★ gap 38 인 이유 — 시안은 글자 사이가 50 인데 우리 링크에는 눌림 여백 12 가 있습니다.
     38 + 12 = 50. 여백을 지우면 집히는 범위(WCAG 2.5.8 24x24)가 줄어듭니다 */
.nav-in{max-width:1264px;margin:0 auto;padding:0 32px;width:100%;
  display:flex;align-items:center;gap:38px}
.brand{font-weight:700;font-size:18px;letter-spacing:-.02em;text-decoration:none;
  color:var(--t-title);white-space:nowrap;transition:color .3s ease}   /* 320 에서 두 줄로 접혔습니다 (09-11) */
.nav-links{display:flex;gap:26px}
.nav-links a{font-size:18px;font-weight:500;color:var(--muted);text-decoration:none;
  white-space:nowrap;   /* 메뉴 글자는 어느 폭에서도 두 줄이 되면 안 됩니다 */
  padding:7px 12px;border-radius:8px;transition:color .2s,background .2s}
.nav-links a:hover{color:var(--t-title);background:color-mix(in srgb,var(--fg) 6%, transparent)}
.nav-links a.on{color:var(--t-title)}
.nav .sp{flex:1}
/* 시안 Button state=login — 160x54 알약, 파란 그라디언트 */
.nav-login{display:inline-flex;align-items:center;justify-content:center;
  width:160px;height:54px;border:0;border-radius:500px;
  background:linear-gradient(90deg,var(--brand) 5%,var(--brand-2));
  color:#fff;font-size:20px;font-weight:700;letter-spacing:-.02em;text-decoration:none;
  transition:filter .2s}
.nav-login:hover{filter:brightness(1.1)}
.nav-prog{position:absolute;left:0;bottom:-1px;height:2px;width:0;background:var(--accent);
  display:block}
/* 메뉴가 두 줄로 접히던 구간 920~821 (2026-09-11 사용자 지적) ──────────────
   .nav-in 이 요구하던 폭 = 64(여백) + 122(로고) + 114(gap 3개) + 456(링크) + 160(로그인)
                        = 916.  920 에서 문서 폭이 905 라 링크가 접혔습니다.
   숨기는 820 까지 한 줄로 버티도록 980 에서 미리 줄입니다 — 980 은 이미 쓰는
   브레이크포인트입니다(새로 만들지 않습니다 · 반응형 원칙 ⑦). */
@media(max-width:980px){
  .nav-in{gap:20px}
  .nav-links{gap:16px}
  .nav-links a{font-size:16px;padding:6px 9px}
}
/* ── 좁은 폭 — 상단바도 시안 값을 상한으로 두고 줄입니다 (2026-09-11) ────────
   여기가 메뉴가 사라지는 자리라, 바의 성격이 "로고 + 주요 액션" 으로 바뀝니다.
   그런데 높이 80 · 버튼 160x54 · 글자 20 이 320 까지 데스크톱 값 그대로 내려가고
   있었습니다 — nav 만 반응형 원칙 ③④(시안 값은 상한)를 안 지키고 있었습니다.

   잰 값 (390)   버튼이 바 폭의 43 % · 바 높이의 68 %. 320 에서는 로고가 두 줄
   관례          바 안 터치 목표 권장은 44x44(WCAG 2.5.5)라 54 는 과합니다.
                iOS 는 컨트롤이 들어가는 바에 56pt 를 잡습니다
   고친 값        전부 0.8배. 버튼 폭만 고정을 풀고 내용 + 여백으로

   ★ --nav-h 는 CSS 8곳과 landing.js 가 읽습니다(호출할 때마다 다시 읽습니다).
     여기서 변수만 바꾸면 앵커 위치 · 고정 구간 · scroll-margin-top 이 같이 따라옵니다 */
@media(max-width:820px){
  :root{ --nav-h:64px }
  .nav-in{gap:14px;padding:0 22px}
  .nav-links{display:none}
  .nav-login{width:auto;min-width:0;height:44px;padding:0 18px;font-size:16px}
}

/* 헤더가 덮지 않도록 */
.pad-nav{padding-top:var(--nav-h)}

/* ── 푸터 ──
   여기에 있던 .foot 공용 규칙은 2026-09-10 삭제했습니다 — 시안 footer 로 바뀌면서
   site.css 쪽 하나로 모았습니다. shell.css 가 뒤에 붙어 조용히 이기고 있었습니다.
   .foot 을 쓰는 페이지는 index.html 하나뿐입니다(app.html 은 .foot-u) */

/* ── 목업 설정 (?dev=1 일 때만 나타납니다) ── */
.dev{position:fixed;right:18px;bottom:18px;z-index:95;font-size:12px}
.dev-tg{background:var(--fg);color:var(--bg);border:0;border-radius:100px;
  padding:9px 15px;font-size:12px;font-weight:700;cursor:pointer;
  box-shadow:0 10px 30px -10px rgba(0,0,0,.5)}
.dev-p{background:var(--surface);border:1px solid var(--line);border-radius:14px;
  padding:14px 16px;margin-bottom:10px;box-shadow:0 18px 50px -18px rgba(0,0,0,.4);
  display:grid;gap:11px;min-width:216px}
.dev-r{display:flex;align-items:center;justify-content:space-between;gap:14px}
.dev-r>span{color:var(--muted);font-size:11.5px}
.seg{display:inline-flex;border:1px solid var(--line);border-radius:7px;overflow:hidden}
.seg button{font:inherit;font-size:11.5px;font-weight:700;padding:5px 10px;border:0;
  cursor:pointer;background:transparent;color:var(--muted);transition:background .15s,color .15s}
.seg button:hover{color:var(--fg)}
.seg button[aria-pressed="true"]{background:var(--fg);color:var(--bg)}

/* ── 샘플 안내 띠 ── */
.notice{background:color-mix(in srgb,var(--accent) 9%, transparent);
  border-bottom:1px solid color-mix(in srgb,var(--accent) 22%, transparent);
  font-size:12.5px;color:var(--fg)}
.notice .in{max-width:1140px;margin:0 auto;padding:9px 32px;display:flex;
  align-items:center;gap:9px}
.notice b{font-weight:700}
.notice span{color:var(--muted)}
@media(max-width:800px){.notice .in{padding:9px 22px}}

/* ── 로그인 · 회원가입 ── */
.auth{min-height:100vh;display:grid;place-items:center;
  padding:calc(var(--nav-h) + 40px) 24px 56px}
/* ★ 2026-09-15 — **한 칸입니다**(사용자 지시). 왼쪽 문구 칸(`.auth-say` —
   *"현미경 영상을 올리면 / 운동성을 분석합니다"*)을 지우고 폼을 화면 가운데로 옮겼습니다.
   ~~1fr + 420px 두 칸 · gap 80~~ 은 그 문구가 있을 때의 배치였습니다 —
   문구만 지우면 왼쪽 칸이 빈 채로 남아 폼이 오른쪽으로 치우칩니다.
   ★ 좁은 폭(<=900)에서 이미 하던 배치가 그대로 기본이 된 것이라,
     그 미디어쿼리와 `.auth-say` 규칙은 **할 일이 없어져 걷어냈습니다**(죽은 규칙) */
.auth-grid{width:100%;max-width:420px;display:grid;align-items:center}
.auth-box{width:100%;background:var(--surface);border:1px solid var(--line);
  border-radius:18px;padding:38px 36px;box-shadow:0 30px 70px -46px rgba(0,0,0,.45)}
/* ★ 6px + .desc 의 30px 로 벌어져 있던 자리입니다. .desc 를 지웠으므로(2026-09-14)
   제목이 입력칸에 붙지 않게 여기서 띄웁니다 */
.auth-box h1{font-size:25px;font-weight:800;letter-spacing:-.035em;margin-bottom:24px}
.auth-box .desc{font-size:14.5px;line-height:1.7;color:var(--muted);margin-bottom:30px}
/* 가입 신청을 보낸 뒤 제목으로 초점을 옮깁니다(낭독용). 눌러서 가는 자리가 아니라 테두리는 그리지 않습니다 — 2026-09-17 */
.auth-box h1:focus{outline:0}
.field{margin-bottom:18px}
.field label{display:block;font-size:13px;font-weight:700;color:var(--muted);
  margin-bottom:7px}
.field input{width:100%;font:inherit;font-size:15.5px;padding:13px 15px;border-radius:9px;
  border:1px solid var(--line);background:var(--surface);color:var(--fg);
  transition:border-color .18s}
.field input:focus{outline:0;border-color:var(--accent)}
/* 입력을 받지 않습니다. 채울 것이 없다는 게 보이도록 눌린 면으로 둡니다 */
.field input:disabled{background:color-mix(in srgb,var(--fg) 4%, transparent);
  border-style:dashed;cursor:default;-webkit-text-fill-color:var(--muted)}
.field input::placeholder{color:var(--muted);opacity:.9}
.field input:disabled::placeholder{font-size:13.5px}
.auth .btn{width:100%;text-align:center;padding:15px 20px;margin-top:14px;font-size:15px}
/* 로그인 오류 한 줄 (2026-09-17) */
.auth-err{margin:4px 0 0;font-size:13.5px;font-weight:600;color:var(--bad)}
/* 비밀번호 칸 — 오른쪽 안에 `표시` 버튼 · 칸 아래 조건 한 줄(2026-09-18 · shell.js `pwToggle`) */
.pw-wrap{position:relative}
.pw-wrap input{padding-right:74px}
.pw-eye{position:absolute;top:50%;right:8px;transform:translateY(-50%);
  font:inherit;font-size:13px;font-weight:700;color:var(--muted);background:0;border:0;
  border-radius:7px;padding:7px 9px;cursor:pointer}
.pw-eye:hover{color:var(--fg);background:color-mix(in srgb,var(--fg) 6%, transparent)}
.field-hint{margin:7px 0 0;font-size:12.5px;line-height:1.5;color:var(--muted)}
/* 비밀번호를 바꾼 뒤 한 줄 — 오류 줄(.auth-err)과 같은 자리 · 같은 크기 */
.pw-ok{margin:4px 0 0;font-size:13.5px;font-weight:600;color:var(--ok)}
/* `내 계정` 의 변경 칸 — 오른쪽 열 하나를 씁니다 */
.pw-form .btn{margin-top:14px}
.auth-alt{margin-top:24px;font-size:14px;color:var(--muted);text-align:center}
.auth-alt a{color:var(--fg);font-weight:700;text-decoration:none}
.auth-alt a:hover{text-decoration:underline}
.auth-note{margin-top:30px;padding-top:20px;border-top:1px solid var(--line);
  font-size:13px;color:var(--muted);line-height:1.75}
/* 가입 버튼 아래 처리방침 안내 한 줄 (2026-09-18) — 동의 체크가 아니라 고지입니다(2023 법 개정 · privacy.html 주석) */
.auth-legal{margin:14px 0 0;font-size:12.5px;line-height:1.6;color:var(--muted);text-align:center}
.auth-legal a{color:var(--fg);font-weight:600}

/* ── Pretendard 를 쓰는 설득 영역 밖 페이지 (2026-09-18) — 로그인 · 가입 · 처리방침 (`body.ptd`) ──
   A2Z 서브셋은 랜딩 글자만 담아 사람이 입력한 글자 · 오류 문구를 못 덮습니다(사용자 결정) */
body.ptd{--sans:"Pretendard Variable",Pretendard,-apple-system,"Malgun Gothic","맑은 고딕",sans-serif}

/* ── 개인정보 처리방침 (2026-09-18 · privacy.html) — 읽는 폭 720 ── */
.pv-doc{max-width:720px;margin:0 auto;padding:calc(var(--nav-h, 64px) + 40px) 22px 80px;
  font-size:14.5px;line-height:1.75;color:var(--fg)}
.pv-doc h1{font-size:26px;font-weight:800;letter-spacing:-.03em;margin:0 0 18px}
.pv-doc h2{font-size:16.5px;font-weight:800;letter-spacing:-.02em;margin:36px 0 10px}
.pv-doc p{margin:0 0 8px}
.pv-doc ul{margin:0;padding-left:20px}
.pv-lead{color:var(--muted)}
.pv-tbl{width:100%;border-collapse:collapse;font-size:14px}
.pv-tbl th,.pv-tbl td{border:1px solid var(--line);padding:9px 12px;text-align:left;vertical-align:top}
.pv-tbl th{background:color-mix(in srgb,var(--fg) 4%, transparent);font-weight:700}
/* 회사가 채울 자리 — 남아 있으면 공개하면 안 됩니다(smoke.sh 가 ✗) */
.pv-todo{background:color-mix(in srgb,var(--warn) 18%, transparent);color:var(--fg);padding:1px 6px;border-radius:4px;font-weight:600}

/* ── 대시보드 셸 ── */
.app{display:grid;grid-template-columns:236px minmax(0,1fr);height:100vh;height:100dvh;overflow:hidden}
@media(max-width:1010px){.app{grid-template-columns:1fr}}

.side{border-right:1px solid var(--line);padding:18px 14px 20px;
  display:flex;flex-direction:column;gap:24px;
  background:color-mix(in srgb,var(--fg) 2.5%, transparent)}
@media(max-width:1010px){.side{border-right:0;border-bottom:1px solid var(--line);
  flex-direction:row;align-items:center;gap:14px;padding:12px 22px;overflow-x:auto}}
.side .bd{display:flex;align-items:baseline;gap:8px;padding:6px 10px;
  font-weight:800;font-size:16.5px;letter-spacing:-.035em;
  text-decoration:none;color:var(--fg)}
.side-nav{display:grid;gap:2px}
@media(max-width:1010px){.side-nav{display:flex;gap:4px}}
.side-nav a{display:flex;align-items:center;gap:10px;text-decoration:none;
  color:var(--muted);font:inherit;font-size:14px;font-weight:600;padding:10px 11px;
  border-radius:9px;cursor:pointer;text-align:left;width:100%;white-space:nowrap;
  transition:background .16s,color .16s}
.side-nav a:hover{background:color-mix(in srgb,var(--fg) 6%, transparent);color:var(--fg)}
.side-nav a[aria-current="page"]{background:var(--accent);color:var(--on-accent)}
.side-nav svg{width:17px;height:17px;flex:none}
/* 목적지 옆 건수 배지 (2026-09-17 · app.js `NAV`). 모양은 대기열 번호(`.qrow .q-no`)와 같은 알약입니다 */
.side-nav .nav-n{margin-left:auto;min-width:20px;height:20px;padding:0 6px;border-radius:100px;
  font-size:11.5px;font-weight:700;line-height:20px;text-align:center;
  background:color-mix(in srgb,var(--accent) 15%, transparent);color:var(--accent)}
.side-nav .nav-n[hidden]{display:none}
.side-nav a[aria-current="page"] .nav-n{background:color-mix(in srgb,var(--on-accent) 22%, transparent);
  color:var(--on-accent)}
@media(max-width:1010px){.side-nav .nav-n{margin-left:6px}}
.side .foot-u{margin-top:auto;border-top:1px solid var(--line);padding:12px 10px 0;
  font-size:12.5px;color:var(--muted);display:flex;align-items:center;gap:10px}
@media(max-width:1010px){.side .foot-u{margin:0 0 0 auto;border:0;padding:0}}
.side .foot-u b{font-weight:700;color:var(--fg)}
.side .foot-u button{font:inherit;font-size:12.5px;color:var(--muted);
  background:0;border:0;cursor:pointer}
.side .foot-u button:hover{color:var(--fg)}
/* 화면 밝기 — 하루에 한 번 쓸까 말까 한 것이라 우상단(가장 눈에 띄는 자리)에서
   내려왔습니다. 나중에 내 정보/설정 화면이 생기면 그쪽으로 옮깁니다 (09-07) */
.side .foot-u .mode{margin-left:auto;display:grid;place-items:center;
  width:27px;height:27px;border-radius:8px;transition:background .16s,color .16s}
.side .foot-u .mode:hover{background:color-mix(in srgb,var(--fg) 8%, transparent);
  color:var(--fg)}
.side .foot-u .mode svg{width:15px;height:15px}
/* ── 계정 버튼 + 메뉴 (2026-09-18) ── shell.js `initAcctMenu`
   사이드바 맨 아래 한 줄 = 이니셜 · 이름 · 역할 · 펼침 표시. 누르면 위로 메뉴가 뜹니다(Linear · Notion · Vercel).
   ★ 규칙은 `.side .acct …` 로 씁니다 — 위 `.side .foot-u button`(0,2,1)이 메뉴 항목 글자색·크기를 덮습니다 */
.side .acct{position:relative;padding:10px 0 0}
.side .acct .acct-btn{display:flex;align-items:center;gap:10px;width:100%;min-width:0;padding:7px 8px;
  border-radius:10px;font:inherit;color:var(--fg);text-align:left;transition:background .16s}
.side .acct .acct-btn:hover,.side .acct .acct-btn[aria-expanded="true"]{
  background:color-mix(in srgb,var(--fg) 7%, transparent)}
.acct-av{flex:none;width:30px;height:30px;border-radius:50%;display:grid;place-items:center;
  font-size:13px;font-weight:800;color:var(--accent);background:color-mix(in srgb,var(--accent) 14%, transparent)}
.acct-t{display:flex;flex-direction:column;min-width:0;line-height:1.3}
.acct-t b{font-size:13.5px;font-weight:700;color:var(--fg);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.acct-t small{font-size:11.5px;color:var(--muted)}
.acct-chev{flex:none;width:15px;height:15px;margin-left:auto;color:var(--muted)}
/* `내 계정` 을 보고 있을 때 — 사이드바 목적지의 현재 표시와 같은 뜻 */
.side .acct .acct-btn.on .acct-av{background:var(--accent);color:var(--on-accent)}
.acct-menu{position:absolute;left:0;right:0;bottom:calc(100% + 6px);z-index:40;display:flex;flex-direction:column;
  padding:6px;border:1px solid var(--line);border-radius:12px;background:var(--surface);
  box-shadow:0 18px 40px -18px rgba(0,0,0,.35)}
.acct-menu[hidden]{display:none}
.side .acct-menu [role="menuitem"]{display:flex;align-items:center;gap:10px;width:100%;padding:9px 10px;
  border:0;border-radius:8px;background:none;font:inherit;font-size:13.5px;color:var(--fg);
  text-align:left;text-decoration:none;cursor:pointer}
.side .acct-menu [role="menuitem"] svg{flex:none;width:16px;height:16px;color:var(--muted)}
.side .acct-menu [role="menuitem"]:hover,.side .acct-menu [role="menuitem"]:focus{
  outline:0;background:color-mix(in srgb,var(--fg) 7%, transparent)}
.side .acct-menu [role="menuitem"]:focus-visible{box-shadow:inset 0 0 0 2px var(--accent)}
.side .acct-menu [role="menuitem"][aria-current="page"]{color:var(--accent);font-weight:700}
.side .acct-menu .acct-out{margin-top:4px;border-top:1px solid var(--line);border-radius:0 0 8px 8px;padding-top:12px}

.appmain{min-width:0;display:flex;flex-direction:column;min-height:0}
/* ★ 2026-09-16 — 작업 영역의 상단바 높이는 **`--app-top-h`** 를 씁니다.
   전에는 랜딩과 `--nav-h` 를 같이 써서 **820에서 얇아졌습니다.** 그 값은 랜딩 네비의
   사정(링크가 숨는 폭)에서 나온 것이라, 대시보드·관리에는 근거가 없는 문턱이었습니다.
   그래서 760~820 의 60px 안에서 화면이 세 번 바뀌었습니다(상단바 820 · 열 800 ·
   건수 760). 이제 **800 하나**로 모입니다 — 랜딩은 820 그대로입니다 */
.app{--app-top-h:80px}
@media(max-width:800px){.app{--app-top-h:64px}}
.apptop{position:sticky;top:0;z-index:20;min-height:var(--app-top-h);
  border-bottom:1px solid var(--line);display:flex;align-items:center;gap:16px;
  padding:0 30px;background:color-mix(in srgb,var(--bg) 90%, transparent);
  backdrop-filter:blur(12px)}
/* ── 대시보드 타입 스케일 (2026-09-14) ─────────────────────────────────────
   ★ 고치기 전 1920 실측: 화면 제목 16 · 섹션 제목 12 · 본문 15.5 · 표 셀 13.5
     제목이 본문과 같거나 작아서 **위계가 평평하거나 뒤집혀** 있었습니다.
     큰 것이 타일 숫자 하나뿐이라 화면 전체가 작아 보였습니다(사용자 지적).
   ★ 본문(15.5)은 안 건드립니다 — 대시보드 본문 14~16px 은 관례 범위 안이고,
     여기를 키우면 표가 무너집니다. 키워야 할 것은 **제목**입니다(관례 헤더 18~24).
   ★ 좁은 폭에서는 줄입니다 — 데스크톱 값이 상한입니다(랜딩과 같은 원칙) */
.apptop h1{font-size:24px;font-weight:800;letter-spacing:-.03em}
@media(max-width:1010px){.apptop h1{font-size:20px}}
@media(max-width:640px){.apptop h1{font-size:18px}}
/* 상단바 왼쪽 = 경로. 마지막 조각이 제목입니다.
   화살표 버튼을 제목 앞에 끼우면 화면마다 제목이 밀립니다 — 그래서 경로로 둡니다 */
/* 경로 + 제목을 한 줄로 묶습니다. 제목은 nav 밖에 있어야 합니다 —
   페이지 제목이 내비게이션 랜드마크 안에 있으면 랜드마크로 훑는 사람에게
   "경로 안의 무엇" 으로 잡힙니다. 시각적으로는 그대로 한 줄입니다. */
.pathline{display:flex;align-items:baseline;gap:7px;min-width:0}
.crumbs{display:flex;align-items:baseline;min-width:0}
.crumbs:empty{display:none}
.crumbs ol{display:flex;align-items:baseline;gap:7px;list-style:none;margin:0;padding:0;min-width:0}
/* 구분 기호는 CSS 로 그립니다(APG Breadcrumb 예제와 같은 방식).
   마크업에 두면 aria-hidden 을 따로 붙여야 합니다. */
.crumbs::after{content:"›";margin-left:7px;color:var(--muted);opacity:.45;font-size:14px;flex:none}
.crumbs a{font:inherit;font-size:14px;font-weight:700;color:var(--muted);white-space:nowrap;
  text-decoration:none;transition:color .16s}
.crumbs a:hover{color:var(--fg);text-decoration:underline}
.crumbs a:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:4px}
/* 제목으로 초점을 옮기므로 초점 표시가 필요합니다.
   tabindex="-1" 이라 Tab 순서에는 안 들어갑니다. */
.apptop h1:focus-visible{outline:2px solid var(--accent);outline-offset:4px;border-radius:4px}
.apptop .crumb{font-size:12.5px;color:var(--muted)}
.apptop .sp{flex:1}
.apptop .out{font:inherit;font-size:13px;font-weight:600;color:var(--muted);
  background:0;border:1px solid var(--line);border-radius:7px;padding:6px 12px;cursor:pointer}
/* 이 화면에서 가장 자주 하는 행동. 사이드바 메뉴에만 있던 것을 CTA 로 올렸습니다 */
.apptop .topnew{display:inline-flex;align-items:center;gap:7px;flex:none;
  font:inherit;font-size:13.5px;font-weight:700;color:var(--on-accent);
  background:var(--accent);border:0;border-radius:8px;padding:9px 15px;cursor:pointer;
  transition:transform .16s ease, box-shadow .16s ease}
.apptop .topnew:hover{transform:translateY(-1px);
  box-shadow:0 12px 22px -12px color-mix(in srgb,var(--accent) 85%, transparent)}
.apptop .topnew:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.apptop .topnew svg{width:15px;height:15px}
/* ★ 2026-09-16 — ~~560~~ -> **640.** `새 검사` 버튼이 작아지는 폭인데, 560 은
   이 버튼 하나만 쓰던 값이라 작업 영역에 문턱이 하나 더 있는 셈이었습니다.
   640 에는 이미 여럿이 모여 있어 **한 폭에서 한 번만** 바뀝니다 */
@media(max-width:640px){.apptop .topnew{padding:9px 12px;font-size:13px;gap:6px}}
.apptop .out:hover{color:var(--fg);border-color:var(--fg)}
@media(max-width:800px){.apptop{padding:0 22px}}
.appscroll{flex:1;min-height:0;overflow-y:auto}

.appbody{padding:26px 30px 90px}
@media(max-width:800px){.appbody{padding:22px 22px 70px}}

.grid2{display:grid;grid-template-columns:minmax(0,1fr) 372px;gap:20px;align-items:start}
/* 1fr 은 최소 폭이 min-content 라, 안에 표가 있으면 그 표 폭만큼 열이 벌어집니다.
   minmax(0,1fr) 이라야 열이 화면에 맞고 표는 자기 상자 안에서 굴러갑니다 */
/* 쌓이면 두 열의 경계 바로 뒤에 섹션 제목이 옵니다 — 열 안(28)과 같은 값이어야
   제목 위 여백이 폭에 따라 달라지지 않습니다(가로 gap 은 20 그대로) */
@media(max-width:1120px){.grid2{grid-template-columns:minmax(0,1fr);row-gap:28px}}
/* ── 대시보드 배치 ──────────────────────────────────────────────────────
   ~~2026-09-14 타일을 곁열로 · 09-15 타일은 요약 보조 · `.dashgrid` 이름~~ — 곁열째 없어졌습니다. */
/* ★ 2026-09-17 — ~~`.dashgrid` 두 열(본문 + 곁열 372px) · `.dash-top` · `.dash-side`~~ 를 지웠습니다.
   대시보드 곁열이 빠져 본문 한 열입니다(app.js `dash`) */

/* ★ 2026-09-17 — ~~`.tiles` · `.tile` 현황 타일~~ 을 지웠습니다. 쓰던 곳(대시보드 곁열)이 없어졌습니다 */

/* 진행 중 카드 — 가짜 진행률을 쓰지 않습니다. 단계와 경과 시간만 */
.run{background:var(--surface);border:1px solid var(--line);border-radius:16px;
  padding:22px 24px;width:100%;text-align:left;font:inherit;cursor:pointer;
  display:block;transition:border-color .18s,box-shadow .18s}
.run:hover{border-color:color-mix(in srgb,var(--accent) 50%, var(--line));
  box-shadow:0 12px 30px -20px rgba(0,0,0,.45)}
.run .hd{display:flex;align-items:baseline;gap:12px;margin-bottom:18px}
.run .hd b{font-size:15.5px;font-weight:800;letter-spacing:-.025em}
.run .hd em{font-style:normal;font-size:12.5px;color:var(--muted);margin-left:auto;
  font-variant-numeric:tabular-nums}
/* ★ 2026-09-17 — ~~`.steps3` 3단계 막대~~ 를 지웠습니다. 남은 것은 `결과 대기 중` 한 줄입니다(app.js `waitBar`) */
.pstep{display:grid;gap:9px}
/* 흐르는 막대는 접수 줄(`.q-wait`)도 같이 씁니다 — 모양을 한 곳에서 정합니다 */
.pstep .ln,.q-wait .ln{height:4px;border-radius:100px;background:var(--ghost);overflow:hidden;
  position:relative}
.pstep .ln i,.q-wait .ln i{position:absolute;inset:0;width:0;background:var(--accent);border-radius:100px}
.pstep.now .ln i,.q-wait .ln i{width:100%;
  background:linear-gradient(90deg,transparent,var(--accent),transparent);
  animation:sweep 1.4s linear infinite}
@keyframes sweep{0%{transform:translateX(-100%)}100%{transform:translateX(100%)}}
/* 업로드 중인 줄 — 흐르는 막대가 아니라 **실제 진행**(폭은 app.js 가 %로).
   ★ 2026-09-18 — 흐르는 막대의 그라데이션(양끝 투명)이 남아 흐리게 보였고, 0.2초마다 폭만 바뀌어 계단처럼 움직였습니다
     (사용자 지적). 단색 + 폭 전환 0.2초(=진행 갱신 간격)로 이어 붙입니다. `결과 대기 중` 은 흐르는 막대 그대로 —
     **채워지는 것 / 흐르는 것**으로 둘을 가릅니다 */
.q-wait .ln.det i{animation:none;background:var(--accent);transition:width .2s linear}
@media(prefers-reduced-motion:reduce){.pstep.now .ln i,.q-wait .ln i{animation:none;
  background:var(--accent)}}
/* 접수 줄 — 이름 · 막대(남는 폭) · `결과 대기 중` (app.js `queueRow`) */
.qrow .q-wait{display:flex;align-items:center;gap:clamp(20px, 7.8%, 72px);container-type:inline-size}
/* 이름 + 요청 번호 한 덩어리 — 모든 줄이 같은 간격(요청 번호 margin-left 8) */
.qrow .q-id{display:flex;align-items:baseline;flex:none;min-width:0}
.q-wait > :first-child{flex:none;font-variant-numeric:tabular-nums}
/* ★ 숫자 폭 고정 — KPN 번호마다 폭이 달라 막대 시작점이 줄마다 2~4px 어긋났습니다(실측 824 · 822 · 826).
     표의 대상 칸(`.tbl .w-sub` 등폭)과 같은 처방입니다 */
/* ★ 2026-09-18 — 최소 40 -> 120(사용자 지적: 너무 짧음). 641~1440 의 여러 폭(641 · 700 · 1011 · 1280)에서
     막대가 **최소 40 에 붙어 있었고** `결과 대기 중` 까지 조금씩 잘렸습니다. 사이 간격(72 고정 × 3)이 자리를 다 먹었습니다.
     -> 간격이 칸 폭을 따라 줄어듭니다(clamp 20 ~ 72 · 7.8 % = 대시보드 1440 의 칸 926 에서 72 — 그 폭의 모양은 그대로).
     -> 그래도 120 을 못 받는 좁은 칸에서는 막대를 뺍니다(아래 @container). 화면 폭이 아니라 **칸 폭**으로 가릅니다 —
        같은 1280 에서도 대시보드(넓은 칸)와 접수 현황(좁은 칸)이 다르기 때문입니다 */
.q-wait .ln{flex:1;min-width:120px;
  margin-right:calc(clamp(20px, 7.8%, 72px) - 12px)}   /* 오른쪽도 왼쪽과 같은 간격 — 상태 칸 사이 gap 12 를 빼고 더합니다 */
/* 640 이하는 줄이 두 층이라 막대 둘 자리가 없습니다 — 글자만 남깁니다(기존 문턱) */
@media(max-width:640px){.q-wait .ln{display:none}}
/* 이름(65) + 요청 번호(103) + 막대 120 + 간격 20 + 오른쪽 8 ≈ 316 — 여유를 두고 330
   (`결과 대기 중` 이 상태 칸으로 나가 09-18 에 440 -> 330) */
@container (max-width:330px){.q-wait .ln{display:none}}
/* ★ 2026-09-17 — 막대가 남는 폭을 다 먹어 너무 길었습니다(사용자 지적 · 1440 대시보드 829px).
     막대를 자르는 대신 **다른 요소에 자리를 더 줍니다** — 막대 양옆 16 -> 72 · 경과 칸 51 -> 96.
     ★ 같은 날 ~~이름 칸 최소 폭 110~~ 을 뺐습니다(사용자 지적) — 글자(65)보다 넓은 칸이 이름 뒤에
       빈칸을 만들어 막대 왼쪽 간격 73 · 오른쪽 28 로 **좌우가 달랐습니다.** 간격을 gap 하나로 줘야
       양옆이 늘 같습니다. 640 이하는 막대가 없고 폭이 좁아 넓은 폭에만 겁니다(기존 문턱) */
@media(min-width:641px){
  .qrow .q-el{min-width:96px}
}
.pstep small{font-size:12.5px;color:var(--muted);font-weight:600}
.pstep.now small{color:var(--fg)}

.tbl{width:100%;border-collapse:collapse;font-size:14.5px}
/* ★ 2026-09-14 — 거르거나 정렬할 때마다 **칸 폭이 통째로 달라졌습니다**(사용자 지적).
   기본 table-layout:auto 는 그때 남아 있는 **내용**을 보고 폭을 다시 잽니다.
   `오류` 만 걸면 글자가 짧아져 열이 왼쪽으로 밀리는 식입니다(실측 501 -> 504 -> 501).
   내용이 바뀌어도 자리가 안 움직이려면 폭이 **선언돼** 있어야 합니다.
   -> .fixed + 아래 폭 클래스. 폭을 안 준 열끼리 남은 자리를 똑같이 나눕니다 */
/* ── 관리 표 셋 — **대시보드 검사 기록 표와 같은 규칙** (2026-09-15 재구성) ────
   ★ 폭을 선언하지 않습니다. `table-layout:auto` 가 남는 폭을 각 칸이 필요한 만큼에
     비례해 나눠 줍니다.
   ★ 전에는 `table-layout:fixed` + `th:nth-child(n){width:%}` **58개**였습니다.
     그 값들은 `대상 273px`(이름에 배치가 붙던 시절)을 기준으로 만든 것이라
     이름이 `KPN 1234` 로 짧아지면서 전부 낡았습니다.
     비율을 다시 계산하는 대신 **선언을 없앱니다** — 칸 폭은 담긴 것이 정합니다.
   ★ 좁아지면 **열을 접습니다**(아래). 접는 문턱은 표의 최소 폭을 재서 정합니다 —
     `.tbl.fixed` 시절의 `합 805 -> 531` 같은 계산은 더 이상 쓰지 않습니다. */

/* 긴 글이 든 칸만 말줄임을 겁니다 — 행 높이가 들쭉날쭉하면 훑기 어렵습니다.
   `.tbl .w-sub`(대상)는 이미 공통 규칙이 있습니다(min-width:641 블록).
   ★ 2026-09-15 — 여기에 `max-width:0` 을 같이 걸었다가 **로그 표가 망가졌습니다**:
     auto 배치에서 `max-width:0` 은 그 칸을 최소 폭으로 눌러 버려서, 남는 폭이
     시각·종류·주체로 몰리고 `내용` 이 `연구…` 로 뭉개졌습니다(실제 크롬 확인).
     말줄임은 **모자랄 때만** 도는 안전망이어야 합니다 — 폭을 누르면 안 됩니다 */
/* ★ 2026-09-16 — 로그의 말줄임 규칙(`.adm-c-file`·`.adm-ev`)을 뺐습니다. 칸이
     `행위자 · 작업 · 대상 · 결과` 로 바뀌면서 **긴 글이 든 칸이 하나도 없습니다** —
     넷 다 낱말 하나 아니면 번호입니다. 말줄임은 긴 글이 있을 때만 답니다 */
/* ★ 2026-09-16 — `.w-sub`(대상)를 더했습니다. 그 칸은 `KPN 1234` + `R-2418` **두 조각**
   이라 조합에 따라 칸의 **최소 폭**이 달라지고, 그러면 `auto` 가 남는 폭을 나누는
   비율이 바뀌어 찾기를 칠 때마다 칸이 미세하게 움직였습니다(실측 최대 7.6px).
   한 덩어리로 묶으면 최소 폭 = 최대 폭이 되어 그 변수가 사라집니다.
   ≤640 에서는 어차피 모든 셀이 nowrap 이고 요청 번호가 아랫줄로 내려갑니다 */
.tbl-req .adm-c-file,.tbl-req .adm-c-by,.tbl-req .w-sub{white-space:nowrap}
/* 칩이 둘 붙는 칸(계정의 상태 + 로그인 잠금) — 2026-09-18 사용자 지적으로 사이를 띄웁니다.
   머리줄(.adm-d-h)은 flex gap 이 이미 띄우므로 표 안에서만 걸립니다 */
.tbl .tag + .tag{margin-left:6px}
/* 로그의 `IP 주소` 칸 — 값 폭만 차지하고 늘어나지 않습니다(2026-09-18). IPv6(최대 45자)는 잘리고 툴팁으로 봅니다 */
.tbl-log .adm-c-ip{white-space:nowrap;max-width:190px;overflow:hidden;text-overflow:ellipsis}
/* 계정 표(2026-09-17)는 요청 표와 같은 칸 규칙을 씁니다 — 이름·이메일이 `.w-sub`, 역할·마지막 로그인이 접히는 칸 */
.tbl-user .adm-c-file,.tbl-user .adm-c-by,.tbl-user .w-sub{white-space:nowrap}

/* ~~로그만 시각을 두 줄로 세웁니다~~ -> **2026-09-16 없앴습니다.**
   그 규칙은 *"연도가 붙어 한 줄이면 148 이 필요한데 1160 에서 `2026-09-1…` 로
   잘렸습니다"* 를 고치려고 둔 것인데, **자리를 뺏던 `내용` 칸이 없어졌습니다.**
   재보니 640 까지 전 폭에서 한 줄로 잘림 0 이고, **행 높이가 77.4 -> 58** 입니다
   (로그는 한 화면에 몇 줄 들어오나가 그대로 쓸모입니다).
   ≤640 에서 두 줄로 서는 것은 이제 **표 넷이 같이 쓰는 규칙**(`.tbl .at .t`)입니다 */

/* ★ 2026-09-16 — 로그 행은 **행 전체가 눌리지 않습니다.** 눌리는 것은 `대상` 한 칸입니다.
   `.tbl tbody tr{cursor:pointer}` 는 전역이라 로그 행도 손가락이 떴는데 눌러도 아무 일이
   없었습니다(거짓 어포던스). 로그에서만 풉니다 — 행 강조(:hover 배경)는 남깁니다.
   다섯 칸을 가로로 훑을 때 줄을 놓치지 않게 하는 장치라 뜻이 다릅니다.
   링크 모양은 `.sec-link` 와 같은 집 규칙입니다(강조색 · 밑줄은 hover 에서만) */
/* ~~.tbl-log tbody tr{cursor:default}~~ — 2026-09-17 아래 `.tbl tbody tr.tap` 로 갈음(이 규칙은 한 번도 이긴 적이 없습니다) */
.tbl-log td a{color:var(--accent);text-decoration:none;font-weight:600}
.tbl-log td a:hover{text-decoration:underline}

/* ── 좁아지면 접습니다 — **검사 기록 표와 같은 문턱(800)** ────────────────────
   ★ 관리 `검사 기록` 은 규칙이 여기 없습니다. 공용 클래스(`w-dist`·`w-cnt`·`w-vd`)를
     쓰므로 `@media(max-width:800px){.tbl .w-dist,.tbl .w-cnt{display:none}}` 가
     그대로 걸립니다. **같은 표는 한 곳에서만 정합니다.**
   ★ 실측(2026-09-15) — 표의 최소 폭은 요청 481 · 검사 기록 635 · 로그 316 입니다.
     순수하게 `안 들어가는 폭` 만 따지면 문턱이 더 아래(요청 ~512 · 로그 ~347)지만,
     그 폭에서는 다섯 열이 60px 씩 나눠 갖게 되어 읽을 수가 없습니다.
     **한 제품에서 표는 같은 폭에서 같이 접습니다** — 800 은 검사 기록 표가 쓰는 값이고
     정본 문턱 넷 중 하나입니다(1120·1010·800·640). */
@media(max-width:800px){
  /* 요청 — 누가 올렸나 · 파일 크기는 상세에서 봅니다 */
  .tbl-req .adm-c-by,.tbl-req .adm-c-file,.tbl-user .adm-c-by,.tbl-user .adm-c-file{display:none}
}
/* ★ 2026-09-16 — 로그의 접는 문턱만 **800 -> 640** 으로 내렸습니다.
   ~~`한 제품에서 표는 같은 폭에서 같이 접습니다`~~ 를 로그에만 풀어 줍니다(위 결정 뒤집음).
   사유: 그 결정은 로그의 뒤 두 칸이 `주체 · 내용`(보조)이던 시절 것입니다. 지금 다섯 칸은
   **감사 기록의 필수 요소**(OWASP: 시각 · 행위자 · 작업 · 대상 · 결과)라 하나를 접으면
   그 값을 볼 곳이 아예 없어집니다 — 요청 표가 접는 `올린 사람`은 상세에 남지만
   로그에는 상세가 없습니다. 실측으로 다섯 칸이 **줄바꿈 없이** 들어가는 최소 폭은
   399.2px(= 화면 ~507)이라 800 에서 접으면 읽히는 폭을 160 넘게 버리는 셈이었습니다.
   640 아래에서만 `행위자`가 빠지고 남는 넷이 `언제 · 무엇이 · 어디에 · 어떻게 됐나` 입니다 */
/* 요청 번호를 아랫줄로. 대상 칸의 최소 폭이 **157 -> 103** 으로 줄어
   390 에서 표가 옆으로 굴러가던 것이 없어집니다(2026-09-15 실측).
   시각 칸이 `09-07 / 09:41` 로 쌓이는 것과 같은 처리입니다.
   ★ 2026-09-16 — 문턱을 ~~900~~ -> **640** 으로 내렸습니다(비정본 문턱 하나 제거).
     900 은 *"390 에서 굴러간다"* 를 고치려고 잡은 값인데 **너무 일찍 쌓고 있었습니다** —
     실측으로 번호를 한 줄에 둔 채 641 까지 잘림 0 · 가로 스크롤 0 이고,
     641~900 에서 **행 높이가 76.5 -> 58** 로 내려갑니다(한 화면에 더 들어갑니다).
     쌓기가 실제로 필요한 폭은 ~365 아래입니다(360 에서 표 255 vs 칸 252) */
@media(max-width:640px){
  /* ★ 2026-09-18 — `IP 주소` 도 **행위자와 함께** 접습니다. 이 폭에서 이미 행위자가 빠지는데
     IP 만 남겨 두면 *"누가"* 없이 *"어디서"* 만 남습니다. 월 점검은 넓은 화면에서 합니다 */
  .tbl-log .adm-c-by,.tbl-log .adm-c-ip{display:none}
  .tbl .adm-rid{display:block;margin:2px 0 0}
  /* 상태 칸만 **줄바꿈을 허용합니다.** 이 폭의 셀은 전부 `white-space:nowrap` 인데,
     그러면 `실패` 칩과 `확인` 이 무조건 한 줄에 붙어 그 칸이 혼자 넓어집니다.
     풀어 두면 자리가 있을 때는 나란히, 모자라면 아랫줄로 갑니다 —
     **실측 320 에서 표가 276 -> 241 로 줄어듭니다.** 행 높이는 안 늘어납니다
     (이 폭에서는 시각·대상이 이미 두 줄짜리입니다) */
  .tbl td:has(> .rowseen){white-space:normal}
  .tbl .rowseen{margin:2px 0 0 8px}
}

.tbl th{font-size:12.5px;font-weight:700;color:var(--muted);text-align:left;
  padding:0 12px 11px;border-bottom:1px solid var(--line);letter-spacing:.04em}
/* ★ 2026-09-15 — 행 세로를 **14 하나로** 모았습니다(사용자 결정).
   전에는 표 15 · 작은 표 12 · 목록 13 · 지표/타일 14 로 네 값이었습니다.
   기준은 이미 있던 값을 씁니다 — `.m` 과 `.tile` 이 쓰던 **14** 입니다.
   ★ 카드에 닿는 면만 **12** 로 눌러 `가장자리 -> 글자 = 23` 을 지킵니다
     (1 + 카드 10 + 12). `.metrics .m:first-child{padding-top:0}` 과 같은 관용구입니다 */
.tbl td{padding:14px 12px;border-bottom:1px solid var(--line)}
/* ★ 마지막 행 밑에 선이 하나 더 남아 카드 바닥 위에 떠 있었습니다(사용자 지적 2026-09-14).
   .metrics .m:last-child 와 같은 처리입니다 — 목록의 마지막 항목에는 구분선을 넣지
   않습니다(Material 3 divider 지침). tr.grp 는 이미 border-bottom:0 입니다 */
.tbl tbody tr:last-child td{border-bottom:0}
/* ★ 2026-09-14 — 아래 여백만 26 이었습니다(좌·우·위는 23).
   셀 세로 padding 이 카드 10 + 테두리 1 위에 그대로 얹혀서입니다.
   행 사이 리듬은 그대로 두고 **카드에 닿는 면만** 12 로 줄입니다 -> 23 */
.panel.tight .tbl tbody tr:last-child td{padding-bottom:22px}   /* 2026-09-17 카드 10 이 셀로 옮겨 와 12 -> 22 */
/* 위쪽도 같은 처리 — 단 **열 제목을 숨긴 표**(대시보드 `최근 검사`)만입니다.
   제목이 보이는 표는 그 제목이 카드에 닿는 면이라 첫 행은 제목과의 사이입니다 */
.panel.tight .tbl.compact tbody tr:first-child td{padding-top:22px}
/* ── 카드·행 전체를 누를 수 있게 ────────────────────────────────
   ★ 카드나 행을 통째로 <button> 으로 감싸지 않습니다. 그러면 안의 모든 글자가
   하나의 긴 접근 이름이 되고, 표의 <tr> 은 <a> 로 감쌀 수도 없습니다.
   관례대로 **대표 정보 하나만 링크**로 만들고 그 링크의 ::after 를 부모 전체로
   늘립니다(Inclusive Components · Adrian Roselli).
   ★ 대가: 그 영역의 글자를 드래그로 선택할 수 없습니다.
     다만 전에도 <tr> 에 클릭 핸들러가 있어 드래그하면 화면이 넘어갔으므로
     새로 잃는 것은 없습니다. */
/* ★ 2026-09-14 — 링크만 늘리던 것을 **버튼까지** 늘립니다.
   관리 화면의 행은 이동이 아니라 펼치기라 <button> 이고, 그래서 이 규칙이
   안 걸려 마우스로만 열렸습니다 */
/* ★ 2026-09-16 — `:not(.rowseen)` 을 답니다. 이 규칙은 **행을 대표하는 하나**를
   행 전체로 늘리라는 것인데, 행 안에 버튼이 **둘** 생기면서(대상 링크 + 처리 버튼)
   둘 다 늘어났고, 나중에 오는 처리 버튼이 위에 깔려 **행 전체가 `확인` 이 됐습니다**
   — 대상을 눌러도 상세로 못 갔습니다(사용자 지적).
   처리 버튼은 자기 상자만 쓰고, 링크의 덮개 **위에** 서야 눌립니다(z-index:2) */
.tap{position:relative}
.tap :is(a,button):not(.rowseen)::after{content:"";position:absolute;inset:0;z-index:1}
.tap a{text-decoration:none;color:inherit}
.tap .rowseen{position:relative;z-index:2}
.tap :is(a,button):not(.rowseen):focus-visible{outline:0}
/* 행을 대표하는 것에 초점이 갈 때만 행을 두릅니다 — 처리 버튼은 **자기 고리**를
   갖습니다. 안 그러면 작은 버튼에 초점이 갔는데 행 전체가 둘러져 어디인지 모릅니다 */
.tap:has(:is(a,button):not(.rowseen):focus-visible){outline:2px solid var(--accent);
  outline-offset:-2px}

/* 재는 행 — **보이지 않고 높이도 없지만 폭에는 기여합니다.** 이것이 칸 폭을
   내용 변화로부터 떼어 놓는 장치입니다 -> shell.js `sizerBody` 의 주석이 정본 */
.tbl .sizer td{padding-top:0;padding-bottom:0;height:0;line-height:0;
  border:0;overflow:hidden;visibility:hidden}
.tbl tbody.sizer tr{cursor:default}
.tbl tbody.sizer tr:hover{background:transparent}

/* ★ 2026-09-17 — 손가락 커서는 **실제로 눌리는 행(`.tap`)에만** 겁니다. 전에는 모든 행에 걸고
     로그만 `.tbl-log tbody tr{cursor:default}` 로 풀었는데, 그 규칙이 **특정도가 같고 파일 앞**이라
     처음부터 지고 있었습니다(CLAUDE.md 함정 ① · 사용자 지적). 요청 상세의 기록 표도 같았습니다 */
.tbl tbody tr{transition:background .16s}
.tbl tbody tr.tap{cursor:pointer}
.tbl tbody tr:hover{background:color-mix(in srgb,var(--fg) 4%, transparent)}
.tbl .mono{font-variant-numeric:tabular-nums}
/* ★ 2026-09-16 — 대상 칸도 **자릿수 고정 숫자**로. KPN 은 `.mono` 가 아니라
   비례폭이어서 값마다 폭이 달랐습니다 — 실측 `KPN 0771` 67.2 vs `KPN 0644` 72.1.
   그래서 찾기로 넓은 값이 사라지면 재는 행(`KPN 1234` 69.2)이 최댓값이 되어
   칸이 4.4px 줄었습니다. 등폭으로 두면 **어떤 KPN 이든 같은 폭**이라 재는 행
   하나로 충분합니다. 표에서 숫자를 세로로 비교할 때의 관례이기도 합니다 */
.tbl .w-sub{font-variant-numeric:tabular-nums}
.tag{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;
  padding:4px 10px;border-radius:100px;
  background:color-mix(in srgb,var(--ok) 15%, transparent);color:var(--ok)}
.tag.w{background:color-mix(in srgb,var(--warn) 15%, transparent);color:var(--warn)}
/* ★ 2026-09-14 — .tag.ok 규칙이 **아예 없어서** `완료` 가 기본값(초록)으로 떨어졌고,
   `접수`(변형 없음)도 같은 초록이었습니다. 둘이 구분이 안 됐습니다(사용자 지적).
   이제 넷이 각자 색을 갖습니다 — 중립 · 파랑 · 초록 · 빨강 */
.tag.ok{background:color-mix(in srgb,var(--ok) 15%, transparent);color:var(--ok)}
.tag.n{background:color-mix(in srgb,var(--fg) 8%, transparent);color:var(--muted)}
.tag.b{background:color-mix(in srgb,var(--bad) 14%, transparent);color:var(--bad)}
/* 끝났지만 성공이 아닌 상태(취소) — **테두리만.** 채운 회색(.n)은 `접수` 가 씁니다.
   가장 다른 두 상태가 같아 보이면 안 됩니다 (2026-09-15) */
.tag.off{background:transparent;color:var(--muted);
  box-shadow:inset 0 0 0 1px var(--line)}

/* 새 검사 — 영상 업로드 카드
   ★ 2026-09-18 — 카드 전체가 점선 드롭존이던 것을 다시 짰습니다(KRDS 파일 업로드 컴포넌트).
     카드(.panel.upl) 안에 점선 드롭 영역 · 고른 영상 항목 · 오류 · 아래 줄(KPN + 요청 버튼) */
.upl .upl-h{font-size:17px;font-weight:800;letter-spacing:-.03em;color:var(--fg);
  text-transform:none;margin:0 0 16px}
.drop{border:1.5px dashed var(--line);border-radius:12px;padding:28px 20px;text-align:center;
  background:color-mix(in srgb,var(--fg) 2%, transparent);transition:border-color .15s,background-color .15s}
.drop-hint{font-size:14px;line-height:1.6;color:var(--muted);margin:0 auto 16px;max-width:34em;text-wrap:balance}
/* 끌어다 놓는 중 */
.drop.over{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 6%, transparent)}
/* 고른 영상 — 아이콘 · 이름(한 줄 말줄임) / 형식 · 크기 · 길이 · 해상도 · 삭제 */
.up-files{list-style:none;margin:12px 0 0;padding:0}
.up-file{display:grid;grid-template-columns:40px minmax(0,1fr) auto;gap:14px;align-items:center;
  padding:12px 10px 12px 14px;border:1px solid var(--line);border-radius:12px;
  background:color-mix(in srgb,var(--fg) 2%, transparent)}
.up-file.bad{border-color:color-mix(in srgb,var(--bad) 55%, var(--line))}
.up-ic{width:40px;height:40px;border-radius:10px;display:grid;place-items:center;
  background:color-mix(in srgb,var(--accent) 12%, transparent);color:var(--accent)}
.up-file.bad .up-ic{background:color-mix(in srgb,var(--bad) 12%, transparent);color:var(--bad)}
.up-ic svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linejoin:round}
.up-txt{min-width:0;display:flex;flex-direction:column;gap:3px}
.up-name{font-size:15px;font-weight:700;color:var(--fg);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.up-meta{font-size:13px;color:var(--muted);font-variant-numeric:tabular-nums}
.up-del{width:36px;height:36px;border:0;border-radius:9px;background:0;color:var(--muted);
  cursor:pointer;display:grid;place-items:center}
.up-del:hover{color:var(--fg);background:color-mix(in srgb,var(--fg) 6%, transparent)}
.up-del svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round}
/* 업로드 중 — 실제 진행 막대 · 안내 한 줄(2026-09-18). 막대 모양은 접수 줄 막대(.ln)와 같은 둥근 줄 */
.up-bar{height:6px;border-radius:100px;background:var(--ghost);overflow:hidden;margin-top:14px}
.up-bar i{display:block;height:100%;background:var(--accent);border-radius:100px;transition:width .2s linear}
.up-note{margin:12px 0 0;font-size:13.5px;font-weight:600;color:var(--fg)}
/* 기준 오류 — 로그인 오류(.auth-err)와 같은 글자 */
.drop-err{list-style:none;margin:10px 0 0;padding:0;font-size:13.5px;font-weight:600;color:var(--bad)}
.drop-err:empty{display:none}
.drop-err li + li{margin-top:4px}
/* 아래 줄 — KPN 은 네 자리라 칸도 그만큼만(GOV.UK `input width` — 칸 폭이 받을 글자 수를 알려 줍니다) */
.upl-foot{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:16px;
  margin-top:22px;padding-top:20px;border-top:1px solid var(--line)}
.upl-foot .field{margin:0}
.upl .kpn-field input{width:8.5em}
.upl-act{display:flex;gap:12px;flex-wrap:wrap}
@media(max-width:640px){
  .upl-act{width:100%}
  .upl-act .btn{flex:1 1 0;justify-content:center;text-align:center}
}
/* 끌어다 놓을 수 없는 기기(휴대폰)에는 끌어다 놓으라는 말을 안 합니다 — 버튼만 */
@media(hover:none) and (pointer:coarse){.drop-hint{display:none}}
/* h2 로 바꿨습니다(구조). 크기·여백은 여기서 다 정하므로 보이는 것은 그대로입니다 */
/* ★ 12px/uppercase eyebrow 였습니다 — 본문(15.5)보다 작아 제목 구실을 못 했습니다.
   eyebrow 층은 .panel h3(11.5) 하나만 남기고, 이쪽은 **보통 제목**으로 올립니다 */
.sec-h{font-size:15px;font-weight:700;letter-spacing:-.01em;color:var(--fg);margin:0 0 14px}
@media(max-width:640px){.sec-h{font-size:14px}}
/* ★ 행이 자기 위아래 여백(14px)을 갖는데 카드도 22px 를 줘서 **세로에서만** 겹쳤습니다.
   잰 값(2026-09-14 사용자 지적) — 좌우 23 · 위아래 37 · 행 사이 28.
   위아래만 가장자리보다 14px 더 떠서 카드가 헐렁해 보였습니다.
   관례: Bootstrap 은 이런 목록을 .card-body **밖**에 둡니다(card 여백을 안 받습니다).
   Material 은 목록 컨테이너의 위아래 여백을 8dp 로 잡습니다. 둘 다 카드 여백 위에
   행 여백을 **더하지 않습니다.** 첫 행 위 · 마지막 행 아래를 카드에 맡겨 사방 23 으로
   맞춥니다 — 행 높이(55~56px = M3 한 줄 목록 56dp)는 그대로 둡니다 */
.panel{background:var(--surface);border:1px solid var(--line);border-radius:16px;padding:22px}
.metrics .m:first-child{padding-top:0}
.metrics .m:last-child{padding-bottom:0}
.panel+.panel{margin-top:18px}
.panel h3{font-size:11.5px;font-weight:800;letter-spacing:.08em;color:var(--muted);
  margin-bottom:16px;text-transform:uppercase}
.shotbox{border-radius:12px;overflow:hidden;border:1px solid var(--line)}
.shotbox img{width:100%;display:block}
.cap{font-size:12.5px;color:var(--muted);margin-top:12px;line-height:1.65}

/* 고정 헤더에 앵커가 가리지 않도록 */
[id]{scroll-margin-top:calc(var(--nav-h) + 12px)}
/* 분석 과정은 화면 맨 위에 딱 붙어야 고정 구간이 시작됩니다 */
.pipesec{scroll-margin-top:0}

/* 고정 구간의 wrap 이 flex 아이템이라 내용 폭으로 줄어들고 있었습니다 */
.pin > .wrap{flex:1;min-width:0}

/* ── 분석 과정: 눌러서 조작하는 부분 ── */
.st{border:0;background:transparent;width:100%;display:block;text-align:left;
  font:inherit;cursor:pointer}
.st:hover{opacity:.72}
.st.on{cursor:default}
.st:focus-visible{outline:2px solid var(--accent);outline-offset:2px}


/* 등급 칩 — 검출 단계부터 나타납니다 */
.filt{position:absolute;left:0;right:0;bottom:0;z-index:3;padding:16px 18px;
  display:flex;gap:8px;flex-wrap:wrap;opacity:0;pointer-events:none;
  transition:opacity .4s ease}
.filt.on{opacity:1;pointer-events:auto}
.filt button{background:rgba(8,13,22,.62);border:1px solid rgba(255,255,255,.18);
  border-radius:100px;padding:6px 13px;font:inherit;font-size:11.5px;font-weight:700;
  color:#fff;cursor:pointer;display:inline-flex;align-items:center;gap:7px;
  backdrop-filter:blur(6px);transition:opacity .2s,border-color .2s}
.filt button:hover{border-color:rgba(255,255,255,.45)}
.filt button i{width:7px;height:7px;border-radius:2px;display:block}
.filt button[aria-pressed="false"]{opacity:.42}
@media(max-width:900px){.filt{position:static;padding:20px 0 0}
  .filt button{background:color-mix(in srgb,var(--fg) 8%, transparent);
    border-color:var(--line);color:var(--fg)}}

/* 대시보드 안의 안내 띠는 본문 여백에 맞춥니다 */
.appmain .notice .in{max-width:none;padding:10px 30px}
@media(max-width:800px){.appmain .notice .in{padding:10px 22px}}

/* 버튼은 색을 물려받지 않습니다. 밝기 전환에 안 따라오던 원인 */
/* 진행 카드는 <button> 입니다. 버튼은 폭이 auto 라 그리드 밖(일반 흐름)에
   놓이면 내용만큼 늘어나 화면을 밀어냅니다 — 모바일 가로 스크롤의 주범이었습니다 */
.run{color:var(--fg);width:100%}
.run .hd b{color:var(--fg)}


/* ── 진행 중 카드 (주인공) ── */
.run.big{display:grid;grid-template-columns:200px minmax(0,1fr);gap:22px;
  padding:22px;align-items:center}
@media(max-width:640px){.run.big{grid-template-columns:1fr}}
.run-thumb{border-radius:12px;overflow:hidden;border:1px solid var(--line);
  aspect-ratio:16/10;background:#0b1119}
.run-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.run-body{min-width:0}
.run-state{font-size:13px;font-weight:700;color:var(--accent);margin:-10px 0 16px}

/* ── 대기열 ── */
/* ★ 2026-09-14 — 가장자리에서 글자까지가 좌 17 · 위 14 였습니다(표준 23).
   카드가 여백을 안 갖고 줄이 자기 여백만 갖고 있었습니다.
   줄의 좌우를 22 로 올리고(1+22=23), 카드에 위아래 9 를 줘서(1+9+13=23) 맞춥니다.
   ★ 카드에 **좌우** 여백을 주면 안 됩니다 — 구분선이 안쪽으로 밀려
     `가장자리까지 그어지는 목록 구분선` 이 깨집니다 */
.qlist{margin-top:10px;border:1px solid var(--line);border-radius:16px;overflow:hidden;
  background:var(--surface)}
/* ★ 2026-09-17 — 위아래 8 을 ~~목록(`padding:8px 0`)~~ 에서 **첫·끝 행**으로 옮겼습니다(사용자 지적).
   목록이 여백을 갖고 있으면 행을 hover 로 칠할 때 그 8px 에 색이 안 들어가 칠이 안쪽 사각형에만
   생겼습니다. 가장자리 -> 글자 23 은 그대로입니다(테두리 1 + 첫·끝 행 22) */
.qrow:first-child{padding-top:22px}
.qrow:last-child{padding-bottom:22px}
/* 큰 카드가 여럿 쌓일 때 — 목록이 카드 뒤에 붙을 때와 같은 간격입니다 */
.run.big + .run.big{margin-top:10px}
/* ★ 2026-09-15 — 칸을 **목록이** 정합니다(subgrid). 저장소 첫 subgrid 입니다.
   ★ 왜 — `.qrow` 가 각자 그리드라 행마다 칸 폭이 따로 잡혔습니다. 한 목록에
     `분석 완료 / 소요 1:17` 과 `대기 / 요청 09-15 15:40` 이 같이 있으면 상태도
     시각도 줄이 안 맞습니다(사용자 지적 2026-09-15). 전에는 상태 74px · 시각 52px
     **고정**이라 우연히 맞아 있었는데, 요청 시각이 들어오면서 52 로는 모자라
     max-content 로 바꾼 것이 이 증상을 드러냈습니다.
   ★ 고정 폭으로 되돌리지 않습니다 — `분석 완료` · `요청 09-15 15:40` 의 폭을
     외워 박는 값이 되고, 글자가 바뀌면 또 틀립니다. 칸 폭은 목록이 담은 것이 정합니다 */
.qlist{display:grid;grid-template-columns:26px minmax(0,1fr) max-content max-content max-content}
.qrow{display:grid;grid-template-columns:subgrid;grid-column:1 / -1;gap:12px;
  align-items:center;padding:14px 22px;font-size:14px;
  border-bottom:1px solid var(--line)}
.qrow:last-child{border-bottom:0}
.qrow .q-no{font-size:11.5px;font-weight:700;color:var(--muted);
  background:color-mix(in srgb,var(--fg) 7%, transparent);border-radius:100px;
  text-align:center;line-height:20px;height:20px}
.qrow .q-sub{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  margin-left:6px}   /* 2026-09-17 번호와 이름 사이 12 -> 18(사용자 요청). 칸 간격(gap)은 다른 칸과 같이 두고 이름만 띄웁니다 */
.qrow .q-st{font-size:12.5px;color:var(--muted)}
.qrow .q-st.w{font-weight:600}
.qrow .q-st.up, .qrow .q-wl.up{color:var(--accent);font-weight:700}
.qrow .up-p{display:inline-block;width:4ch;text-align:right;font-variant-numeric:tabular-nums}
/* 요청 번호 — 이름 옆에 작게(2026-09-18 · shell.js `reqCode`). 접수 줄 · 새 결과 카드 · 결과 머리가 같이 씁니다 */
.q-code{flex:none;font-size:12px;font-weight:500;letter-spacing:0;color:var(--muted);white-space:nowrap}
.qrow .q-code, .res-id .q-code{margin-left:8px}
.qrow .q-el{font-size:12.5px;color:var(--muted);text-align:right}
/* ★ 다섯 칸이 한 줄에 들어가려면 줄 안쪽이 358.5 는 돼야 합니다
     (26 + 대상 65 + 상태 74 + 시각 97.9 + 버튼 47.6 + 칸 사이 48 · 2026-09-15 실측).
     줄 안쪽은 뷰포트에서 약 100 을 뺀 값이라 **640 아래에서 모자랍니다** —
     실제로 390 에서 대상 칸이 **0px 으로 눌려 이름이 통째로 사라졌습니다.**
   -> 640 이하는 두 줄로 세웁니다. 위 = 번호·대상, 아래 = 요청 시각.
      취소는 두 줄에 걸쳐 오른쪽에 섭니다(누르는 자리가 한 곳이어야 합니다).
   ★ 상태(`대기`)는 여기서 숨깁니다 — 이 목록의 제목이 이미 `대기 N건` 이고,
     칸이 모자랄 때 가장 먼저 버릴 것은 **이미 다른 곳에 있는 말**입니다.
     문턱 640 은 표가 열을 접는 것과 같은 값입니다(새 값이 아닙니다) */
/* 아래층에만 쓰는 `결과 대기 중 · ` — 넓은 폭에서는 이름 옆에 있습니다 */
.qrow .q-wl{display:none}
.qrow .q-wl, .qrow .q-t{white-space:nowrap}   /* 줄을 바꿔도 `N:NN 경과` 는 한 덩어리로 */
/* ★ 2026-09-18 — ~~취소가 있는 줄(.qcan)만~~ 두 층이던 것을 **모든 줄**로 넓혔습니다(사용자 지적).
     접수 현황에서도 윗층(`KPN 1234 R-260918-007 결과 대기 중`)이 한 줄 고정(nowrap)이라 400 아래에서
     글자가 잘렸고, 대시보드·새 검사의 대기열은 두 층조차 아니어서 390 에서 요청 번호가 잘렸습니다.
       위층   번호 · 이름 · 요청 번호(자리가 모자라면 요청 번호만 다음 줄로) · [분석 완료]
       아래층  결과 대기 중 · N:NN 경과   /   N:NN 소요
     `결과 대기 중` 을 버리지 않고 아래층으로 내립니다 — 새 결과와 섞인 목록에서는 이 말이 둘을 가릅니다 */
@media(max-width:640px){
  .qrow{grid-template-columns:26px minmax(0,1fr) max-content;row-gap:6px}
  .qrow .q-sub{white-space:normal;overflow:visible;display:flex;flex-wrap:wrap;align-items:baseline;
    column-gap:8px;row-gap:2px}
  .qrow .q-sub .q-code{margin-left:0}   /* 간격은 column-gap 이 줍니다 — 다음 줄로 내려가도 들여쓰기가 안 생깁니다 */
  .qrow .q-st.w{display:none}          /* 아래층 `q-wl` 이 같은 말을 합니다 */
  .qrow .q-id{flex-wrap:wrap;column-gap:8px;row-gap:2px}
  .qrow .q-wl{display:inline}
  .qrow .q-st{grid-column:3;grid-row:1}
  .qrow.qcan .q-st{display:none}
  .qrow .q-el{grid-column:2;text-align:left;margin-left:6px}   /* 아래층도 이름과 같은 자리에서 시작 */
  .qrow.qcan .qcancel{grid-column:3;grid-row:1 / span 2;align-self:center}
}
.qrow .q-lb{font-weight:700}
/* 값은 `.find .fclear` 와 같습니다 — 같은 제품에서 같은 크기의 잠긴 버튼입니다.
   ★ 한 곳에서 고치면 둘 다 바뀌도록 선택자를 묶어 뒀습니다(아래 `.find .fclear`) */
.qrow .qcancel{font:inherit;margin:-6.5px 0}
/* ★ 2026-09-17 — 음수 여백은 **버튼이 행 높이를 정하지 않게** 하려는 것입니다(사용자 지적).
   버튼(38)이 글자(25)보다 커서 `취소` 가 있는 행만 66 이고 없는 행은 53 이었습니다 —
   한 화면의 두 목록이 높이가 달랐습니다. 누르는 크기(38)는 그대로이고 행 여백 위로 겹칠 뿐입니다 */

/* ── 표 ── */
/* ★ 표를 담는 카드. 카드 padding 과 셀 padding 을 둘 다 크게 주면 여백이 두 번
   겹쳐 표마다 값이 달라집니다 — 2026-09-14 전에 세 표가 좌 25 · 27 · 37 로 제각각이었고
   대시보드에서 위아래 카드 여백이 눈에 띄게 달랐습니다(사용자 지적).
   기준은 .panel · .run.big 과 같은 **가장자리에서 글자까지 23px** 입니다.
   테두리 1 + 카드 10 + 셀 12 = 23
   ★ 2026-09-17 — **카드 여백 10 을 가장자리 셀로 옮겼습니다**(사용자 지적 · 관례).
     카드가 여백을 갖고 있어 행 구분선과 hover 칠이 카드 안쪽 10px 에서 끊겼고, 같은 화면의
     목록(`.qlist` — 줄이 여백을 가짐)은 가장자리까지 가서 **둘이 달랐습니다.**
     Material 3 목록·데이터 표의 state layer · Carbon Data table · Polaris IndexTable 은
     전부 행 칠과 구분선이 **카드 가장자리까지** 갑니다 — 글자만 안쪽입니다.
     이제 `테두리 1 + 가장자리 셀 22 = 23` 입니다. 글자 자리는 그대로입니다.
   ★ 열 제목 위도 같이 22 로 모았습니다 — 관리 요청 상세의 기록 표만 제목이 카드 위 10 에
     붙어 있었습니다(재다가 발견 · 다른 표는 23) */
.panel.tight{padding:0;overflow:hidden}
/* ★ `table.tbl` — 요소 하나만큼 특정도를 올립니다. `≤640` 의 `.panel.tight .tbl.compact td{padding:14px 6px}` 가
     같은 특정도에 파일 뒤라 가장자리 22 를 이겼습니다(CLAUDE.md 함정 ① · 390 에서 좌 7 실측) */
.panel.tight table.tbl :is(th,td):first-child{padding-left:22px}
.panel.tight table.tbl :is(th,td):last-child{padding-right:22px;width:1%;white-space:nowrap}
/* ★ 2026-09-17 — **마지막 칸은 글자 폭만** 씁니다(`width:1%` · 사용자 지적). auto 배치가 남는 폭을
     전 칸에 비례해 나눠 주는데, 마지막 칸은 왼쪽 정렬 글자라 그 몫이 **카드 오른쪽 여백**처럼
     보였습니다 — 글자 끝 -> 카드 끝이 23 이어야 하는데 최근 검사 164 · 검사 기록 120 · 로그 125(1440 실측).
     09-15 의 *"폭은 못 자라게 묶을 칸으로 제어"* 그대로이고, 칸 폭은 재는 행(가장 긴 값)이 정하므로
     거를 때 흔들리지 않습니다 */
/* 정렬 제목은 버튼이 좌우 12 를 갖습니다 -> 셀은 10 만 */
.panel.tight table.tbl th.sortable:first-child{padding-left:10px}
.panel.tight table.tbl th.sortable:last-child{padding-right:10px}
/* ★ 재는 행(`tbody.sizer`)은 높이 0 이어야 합니다. 위 `tr:last-child td` 가 여기에도 걸려
     아래 여백이 붙었습니다 — 전에는 12, 옮기고 나서 22 로 커져 드러났습니다(390 실측) */
.panel.tight .tbl tbody.sizer tr td{padding-top:0;padding-bottom:0}
.panel.tight .tbl thead th{padding-top:22px}
.tbl td.dim{color:var(--muted)}
.tbl .at .t{margin-left:7px}
/* 좁은 화면 — 여섯 열을 그대로 밀어 넣으면 글자가 한 자씩 끊깁니다.
   부차적인 두 열(등급 분포 · 분석 개체)을 접고 나머지는 줄바꿈을 막습니다. */
/* ★ 접는 기준을 800px 로 올립니다(전엔 640).
   641~780px 에서 여섯 열이 담는 칸(556~700)보다 넓어(684) 표가 좌우로 밀리고
   오른쪽 `판정` 칸이 잘렸습니다(사용자 확인 2026-09-08).
   네 열(시각·대상·총 운동성·판정)만 남기면 460px 이라 넉넉히 들어갑니다. */
@media(max-width:800px){.tbl .w-dist,.tbl .w-cnt{display:none}}
@media(max-width:640px){
  /* 세로는 넓은 화면과 같은 14, 가로만 6 으로 좁힙니다(열을 밀어 넣는 폭입니다).
     ★ `.tbl.compact td` 는 **파일 뒤**에 같은 특정도로 또 선언돼 있어 여기서 집니다
       (CLAUDE.md 함정 ①). 그래서 대시보드 `최근 검사` 만 가로 12 로 남아
       좌 여백이 29 가 됐습니다 — 특정도를 한 단계 올려 이깁니다 */
  .tbl th,.tbl td,.tbl.compact td{padding:14px 6px;white-space:nowrap}
  .panel.tight .tbl.compact td{padding:14px 6px}
  .tbl th{font-size:11.5px}
  .tbl .at .t{display:block;margin:2px 0 0;font-size:12px;opacity:.8}
  /* ★ max-width:88px 클램프를 뺐습니다. 열이 여섯이던 시절의 값인데,
     X1 로 이름이 길어지면서(`KPN 1234 · 배치 B-07`) 390px 에서도 잘렸습니다.
     이제 네 열이라 자리가 남습니다 — 말줄임은 안전망으로만 남깁니다.
     ★ 2026-09-15 — 이름이 `KPN 1234` 로 다시 짧아졌습니다(배치 제외).
       여기 있던 **두 줄 쌓기**(~~`.w-sub .sep`~~ · ~~`.w-sub .s2`~~)는 쪼갤 것이
       없어져 뺐습니다. 말줄임만 안전망으로 남습니다 */
  .tbl .w-sub{overflow:hidden;text-overflow:ellipsis}
  /* ★ 2026-09-15 — 좌우를 8 -> 16 으로 올렸습니다. `1 + 16 + 셀 6 = 23` 이라
     좁은 폭에서도 **표준 23** 이 섭니다(전에는 15 였습니다).
     ~~"여섯 열을 밀어 넣는 폭이라 2px 도 아깝다"~~ 가 8 의 근거였는데,
     ≤800 에서 열이 **넷**으로 줄어 그 근거가 없어졌습니다.
     실측 — 390·640 에서 표가 카드 안에 그대로 들어갑니다(283.3/299 · 532/549).
     320 은 표의 최소 폭이 268.2 라 카드 안(229)을 넘지만, **8 일 때도 넘었습니다**
     (245 < 268.2). 그 폭에서는 원래 표가 카드 안에서 옆으로 굴러갑니다 */
  /* ~~.panel.tight{padding:10px 16px}~~ — 2026-09-17 가장자리 셀 22 로 옮겼습니다(1 + 22 = 23 · 위 `.panel.tight` 주석) */
  .tbl th.sortable button{gap:3px}
  .tbl .arw{margin-left:1px}
}
/* 아주 좁은 화면(360 등)에서는 판정을 점 하나로 줄입니다.
   색이 곧 판정이고, 바로 옆에 총 운동성 숫자가 있습니다 */
@media(max-width:380px){
  .tbl .w-vd .vd-dot{font-size:0;gap:0}
  .tbl .w-vd .vd-dot i{width:10px;height:10px}
  /* 글자가 빠지면 점 하나만 남는데, 왼쪽 정렬이면 열 머리(`판정`)와 따로 놉니다.
     아이콘만 남는 열은 머리글까지 같이 가운데로 맞춥니다 (사용자 지적 2026-09-08).
     버튼 좌우 padding 이 14px 로 대칭이라 text-align 만으로 가운데에 섭니다. */
  .tbl th.w-vd,.tbl td.w-vd{text-align:center}
}
.tbl .num{text-align:right}
.tbl th.num{text-align:right}
/* ★ 2026-09-15 — `.w-dist{width:150px}` 와 `.w-vd{width:104px}` 를 뺐습니다.
   표의 남는 폭은 auto 배치가 **각 칸이 필요한 만큼에 비례해 전 칸에 나눠** 줍니다.
   폭을 박은 칸은 그 분배에서 빠지므로, 이 두 못이
     · 막대를 150 에 묶어 126 에서 못 자라게 했고(사용자 지적: "더 늘릴 수 있는데 짧다")
     · 나머지 네 칸에만 남는 폭이 몰리게 했습니다
   1440 실측 — 이웃한 내용 사이의 간격(글자 끝 -> 다음 글자 시작)
     못이 있을 때  129.4 · 181.8 · 112.2 · 119.5 · 24
     못을 뺐을 때  100.0 · 139.0 ·  90.8 ·  98.0 · 24   막대 126 -> 200.8
   ★ 맨 끝의 24 는 폭으로 안 바뀝니다 — 오른쪽 정렬 숫자와 왼쪽 정렬 태그가 만나는
     경계라 셀 여백 12+12 로 고정입니다. 그건 정렬의 문제이고 아직 미결입니다 */
/* ★ X1 로 대상 이름에 `배치` 가 붙어 길어지면서 중간 폭(700·800·980px)에서
   두 줄로 접혔습니다. 표는 줄바꿈보다 말줄임이 맞습니다 —
   행 높이가 들쭉날쭉하면 훑기 어렵습니다.
   ★ 2026-09-15 — 그 이름이 `KPN 1234` 로 짧아졌습니다(배치 제외). 규칙은
     **그대로 둡니다** — nowrap 은 이름이 짧아도 필요하고(칸이 눌리면 접힙니다),
     말줄임은 안전망입니다. 다만 **이 값들의 근거가 된 폭은 다시 재야 합니다**
 재측정 후보.
   640px 이하는 카드형으로 바뀌므로 그쪽 규칙(위 미디어쿼리)을 건드리지 않게
   min-width 로 범위를 나눠 겁니다. */
@media(min-width:641px){
  /* 폭은 지정하지 않습니다 — 30% 로 묶었더니 641~700 · 980px 에서
     이름이 말줄임으로 잘렸습니다. 다른 칸은 폭이 고정이라(w-dist·w-vd·w-cnt)
     남는 자리를 이 칸이 가져갑니다. 말줄임은 정말 모자랄 때만 도는 안전망입니다. */
  .tbl .w-sub{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .tbl .at{white-space:nowrap}
  /* 수치("78.3 %")와 상태("사용 가능")도 마찬가지입니다.
     숫자와 단위가 갈라지면 읽는 순간 값이 아니라 줄로 보입니다. */
  .tbl .num,.tbl .w-vd{white-space:nowrap}
}
/* ★ G9 에서 이 칸을 td -> th 로 바꿨습니다(구분 행이라 scope="rowgroup").
   그때 이 선택자를 안 고쳐 .tbl th 의 padding(0 14px 11px)으로 떨어져
   위 여백이 사라졌습니다(사용자 지적 2026-09-08). 둘 다 걸어 둡니다. */
.tbl tr.grp td,.tbl tr.grp th{padding:18px 12px 8px;font-size:11.5px;font-weight:800;
  letter-spacing:.06em;color:var(--muted);border-bottom:0}
.tbl tr.grp{cursor:default}
.tbl tbody tr.grp:hover{background:transparent}
/* 세로는 다른 표와 같은 14 입니다 — 이 표를 조밀하게 만드는 것은 **글자 크기**입니다 */
.tbl.compact td{padding:14px 12px;font-size:13.5px}
/* 좁은 곁열에 들어가는 표입니다. 대상이 길면 줄바꿈 대신 말줄임으로 —
   X1 에서 이름에 `배치` 가 붙으면서 1220px 근처에서 두 줄로 접혔습니다.
   ★ 2026-09-15 — 이름이 짧아져 그 폭은 더 안 눌립니다. 규칙은 안전망으로 둡니다.
     ★ 바로 아래 컨테이너 쿼리 **520 은 대상 154.3 을 포함해 만든 값**이라
       재측정 대상입니다.
   히어로의 검사 기록 표가 쓰던 방식과 같습니다.
   (.hs-tbl 은 지금 없습니다 — 2026-09-14 확인) */
.tbl.compact td{white-space:nowrap}
/* 여기도 폭을 빼 둡니다 — 44% 로 묶었더니 1220px 에서 세 줄 다 잘렸습니다. */
.tbl.compact .w-sub{overflow:hidden;text-overflow:ellipsis}

/* ── `최근 검사` 카드가 좁아질 때 — 2026-09-15 ───────────────────────────
   ★ 증상 — 1121~1207 에서 표가 **카드 밖으로 삐져나갑니다**(1121 에서 98px).
     `.tbl.compact` 는 셀이 전부 nowrap 이라 최소 폭(504.7) 아래로 못 줄어드는데,
     그 구간에서 카드 안쪽이 396 밖에 안 됩니다. 기존 결함입니다 —
     전에는 `.w-dist{width:150px}` 때문에 최소 폭이 520.6 이라 더 심했습니다.

   ★ 왜 미디어쿼리가 아니라 컨테이너 쿼리인가
     이 표를 좁게 만드는 것은 **뷰포트가 아니라 담긴 칸**입니다. 곁열이 372px 고정이라
     본문 칸만 좁아지는 구간이 생깁니다(≤1120 에서는 한 열이 되어 오히려 넓어집니다).
     뷰포트로 적으면 `1121~1207` 같은 값을 새로 만들어야 하고, 그 값은 사이드바 236 ·
     곁열 372 · 여백 60 · gap 20 을 전부 외운 숫자라 어느 하나만 바뀌어도 틀립니다.
     -> 칸 폭을 직접 묻습니다. 저장소 첫 컨테이너 쿼리입니다.

   문턱의 근거 — 이 표의 **최소 폭 + 여유 15** 입니다.
   ~~520~~ (최소 폭 504.7 = 시각 63 · **대상 154.3** · 등급분포 134 · 운동성 67.3 · 판정 86)
   -> **452** (최소 폭 437.2 = 시각 63 · **대상 86.8** · 등급분포 134 · 운동성 67.3 · 판정 86)
   ★ 2026-09-15 재측정 — 대상 이름에서 배치가 빠져 154.3 -> 86.8 로 줄었습니다
     520 은 없어진 이름의 폭을 문턱에 박아둔 값이 됐습니다.
   ★ 접는 것 자체는 **여전히 필요합니다** — 1121 에서 카드 안쪽이 396 이라
     437.2 가 여전히 안 들어갑니다. 바뀐 것은 `언제 접느냐` 뿐입니다 */
.dash-recent{container-type:inline-size}
@container (max-width:452px){
  /* 다섯 칸 중 접을 것은 막대입니다 — `총 운동성` 숫자가 같은 값을 이미 말합니다.
     검사 기록 표가 ≤800 에서 접는 것과 같은 칸입니다(.tbl .w-dist) */
  .tbl.compact .w-dist{display:none}
}

/* 등급 분포 막대 */
/* 막대는 7px 이라 마우스를 정확히 올리기 어렵습니다. 보이는 굵기는 그대로 두고
   집히는 범위만 넓힙니다 — WCAG 2.5.8(AA) 의 최소 24px 을 넘기되, 위아래로
   셀 안쪽(padding 12px)까지만 늘려 옆 행의 막대와 겹치지 않게 합니다.
   ★ 범위는 바깥 상자에 겁니다 — .dist 는 모서리를 둥글리려 overflow:hidden 이라
     그 안에 가상 요소를 두면 잘려서 4px 밖에 안 늘어납니다(실제로 그랬습니다). */
.disthit{position:relative;display:block}
.disthit::after{content:"";position:absolute;inset:-12px -6px}
.dist{display:flex;height:7px;border-radius:100px;overflow:hidden;
  background:var(--ghost);min-width:110px}
.dist i{display:block;height:100%}

/* 판정 — 전부 같은 값일 때 눈에 안 띄도록 알약 대신 점 */
.vd-dot{display:inline-flex;align-items:center;gap:7px;font-size:13px;font-weight:600;
  white-space:nowrap}
.vd-dot i{width:7px;height:7px;border-radius:50%;display:block;background:var(--muted)}
.vd-dot.ok{color:var(--fg)}      .vd-dot.ok i{background:var(--ok)}
.vd-dot.warn{color:var(--warn)}  .vd-dot.warn i{background:var(--warn)}
.vd-dot.bad{color:var(--bad)}    .vd-dot.bad i{background:var(--bad)}

/* 대상 필터 */

/* 검사 기록 찾기 — 대상과 기간을 따로 둡니다 */
/* `--fbar-h` = 적용된 필터 바의 줄 높이. **태그 · 조건 지우기 · 건수가 같이 씁니다** */
.find{--fbar-h:34px;
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:16px}
.find .fx{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:700;
  color:var(--muted);white-space:nowrap}
.find input{font:inherit;font-size:13px;color:var(--fg);background:var(--surface);
  border:1px solid var(--line);border-radius:8px;padding:8px 11px;min-width:0;
  transition:border-color .16s}
.find input:focus{outline:0;border-color:var(--accent)}
/* ★ 2026-09-16 — `#admQ` 를 덧붙였습니다. id 선택자라 관리 입력칸에는 안 걸려서
   입력칸이 **안내 문구만큼 늘어나** 있었고, 그 때문에 840~900 에서 찾기 줄이
   두 줄로 접히며 표가 19px 들썩였습니다(실측) */
.find #fq,.find #admQ{width:180px}
/* 기간 — 프리셋 한 줄. 마지막 칸이 자체 달력을 엽니다 */
.find .seg{display:inline-flex;border:1px solid var(--line);border-radius:8px;overflow:hidden;
  background:var(--surface)}
.find .seg button{font:inherit;font-size:12.5px;font-weight:700;color:var(--muted);
  background:0;border:0;border-left:1px solid var(--line);padding:8px 12px;cursor:pointer;
  white-space:nowrap;transition:background .16s,color .16s}
.find .seg button:first-child{border-left:0}
.find .seg button:hover{color:var(--fg);background:color-mix(in srgb,var(--fg) 5%, transparent)}
.find .seg button[aria-pressed="true"]{background:var(--accent);color:var(--on-accent)}

/* 자체 달력 — 네이티브 팝업은 색·서체·스크롤을 우리가 못 정합니다 */
.cal{position:fixed;z-index:70;width:262px;padding:12px;border-radius:14px;
  background:var(--surface);border:1px solid var(--line);
  box-shadow:0 26px 54px -20px rgba(0,0,0,.4);
  opacity:0;visibility:hidden;transform:translateY(-4px);
  /* ★ visibility 를 전환에 태우면 여는 순간(t=0)에는 아직 hidden 이라
     그때 focus() 가 조용히 거부됩니다. 닫을 때만 페이드가 끝난 뒤 감추고,
     열 때는 즉시 보이게 합니다(0s). 초점을 안으로 넣으려면 이래야 합니다. */
  transition:opacity .14s ease, transform .14s ease, visibility 0s .14s}
.cal.on{opacity:1;visibility:visible;transform:none;
  transition:opacity .14s ease, transform .14s ease, visibility 0s}
.cal-hd{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}
/* 제목을 눌러 월 선택으로 — Material 날짜 선택기와 같은 방식.
   멀리 있는 달로 갈 때 화살표를 여러 번 누르지 않아도 됩니다. */
.cal-hd .cal-my{font:inherit;font-size:13.5px;font-weight:800;color:var(--fg);
  background:0;border:0;padding:4px 9px;border-radius:8px;cursor:pointer;
  display:inline-flex;align-items:center;gap:6px;transition:background .14s}
.cal-hd .cal-my:hover{background:color-mix(in srgb,var(--fg) 8%, transparent)}
.cal-hd .cal-my:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.cal-hd .cal-my i{width:0;height:0;border:4px solid transparent;
  border-top-color:currentColor;margin-top:3px;transition:transform .16s}
.cal-hd .cal-my.open i{transform:rotate(180deg);margin-top:-3px}

/* 월 선택 — 3열 × 4줄. 날짜 보기와 높이를 비슷하게 맞춰 팝업이 덜 튑니다 */
.cal-mg{display:grid;grid-template-columns:repeat(3,1fr);gap:4px}
.cal-mg .cal-m{font:inherit;font-size:12.5px;height:52px;border:0;border-radius:8px;
  background:0;color:var(--fg);cursor:pointer;transition:background .14s,color .14s}
.cal-mg .cal-m:hover{background:color-mix(in srgb,var(--fg) 8%, transparent)}
.cal-mg .cal-m.now{box-shadow:inset 0 0 0 1px var(--line);font-weight:800}
.cal-mg .cal-m.cur{background:var(--accent);color:var(--on-accent);font-weight:800;box-shadow:none}
.cal-mg .cal-m:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.cal-hd .mv{width:26px;height:26px;border:0;border-radius:7px;background:0;cursor:pointer;
  color:var(--muted);font-size:17px;line-height:1;transition:background .16s,color .16s}
.cal-hd .mv:hover{background:color-mix(in srgb,var(--fg) 7%, transparent);color:var(--fg)}
.cal-wd,.cal-gr{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}
.cal-wd span{text-align:center;font-size:11px;font-weight:700;color:var(--muted);padding:4px 0}
.cal-gr .d{font:inherit;font-size:12.5px;font-variant-numeric:tabular-nums;
  height:32px;border:0;border-radius:8px;background:0;color:var(--fg);cursor:pointer;
  transition:background .14s,color .14s}
.cal-gr .d:hover{background:color-mix(in srgb,var(--fg) 8%, transparent)}
.cal-gr .d.out{color:var(--muted);opacity:.45}
/* 다른 달의 날이라도 고른 날이면 흐리게 두지 않습니다 — 비활성처럼 보입니다 */
.cal-gr .d.out.edge,.cal-gr .d.out.mid{opacity:1}
.cal-gr .d.out.edge{color:var(--on-accent)}
.cal-gr .d.now{box-shadow:inset 0 0 0 1px var(--line);font-weight:800}
.cal-gr .d:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* ── 기간 표시 ──────────────────────────────────────────────────
   전에는 양 끝이 둘 다 완전히 둥근 알약이라 "날짜 두 개" 로 보였습니다.
   관례대로 시작은 왼쪽만, 끝은 오른쪽만 둥글게 하고 사이를 띠로 잇습니다.
   ★ 칸 사이 2px 틈은 띠를 좌우로 2px 넘겨 메웁니다 — 가상 요소라 레이아웃은
     한 픽셀도 안 바뀝니다(gap 을 없애면 칸 폭이 다 바뀝니다). */
.cal-gr .d{position:relative;isolation:isolate}
.cal-gr .d.mid::before,
.cal-gr.range .d.edge::before{content:"";position:absolute;top:0;bottom:0;z-index:-1;
  background:color-mix(in srgb,var(--accent) 14%, transparent)}
.cal-gr .d.mid::before{left:-2px;right:-2px}
/* ★ 띠도 알약과 같은 곡률을 씁니다.
   안 그러면 둥근 알약 뒤로 각진 배경이 삐져나와 모서리에 각이 남습니다. */
.cal-gr.range .d.edge-s::before{left:0;right:-2px;border-radius:8px 0 0 8px}
.cal-gr.range .d.edge-e::before{left:-2px;right:0;border-radius:0 8px 8px 0}
/* 하루짜리 기간 — 띠 없이 알약 하나 */
.cal-gr.range .d.edge-s.edge-e::before{display:none}
/* 한 주의 양 끝에서는 넘긴 만큼을 되돌리고, 띠도 같이 둥글게 */
.cal-gr .d:nth-child(7n+1)::before{left:0;border-top-left-radius:8px;border-bottom-left-radius:8px}
.cal-gr .d:nth-child(7n)::before{right:0;border-top-right-radius:8px;border-bottom-right-radius:8px}

/* 양 끝 날짜 자체 */
.cal-gr .d.edge{color:var(--on-accent);font-weight:800;background:0}
.cal-gr .d.edge::after{content:"";position:absolute;inset:0;z-index:-1;
  border-radius:8px;background:var(--accent)}
/* 기간일 때는 안쪽을 각지게 — 그래야 띠와 이어져 하나의 구간으로 읽힙니다.
   시작만 고른 상태(.range 아님)에서는 알약 그대로 둡니다. */
.cal-gr.range .d.edge-s::after{border-radius:8px 0 0 8px}
.cal-gr.range .d.edge-e::after{border-radius:0 8px 8px 0}
.cal-gr.range .d.edge-s.edge-e::after{border-radius:8px}   /* 하루짜리 기간 */
/* ★ 줄이 바뀌는 자리는 바깥쪽을 둥글게 되돌립니다.
   기간이 다음 주로 넘어가면 끝 날짜가 줄 맨 앞에 오는데, 그때 왼쪽이 각져 있으면
   앞이 잘린 것처럼 보입니다. 줄 끝도 같은 이유로 오른쪽을 둥글게 합니다. */
.cal-gr.range .d:nth-child(7n+1)::after{border-top-left-radius:8px;border-bottom-left-radius:8px}
.cal-gr.range .d:nth-child(7n)::after{border-top-right-radius:8px;border-bottom-right-radius:8px}
.cal-gr .d.edge.now{box-shadow:none}   /* 오늘 테두리가 알약 위에 겹치지 않게 */

/* ★ 2026-09-16 (2차) — 달력 아랫줄(고른 구간 + `적용`).
   ~~한 줄로 늘어놓고 줄바꿈을 허용~~ 했더니 로그에서 두 줄로 갈라졌고, 그걸 피하려고
   달력만 300px 로 넓혀 **두 화면의 달력 폭이 달라졌습니다**(사용자 지적, 09-16).
   -> **세로로 쌓습니다.** Apple HIG 는 날짜·시각을 세로로 놓을 때 `시작`·`끝` 라벨을
   달고 시간순으로 위에서 아래로 두라고 합니다. 폭이 262 로 돌아오고 줄바꿈이 없습니다.
   시각은 시·분 **따로** 받습니다(Kibana absolute 탭) — 방향키로 오르내립니다 */
.cal-ft{display:grid;gap:7px;margin-top:10px;padding-top:10px;
  border-top:1px solid var(--line)}
.cal-row{display:flex;align-items:center;gap:8px;
  font-size:12px;font-variant-numeric:tabular-nums;color:var(--fg)}
.cal-lb{flex:none;width:26px;font-size:11.5px;font-weight:700;color:var(--muted)}
.cal-rv{font-weight:700}
/* 시:분은 오른쪽 끝에 맞춥니다 — 두 줄의 칸이 세로로 나란히 섭니다 */
.cal-hm{display:flex;align-items:center;gap:2px;margin-left:auto}
.cal-hm b{font-weight:700;color:var(--muted)}
.cal-tm{font:inherit;font-size:12px;font-variant-numeric:tabular-nums;width:28px;
  text-align:center;color:var(--fg);background:var(--surface);
  border:1px solid var(--line);border-radius:7px;padding:3px 0}
.cal-tm:focus{outline:0;border-color:var(--accent)}
.cal-ok{font:inherit;font-size:12.5px;font-weight:700;white-space:nowrap;justify-self:end;
  color:var(--on-accent);background:var(--accent);border:0;border-radius:8px;
  padding:7px 13px;cursor:pointer;margin-top:1px}
/* ★ `disabled` 가 아니라 `aria-disabled` 입니다 — 진짜로 끄면 Tab 이 건너뛰어서
   키보드로는 버튼이 있는 줄도 모릅니다. 모양만 꺼 놓고 초점은 받습니다 */
.cal-ok[aria-disabled="true"]{opacity:.42;cursor:default}
.cal-ok:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* ── 적용된 필터 바 — **늘 자기 줄입니다** (2026-09-16) ────────────────────
   왼쪽 = 걸린 조건(태그) · 오른쪽 = 그 결과(건수 · 조건 지우기).
   ★ 왜 자기 줄인가 — 태그는 **있을 수도 없을 수도 있는 것**인데 건수·조건 지우기
     사이에 끼워 두니, 없을 때 자리를 예약해야 했고 그러면 **빈 자리가 남았습니다**
     (사용자 지적. 태그가 있을 수 없는 시스템 로그에도 남았습니다).
     예약을 풀면 줄 수가 바뀌어 표가 들썩입니다. 자기 줄을 주면 둘 다 없습니다 —
     그 줄은 건수가 늘 잡고 있어 **높이가 안 변합니다**.
   ★ 관례 — Carbon 은 적용된 필터에 `filter display bar` 라는 자리를 줍니다
     (태그 · 넘침 개수 · 한 번에 지우기). Polaris 도 적용된 알약을 검색·탭 줄
     **아래 자기 줄**에 둡니다. 건수 옆에 끼우는 물건이 아니었습니다.
   ★ ~~`@800 .find .fmeta{flex-basis:100%}`~~ 는 이제 전 폭 기본값이라 없앴습니다 */
/* ★ 2026-09-16 — 줄 높이를 **한 값으로 묶습니다.** 셋의 높이가 제각각이라
   (태그 36.8 · 조건 지우기 35 · 건수 글자 19) 조건을 걸면 줄이 16px 자라고
   **표가 들썩였습니다**(사용자 지적). 가로 자리를 예약하면 건수가 오른쪽 끝에서
   떨어지므로(그것도 지적받았습니다) **높이만** 묶습니다 — 숫자는 `--fbar-h` 한 곳에 */
.find .fmeta{display:flex;align-items:center;gap:10px;row-gap:14px;flex-basis:100%;
  margin-left:0;min-height:var(--fbar-h)}
/* ★ 2026-09-16 — `white-space:nowrap` 이 같이 있어야 합니다(사용자 지적).
   높이를 `--fbar-h` 로 묶어 놓고 세로 여백을 0 으로 뒀는데, 자리가 좁아지면
   `조건 지우기` 가 **버튼 안에서 두 줄로 접혀** 글자가 위아래로 꽉 차 보였습니다.
   줄을 안 접으면 대신 **덩어리째 아랫줄로** 내려갑니다 — 그게 맞는 줄바꿈입니다.
   건수도 같습니다(`1건 / 전체 123건` 이 세 줄로 접혔습니다) */
.find .ftag,.find .fclear,.find .fcount{white-space:nowrap}
.find .ftag,.find .fclear{height:var(--fbar-h);padding-top:0;padding-bottom:0;
  display:inline-flex;align-items:center}
/* 건수부터는 오른쪽 끝에 붙습니다 — 태그만 왼쪽에 남습니다 */
.find .fcount{margin-left:auto}
/* ★ 2026-09-16 — 여기 있던 `min-width:106px` 는 **뺐습니다.** 건수가 한 줄을
   다른 것들과 다투던 시절의 처방인데, 이제 적용된 필터 바가 자기 줄이라
   글자가 늘었다 줄었다 해도 줄 수가 안 변합니다 */
.find .fcount{font-size:12.5px;color:var(--muted);
  font-variant-numeric:tabular-nums}
/* ★ 2026-09-16 — `.adm-empty .fclear` 를 더했습니다. 값은 한 곳에 두되 **자리마다
   선택자를 답니다** — `.find` 안에 있을 때만 걸려 있어서, 빈 상태 안의 같은 버튼이
   브라우저 기본 모양(회색 입체 버튼)으로 남아 있었습니다(사용자 지적) */
.find .fclear,.tbl-empty .fclear,.adm-empty .fclear,.qrow .qcancel{font:inherit;font-size:12.5px;font-weight:700;
  color:var(--muted);background:0;border:1px solid var(--line);border-radius:8px;
  padding:7px 12px;cursor:pointer}
.find .fclear:hover,.tbl-empty .fclear:hover,.adm-empty .fclear:hover,.qrow .qcancel:hover{color:var(--fg);border-color:var(--fg)}
/* 취소는 되돌릴 수 없습니다 — 손을 올리면 그 색으로 답합니다(누르기 전 확인 대화도 뜹니다) */
.qrow .qcancel:hover{color:var(--bad);border-color:var(--bad)}
/* 조건이 없을 때는 **아예 빠집니다** — 그래야 건수가 줄의 오른쪽 끝에 붙습니다
   (사용자 지적 2026-09-16: 자리를 예약해 두니 건수가 애매한 자리에 떴습니다).
   적용된 필터 바가 자기 줄을 갖게 되어 버튼이 없어져도 **건수가 그 줄을 잡고
   있으므로** 높이가 안 변합니다 — 예약할 이유가 없어졌습니다 */
.find .fclear.off{display:none}

/* ── 찾기 칸의 제안 드롭다운 (2026-09-16) ─────────────────────────────────────
   Datadog·GitHub 이 자동완성으로 **키와 값을 제안**하는 자리이고, 고르면 태그가
   됩니다 -> shell.js `comboInput` 의 주석이 정본 */
.find .cb{position:relative;display:inline-flex}
.find .sug{position:absolute;top:calc(100% + 6px);left:0;z-index:30;
  min-width:240px;max-height:300px;overflow-y:auto;margin:0;padding:6px;
  list-style:none;background:var(--surface);border:1px solid var(--line);
  border-radius:12px;box-shadow:0 12px 32px rgba(0,0,0,.12)}
/* ★ 2026-09-16 — 축 이름과 값의 **구분이 약했습니다**(사용자 지적: `작업` 과
   `결과 수신` 이 비슷해 보임). 셋으로 벌립니다 — 축은 **작고 흐리고 대문자 간격**,
   값은 **크고 진하며 한 칸 들여쓰기**, 그리고 축 사이에 **가는 줄** */
.find .sg-h{padding:10px 10px 5px;font-size:10.5px;font-weight:800;letter-spacing:.1em;
  color:var(--muted);text-transform:uppercase}
.find .sg-h+.sg{margin-top:0}
.find .sg-h:not(:first-child){margin-top:6px;border-top:1px solid var(--line);
  padding-top:11px}
.find .sg{display:flex;align-items:center;gap:10px;padding:8px 10px 8px 18px;
  border-radius:8px;font-size:13.5px;font-weight:600;color:var(--fg);cursor:pointer}
.find .sg .n{margin-left:auto;font-size:11.5px;color:var(--muted);
  font-variant-numeric:tabular-nums}
/* 손을 올린 것과 **키보드로 고른 것**이 같아야 합니다 — 둘이 다르면 어디에 있는지 모릅니다 */
.find .sg:hover,.find .sg.on{background:color-mix(in srgb,var(--accent) 12%, transparent)}
.find .sg-free{color:var(--muted)}
.find .sg-none{padding:10px;font-size:13px;color:var(--muted)}

/* 걸린 조건이 쌓이는 자리. 여러 개면 줄바꿈합니다 */
.find .ftags{display:flex;align-items:center;gap:8px;flex-wrap:wrap}

/* ★ 2026-09-16 — **적용된 필터 태그.** 세그먼트에 칸이 없는 조건(`미확인 실패`)만
   여기 뜹니다. Carbon `Filtering` 의 filter tag 자리이고, `조건 지우기`(Clear all)와
   **짝으로** 씁니다 — 태그의 ✕ 는 그 조건 하나만, `조건 지우기` 는 전부 풉니다.
   모양은 상태 칩(`.tag`)과 같은 알약이되 **누르는 물건**이라 테두리를 답니다 */
/* ★ 2026-09-16 — 기본은 **중립**입니다(사용자 지적). 전에는 `미확인 실패` 하나만
   있던 시절의 빨강이 모든 태그에 걸려, `요청자: 김수정` 까지 경고처럼 보였습니다.
   빨강은 급한 것 하나(`.bad`)에만 남깁니다 — 색이 뜻을 가지려면 아껴 써야 합니다 */
.find .ftag{display:inline-flex;align-items:center;gap:7px;font:inherit;font-size:12px;
  font-weight:700;color:var(--fg);background:color-mix(in srgb,var(--fg) 5%, transparent);
  border:1px solid var(--line);border-radius:100px;
  padding:6px 12px;cursor:pointer;white-space:nowrap}
/* 축 이름 — 값보다 한 단계 낮춥니다. `요청자: 김수정` 에서 앞이 축입니다 */
.find .ftag .ax{font-weight:600;color:var(--muted)}
.find .ftag .ax::after{content:":";margin:0 1px 0 2px}
.find .ftag .x{font-size:13px;color:var(--muted)}
.find .ftag:hover{border-color:var(--fg)}
.find .ftag:hover .x{color:var(--fg)}
/* 급한 것 하나 — 배너가 거는 `미확인 실패` */
.find .ftag.bad{color:var(--bad);background:color-mix(in srgb,var(--bad) 8%, transparent);
  border-color:color-mix(in srgb,var(--bad) 30%, transparent)}
.find .ftag.bad .x{color:color-mix(in srgb,var(--bad) 70%, transparent)}
.find .ftag.bad:hover{border-color:var(--bad)}
.find .ftag.bad:hover .x{color:var(--bad)}
/* ★ 태그는 **자리를 안 남깁니다** — 자기 줄의 왼쪽 끝이라 없으면 그냥 없습니다.
   오른쪽의 건수·조건 지우기는 `margin-left:auto` 로 제자리에 있습니다 */
.find .ftag.off{display:none}
/* ── 찾기 줄이 두 줄이 되는 구간 (≤800) ─────────────────────────────────
   ★ 조건이 걸렸을 때만 두 줄이 되면, `조건 지우기` 가 생겼다 사라질 때마다
     **표 전체가 한 줄 높이만큼 들썩입니다**(사용자 지적 2026-09-15, 700px 미만).
     그래서 이 구간에서는 조건이 있든 없든 **항상** 두 줄로 둡니다 —
     위줄은 조건(대상·기간), 아랫줄은 그 결과(건수·조건 지우기)입니다.
   문턱 800 은 정본입니다. 이 값이어야 하는 이유 —
     .appbody 여백이 760 에서 30 -> 22 로 바뀌어 줄 폭이 **되레 좁아집니다**
     (760 일 때 716 · 761 일 때 701). 그래서 761~764 에도 안 맞는 구간이 생깁니다.
     801 이면 741 이라 덩어리 합 704.3 이 항상 들어갑니다(실측) */
/* ★ 2026-09-16 — 여기 있던 `@800 { .find .fmeta{flex-basis:100%} · .fclear.off }` 를
   통째로 지웠습니다. 적용된 필터 바가 **전 폭에서 자기 줄**이 되어 이 구간만의
   규칙이 필요 없어졌습니다 — 이 파일에서 문턱이 걸린 자리가 하나 줄었습니다 */
@media(max-width:640px){
  /* ★ 2026-09-16 — **세로 간격만 14** 로 올립니다(사용자 지적: 두 줄이 됐을 때
     위아래가 답답합니다). 재보니 절대값보다 **값이 뒤집힌 것**이 원인이었습니다 —
     줄 사이 8 · 필터 바 안 10 · 표까지 14 라, **바깥(8)이 안(10)보다 촘촘**해
     묶음이 거꾸로 읽혔습니다. 이 저장소의 세로 기준은 **14 하나**입니다.
     가로는 8 그대로입니다 — 옆으로 붙는 것끼리는 좁아야 한 덩어리로 읽힙니다 */
  .find{gap:8px;row-gap:14px}
  .find .fx{flex:1 1 100%}
  /* ★ 2026-09-16 — 입력칸도 **줄을 꽉 채웁니다**(사용자 지적). 고정 축 버튼 줄은
     `flex:1` 로 늘어나는데 찾기 칸만 180px 로 남아 두 줄의 길이가 달랐습니다.
     이제 입력칸이 `.cb` 로 감싸여 있어, 늘어나야 하는 것은 **그 감싸개**입니다 —
     `#fq{flex:1}` 만으로는 감싸개가 안 늘어나 입력칸도 그대로였습니다 */
  .find .cb{flex:1}
  .find #fq,.find #admQ{flex:1;width:100%}
  .find .seg{flex:1}
  .find .seg button{flex:1;padding:8px 6px}
  .find .fmeta{margin-left:0}}
.appscroll.fill .find{flex:none}

/* 정렬 가능한 열 제목 */
.tbl th.sortable{padding:0 0 11px}
.tbl th.sortable button{font:inherit;font-size:11.5px;font-weight:700;letter-spacing:.04em;
  color:var(--muted);background:0;border:0;cursor:pointer;padding:0 12px 0;
  display:inline-flex;align-items:center;gap:5px}
.tbl th.sortable.num button{flex-direction:row-reverse}
.tbl th.sortable button:hover{color:var(--fg)}
.tbl th.sortable.on button{color:var(--fg)}
.tbl th .arw{font-size:9px;opacity:.35;line-height:1}
.tbl th.sortable.on .arw{opacity:1;color:var(--accent)}

/* 좁은 곁열용 표 — 카드 밖으로 넘치지 않게 */
/* .tbl.slim 은 2026-09-14 에 지웠습니다 — 곁열에 좁은 표를 두던 시절의 것이고
   호출부가 CSS·JS 양쪽에서 사라져 있었습니다 */

/* ── 넓은 화면 ─────────────────────────────────────────────
   좌측 본문만 늘어나 우측과 균형이 깨지던 문제. 관례대로 내용 컨테이너에
   최대 폭을 걸고 가운데로 모읍니다. 배경·테두리는 화면 끝까지 갑니다. */
:root{ --app-max:1440px }
.appbody{max-width:var(--app-max);margin:0 auto;width:100%}
.apptop{padding:0;display:block}
.apptop .in{max-width:var(--app-max);margin:0 auto;width:100%;
  min-height:var(--app-top-h);padding:0 30px;display:flex;align-items:center;gap:16px}
.appmain .notice .in{max-width:var(--app-max);margin:0 auto;padding:10px 30px}
@media(max-width:800px){
  .apptop .in{padding:0 22px;gap:10px}
  .appmain .notice .in{padding:10px 22px}
}
/* ★ 2026-09-16 — 부제(`35건`)를 ~~760~~ -> **640** 까지 살립니다(사용자 결정).
   재보니 640 에서도 들어갑니다. 정보를 더 오래 보여주는 쪽이 낫고, 800 에 몰린
   다른 변화와 겹치지 않아 **한 폭에서 한 가지만** 바뀝니다 */
@media(max-width:640px){
  .apptop .crumb{display:none}
}

/* ── 분석 완료 카드 ── */
.run.done{border-color:color-mix(in srgb,var(--ok) 45%, var(--line));
  background:color-mix(in srgb,var(--ok) 5%, var(--surface))}
.run.done .run-state{color:var(--ok)}
.run.done .run-state .mono{color:var(--muted);font-weight:600}
.run-cta{display:inline-block;margin-top:16px;font-size:13.5px;font-weight:800;
  color:var(--ok)}
.run.done:hover .run-cta{text-decoration:underline}

/* 갈 데가 있을 때만 누를 수 있게 보입니다 */
div.run{cursor:default}
div.run:hover{border-color:var(--line);box-shadow:none}
div.run.done:hover{border-color:color-mix(in srgb,var(--ok) 45%, var(--line))}

/* 섹션 제목 + 오른쪽 이동 링크 */
/* ★ 위 margin 을 무조건 주면 **열의 첫 자식일 때** 맨 앞 .sec-h(0)와 28px 어긋납니다.
   검사 결과 화면에서 왼쪽 `지표` 와 오른쪽 `같은 KPN 이력` 이 그래서 안 맞았습니다
   (사용자 지적 2026-09-14). 제목이 링크를 달았을 뿐 같은 섹션 제목이라
   아래 `* + .sec-h` 와 같은 규칙을 씁니다 */
.sec-row{display:flex;align-items:baseline;justify-content:space-between;gap:16px;
  margin:0 0 14px}
* + .sec-row{margin-top:28px}
.sec-row .sec-h{margin:0}
/* ★ 두 번째 이후의 섹션 제목은 위를 띄웁니다 (2026-09-14).
   전에는 같은 값을 app.js 에서 `style="margin:28px 0 14px"` 로 다섯 곳에 박고 있었고,
   .sec-row 에는 CSS(바로 위)와 인라인이 **같은 값으로 중복**돼 있었습니다.
   첫 제목은 앞 형제가 없어 그대로 0 입니다 — 클래스를 따로 붙일 필요가 없습니다.
   .sec-row .sec-h 는 특정도가 높아 계속 0 입니다 */
* + .sec-h{margin-top:28px}
/* 블록 사이 간격도 같은 값으로. 전에는 .grid2 에 인라인 22px 이 붙어 있었습니다 */
* + .grid2{margin-top:28px}
.sec-link{font:inherit;font-size:12.5px;font-weight:700;color:var(--accent);text-decoration:none;
  background:0;border:0;cursor:pointer;padding:0}
.sec-link:hover{text-decoration:underline}

/* ── 랜딩 밖의 .btn (2026-09-14) ─────────────────────────────────────────
   ★ 랜딩 시안 반영(2026-09-10)이 site.css 의 **기본** .btn 을 랜딩 CTA 로 바꿨습니다
     (270x70 고정 · 투명 + 테두리 · hover 에 그라디언트가 채워지는 모양).
     .btn 의 기본형은 site.css 에만 있어서 그 변경이 로그인·가입·대시보드까지
     그대로 따라왔습니다 — 랜딩 작업이 만든 회귀입니다. 증상 셋:
       로그인·가입  주 동작이 테두리만 남아, 상단 내비의 `서비스 소개`(채운 알약)보다
                  약해 보였습니다 — 위계가 뒤집힙니다
       새 검사     주 버튼과 잠긴 버튼(.ghost)이 둘 다 투명이라 구분이 사라졌습니다
       크기       270x70 은 랜딩 CTA 치수입니다. 작업 영역 버튼이 아닙니다
   ★ 알약 모양 자체는 시안 언어라 지킵니다. **채움과 크기만** 되돌립니다 */
/* 설득 영역(로그인 · 가입 · 404) — 랜딩과 같은 브랜드 그라디언트를 채웁니다.
   ★ 이 셋에는 어두운 모드가 없습니다 — 복원 코드가 app.js 에만 있어서 늘 밝은 모드입니다.
     그래서 모드에 안 뒤집히는 --brand 그라디언트를 그대로 써도 됩니다 */
.auth .btn, .nf .btn{color:#fff;box-shadow:none}
.auth .btn::before, .nf .btn::before{opacity:1}

/* 작업 영역(대시보드) — 어두운 모드가 있으므로 **모드 연동 짝**을 씁니다.
   ★ 여기에 랜딩 그라디언트를 쓰면 안 됩니다. --brand 는 시안 고정값이라 모드에 따라
     안 뒤집히는데 글자색 --on-accent 는 뒤집혀서, 어두운 모드에서 거의 검정 글자(#04101F)가
     진한 파랑 위에 놓입니다 (2026-09-14 사용자 지적).
     대시보드 주 CTA(.topnew)가 쓰는 토큰과 같게 맞춥니다 */
body.work .btn{background:var(--accent);color:var(--on-accent);box-shadow:none}
body.work .btn::before{opacity:0}
body.work .btn:hover{color:var(--on-accent);filter:brightness(1.08)}
body.work .btn:hover::before{opacity:0}
body.work .btn{width:auto;min-width:168px;height:52px;font-size:16px;padding:0 24px}
.auth .btn{height:54px;font-size:16px}

/* 잠긴 버튼 — 테두리만. 채우지 않습니다 */
.btn.ghost{background:transparent;color:var(--fg);
  border:1px solid var(--line);font-weight:600}
.auth .btn.ghost::before,
body.work .btn.ghost::before{opacity:0}
/* ★ site.css 의 .btn:hover{color:#fff} 는 채움을 전제로 합니다.
   안 채우는 잠긴 버튼에 그대로 걸리면 투명 배경 위 흰 글자가 되어 사라집니다 */
/* ★ 배경도 여기서 되돌려야 합니다 — 위 `body.work .btn` 이 `.btn.ghost` 보다
   특정도가 높아(0,2,1 vs 0,2,0) 파일 순서와 무관하게 이깁니다.
   실제로 한 번 당했습니다: 잠긴 버튼이 주 버튼과 똑같이 채워졌습니다 */
.auth .btn.ghost, .auth .btn.ghost:hover,
body.work .btn.ghost, body.work .btn.ghost:hover{
  background:transparent;color:var(--fg);filter:none}
.btn:disabled{opacity:.42;cursor:not-allowed}
.btn:disabled:hover{transform:none;opacity:.42}

/* 촬영 영상 — 눌러서 크게 */
button.shotbox{padding:0;background:0;cursor:pointer;position:relative;display:block;
  width:100%}
.shot-play,.lb-play{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:52px;height:52px;border-radius:50%;background:rgba(9,14,24,.62);
  border:1px solid rgba(255,255,255,.35);display:grid;place-items:center;
  transition:transform .18s,background .18s}
.shot-play svg,.lb-play svg{width:22px;height:22px;fill:#fff}
button.shotbox:hover .shot-play{transform:translate(-50%,-50%) scale(1.08);
  background:rgba(9,14,24,.78)}

/* 큰 화면 */
/* 배경 흐리기(backdrop-filter)를 켠 채 투명도를 애니메이션하면 매 프레임
   화면 전체를 다시 흐려야 해서 프레임이 떨어집니다. 흐리기는 빼고,
   배경을 더 진하게 깔아 같은 효과를 냅니다. */
/* 네이티브 <dialog>. 배경은 ::backdrop 이 그립니다.
   ★ 세로 가운데 맞춤을 flex + margin:auto 로 합니다 —
     grid 의 place-items:center 는 내용이 화면보다 크면 위쪽이 잘리고
     스크롤로도 못 올라갑니다(확대 400 %에서 실제로 잘렸습니다). */
dialog.lb{border:0;padding:0;background:transparent;max-width:100%;max-height:100%;
  width:100%;height:100%;overflow:auto;overscroll-behavior:contain}
dialog.lb[open]{display:flex;flex-direction:column;padding:40px 24px;
  animation:lbin .16s ease-out}
dialog.lb::backdrop{background:rgba(4,8,16,.92)}
@keyframes lbin{from{opacity:0}to{opacity:1}}
.lb-in{animation:lbup .22s cubic-bezier(.22,.61,.36,1)}
@keyframes lbup{from{transform:translateY(10px)}to{transform:none}}
@media(prefers-reduced-motion:reduce){.lb,.lb-in{animation:none}}
.lb-in{width:min(1100px,100%);margin:auto}
.lb-frame{position:relative;border-radius:14px;overflow:hidden;
  border:1px solid rgba(255,255,255,.18);background:#0b1119}
.lb-frame img{width:100%;display:block}
.lb-play{width:82px;height:82px}
.lb-play svg{width:34px;height:34px}
.lb-cap{margin-top:16px;font-size:13px;color:rgba(255,255,255,.72);
  display:flex;align-items:baseline;gap:12px;flex-wrap:wrap}
.lb-cap b{color:#fff;font-weight:700}
.lb-cap span{margin-left:auto;font-size:12px;color:rgba(255,255,255,.5)}
.lb-x{position:fixed;top:22px;right:24px;width:40px;height:40px;border-radius:50%;
  background:rgba(255,255,255,.12);border:0;color:#fff;font-size:16px;cursor:pointer}
.lb-x:hover{background:rgba(255,255,255,.22)}
.lb-x:focus-visible{outline:2px solid #fff;outline-offset:3px}

/* ── 확인 대화 (2026-09-15) ──────────────────────────────────────────────
   네이티브 <dialog>. 위 `.lb` 와 같은 이유로 showModal() 을 씁니다(shell.js).
   ★ 버튼 크기는 `body.work .btn`(52px)을 그대로 씁니다 — 새 크기를 만들지 않습니다.
     min-width:168px 만 풉니다. 둘이 flex:1 로 줄을 나눠 가지므로 320px 에서도
     넘치지 않습니다(고정 폭이면 336+여백이라 넘칩니다) */
dialog.ask{border:1px solid var(--line);border-radius:16px;padding:26px;
  background:var(--surface);color:var(--fg);width:min(420px,calc(100% - 32px))}
dialog.ask::backdrop{background:rgba(4,8,16,.62)}
dialog.ask h2{margin:0;font-size:17px;font-weight:800;letter-spacing:-.01em}
dialog.ask p{margin:10px 0 0;font-size:13.5px;line-height:1.6;color:var(--muted)}
.ask-btns{display:flex;gap:10px;margin-top:22px}
body.work .ask-btns .btn{min-width:0;flex:1;padding:0 16px}
/* 되돌릴 수 없는 쪽. 채워서 무게를 주되 글자 대비는 흰 글자로 확보합니다 */
body.work .ask .btn.danger,body.work .ask .btn.danger:hover{
  background:var(--bad);color:#fff;border:0}
body.work .ask .btn.danger:hover{filter:brightness(1.06)}

/* 비어 있는 칸 */
.qempty{border:1px dashed var(--line);border-radius:16px;padding:22px;
  font-size:13.5px;color:var(--muted);text-align:center;
  background:color-mix(in srgb,var(--fg) 2%, transparent)}

/* 완료된 줄 — 눌러서 결과로 */
.qrow.done{cursor:pointer;transition:background .16s}
.qrow.done:hover{background:color-mix(in srgb,var(--ok) 7%, transparent)}
/* 접수 줄 링크(대시보드 · 2026-09-17) — 완료가 아니라 중립 칠입니다 */
.qrow.tap:not(.done){cursor:pointer;transition:background .16s}
.qrow.tap:not(.done):hover{background:color-mix(in srgb,var(--fg) 4%, transparent)}   /* 표 행 hover(.tbl tbody tr)와 같은 값 */
.qrow.done .q-st{color:var(--ok);font-weight:700}
.qrow.done .q-no{background:color-mix(in srgb,var(--ok) 18%, transparent);color:var(--ok)}

/* ── 관리 `요청` — 표(왼쪽)와 상세(오른쪽) ────────────────────────────────
   ★ 2026-09-14 — 표를 화면 높이까지 채우면(아래 .fill) 그 아래 있던 상세가
     통째로 가려집니다. 그래서 상세를 곁칸으로 옮깁니다.
     Material 3 canonical layouts 의 list-detail 그대로입니다 —
     상세가 멀리 뜨던 문제(상세가 y=760 이라 위쪽 행을 누르면 반응이 시야 밖)도 여기서 닫힙니다.
   ★ 접히는 폭을 **1010 으로 맞춥니다** — .appscroll.fill 이 풀리는 폭과 같아야 합니다.
     어긋나면 한 열인데 바깥이 overflow:hidden 이라 상세에 영영 못 닿습니다 */
/* ★ 2026-09-16 — 여기 있던 `.adm-split` 일습(2단 격자 · `-list` · `-side`)과
   `@media(max-width:1199px)` 블록을 **통째로 지웠습니다.** 요청 상세가
   **자기 화면**(`#req/R-2418`)이 되면서 곁칸 자체가 없어졌습니다.
   ★ 곁칸일 때 상세가 쓸 수 있는 폭은 1600 에서 **313px(24%)** 뿐이었습니다 —
     가장 중요한 정보가 가장 좁은 자리에 있었습니다(사용자 지적 2026-09-15).
     Material 3 list-detail 은 `목록 고정 · 상세 유연` 인데 우리는 정반대였습니다.
   ★ 덤으로 **비정본 문턱 1199 가 사라졌습니다**(정본은 1120·1010·800·640).
     `.appscroll.fill:has(.adm-split)` 로 바깥 스크롤을 되살리던 예외 셋도 같이 갔습니다 —
     상세는 `fill:false` 라 그냥 페이지째 굴러갑니다.
   ★ 위 주석 뭉치(1460 문턱 · 곁칸 폭 조정 이력)도 같이 지웠습니다 — 없어진 구조의 이력입니다 */

/* 페이지 줄은 표가 굴러가도 카드 바닥에 붙어 있어야 합니다 */
.adm-pager{flex:none}

/* ── 목록 화면: 표가 화면 아래까지 차고 안에서 스크롤됩니다 ── */
.appscroll.fill{overflow:hidden}
.appscroll.fill .appbody{display:flex;flex-direction:column;height:100%;
  min-height:0;padding-bottom:26px}
/* ★ 2026-09-16 — `:not(.adm-alert)` 를 답니다. 이 규칙은 **표를 담은 판**을 화면
   아래까지 늘리는 것인데, 목록 위의 실패 배너도 `.panel` 이라 같이 걸려서
   **배너가 332px 로 늘어나고 속이 세로로 쌓였습니다**(아이콘 위, 글자 아래).
   배너는 내용만큼만 높으면 됩니다 */
.appscroll.fill > .appbody > .panel:not(.adm-alert){flex:1;min-height:0;display:flex;
  flex-direction:column;overflow:hidden}
.appscroll.fill > .appbody > .adm-alert{flex:none}
/* ★ scrollbar-gutter — 거를 때마다 행 수가 달라지고, 세로 스크롤바가 생겼다 사라지면
   표가 그만큼(약 15px) 넓어졌다 좁아집니다. 그래서 폭을 선언해 놔도 열이 흔들립니다
   (2026-09-14 실측: 879 -> 886 -> 879). 자리를 늘 비워 두면 멈춥니다 */
.tablewrap{flex:1;min-height:0;overflow:auto}
/* ★ 자리를 늘 비우면 안 굴러가는 표에도 빈 띠가 남습니다(2026-09-14 사용자 지적).
   카드 안에서 굴러가는 화면에서만 비웁니다 — 거기서만 스크롤바가 생겼다 사라집니다 */
.appscroll.fill .tablewrap{scrollbar-gutter:stable}
.tablewrap thead th{position:sticky;top:0;z-index:2;background:var(--surface);
  padding-top:12px}
.tablewrap thead th.sortable{padding:12px 0 11px}

/* ── 등급 분포 추이 ── */
.trend{width:100%;height:auto;display:block}
.trend .gl{stroke:var(--line);stroke-width:1}
.trend .base{stroke:var(--accent);stroke-width:1.5;stroke-dasharray:5 4;opacity:.8;fill:none}
.trend .cx,.trend .cy,.trend .cb{font-size:10.5px;fill:var(--muted);
  font-variant-numeric:tabular-nums}
.trend .cx{text-anchor:middle}
.trend .cy{text-anchor:end}
.trend .cb{text-anchor:end;fill:var(--accent);font-weight:700}
/* 총 운동성 꺾은선 — 판정을 가르는 값 하나만 따라갑니다 */
.trend.line .ar{fill:color-mix(in srgb,var(--ok) 9%, transparent);stroke:none}
.trend.line .ln{fill:none;stroke:var(--ok);stroke-width:1.6;
  stroke-linejoin:round;stroke-linecap:round}
.trend.line .pt{fill:var(--surface);stroke:var(--ok);stroke-width:1.6}
.trend.line .pt.now{fill:var(--ok)}

/* 해동 후 활력 예측 — 실측은 점 하나뿐이고 나머지는 예측이라
   선을 끊어 그리고 폭(불확실성)을 같이 칠합니다.
   읽는 값은 SVG 밖 HTML 로 둡니다 — 안에 넣으면 좌표계에 눌려 잘립니다 */
.fc-wrap{display:grid;grid-template-columns:minmax(0,1fr) 132px;gap:8px 26px;
  align-items:center}
.fc-plot{position:relative}
.fc-lg{grid-column:1 / -1;margin-top:2px}
.fc-lg i.dot{box-shadow:inset 0 0 0 4px var(--ok);background:var(--ok)}
.fc-lg i.dash.ok{border-top:2px dashed var(--ok);opacity:1}
.fc-lg i.bandi{width:16px;height:9px;border-radius:3px;
  background:color-mix(in srgb,var(--ok) 22%, transparent)}
.fc-read{flex:none;width:132px;display:flex;flex-direction:column;gap:7px}
.fc-read .k{font-size:12px;color:var(--muted)}
.fc-read b{font-size:32px;font-weight:800;letter-spacing:-.04em;line-height:1}
.fc-read b em{font-style:normal;font-size:15px;font-weight:700;color:var(--muted);margin-left:4px}
.fc-read .tag{align-self:flex-start;font-size:12px;font-weight:800;border-radius:100px;
  padding:4px 11px}
.fc-read .tag.ok{color:var(--ok);background:color-mix(in srgb,var(--ok) 12%, transparent)}
.fc-read .tag.bad{color:var(--bad);background:color-mix(in srgb,var(--bad) 12%, transparent)}
/* ★ 2026-09-16 — ~~820~~ -> **800.** 검사 결과 화면의 활력 예측인데, 820 은
   랜딩 네비에서 온 값이라 여기 쓸 근거가 없었습니다 */
@media(max-width:800px){
  .fc-wrap{grid-template-columns:1fr;gap:14px}
  .fc-read{width:auto;flex-direction:row;align-items:baseline;gap:12px}
}
.trend .ax{fill:var(--muted);font-size:10.5px}
.trend.fc .band{fill:color-mix(in srgb,var(--ok) 11%, transparent);stroke:none}
.trend.fc .pred{fill:none;stroke:var(--ok);stroke-width:2;stroke-dasharray:6 5;
  stroke-linecap:round}
.trend.fc .veil{fill:var(--surface);opacity:.62}
.trend.fc .mark{stroke:var(--fg);stroke-width:1.2;stroke-dasharray:3 4;opacity:.5}
.trend.fc .t0{fill:var(--ok);stroke:var(--surface);stroke-width:2}
.trend.fc .tp{stroke:var(--surface);stroke-width:2.5}
.trend.fc .tp.ok{fill:var(--ok)}
.trend.fc .tp.bad{fill:var(--bad)}
.trend.fc .caret{fill:var(--fg);opacity:.5}
/* 호버 층 — 훑는 자리의 값과 폭을 그 자리에서 보여줍니다 */
.trend.fc .hitzone{fill:transparent}
.trend.fc .hx,.trend.fc .hd{opacity:0;transition:opacity .12s}
.trend.fc .hx{stroke:var(--fg);stroke-width:1;opacity:0}
.trend.fc .hx.on{opacity:.45}
.trend.fc .hd{fill:var(--surface);stroke:var(--ok);stroke-width:2.4}
.trend.fc .hd.on{opacity:1}
/* ★ 2026-09-18 — 두 축 예측(`.fc-wrap.two`). 총 운동성은 `같은 KPN 이력` 과 같은 초록, 전진은 파랑.
   기준선 둘(총 40 · 전진 30)은 선 색과 겹치지 않게 중립색으로. 끝점 색은 그 축이 판정 경계 어디에 있는지 */
.trend.fc .band.pr{fill:color-mix(in srgb,var(--accent) 9%, transparent)}
.trend.fc .pred.pr{stroke:var(--accent)}
.trend.fc .t0.pr{fill:var(--accent)}
.trend.fc .hd.pr{stroke:var(--accent)}
.trend.fc .tp.warn{fill:var(--warn)}
.fc-wrap.two .trend .base{stroke:var(--muted);opacity:.7}
.fc-lg i.dash.pr{border-top:2px dashed var(--accent);opacity:1}
.fc-wrap.two .fc-lg i.dash:not(.ok):not(.pr){border-top-color:var(--muted)}
.fc-read b.fc-vd{font-size:24px;letter-spacing:-.03em;line-height:1.15}
.fc-vals{font-size:12.5px;line-height:1.6;color:var(--muted)}
/* 예시 표시 — 실제 결과와 섞이지 않게 제목 옆에 답니다 */
.chip-x{flex:none;font-size:11px;font-weight:700;color:var(--muted);
  border:1px solid var(--line);border-radius:100px;padding:3px 9px;background:var(--surface)}
.legend-s{display:flex;gap:18px;flex-wrap:wrap;margin-top:14px;font-size:12.5px;
  color:var(--muted)}
.legend-s span{display:inline-flex;align-items:center;gap:7px}
.legend-s i{width:9px;height:9px;border-radius:3px;display:block}
.legend-s i.dash{width:16px;height:0;border-radius:0;border-top:1px dashed var(--accent);
  opacity:.75}
.legend-s i.dot{width:9px;height:9px;border-radius:50%;background:var(--surface);
  box-shadow:inset 0 0 0 2px var(--ok)}

@media(max-width:1010px){
  .app{height:auto;overflow:visible}

  /* ★ 목적지(대시보드 · 검사 기록)와 로그아웃 · 화면 밝기가 스크롤하면 사라져
     맨 위까지 올라가야만 닿았습니다(사용자 지적 2026-09-08).
     관례는 내비를 계속 닿게 두는 것입니다 —
     NN/g: 숨은 메뉴는 과업 완료율을 21 % 떨어뜨립니다 ·
     Material: 계층을 내려가도 계속 보이게 · Apple HIG: 탭 바는 항상 보입니다.
     ★ 반투명 배경이면 밑 내용이 비칩니다 -> 같은 색을 --bg 위에 얹어 불투명하게.
     ★ 상단바도 sticky 라 겹칩니다 -> 줄 높이만큼 내려 쌓습니다(--side-h, app.js 가 잽니다). */
  .side{position:sticky;top:0;z-index:30;box-sizing:border-box;
    background:color-mix(in srgb,var(--fg) 2.5%, var(--bg))}
  /* ★ 배경 흐리기를 끕니다. sticky 인 채로 매 프레임 뒤 내용을 다시 흐리면
     스크롤할 때 위가 떨립니다. 위에 불투명한 사이드바 줄이 얹혀 있어
     좁은 화면에서는 흐림이 주는 것도 거의 없습니다. */
  .apptop{top:var(--side-h,59px);background:var(--bg);backdrop-filter:none}
  .appscroll,.appscroll.fill{overflow:visible}
  .appscroll.fill .appbody{height:auto}
  /* ★ 2026-09-16 — 여기서부터는 **페이지가 통째로 스크롤됩니다**(`fill` 이 풀립니다).
     거를 때마다 행 수가 달라지고 세로 스크롤바가 생겼다 사라지면 **쓸 수 있는 폭이
     15px 오갑니다.** 그 15 에 걸리는 폭(실측 1010 · 360)에서 찾기 줄의 줄 수가 바뀌어
     표가 47.9 / 21.9px 들썩였습니다. 자리를 늘 비워 둡니다.
     ★ **넓은 폭에는 안 겁니다** — `fill` 이 살아 있어 페이지가 아예 안 스크롤됩니다
       (실측: body 1440 고정). 거기까지 걸면 얻는 것 없이 15px 만 버립니다 */
  html:has(.app){scrollbar-gutter:stable}


  /* ── 모바일 전수 점검 뒤 손본 것 (2026-09-07) ────────────────────
     좁은 화면에서는 안쪽 스크롤 컨테이너를 풀어 페이지가 통째로 스크롤됩니다.
     그래서 안에서 넘치는 것이 그대로 문서 폭을 밀어, 화면이 축소돼 보이고
     쓸데없는 가로 스크롤이 생겼습니다. */

  /* 표는 칸 폭이 고정이라 좁은 화면에서 반드시 넘칩니다. 패널 안에서 굴립니다 */
  .panel.tight{overflow-x:auto}

  /* 상단바 — 한 줄에 들어가게. 브랜드가 두 줄로 쪼개지고 있었습니다 */
  .side{gap:8px;padding:10px 12px}
  .side .bd{white-space:nowrap;padding:4px 2px;font-size:15px}
  .side-nav{gap:2px}
  .side-nav svg{display:none}
  .side-nav a{font-size:12.5px;padding:8px 7px;gap:0}
  .side .foot-u{gap:0}
  /* 계정 버튼은 이니셜 원 하나로 접고, 메뉴는 아래로 오른쪽에 붙여 엽니다(상단바 · GitHub 아바타 메뉴) */
  .side .acct{padding:0}
  .side .acct .acct-btn{padding:4px;width:auto}
  .side .acct .acct-t,.side .acct .acct-chev{display:none}
  .acct-menu{position:fixed;left:auto;bottom:auto;width:200px}   /* top · right 는 JS 가 버튼 자리로 */
  .side .foot-u button{font-size:12px}

  /* 알림 띠 — 두 문장이 옆으로 붙어 어정쩡하게 접혔습니다 */
  .notice .in{display:block}
  .notice .in span{display:block;margin-top:1px}

}
/* ★ 2026-09-15 — 여기 있던 좁은 폭 타일 규칙 셋을 지웠습니다.
   ~~.tiles 가로 3개(≤1010 · ≤760)~~ · ~~.tile{padding:14px 12px}(≤640)~~ ·
   ~~.tile b 24px · span 11.5px~~ — 전 폭이 `한 판에 세 줄` 하나로 통일됐습니다.
   그 축소판이 의도된 예외(타일 여백 15/13)였고, 예외도 같이 없어졌습니다 */

/* ★ 2026-09-17 — 줄이 넘치면 목적지를 **둘째 줄**로 내립니다(사용자 결정).
   `접수 현황` 이 목적지로 올라와 셋이 되자 한 줄(로고 · 목적지 · 로그아웃)이 약 430 아래에서
   넘쳤습니다(390 에서 로그아웃 끝 410 / 화면 375 · 실측). 첫 줄은 로고 · 밝기 · 로그아웃,
   둘째 줄은 목적지 셋을 같은 폭으로.
   ★ ~~@media(max-width:640px)~~ 가 아니라 **클래스**입니다 — `side-wrap` 은 shell.js
     `syncStickyTop` 이 **실제로 넘칠 때만** 답니다(C안). 문턱 숫자가 없습니다.
     관리 화면(목적지 둘)은 넘치지 않으면 안 붙습니다.
   ★ 1010 블록보다 뒤에 둡니다 — 같은 특정도면 뒤가 이깁니다 */
.side.side-wrap{flex-wrap:wrap;row-gap:8px}
.side.side-wrap .side-nav{order:3;flex:1 0 100%;display:grid;
  grid-template-columns:repeat(auto-fit,minmax(0,1fr))}
.side.side-wrap .side-nav a{justify-content:center}

/* 로고가 "Pre-Sperm AI" 로 길어져 360 에서 상단바가 넘쳤습니다.
   ★ 위의 1010 블록보다 뒤에 있어야 이깁니다 — 같은 특정도면 뒤가 이깁니다 */
@media(max-width:380px){
  .side{padding:10px 10px;gap:6px}
  .side-nav a{padding:8px 5px}
}


/* ══ 관리 화면 (admin.html) — 2026-09-14 ═══════════════════════════════════
   관리자가 보는 감독용 페이지입니다. **작업 대기열이 아닙니다** —
   이상이 없으면 안 열어도 되게 만듭니다(성공 기준이 "볼 일이 없는 것").
   그래서 첫 줄이 상태이고, 표는 막힌 것만 도드라지게 합니다.
   ★ 2026-09-14 — 전용 상단바(.adm-top)와 래퍼(.adm-wrap)를 걷어냈습니다.
     이 화면만 가로 헤더라 같은 제품으로 안 보였습니다(사용자 지적).
     지금은 app.html 과 **같은 셸**(.app > .side + .appmain)을 씁니다 —
     여백·폭·상단바가 저절로 같아지고, h1 과 nav 랜드마크도 같이 생깁니다 */

.adm-sec{margin-bottom:0}
.adm-sec .tablewrap{overflow-x:auto}

/* ── 지금 손이 필요한가 — **배너 하나** (2026-09-16) ────────────────────────
   ★ 여기 있던 `.adm-kpi` 지표 카드 줄(다섯 장)을 지웠습니다. 카드가 **세는 일과
     거르는 일을 겸직**하는데 그 안에서 성격이 또 갈렸습니다(줄어드는 값 / 누적 기록).
     거르개는 **찾기 줄 하나**로 모았고(Polaris 인덱스 상단 · Carbon 툴바 · GitHub 탭),
     급한 것만 배너로 격상했습니다(Datadog 의 Triggered Monitors 와 같은 발상).
     자세한 근거는 admin.js 의 같은 날짜 주석에 있습니다.
   ★ 배너는 **상세의 실패 배너와 같은 컴포넌트**(`.adm-alert`)입니다 —
     같은 뜻은 같은 모양으로 냅니다. 0건이면 아예 안 그립니다 */
.adm-sec+.find,.adm-alert+.find{margin-top:14px}

/* ★ 2026-09-16 — 행의 처리 슬롯. **늘 보입니다**(Carbon: 행 동작은 늘 보이는 것이
   기본이고, hover 로 숨기면 있는 줄 모릅니다). 상태 칩 옆에 붙어 한 칸을 같이 씁니다.
     미확인  테두리 있는 `확인`   — 누르는 물건으로 읽힙니다
     확인됨  `✓ 확인함`          — 상태로 읽히되 눌러서 되돌릴 수 있습니다
   `aria-pressed` 가 눌림 여부를 같이 말합니다 — 모양만으로 전하지 않습니다 */
.tbl .rowseen{margin-left:8px;font:inherit;font-size:11.5px;font-weight:700;
  display:inline-flex;align-items:center;gap:4px;vertical-align:middle;
  color:var(--muted);background:0;border:1px solid var(--line);border-radius:100px;
  padding:3px 9px;cursor:pointer;white-space:nowrap;
  transition:color .16s,border-color .16s}
.tbl .rowseen:hover{color:var(--fg);border-color:var(--fg)}
.tbl .rowseen svg{width:11px;height:11px;fill:none;stroke:currentColor;
  stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round}
/* 처리된 것은 **상태처럼** 보입니다 — 테두리를 빼고, 손을 올리면 되돌릴 수 있음을 알립니다.
   ★ 2026-09-16 — ~~`padding-left:0` -> hover 에서 `9px`~~ 을 없앴습니다. 여백이 바뀌니
     손을 올릴 때마다 **글자가 옆으로 밀렸습니다**(사용자 지적). 여백은 두 상태가 같고
     **테두리 색만** 바뀝니다 — 자리는 그대로 두고 눌리는 물건임만 알립니다 */
.tbl .rowseen.on{border-color:transparent;color:var(--muted)}
.tbl .rowseen.on:hover{border-color:var(--line)}

/* 확인 처리 — 배너 오른쪽. 상태를 바꾸는 것이 아니라 *"봤다"* 는 표시입니다 */
.a-act{flex:none;margin-left:auto;align-self:flex-start;font:inherit;font-size:12.5px;
  font-weight:700;color:var(--muted);background:transparent;
  border:1px solid var(--line);border-radius:100px;padding:7px 14px;cursor:pointer;
  white-space:nowrap;transition:color .16s,border-color .16s}
.a-act:hover{color:var(--fg);border-color:var(--fg)}
/* ★ 자리는 남기고 보이기만 감춥니다 — `display:none` 이면 배너가 14.9px 줄어
   표가 통째로 들썩입니다(2026-09-16 실측). `visibility` 는 탭 순서에서도 빠집니다 */
.a-act.off{visibility:hidden}

.adm-sec{margin-bottom:0}
.adm-sec .tablewrap{overflow-x:auto}

/* 손이 필요한 것만 눈에 띄게 — 정상 건은 조용해야 합니다.
   ★ 2026-09-14 — 전에는 접수와 실패가 **같은 주황 막대**였습니다(사용자 지적).
     급한 정도가 다릅니다: 실패는 조치가 필요하고, 접수는 아직 안 간 것입니다 */
/* ★ 행을 통째로 굵게 하지 않습니다 — 2026-09-14 에 그것 때문에 `접수 시각` 이
   실패 행에서만 잘렸습니다(굵은 글씨가 넓어 112px 을 넘김. 사용자 지적).
   강조가 이미 셋입니다: 왼쪽 빨간 막대 · 빨간 `실패` 태그 · 배너 집계.
   여기에 굵기까지 더하는 건 군더더기입니다 */
/* ★ 2026-09-16 — `.adm-fail .adm-pick`(펼치기 버튼 굵게)과 `.adm-on`(펼친 행 강조)을
   지웠습니다. 행이 링크가 되고 상세가 자기 화면이 되며 둘 다 대상이 없어졌습니다 */
.adm-fail .w-sub a{font-weight:700}
/* ★ 2026-09-16 — **미확인 실패 행은 옅게 칠합니다**(사용자 제안).
   Polaris `IndexTable` 의 `tone="critical"` 과 같은 자리입니다 — 그 시스템은
   행 배경으로 상태를 말하는 기능을 컴포넌트에 넣어 뒀습니다.
   ★ 색만으로 전하는 것이 아닙니다 — 칩(`실패`) · `확인` 버튼 · 왼쪽 띠가 이미 있고
     칠은 **네 번째 신호**입니다. 접근성 지침이 요구하는 조건이 그것입니다.
   ★ 칠하는 것은 **미확인**뿐입니다. 확인 처리하면 빠지므로, 실패가 쌓여도
     화면이 붉게 덮이지 않습니다 — 칠의 양이 `할 일의 양` 과 같습니다.
   ★ hover 는 더 진하게 따로 둡니다. 안 그러면 손을 올릴 때 중립 hover 가 이겨서
     칠이 사라지고, 행 강조가 죽은 것처럼 보입니다 */
.tbl-req tbody tr.adm-fail{background:color-mix(in srgb,var(--bad) 5%, transparent)}
.tbl-req tbody tr.adm-fail:hover{background:color-mix(in srgb,var(--bad) 11%, transparent)}
.adm-fail td:first-child{box-shadow:inset 3px 0 0 var(--bad)}
.adm-wait td:first-child{box-shadow:inset 3px 0 0 color-mix(in srgb,var(--warn) 55%, transparent)}

/* 요청 번호 — 대상 뒤에 작게 붙습니다. 번호로도 찾을 수 있어야 하는데
   열을 하나 더 만들면 좁은 폭에서 제일 먼저 접힙니다 */
.adm-rid{margin-left:9px;font-size:11.5px;color:var(--muted);font-weight:400}
/* ★ 2026-09-21 — 계정 상세 `검사 요청` 곁칸은 칸이 좁으면 요청 번호를 **둘째 줄로** 내립니다.
   증상 — 두 칸 배치(>1120)에서 곁칸이 372 고정인데 표가 한 줄로 403~405 라 **상태 칩이 33px 잘렸습니다.**
   요청 번호가 `R-2416` 꼴에서 `R-260918-003` 꼴로 길어진 뒤부터이고, 판넬이 overflow:hidden 이라 가로 넘침 검사에 안 잡혔습니다.
   화면 폭이 아니라 **칸 폭**으로 가릅니다 — `최근 검사` 카드(.dash-recent)와 같은 방식입니다.
   문턱 = 이 표의 최소 폭 404.9(`KPN 미입력` · 12자 번호 · 상태 칩 넷 중 가장 넓은 것) + 여유 15 */
.adm-ureqs{container-type:inline-size}
@container (max-width:420px){.adm-ureqs .adm-rid{display:block;margin:2px 0 0}}

/* 찾기 줄 — 검사 기록(.find)과 같은 구조를 씁니다. 관리 화면은 판 밖에 둡니다 */
/* ★ 배너 바로 아래에 붙어 사이가 0 이었습니다(2026-09-14 사용자 지적).
   구획 제목과 같은 28px 리듬을 씁니다 — 앞에 형제가 있을 때만 */
.adm-find{margin-bottom:14px}
* + .adm-find{margin-top:28px}
/* ★ 2026-09-16 — 여기 있던 관리 전용 건수 규칙을 **통째로 지웠습니다**:
     ~~`.adm-find .fcount{margin-left:14px}`~~ · ~~`.fcount.off{display:none}`~~ ·
     ~~`@media(max-width:900px){.fcount{flex-basis:100%}}`~~
   건수와 `조건 지우기` 가 이제 **공용 `.fmeta`** 안에 있습니다(검사 기록과 같은 구조).
   그 안에서 `flex-basis:100%` 가 걸리면 건수만 제 줄로 내려가 **조건을 걸 때마다
   찾기 줄이 19px 자랍니다** — 실측 801~900 에서 그랬습니다.
   ≤800 에서 덩어리째 두 줄로 두고 자리를 남기는 일은 공용 규칙이 이미 합니다 */
.adm-find .seg button .n{margin-left:6px;font-size:11px;opacity:.7}
/* ★ 2026-09-15 — 상태가 넷에서 **다섯**(취소 추가)이 되면서 320px 에서 줄 밖으로
   나갔습니다(실측: 줄 폭 261 · 칩 묶음 최소 312 — 버튼이 더는 안 줄어듭니다).
   문턱 380 은 **이미 있는 값**입니다(바로 위 `.tbl .w-vd` 가 쓰는 것과 같은 문턱).
   ★ 옆으로 굴리지 않고 **접습니다** — 굴리면 스크롤 막대가 24px 짜리 칩 줄 안에
     끼고, 무엇보다 안 보이는 칩이 생깁니다. 이 화면은 감독용이라 건수를 감추지
     않습니다(페이지 나누기를 쓰는 것과 같은 이유). 접는 쪽은 Material 의
     filter chip 관례이기도 합니다 — *"한 줄로 굴리거나 여러 줄로 접는다"* */
@media(max-width:380px){
  .adm-find .seg{flex-wrap:wrap}
}

/* ★ 2026-09-16 — 적용된 필터 바의 셋(태그 · 건수 · 조건 지우기)이 한 줄에 안 듭니다
   (실측 320: 필요 약 330 vs 쓸 수 있는 폭 260). **건수를 늘 자기 줄로** 올려
   두 줄로 고정합니다 — 태그가 생겼다 사라져도 줄 수가 안 변합니다(태그+버튼 = 220).
   문턱 380 은 이 파일이 이미 세 곳에서 쓰는 값입니다 — 새로 만들지 않았습니다 */
@media(max-width:380px){
  .find .fmeta{flex-wrap:wrap}
  .find .fcount{flex-basis:100%;margin-left:0;order:-1}
  .find .fclear{margin-left:auto}
  /* ★ 여기서만 **자리를 남깁니다.** 건수가 윗줄로 올라가 아랫줄엔 태그와 이 버튼뿐이라,
     버튼이 사라지면 그 줄이 통째로 없어져 표가 올라옵니다 */
  .find .fclear.off{display:block;visibility:hidden}
}

/* 페이지 나누기 — 관리 도구의 관례. 무한 스크롤은 "몇 건이고 끝이 어디인지" 를
   숨겨서 감독 화면에 안 맞습니다 */
.adm-pager{display:flex;align-items:center;gap:12px;padding:14px 12px 2px;
  font-size:12.5px;flex-wrap:wrap}
.adm-pager .sp{flex:1}
/* ★ 버튼이 표에서 제일 도드라지면 안 됩니다 — 데이터가 주인공입니다.
   .btn(랜딩 CTA 270x70)을 물려받던 것을 글자 버튼으로 바꿨습니다 (2026-09-14) */
.adm-pg{font:inherit;font-size:12.5px;font-weight:600;color:var(--muted);
  background:0;border:0;border-radius:7px;padding:6px 10px;cursor:pointer;
  transition:background .16s,color .16s}
.adm-pg:hover:not(:disabled){background:color-mix(in srgb,var(--fg) 6%, transparent);
  color:var(--fg)}
.adm-pg:disabled{opacity:.35;cursor:default}
.adm-pg-n{font-size:13px;font-weight:800;color:var(--fg);letter-spacing:-.01em}
.adm-pg-n span{font-weight:600;color:var(--muted);margin-left:3px}

/* 사이드바 하단 — 관리에서 대시보드로 나가는 길 */

/* ★ 2026-09-15 — 여기 있던 `@760 에서 .adm-c-by,.adm-c-file 접기` 를 위쪽 표 절로
   옮겼습니다(문턱 800 · 표별로). 문턱 둘이 서로 다른 곳에 있으면 어느 쪽이 도는지
   알 수 없습니다 — 표 이야기는 표 절에 모읍니다 */
/* ★ 2026-09-16 — 여기 있던 `@760 .adm-nums{gap:18px}` 를 지웠습니다.
   `.adm-nums` 는 저장소 어디에서도 안 뿜습니다(2026-09-14 에 배너를 지우며 죽었습니다) */

/* ── 요청 상세 ─────────────────────────────────────────────────────────
   ★ 2026-09-16 — 여기 있던 곁열 시절 규칙을 지웠습니다:
     ~~`.adm-d-cols`~~(+`@820`) · ~~`.adm-d-info`~~ · ~~`.adm-d-see`~~ · ~~`.adm-go`~~ ·
     ~~`.adm-d-when`~~ · ~~`.adm-d-do`~~(이미 죽어 있던 것).
     상세가 전용 화면이 되며 골격은 공용 `.grid2` 가 맡습니다 —
     **비정본 문턱 820 이 같이 사라졌습니다**.
   ★ 머리줄(`.adm-d-h`)만 남습니다 — 상태 칩 한 줄과 그 아래 구분선입니다 */
/* ★ 2026-09-16 — 머리줄 **전체가 상태의 자리**입니다(사용자 결정).
   전에는 칩 하나(40.8 x 29)가 1,129px 줄에 홀로 떠 있었습니다 — 줄의 3.6% 입니다.
   왼쪽 3px 색 띠는 **목록 행(`.adm-fail`)이 쓰는 값 그대로**입니다 — 같은 뜻을
   같은 모양으로 냅니다. 새 어휘를 만들지 않았습니다.
   ★ 띠를 `box-shadow:inset` 이 아니라 **가상 요소**로 그립니다 — inset 그림자는
     아래 여백(16)까지 덮어서, 칩이 띠의 가운데가 아니라 **위쪽에 붙어 보였습니다**
     (2026-09-16 사용자 지적). `top:0;bottom:16px` 이면 띠가 **내용 상자와 정확히
     같은 높이**가 되고, 그 안에서 `align-items:center` 가 칩을 가운데에 둡니다 */
/* 머리줄 오른쪽 동작(계정 상세 `사용 중지` · 2026-09-17) — `.a-act` 의 margin-left:auto 가 오른쪽으로 밉니다 */
.adm-d-h .a-act{align-self:center}
.adm-d-h .adm-d-note{margin-left:auto;font-size:12.5px}
.adm-d-h{position:relative;display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:0 0 16px 14px;margin-bottom:4px;border-bottom:1px solid var(--line)}
.adm-d-h::before{content:"";position:absolute;left:0;top:0;bottom:16px;width:3px;
  border-radius:2px;background:var(--line)}
.adm-d-h.st-failed::before{background:var(--bad)}
.adm-d-h.st-queued::before{background:color-mix(in srgb,var(--warn) 55%, transparent)}
.adm-d-h.st-done::before{background:var(--ok)}

/* 큰 칩 — 상세에서만. 목록 행의 칩은 칸 폭에 물려 있어 그대로 둡니다 */
.tag-lg{font-size:15px;padding:8px 16px;gap:8px}
.tag-lg svg{flex:none;width:15px;height:15px;fill:none;stroke:currentColor;
  stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}

/* ★ 2026-09-16 — 실패·취소를 **배너**로 (사용자 지적: 줄글처럼 적혀 있었습니다).
   ~~`.adm-why`(주황 한 줄) + `.adm-err-m`(작은 조각 넷)~~ 을 갈음합니다.
   `.panel` 을 물려받아 여백 22 + 테두리 1 = **가장자리→글자 23** 이 그대로 지켜집니다.
   색·아이콘·테두리 셋이 겹칩니다 — NN/g 가 요구하는 **중복 지시자 셋**
   ★ 속 이름에 **`a-` 접두사**가 붙어 있습니다. 처음에 `.t`·`.n`·`.m` 으로 짰다가
     `site.css` 의 `.m{…border-bottom:1px solid var(--line)}` 에 물려서 배너 안에
     **줄이 하나 그어졌습니다**(실제 크롬에서 보임). `.m` 은 CLAUDE.md 가 적어 둔
     겹치는 이름 28종 중 하나입니다 — 적어 놓고 또 당했습니다 */
.adm-alert{display:flex;align-items:flex-start;gap:14px;margin:14px 0 0}
/* 글이 **한 줄뿐인** 배너(목록 위 `미확인 실패 N건`)는 세로 가운데입니다.
   위 정렬은 글이 여러 줄인 상세의 실패 배너를 위한 것이라 한 줄짜리에는 안 맞습니다
   — 오른쪽 버튼(38)이 글(22)보다 높아 글자가 위로 붙어 보였습니다(사용자 지적) */
.adm-alert.solo{align-items:center}
.adm-alert .a-ic{flex:none;display:flex;width:22px;height:22px;margin-top:1px}
.adm-alert svg{width:22px;height:22px;fill:none;stroke:currentColor;
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.adm-alert p{margin:0}
.adm-alert .a-t{font-size:15px;font-weight:700;line-height:1.45}
.adm-alert .a-n{margin-top:5px;font-size:13.5px;color:var(--fg)}
/* 기계값과 mock 안내는 **맨 아래 흐리게** — 전에는 `다음에 할 일`과 같은 무게였습니다 */
.adm-alert .a-m{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  margin-top:10px;font-size:12px;color:var(--muted)}
.adm-alert .a-m .mono{letter-spacing:.02em}
.adm-alert.bad{background:color-mix(in srgb,var(--bad) 7%, transparent);
  border-color:color-mix(in srgb,var(--bad) 32%, transparent)}
.adm-alert.bad .a-ic,.adm-alert.bad .a-t{color:var(--bad)}
/* 승인 대기(2026-09-17) — 오류가 아니라 **처리할 일**이라 주의색입니다(Polaris `attention` · Carbon 노랑) */
.adm-alert.warn{background:color-mix(in srgb,var(--warn) 8%, transparent);
  border-color:color-mix(in srgb,var(--warn) 34%, transparent)}
.adm-alert.warn .a-ic,.adm-alert.warn .a-t{color:var(--warn)}
/* 취소는 경고가 아닙니다 — 같은 상자, 중립색 (Carbon) */
.adm-alert.off .a-ic,.adm-alert.off .a-t{color:var(--muted)}
.adm-alert.off .a-t{font-weight:600}
/* 원인 기록 칸 (2026-09-17) — 배너 글 아래에 펼칩니다. 고르기는 알약 모양 라디오 */
.adm-alert .a-body{min-width:0;flex:1}
.adm-alert .a-n b{font-weight:700}
.adm-alert .a-form{display:grid;gap:12px;margin-top:14px;max-width:560px}
.adm-alert .a-form fieldset{border:0;margin:0;padding:0;min-width:0}
.adm-alert .a-lg{display:block;margin:0 0 6px;padding:0;font-size:12px;font-weight:700;color:var(--muted)}
.adm-alert .a-opts{display:flex;flex-wrap:wrap;gap:6px}
.adm-alert .a-opt{position:relative;display:inline-flex}
.adm-alert .a-opt input{position:absolute;inset:0;margin:0;opacity:0;cursor:pointer}
.adm-alert .a-opt span{font-size:12.5px;font-weight:600;padding:6px 11px;border-radius:100px;
  border:1px solid var(--line);background:var(--bg);color:var(--fg)}
.adm-alert .a-opt input:checked + span{background:var(--fg);border-color:var(--fg);color:var(--bg)}
.adm-alert .a-opt input:focus-visible + span{outline:2px solid var(--accent);outline-offset:2px}
.adm-alert textarea{display:block;width:100%;box-sizing:border-box;font:inherit;font-size:13px;
  padding:8px 10px;border:1px solid var(--line);border-radius:8px;background:var(--bg);
  color:var(--fg);resize:vertical}
.adm-alert .a-btns{display:flex;gap:8px}
.adm-alert .a-btns .a-act{margin-left:0}
/* ★ 2026-09-17 — ~~글자색(--fg)으로 채움~~ -> **강조색**(사용자 지적: 계정 `승인` 이 검정 알약).
   작업 화면의 주 동작은 전부 강조색입니다 — `body.work .btn` · 달력 `적용`. 원인 기록 `기록` 도 같이 바뀝니다 */
.adm-alert .a-primary{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}
.adm-alert .a-primary:hover{color:var(--on-accent);border-color:var(--accent);filter:brightness(1.08)}
/* 사용자 쪽 실패 화면의 할 일 버튼 (2026-09-17) — 글 아래 한 줄 */
.adm-alert .fail-act{display:flex;gap:10px;flex-wrap:wrap;margin-top:14px}

/* 아무것도 안 골랐을 때 — ★ 점선 상자(.qempty)를 쓰면 판 안에 판이 되어
   테두리가 두 겹이 되고 여백이 46px 로 두 번 겹칩니다.
   판이 이미 경계를 그리고 있으므로 글자만 둡니다 */
.adm-none{margin:0;font-size:13.5px;color:var(--muted)}
/* 여백 없는 판(표용) 안의 빈 안내 — 표의 가장자리 셀과 같은 22 · 행 세로 14 (2026-09-17 계정 상세에서 발견) */
.panel.tight > .adm-none{padding:14px 22px}
/* 표 자리가 비었을 때 — 늘어난 카드의 **가운데**에 세웁니다 (2026-09-16).
   점선 상자(.qempty)는 안 씁니다 — 판이 이미 경계를 그리고 있습니다 */
/* ★ 2026-09-16 — 이름에서 `adm-` 을 뗐습니다. 대시보드 `검사 기록` 도 같은 모양을
   써야 하는데 관리 전용 이름이라 못 쓰고 있었습니다(사용자 지적: 그 화면만 달랐습니다).
   `.adm-empty` 는 옛 이름으로 남겨 둡니다 — 같은 규칙을 두 번 쓰지 않습니다 */
.tbl-empty,.adm-empty{flex:1;min-height:140px;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:14px;text-align:center}
/* ★ 2026-09-18 — 검사 기록(`#rows`)은 빈 상자가 **표를 감싼 칸 안**에 있습니다(관리 화면은 판 바로 아래).
   그 칸이 늘어나지 않아 `flex:1` 이 걸릴 데가 없었고, 글자가 판 맨 위에 붙어 있었습니다(사용자 지적).
   빈 상태일 때만 그 칸을 늘립니다 — 표가 들었을 때는 건드리지 않습니다 */
.appscroll.fill .panel > .tablewrap:has(> .tbl-empty){flex:1;min-height:0;
  display:flex;flex-direction:column}
.tbl-empty p,.adm-empty p{margin:0;font-size:13.5px;color:var(--muted)}

/* ★ 2026-09-16 — `.adm-pick`(행을 여는 버튼)을 지웠습니다.
   행은 이제 **상세로 가는 링크**입니다 — `.tap a` 가 이미 색·밑줄을 맡습니다 */

/* .adm-log(접이식 목록)은 2026-09-14 에 지웠습니다 — 시스템 로그가 목적지 하나를
   갖고 표가 됐습니다. 맨 아래 접어 두던 자리가 아닙니다 */
