/* ═══════════════════════════════════════════════════════════════════════
   통합 업무 포털 — 진입(로그인) 화면
   레퍼런스 = motionsites.ai "Bloom AI" 히어로. **최대한 그대로** 재현한다
   (사용자 지시 2026-07-30: 앞선 규칙은 접고 레퍼런스에 최대한 유사하게).

   레퍼런스에서 남긴 것
     · 유리의 재질 문법: ::before 그라디언트 테두리(위·아래가 밝고 중앙이 투명),
       유리 2등급(가벼운 유리 / blur 50px 무거운 유리)
     · 회색조 텍스트 계단 (white / 80% / 60% / 50%)
     · 조작 요소 hover:scale(1.05), active:scale(.95)
     · 배경도 레퍼런스와 같은 방식 = **실사 영상 루프** (NASA 4K ISS, 퍼블릭 도메인,
       자체 호스팅). 렌더한 구·정지 사진으로는 "선명 + 회전"이 성립하지 않는다 — 상세 근거는 bg.js.
   레퍼런스에서 떠난 것 (전부 사용자 지시, 2026-07-30)
     · 2패널 분할 폐기 → 로그인 카드 하나만 화면 왼쪽 중앙에. 우측 패널·미리보기 카드는
       삭제하고 그 내용은 **로그인 성공 후 화면 중앙에 뜨는 허브(.hub)** 로 옮겼다.
     · 서체 3종 혼용(Poppins/Pretendard/Source Serif) 폐기 → **Pretendard 단일**.
     · 배경 색조는 청록이 아니라 파랑 — 실제 지구가 파랗고, 사용자 지시도 블루다.
   ═══════════════════════════════════════════════════════════════════════ */

/* ★ 서체 = **Pretendard 단일** (사용자 지시 2026-07-30 + CI 규정 2026-07-27).
   ⚠ 예전엔 라틴 Poppins / 한글 Pretendard / 강조구 Source Serif 4 로 셋을 섞었다.
     레퍼런스를 따라간 것이었지만 한 화면에서 글자마다 패밀리가 갈려 어수선했다
     ("이상하게 폰트를 나눠서 쓴다"). 셋 다 걷어내고 Pretendard 로 통일한다.
   웨이트는 CI 대로 400/600/700 3단만 쓴다(500 은 400 으로 흡수). */
@font-face{font-family:"Pretendard";font-weight:400;font-display:swap;
  src:url("/assets/fonts/Pretendard-Regular.subset.v2.woff2") format("woff2")}
@font-face{font-family:"Pretendard";font-weight:600;font-display:swap;
  src:url("/assets/fonts/Pretendard-SemiBold.subset.v2.woff2") format("woff2")}
@font-face{font-family:"Pretendard";font-weight:700;font-display:swap;
  src:url("/assets/fonts/Pretendard-Bold.subset.v2.woff2") format("woff2")}

:root{
  /* 국·영문 한 패밀리. CI 폴백 스택 그대로. */
  --sans:"Pretendard",-apple-system,"Segoe UI",Roboto,"Malgun Gothic",sans-serif;

  /* ★ 레퍼런스는 **유채색 0**. 화면의 색은 전부 배경(영상/사진)에서 온다.
     UI 까지 물들이면 색이 이중으로 얹혀 탁해진다(우리가 그 상태였다). */
  --w:#FFFFFF;
  --w80:rgba(255,255,255,.80);
  --w60:rgba(255,255,255,.60);
  --w50:rgba(255,255,255,.50);
  --w40:rgba(255,255,255,.40);
  --err:#F2A9A2;      /* 오류만 예외 — 무채색으로는 경고가 안 읽힌다 */
  --radius:1rem;
  --ease:cubic-bezier(.22,1,.36,1);
}

*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
body{
  font-family:var(--sans);color:var(--w);background:#080808;   /* 무채색 암부 */
  overflow:hidden;-webkit-font-smoothing:antialiased
}

/* ── 스크림 ───────────────────────────────────────────────────────────────
   ⚠ 예전엔 좌측 절반을 62%까지 눌렀다. 그때는 좌측이 **화면 절반짜리 투명 유리 슬랩**이라
     그 위의 흰 글자를 앉히려면 그만큼 필요했다. 지금은 로그인 카드가 작고 자체 틴트를
     가지므로 그 스크림이 배경만 탁하게 만들고 있었다(사용자 지적 2026-07-30: "너무 탁하다").
   → 화면 전체를 덮는 가로 그라디언트 대신, **카드가 앉는 자리에만 부드러운 그늘**을 둔다.
     나머지 지구는 그대로 맑게 보인다.
   두 번째 층은 **우측 가장자리 그늘** — 우측 세로 레일(카드 없는 맨텍스트)이 밝은 구름 위에
   놓일 때를 대비한 것이다. 가장자리 비네트라 카드처럼 보이지 않고, 화면 중앙은 건드리지 않는다. */
#bgshade{position:fixed;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(820px 660px at 19% 50%, rgba(0,0,0,.46), transparent 74%),
    linear-gradient(270deg, rgba(0,0,0,.40) 0%, rgba(0,0,0,.15) 14%, transparent 30%)}

/* ── 배경 = 실사 영상 (NASA ISS 4K, 17.5초 무한루프) ────────────────────────
   회전은 CSS 가 아니라 **영상 자체**가 만든다. 예전엔 여기에 transform 팬·줌을
   걸었는데, 그건 사진을 미는 것이라 "지구가 도는" 것으로 보이지 않았다.
   ⚠ CSS 애니메이션을 다시 얹지 말 것 — 영상의 실제 움직임과 겹쳐 어지러워진다.
   원본이 1.68:1 로 16:9 보다 좁아, 와이드 화면에서는 위아래가 잘린다.
   대각선 지평선이 화면에 남아야 하므로 아래쪽을 조금 더 살리는 위치로 잡는다. */
/* ★ 포스터 층 — 영상 아래에 항상 깔린다(2026-08-09). 기하는 #bg 와 정확히 같아야
   영상이 얹힐 때 그림이 밀리지 않는다(object-fit:cover ↔ background-size:cover,
   object-position ↔ background-position 이 1:1 대응). 둘을 따로 고치지 말 것. */
#bgposter{
  position:fixed;inset:0;z-index:0;
  background-color:#02070C;
  background-size:cover;background-position:50% 40%;background-repeat:no-repeat;
  pointer-events:none
}
#bg{
  position:fixed;inset:0;z-index:0;
  width:100%;height:100%;
  object-fit:cover;object-position:50% 40%;
  /* ⚠ 여기에 배경색을 주지 않는다. 영상이 준비되기 전·실패했을 때 이 요소가
     아래 포스터 층을 가리면 안 된다(투명해야 포스터가 보인다). */
  opacity:0;transition:opacity 1.1s ease;
  pointer-events:none
}
#bg.on{opacity:1}
/* ⚠ 좌하단 radial(80% 검정, 1300×950)을 뺐다 — 그건 하단에 회사 문구가 있던 시절의
   잔재였고, 문구가 삭제된 지금은 배경을 탁하게 만드는 역할만 했다.
   프레임에 무게만 주는 아주 옅은 하단 그라디언트만 남긴다. */
.vignette{position:fixed;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(202deg, transparent 52%, rgba(0,0,0,.30) 100%)}

/* ⚠ 모바일은 우측 패널이 숨겨져 **화면 전체가 좌측 패널**이 된다.
   좌측을 누르는 위 스크림을 그대로 두면 배경 지구가 아예 안 보인다(390px 실측으로 확인).
   가로 방향 스크림을 세로 방향으로 바꿔 위쪽엔 지구를 남기고 아래로 갈수록 눌러
   텍스트 가독을 확보한다. */
@media (max-width:720px){
  /* 모바일은 카드가 화면 가운데다 → 그늘도 가운데. 데스크톱과 같은 원리(카드 자리에만). */
  #bgshade{background:radial-gradient(560px 540px at 50% 46%, rgba(0,0,0,.44), transparent 76%)}
  .vignette{background:linear-gradient(180deg, transparent 48%, rgba(0,0,0,.32) 100%)}
  /* 세로 화면은 영상의 27% 폭만 보인다. 가운데(50%)를 잡으면 육지만 걸려 밋밋해지므로
     대각선 지평선과 검은 우주가 함께 들어오는 오른쪽을 기준으로 잡는다(실측). */
  #bg{object-position:88% 50%}
  #bgposter{background-position:88% 50%}   /* 포스터 층도 같은 기준으로 (2026-08-09) */
}

.stage{position:relative;z-index:2;height:100%;display:flex}

/* ═══ 유리 ══════════════════════════════════════════════════════════════
   레퍼런스의 .liquid-glass / .liquid-glass-strong 2등급을 그대로 옮긴다.
   테두리를 ::before 로 만드는 이유도 같다 — border 는 굴절 영역과 겹쳐 이중선이 된다. */
.g{position:relative;overflow:hidden;
  background:rgba(255,255,255,.012);
  background-blend-mode:luminosity;
  backdrop-filter:blur(4px);
  -webkit-backdrop-filter:blur(4px);
  border:none;
  box-shadow:inset 0 1px 1px rgba(255,255,255,.10)}

/* 그라디언트 테두리 — 위·아래가 밝고 중앙이 사라진다(레퍼런스 값 그대로) */
.g::before{
  content:"";position:absolute;inset:0;pointer-events:none;border-radius:inherit;
  padding:1.4px;
  background:linear-gradient(180deg,
    rgba(255,255,255,.45) 0%, rgba(255,255,255,.15) 20%,
    transparent 40%, transparent 60%,
    rgba(255,255,255,.15) 80%, rgba(255,255,255,.45) 100%);
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
          mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask-composite:exclude}

/* 무거운 유리(폼·CTA·큰 패널) — 레퍼런스 liquid-glass-strong */
.g.strong{
  backdrop-filter:blur(50px);-webkit-backdrop-filter:blur(50px);
  box-shadow:4px 4px 4px rgba(0,0,0,.05), inset 0 1px 1px rgba(255,255,255,.15)}
.g.strong::before{
  background:linear-gradient(180deg,
    rgba(255,255,255,.50) 0%, rgba(255,255,255,.20) 20%,
    transparent 40%, transparent 60%,
    rgba(255,255,255,.20) 80%, rgba(255,255,255,.50) 100%)}

.g > *{position:relative}

/* 굴절 — 크기가 고정된 요소에만. 변위맵은 고정 픽셀이라 크기가 어긋나면 굴절이 밀린다. */
/* ⚠ saturate/brightness 를 걸었다가 뺐다 — 레퍼런스 유리는 **색을 더하지 않는다**.
   증폭하면 배경색이 1.5배로 튀어 "색 있는 렌즈"가 되고 무채색 규율이 깨진다.
   굴절(변위맵)은 우리가 추가한 것이라 유지하되, 세기만 남긴다. */
.lnForm{backdrop-filter:url(#lnForm) blur(3px)}
/* ⛔ `.lnCard`(허브 타일 굴절)는 **없앴다** (2026-08-09). 허브가 앱과 같은 판이 되면서
   타일은 유리가 아니라 불투명 패널이 됐고(`.hub .fcard{backdrop-filter:none}`),
   그 뒤로 이 클래스는 HTML 어디에도 붙지 않은 채 규칙만 남아 있었다.
   index.html 의 `<filter id="lnCard">` 도 함께 지웠다 — 둘은 한 쌍이다. */

@supports not (backdrop-filter: url(#lnForm)){
  .lnForm{
    backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px)}
}
@media (prefers-reduced-transparency: reduce){
  .g{backdrop-filter:none;-webkit-backdrop-filter:none;background:rgba(4,20,19,.94)}
}

/* ═══ 로그인 카드 위치 ═══════════════════════════════════════════════════
   화면 왼쪽 세로 중앙. `.stage`(display:flex)에서 `align-self:center` 로 높이를
   컨텐츠 크기로 줄이고 세로 중앙에 놓는다.
   ⚠ 예전엔 여기에 `.lpanel`(inset 24px 유리 슬랩)이 하나 더 있었다 → 슬랩 테두리와
     폼 카드 테두리가 겹쳐 **이중 테두리**로 보였다(사용자 2회 지적). 슬랩을 완전히 없애고
     카드 하나만 남긴다 — 다시 바깥 패널을 두르지 말 것.
   ⚠ 등장 애니메이션은 자식(`.lin > *`)이, 퇴장 트랜지션(.leaving)은 `.left` 가 담당한다.
     한 요소에 둘을 걸면 animation-fill-mode:forwards 가 transform 을 붙잡아
     .leaving 의 transform 이 먹지 않는다. */
/* 좌 여백은 화면폭에 따라 유동 — 88px 고정이면 넓은 화면에서 왼쪽 끝에 붙어 보였다 */
.left{flex:none;align-self:center;margin-left:clamp(56px,7vw,160px);
  transition:opacity .35s ease, transform .35s ease}
/* 로그인 성공 → login.js 가 붙이는 클래스. 카드가 물러나며 .hub 가 이어 나타난다.
   ⚠ visibility 를 페이드가 끝난 뒤(0.35s)에 감춘다 — 즉시 감추면 물러나는 연출이 사라진다.
     이게 있어야 로그인 폼이 탭 순서에서 빠진다(종전엔 로그인 후에도 이메일·비밀번호·
     로그인 버튼이 계속 포커스를 받았다 — 실측). 키보드는 login.js 의 inert 가 즉시 막고,
     이 선언은 inert 미지원 브라우저의 안전망이다. */
.left.leaving{opacity:0;transform:scale(.96);pointer-events:none;visibility:hidden;
  transition:opacity .35s ease, transform .35s ease, visibility 0s linear .35s}

.lin{position:relative;z-index:1}

/* 시스템 이름 라벨. 슬로건(34px)과 크게 벌려 "이건 라벨"임을 확실히 한다. */
.over{font-size:12px;font-weight:600;letter-spacing:.16em;color:var(--w50);
  margin-bottom:14px;text-shadow:0 1px 3px rgba(0,0,0,.8), 0 0 10px rgba(0,0,0,.55)}

/* 슬로건 — 카드 위에 얹는 헤드라인. 화면이 너무 비어 보인다는 지적으로 복귀시켰다(2026-07-30).
   두 줄의 위계는 **웨이트(600→400) + 밝기(100%→70%)** 로만 만든다.
   ⚠ 세리프 이탤릭으로 갈라 쓰지 말 것 — 전 화면 Pretendard 단일이 사용자 결정이다. */
/* ★ 크기 48 → **34px** (사용자 채택 2026-08-09). 문구는 CI 공통 슬로건이라 **그대로 둔다** —
   바뀐 것은 크기뿐이다.
   근거: 이 화면에서 사람이 하러 온 일은 '로그인'인데, 화면에서 가장 큰 글자(48px)가
   로그인과 무관한 브랜드 문구였다. 위계상 1순위 자리를 브랜드가 점유하고 주 행동(CTA)은
   유리 재질로만 표시돼 있었다 — 크기·색 어느 쪽으로도 CTA 가 1순위가 아니었다.
   34px 는 여전히 화면에서 두 번째로 큰 요소라 브랜드 인상은 유지된다(라벨 12px 과 2.8:1). */
.tag{font-size:clamp(26px,2.6vw,34px);font-weight:600;line-height:1.16;
  letter-spacing:-.03em;color:var(--w);margin-bottom:26px;
  text-shadow:0 2px 20px rgba(0,0,0,.45)}   /* 영상 위 흰 글자라 약한 그림자로 대비 보강 */
.tag span{display:inline-block;font-weight:400;color:rgba(255,255,255,.70)}

/* 사내 알림 한 줄 — 카드 아래. 점 하나 + 텍스트, 배경 없이 가볍게. */
.notice{margin-top:20px;display:flex;align-items:center;gap:9px;
  font-size:12.5px;color:var(--w60);
  text-shadow:0 1px 3px rgba(0,0,0,.8), 0 0 8px rgba(0,0,0,.6)}
.notice i{width:5px;height:5px;border-radius:999px;flex:none;
  background:rgba(255,255,255,.55)}

/* 공식 CI 락업(logo_white.svg) — 화면 우하단에 고정된 서명. 카드 안이 아니라
   viewport 기준 고정이라 어느 화면 크기에서도 늘 같은 자리에 조용히 떠 있다. */
.brand-corner{position:fixed;right:40px;bottom:26px;z-index:3;
  height:20px;width:auto;opacity:.92;pointer-events:none}
@media (max-width:640px){.brand-corner{right:20px;bottom:16px;height:16px}}

/* ═══ 우상단 바로가기 ════════════════════════════════════════════════════
   로그인 없이 갈 수 있는 곳만. 아이콘 없이 텍스트로만(아이콘 전면 삭제 지시 유지). */
/* ⚠ z-index 는 허브(.hub = 6)보다 위여야 한다. 4 였을 때 로그인 후 허브가 화면 전체를
     덮으면서(inset:0) 홈페이지·더존·유튜브·인스타가 **클릭되지 않았다**(2026-08-03 사용자 지적).
     허브가 떠 있어도 바로가기는 눌려야 한다 — 그게 이 자리에 있는 이유다. */
/* ★ 타깃 크기 (2026-08-09) — 종전 실측은 텍스트 링크 42.7×**15**, 아이콘 **16×16** 이었다.
   WCAG 2.2 SC 2.5.8(최소 24×24)에 둘 다 미달이고, 특히 아이콘은 손가락으로 거의 못 누른다.
   → 링크 자체를 **28px 높이 블록**으로 만든다(글자 크기는 그대로).
   ⚠ 인상을 유지하려고 좌표를 보정했다:
     · 좌우 padding 6px 이 생기므로 gap 22 → 10 (시각 간격 10+6+6 = 22, 종전과 같다)
     · 마지막 항목의 오른쪽 padding 만큼 `margin-right:-6px` → 오른쪽 끝 x 좌표 불변
     · 높이가 15 → 28 로 자라므로 `.corner` 의 padding-top 을 30 → 23.5 (= 30 - (28-15)/2)
       → 글자의 세로 중심이 종전과 같은 자리에 남는다. */
.quick{position:fixed;top:30px;right:40px;z-index:7;
  display:flex;align-items:center;gap:10px;margin-right:-6px;
  font-size:12.5px;letter-spacing:-.01em;
  text-shadow:0 1px 3px rgba(0,0,0,.8), 0 0 10px rgba(0,0,0,.55)}
.quick a{color:var(--w80);text-decoration:none;transition:color .16s, background .16s;
  min-height:28px;padding:0 6px;border-radius:8px;
  display:inline-flex;align-items:center;justify-content:center}
.quick a:hover{color:var(--w);background:rgba(255,255,255,.10)}
/* 소셜 = 윤곽선(stroke) SVG. 이모지를 썼다가 "촌스럽다"는 지적으로 교체(2026-07-30).
   ⚠ 이모지 폰트엔 윤곽선 변형이 없다 → 테두리 스타일은 stroke SVG 만 가능하다.
   currentColor 라 텍스트 링크와 같은 색 계단을 그대로 따라간다(색이 튀지 않음). */
/* 아이콘 링크도 같은 28×28 블록. 그림 자체는 16px 그대로 두고 **여백으로만** 넓힌다
   — 아이콘을 키우면 텍스트 링크와의 시각 무게가 어긋난다. */
.quick .ico{display:inline-flex;align-items:center;justify-content:center;color:var(--w80);
  min-width:28px;min-height:28px;padding:0;
  transition:color .16s, transform .18s var(--ease), background .16s}
.quick .ico svg{width:16px;height:16px;display:block}
.quick .ico:hover{color:var(--w);transform:scale(1.1)}

/* 로그인 폼. 높이 이력 176 → 262(이메일 필드) → 300("비밀번호 저장" 체크박스)
   → 변위맵 필터(#lnForm)도 index.html·build_dispmaps.py 에서 같이 400×300 여야 한다. */
.form{width:400px;height:300px;border-radius:20px;padding:22px 24px;
  display:flex;flex-direction:column;gap:14px}

/* ⚠ 비밀번호 설정 화면은 입력칸이 3개다(계정·새 비번·확인) → 위 262px 고정에 안 들어가
   **버튼과 오류 메시지가 잘려 보이지 않았다**(2026-07-31 실제 발생. 오류 안내가 잘려서
   사용자는 "확인 중"에서 멈춘 이유를 알 수 없었다).
   → 이 화면만 높이를 내용에 맞춘다. 굴절(lnForm)은 400×262 고정 맵이라 크기가 어긋나므로
     이 화면에서는 쓰지 않고 일반 블러로 간다(set-password.html 의 class 에 lnForm 없음). */
.form.tall{height:auto}
.fld{display:block}
.fld span{display:block;font-size:11.5px;letter-spacing:.01em;color:var(--w50);margin-bottom:8px}
/* 라벨 뒤 작은 꼬리표 — "이 칸은 왜 못 바꾸나"를 그 자리에서 답한다(별도 안내문 없이) */
.fld span em{font-style:normal;font-size:10.5px;color:var(--w40);margin-left:6px}
/* 읽기 전용 칸은 입력칸처럼 보이지 않게 한 단 낮춘다 — 누르면 되는 줄 알고 시도하지 않게 */
.fld input:disabled{background:rgba(255,255,255,.035);color:var(--w60);cursor:default}
/* ⚠ 레퍼런스는 border 클래스를 일절 쓰지 않는다(테두리는 ::before 담당).
   입력칸에 자체 border 를 줬더니 카드 테두리 위에 또 하나의 사각 테두리가 겹쳐
   "이중 테두리"로 보였다(사용자 지적, 2026-07-30). border 를 없애고 옅은 흰색 틴트로만
   구분한다 — 카드에 뚫린 홈처럼 보이게, 별도 패널처럼 보이지 않게. */
/* ★ select(직급 드롭다운, 2026-08-12)도 input 과 같은 규칙을 쓴다 — 안 그러면 브라우저
   기본 select 모양이 그대로 남아 옆 input 과 이질감이 크다. appearance 를 꺼서
   네이티브 화살표를 없애고 배경 SVG 화살표를 오른쪽에 얹는다(색은 무채색 원칙 유지). */
.fld input,.fld select{
  width:100%;height:44px;padding:0 15px;border-radius:12px;
  border:none;background:rgba(255,255,255,.06);
  color:var(--w);font-family:var(--sans);font-size:14.5px;outline:none;
  transition:background .16s, box-shadow .16s}
.fld select{
  appearance:none;-webkit-appearance:none;cursor:pointer;padding-right:34px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,.5)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 12px center;background-size:14px}
.fld select option{background:#1a1a1a;color:var(--w)}
.fld input::placeholder{color:var(--w40)}
/* ══ 비밀번호 보기 (2026-08-14) ═══════════════════════════════════════════════════
   발단: 데스크톱에서는 되는 사람이 폰에서만 "비밀번호가 올바르지 않습니다"를 받았다
   ("제대로 쳤는데"). 값이 가려져 있어 **본인도 무엇이 들어갔는지 확인할 수 없었다.**
   ⚠ 칸 안 오른쪽에 얹는다 → input 의 오른쪽 여백을 그만큼 비워야 글자가 버튼 밑으로
     들어가지 않는다(padding-right).
   ⚠ 터치 대상은 넉넉히 잡는다(폰에서 눌리는 크기). */
/* ★★ 2026-08-18 — 글자 버튼(`보기`/`숨기기`)을 **눈동자 아이콘**으로 바꿨다
     (사용자 지시: "비밀번호 보기를 텍스트 보기가 아닌 눈동자 모양으로 데스크톱과
      모바일 모두"). 감춤 = 눈 · 보임 = 눈에 사선.
   ⚠ 아이콘 도형은 **문서 안 `<symbol>`** 이다(index.html·set-password.html 의 defs).
     벤더 아이콘 파일을 새로 싣지 않는다 — 포털 규율.
   ⚠ 이름은 그림이 아니라 `aria-label` 이 진다(login.js 가 상태에 따라 갱신).
   ⚠ 터치 표적 = **44×44**(입력칸 높이와 같다). 글자 버튼 시절 36×50 보다 크다. */
.pw-wrap{position:relative;display:block}
.pw-wrap input{padding-right:50px}
.pw-eye{
  position:absolute;right:0;top:50%;transform:translateY(-50%);
  width:44px;height:44px;padding:0;border:none;border-radius:12px;
  display:grid;place-items:center;
  background:transparent;color:var(--w60);cursor:pointer;
  transition:background .16s,color .16s}
.pw-eye svg{width:20px;height:20px;display:block;pointer-events:none}
/* 두 도형 중 하나만 보인다 — 상태의 주인은 `aria-pressed` 하나뿐이다(클래스 이중 관리 금지) */
.pw-eye .ic-eye-off{display:none}
.pw-eye[aria-pressed="true"] .ic-eye{display:none}
.pw-eye[aria-pressed="true"] .ic-eye-off{display:block}
.pw-eye:hover{background:rgba(255,255,255,.12);color:var(--w)}
.pw-eye[aria-pressed="true"]{color:var(--w)}
.pw-eye:focus-visible{outline:2px solid rgba(255,255,255,.55);outline-offset:2px}
/* ⛔ 여기 있던 `box-shadow:0 0 0 3px rgba(255,255,255,.14)` 를 뺐다 (2026-08-09).
   `:focus` 는 **autofocus 로도 켜진다** → 화면이 열리자마자 이메일 칸에 흰 후광이 생겨
   화면에서 가장 먼저 눈에 띄는 것이 '주 행동(로그인)'이 아니라 **빈 입력칸**이었다.
   포커스 표시는 아래 `:focus-visible`(키보드일 때만) 한 곳에서만 그린다. */
.fld input:focus{background:rgba(255,255,255,.10)}
/* "비밀번호 저장" 체크박스. accent-color 로 네이티브 체크를 그대로 쓴다 —
   커스텀 체크박스는 키보드 포커스·스크린리더 동작을 직접 다시 만들어야 해서 손해가 크다. */
.chk{display:flex;align-items:center;gap:9px;cursor:pointer;user-select:none}
/* ⚠ accent 를 CI 파랑으로 두지 않는다 — 이 화면은 **유채색 0** 이 원칙이고(색은 배경 영상에서만
   온다), 체크박스 하나에 파랑을 넣으면 그 점만 튄다. 무채색 계단을 따른다. */
.chk input{width:15px;height:15px;margin:0;accent-color:rgba(255,255,255,.85);cursor:pointer}
.chk span{font-size:12.5px;color:var(--w60)}
.chk:hover span{color:var(--w80)}

/* ★ 체크박스 줄 + 비밀번호 재설정 (2026-08-09 신설).
   종전 이 화면에는 **재설정 경로가 아예 없었다** — 비밀번호를 잊으면 화면 안에서
   할 수 있는 일이 없고 아래 문의 메일이 유일한 출구였다. 로그인 화면의 표준 구성요소다.
   ⚠ 두 요소를 한 줄에 둬서 폼 높이(400×300 변위맵)를 늘리지 않는다 — 높이를 바꾸면
     index.html 의 `<filter id="lnForm">` 와 build_dispmaps.py TARGETS 까지 같이 고쳐야 한다. */
.crow{display:flex;align-items:center;justify-content:space-between;gap:12px}
.crow .chk{min-height:28px}
/* 타깃 28px 확보(WCAG 2.2 SC 2.5.8 = 24×24 최소, 여유 두고 28). 밑줄로 링크임을 표시하되
   CTA 보다 가벼워야 하므로 색·굵기는 올리지 않는다. */
.reset{font-size:12.5px;color:var(--w60);text-decoration:none;flex:none;
  min-height:28px;display:inline-flex;align-items:center;
  border-bottom:1px solid rgba(255,255,255,.30);line-height:1.2;padding-bottom:1px}
.reset:hover{color:var(--w);border-bottom-color:rgba(255,255,255,.85)}

.frow{display:flex;align-items:center;gap:14px;margin-top:auto}

/* ⛔⛔ `hidden` 속성이 **먹지 않던 것** (2026-08-14 실측으로 잡음)
   브라우저 기본 규칙 `[hidden]{display:none}` 은 **작성자 규칙에 진다.**
   `.fld{display:block}` 이 같은 특이도(0,1,0)로 뒤에 오므로 `hidden` 을 걸어도
   칸이 **그대로 보였다** — 즉 코드 칸이 평소에도 노출되고, 코드 모드에서 비밀번호 칸이
   안 사라졌다. JS 는 아무 잘못이 없어서 코드만 봐서는 찾을 수 없는 종류의 결함이다.
   (`el.hidden = true` 는 정상 동작했다. 화면만 안 따라왔다.)
   ★ 그래서 **속성 선택자로 특이도를 올려**(0,2,0) 못 박는다. `!important` 는 쓰지 않는다.
   ⚠ 새 칸을 `hidden` 으로 감출 일이 또 생기면 여기 선택자에 **추가**할 것. */
.fld[hidden], .crow[hidden]{display:none}

/* ★ 6자리 코드 로그인 진입 (2026-08-14) — CTA 옆의 **보조** 경로다.
   ⚠ `.reset` 과 같은 문법으로 둔다(밑줄 있는 작은 글자) — 새 시각 언어를 만들지 않는다.
     이 화면의 위계는 CTA(유리 버튼) > 보조 링크 둘 뿐이어야 한다.
   ⚠ 터치 대상 높이를 44px 로 잡는다. `.reset`(28px)보다 크게 두는 이유는 이 링크가
     **폰에서 막힌 사람이 쓰는 문**이기 때문이다 — 정작 그 사람이 못 누르면 뜻이 없다.
   ⛔⛔ 이 링크는 **카드 높이를 늘리면 안 된다.** `.form` 은 400×300 **고정**이고
     유리 굴절 변위맵(#lnForm · build_dispmaps.py TARGETS)이 정확히 그 치수로 구워져 있다.
     내용이 넘치면 컨트롤이 카드 밖으로 새어 나온다 — set-password 화면에서 실제로
     "버튼과 오류 메시지가 잘려" 보였던 그 실패다(위 .form.tall 주석).
     실측으로 두 번 걸렸다:
       ① 긴 문구를 CTA 와 같은 줄에 두자 **주 버튼이 200px → 117px** 로 반토막(390px)
       ② 줄바꿈을 허용하자 데스크톱에서 `scrollHeight` 300 → **329** (카드 밖으로 29px)
   ★ 그래서 **문구를 줄여 CTA 옆에 그대로 태운다.** 줄을 늘리지도, CTA 를 줄이지도 않는다.
     400px 카드: 200(CTA) + 14(gap) + 약 78 = 292 ≤ 352(내부 폭) — 여유가 있다.
     390px 폰: 같은 계산으로 294 안에 들어간다.
   ⚠ 문구를 다시 길게 바꾸지 말 것. 무엇을 하는 링크인지는 **누른 뒤 화면이** 설명한다
     (칸 이름이 '인증 코드'로 바뀌고 버튼이 '코드 받기'가 된다).
   ⚠ flex:none — 안 주면 CTA 와 서로 줄어들기를 다툰다. */
.frow > .cta{flex:none}
/* ⚠ `.alt-login` 은 지금 쓰는 곳이 없다 — 「코드로 로그인」 링크를 2026-08-18 에
   걷었기 때문이다(index.html 그 자리 주석 참조). 규칙은 남겨 둔다: 되살릴 때
   같은 모양으로 돌아오게 하려는 것이고, 죽은 규칙이 화면을 바꾸지는 않는다. */
.alt-login{font-size:12.5px;color:var(--w60);text-decoration:none;flex:none;
  min-height:44px;display:inline-flex;align-items:center;text-align:left;
  border-bottom:1px solid rgba(255,255,255,.30);line-height:1.25;padding-bottom:1px}
.alt-login:hover{color:var(--w);border-bottom-color:rgba(255,255,255,.85)}

/* 코드 칸 — 숫자를 **읽고 확인하는** 칸이라 자간을 벌리고 모노로 둔다.
   ⚠ 16px 미만으로 내리지 말 것: iOS 가 탭하는 순간 화면을 확대하고 되돌리지 않는다. */
#code{font-family:Consolas,"SF Mono",monospace;letter-spacing:.34em;font-size:17px}

/* ═══ CTA — 유리로 되돌린다. 위계는 **색이 아니라 밝기·테두리·부양**으로 (2026-08-10)
   ─────────────────────────────────────────────────────────────────────────
   ▸ 2026-08-09 에 이 버튼을 CI Main(#00A4EB) 불투명 면 + Navy 글자로 바꿨다가 되돌린다.
     사용자 요구는 **유리 재질 복귀**다("원래 컬러는 리퀴드 글래스였을 텐데").
     파란 면은 이 화면의 대전제(§:root — 유채색 0, 색은 배경 영상에서만 온다)를 깼다.

   ★ 되돌리면서 바로잡은 사실 하나 — **"유리 위 흰 글자 2.80:1"은 잘못된 숫자였다.**
     2.80 은 *#00A4EB 불투명 면* 위 흰 글자의 값이지 유리 위의 값이 아니다.
     이 버튼 자리에서 배경 영상(1920 AV1) 70프레임을 훑어 **가장 밝은 프레임(f003)과
     가장 어두운 프레임(f052)** 을 고른 뒤 브라우저 합성 픽셀을 직접 잰 결과:
        유리(틴트 .012) + 흰 글자 →  밝은 8.04 : 1 · 어두운 14.83 : 1 · 포스터 8.46 : 1
     한 번도 4.5 아래로 내려간 적이 없다. 이유는 이 유리가 **밝은 영상을 그대로
     통과시키지 않기 때문**이다 — blur(50px) 가 넓게 평균 내고, 그 아래 #bgshade 가
     카드 자리를 31% 눌러 둔다. 버튼 면은 밝은 프레임에서도 RGB 80 언저리에 머문다.
   ⛔ 그래서 **어두운 글자는 이 자리에서 성립하지 않는다.** 같은 방식으로 실측:
        Navy 글자를 4.5 에 올리려면 흰 틴트 알파가 **.55 이상**이어야 한다
        (.45 = 어두운 프레임 4.19 탈락 / .55 = 5.51 통과).
        알파 .55 는 유리의 상한(.25~.30)을 두 배 넘긴 값 = **흰 알약**이다.
        즉 "유리 + 어두운 글자"는 둘 중 하나를 포기해야만 성립한다 → 유리를 지킨다.

   ★ 그럼 어제 진짜 문제였던 "주 행동으로 안 읽힌다"는 어떻게 푸는가 — **밝기 계단**이다.
     종전 유리 CTA 가 묻힌 진짜 이유는 재질이 같아서가 아니라, 틴트 .012 라
     **입력칸(.06)보다 어두웠다**는 것이다. 카드 안에서 제일 어두운 면이 주 행동이었다.
     → 틴트를 .14 로 올려 **카드 안에서 가장 밝은 면**으로 만든다(.012 → .14, 입력칸 .06).
       유리 상한(.25) 안이라 재질은 그대로고, 실측 대비도 밝은 프레임 5.64:1 로 통과한다.
     ⚠ .16 까지 올려 봤지만 **가로 폰(844×390)에서 hover 최악 픽셀이 4.47** 로 4.5 를
       스쳤다 → .14 가 상한이다. 여기서 더 올리지 말 것(뷰포트 5종 × 배경 3종 실측).
     + 테두리를 카드보다 한 단 밝게(.70/.28 ↔ 카드 .50/.20) · 아래로 부양 그림자 ·
       화살표 원을 .22 로. 넷 다 **무채색**이라 유채색 0 규율을 지킨다.
   ⚠ `.g.strong` 은 클래스 2개(0,2,0)라 `.cta`(0,1,0)로는 못 이긴다 —
     재질을 덮는 규칙은 반드시 `.cta.g.strong`(0,3,0)으로 쓸 것.
     (2026-08-09 판의 `.cta{backdrop-filter:none;box-shadow:none}` 은 실제로는
      한 줄도 적용되지 않고 있었다. 불투명 면이라 눈에 안 띄었을 뿐이다.) */
.cta{
  width:176px;height:46px;border-radius:999px;border:0;
  display:inline-flex;align-items:center;justify-content:center;gap:11px;
  color:var(--w);font-family:var(--sans);font-size:14px;font-weight:600;
  letter-spacing:-.01em;cursor:pointer;
  transition:transform .18s var(--ease), background .16s}
/* 재질 — 카드와 같은 유리(blur 50)지만 **한 단 밝고 한 단 떠 있다** */
.cta.g.strong{
  background:rgba(255,255,255,.14);
  backdrop-filter:blur(50px);-webkit-backdrop-filter:blur(50px);
  box-shadow:inset 0 1px 1px rgba(255,255,255,.30),   /* 윗면 베벨 — 카드(.15)보다 밝게 */
             inset 0 -1px 1px rgba(0,0,0,.16),        /* 아랫면 어둠 = 두께 */
             0 10px 24px -12px rgba(0,0,0,.55)}       /* 부양 — 카드에는 없는 신호 */
/* 테두리도 카드보다 한 단 밝게. 유리 문법(위·아래 밝고 중앙 소멸)은 그대로 따른다. */
.cta.g.strong::before{
  background:linear-gradient(180deg,
    rgba(255,255,255,.70) 0%, rgba(255,255,255,.28) 20%,
    transparent 40%, transparent 60%,
    rgba(255,255,255,.28) 80%, rgba(255,255,255,.70) 100%)}
/* ★ hover 는 **면을 밝히지 않는다** — 테두리·베벨·그림자(전부 글자가 앉지 않는 자리)만
   움직인다. 유리가 한 겹 두꺼워지는 인상은 그대로 나면서 가독성 예산을 쓰지 않는다.
   ⛔ 면 알파를 올리지 말 것. 실측으로 확인했다 — hover 에서 .20 으로 올리자
      가로 폰(844×390) 4.10 · 1024 포스터 4.36 으로 4.5 가 깨졌다.
      (1440 만 보면 4.60 이라 통과처럼 보인다. 한 뷰포트로 판단하면 놓친다.) */
.cta.g.strong:hover{
  box-shadow:inset 0 1px 1px rgba(255,255,255,.42),
             inset 0 -1px 1px rgba(0,0,0,.16),
             0 14px 30px -12px rgba(0,0,0,.62)}
.cta.g.strong:hover::before{
  background:linear-gradient(180deg,
    rgba(255,255,255,.92) 0%, rgba(255,255,255,.38) 20%,
    transparent 40%, transparent 60%,
    rgba(255,255,255,.38) 80%, rgba(255,255,255,.92) 100%)}
.cta:hover{transform:scale(1.05)}
.cta:active{transform:scale(.95)}
/* 비활성(로그인 처리 중)은 밝기 계단을 한 칸 내려 "지금은 누를 수 없다"를 말한다.
   ⚠ 유리 위에서 opacity 로 낮추면 글자까지 흐려진다 → 면 알파만 내리고 글자는 80% 로
     (60% 로 내리면 어두워진 면 위에서 2.2:1 까지 떨어진다 — 실측). */
.cta.g.strong:disabled{background:rgba(255,255,255,.07);
  box-shadow:inset 0 1px 1px rgba(255,255,255,.12)}
.cta.g.strong:disabled::before{background:linear-gradient(180deg,
    rgba(255,255,255,.30) 0%, transparent 40%, transparent 60%, rgba(255,255,255,.30) 100%)}
.cta:disabled{color:var(--w80);cursor:default;transform:none}
.cta i{width:28px;height:28px;border-radius:999px;background:rgba(255,255,255,.22);
  display:grid;place-items:center}
.err{font-size:12px;color:var(--err);min-height:16px}

/* ═══ 로그인 성공 팝업 허브 ═══════════════════════════════════════════════
   화면 정중앙에 4장을 가로로. login.js 가 로그인 성공 시 .show 를 붙인다.
   평소엔 opacity:0·pointer-events:none 로 완전히 숨어 있다(레이아웃엔 항상 존재).

   ★ 시인성 개선 (사용자 지적 2026-07-30) —
     ① 타입 확대: 제목 14 → 22px, 설명 11 → 13.5px + 색 60% → 80%
     ② 여백 축소: 138px 높이에 컨텐츠를 아래로 붙여 위쪽 70px 가 죽어 있었다
        → 116px 로 낮추고 세로 중앙 정렬(justify-content:center)로 채운다
     ③ 대비: **카드를 어둡게 칠하지 않는다.** 한 번 카드에 검정 틴트(.42→.55)를 넣었다가
        "카드가 탁하다"고 지적받았다 — 카드는 **맑은 유리로 유지**하는 것이 요구사항이고,
        가독성은 카드 몸통이 아니라 **글자 자체**에서 확보한다(사진 위 자막의 표준 기법).
        → text-shadow 로 글자 주변만 국소적으로 눌러, 카드 투명도는 손대지 않는다. */
/* ═══ 내 프로필 (좌상단, 로그인 후) ═══════════════════════════════════════
   우상단 바로가기(.quick)와 같은 높이·같은 무채색 계단. 팀이 위·이름이 아래다.
   ⚠ 버튼 배경을 칠하지 않는다 — 이 화면의 유일한 '면'은 로그인 카드와 허브 타일이다.
     클릭 가능하다는 신호는 **이름 아래 꺾쇠**와 hover 시 밝아지는 것으로만 준다. */
/* ⚠⚠ display 를 지정하는 규칙에는 **반드시 [hidden] 짝을 같이 둔다.**
   author 의 display 가 UA 의 `[hidden]{display:none}` 을 이겨서, hidden 을 붙여도
   그대로 보인다 — 실제로 이 화면에서 로그인 전에 프로필 칩과 '내 정보' 패널이
   항상 떠 있었다(2026-07-31 사용자 지적). sop_v5 의 `.work[hidden]` 도 같은 함정이었다. */
.who[hidden],.sheet[hidden]{display:none}

.who{position:fixed;top:26px;left:40px;z-index:7;
  display:flex;flex-direction:column;align-items:flex-start;gap:2px;
  background:none;border:0;padding:4px 6px;margin:-4px -6px;cursor:pointer;
  font-family:var(--sans);text-align:left;border-radius:10px;
  transition:background .16s}
.who:hover{background:rgba(255,255,255,.07)}
.who:focus-visible{outline:2px solid rgba(255,255,255,.5);outline-offset:2px}
.who-team{font-size:11.5px;font-weight:600;letter-spacing:.1em;color:var(--w50);
  text-shadow:0 1px 6px rgba(0,0,0,.8)}
.who-name{font-size:17px;font-weight:600;letter-spacing:-.02em;color:var(--w);
  text-shadow:0 1px 8px rgba(0,0,0,.85)}
/* 꺾쇠 = "여기 눌러서 펼칠 수 있다". hover 에서 살짝 내려가 방향을 한 번 더 말한다. */
.who-caret{width:15px;height:15px;margin-top:1px;color:var(--w50);
  transition:transform .2s var(--ease), color .16s}
.who:hover .who-caret{color:var(--w80);transform:translateY(2px)}
.who[aria-expanded="true"] .who-caret{transform:rotate(180deg)}

/* ═══ 내 정보 수정 패널 ═══ */
.sheet{position:fixed;inset:0;z-index:9;display:grid;place-items:center;padding:20px}
.sheet-bd{position:absolute;inset:0;background:rgba(3,7,13,.55)}
.sheet-card{position:relative;width:min(560px,100%);border-radius:22px;padding:26px 28px;
  display:flex;flex-direction:column;gap:14px;
  animation:pop .34s var(--ease) both}
@keyframes pop{from{opacity:0;transform:translateY(10px) scale(.98)}
               to{opacity:1;transform:none}}
.sheet-card h3{font-size:19px;font-weight:600;letter-spacing:-.02em;color:var(--w)}
.two{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.sheet-sep{margin-top:6px;padding-top:14px;border-top:1px solid rgba(255,255,255,.12);
  font-size:12.5px;font-weight:600;color:var(--w80)}
.sheet-sep span{font-weight:400;color:var(--w40);margin-left:6px}
.sheet-foot{display:flex;align-items:center;gap:14px;margin-top:8px}
.sheet-foot .sp{flex:1}
/* ★ 2026-08-18 — 폰(390)에서 이 줄의 두 링크가 **글자 한가운데서 접혔다**(실측: "닫 기",
   "로그 아웃"). flex 아이템이 줄어들 때 두 글자짜리 라벨은 접힐 자리가 없다.
   ⚠ 데스크톱은 애초에 접히지 않으므로 좌표가 달라지지 않는다(1920·1440 diff 0 확인). */
.sheet-foot .lnk{white-space:nowrap;flex:none}
.lnk{background:none;border:0;padding:4px 2px;cursor:pointer;
  font-family:var(--sans);font-size:12.5px;color:var(--w60);text-decoration:underline;
  text-underline-offset:3px;text-decoration-color:rgba(255,255,255,.25)}
.lnk:hover{color:var(--w)}
/* 저장 결과는 성공·실패를 같은 자리에서 알린다(.err 는 붉은색이라 성공엔 .ok 로 덮는다) */
.err.ok{color:rgba(255,255,255,.85)}

@media (max-width:560px){
  .who{top:18px;left:20px}
  .two{grid-template-columns:1fr}

  /* ★ 「내 정보」 패널을 폰에서 **줄인다** (2026-08-18 사용자 지시: "모바일에서 내 정보
     패널이 너무 크게 켜지는 게 문제 같아").
     실측(390): 칸 6개가 각 44px + 라벨·간격이 얹혀 패널이 화면을 거의 다 덮었다.
     ⛔ 칸을 지우지 않는다 — 줄이는 것이지 없애는 것이 아니다(상시 지침).
     ★ 세 가지를 함께 줄인다: ① 칸 높이 44→38 ② 라벨·칸 사이 여백 ③ 칸끼리 간격.
       그래도 넘치면 **패널 안에서 스크롤**한다(화면 위아래로 배경이 조금 보이게 85dvh).
     ⚠ 눈 아이콘 버튼의 터치 표적(44×44)은 **줄이지 않는다** — 손가락 크기는 화면이
       좁아진다고 작아지지 않는다. 칸보다 크게 남아도 무방하다(칸 밖으로 넘치지 않게
       위아래 가운데 정렬이라 보기에도 그대로다). */
  .sheet{padding:12px}
  .sheet-card{padding:16px 16px 18px;gap:10px;
    max-height:85dvh;overflow-y:auto;overscroll-behavior:contain}
  .sheet-card h3{font-size:17px}
  .fld input,.fld select{height:38px;font-size:14px;padding:0 12px}
  .fld span{margin-bottom:5px}
  .sheet-sep{margin-top:2px;padding-top:10px}
  .sheet-foot{margin-top:4px}
}

/* 볼 수 있는 메뉴가 하나도 없을 때만 나온다(권한 미설정 계정) */
.hubempty{font-size:14px;line-height:1.7;color:var(--w80);text-align:center;
  text-shadow:0 0 3px rgba(0,0,0,.95), 0 2px 16px rgba(0,0,0,.8)}

/* ═══ 허브 타일 — 기본 규칙은 여기 한 곳뿐 ═══════════════════════════════
   ⛔ 2026-08-09 정리: 여기 있던 **유리 시절의 잔재**를 전부 지웠다.
      (264×116 고정 크기 · 4겹 text-shadow 후광 · `backdrop-filter:url(#lnCard)` ·
       `.hub{display:flex;background:rgba(3,7,13,.55)}` · `@keyframes hubPop`)
      허브가 앱과 같은 판이 된 뒤로 아래 [B] 블록의 `.hub .fcard`·`.hub.show` 가
      그것들을 전부 덮어쓰고 있었다 — **지금 화면에는 영향이 없지만**, 같은 선택자가
      한 파일에 두 벌 살아 있으면 다음 사람이 위쪽을 고치고 "안 먹는다"고 헤맨다.
      규칙은 [B] 블록 한 곳에만 둔다. 되살리지 말 것. */
.fcard[aria-disabled]{cursor:default}

/* 등장 스태거 — 카드가 순서대로 떠오른다. 실제 애니메이션(hubRise)은 [B] 블록이 정의한다.
   ⚠ A안(2026-08-09)에서 '관리자'가 카드에서 유틸리티 행으로 내려가 카드는 최대 3장이었다.
   ★ 2026-08-10: '데이터' 메뉴가 붙어 **최대 4장**이 됐다. 4번째 규칙이 없어서 그 카드만
     지연 0 으로 먼저 튀어나왔다 → 같은 간격(.09s)을 이어 .32s 를 준다. */
.hub.show .fcard:nth-child(1){animation-delay:.05s}
.hub.show .fcard:nth-child(2){animation-delay:.14s}
.hub.show .fcard:nth-child(3){animation-delay:.23s}
.hub.show .fcard:nth-child(4){animation-delay:.32s}

/* ═══ 왜 4장인데 격자를 3열 그대로 두는가 (2026-08-10 판단 · 다시 파헤치지 말 것) ══════
   1440·1280 에서 4장은 **3 + 1** 로 앉는다(앞 3장 한 줄, '데이터'가 혼자 아랫줄).
   보기엔 어색하지만 격자를 손대지 않는 것이 맞다 —
     · **'데이터'는 1단계에서 관리자에게만 열린다.** 4장을 보는 사람은 현재 2명이고,
       **나머지 전원은 3장(실적 대시보드·MES·CRM)이라 3열이 딱 맞는다.**
     · 2×2 로 줄이면 3장인 다수가 **2 + 1** 로 접힌다 → 지금의 어색함을 소수(2명)에서
       다수 전원에게 옮기는 셈이다. 문제를 푸는 게 아니라 넓히는 것이다.
     · 4열로 넓히면 카드 폭이 줄어 허브 인상 자체가 바뀐다(이건 레이아웃 변경이지
       버그 수정이 아니다).
   ▸ **다시 볼 시점 = '데이터'가 여러 부서에 열릴 때.** 그때는 4장이 기본이 되므로
     위 근거(다수는 3장)가 뒤집힌다 → 그 시점에 `.hubcards` 의 max-width·minmax 를
     함께 재검토한다. 그전까지 격자는 건드리지 않는다.
   ⚠ 390px 은 1열이라 이 논의와 무관하다(4장 세로, 실측 깨짐 없음). */

/* ═══ 진입 모션 ══════════════════════════════════════════════════════════ */
/* ═══ 포커스 표시 ══════════════════════════════════════════════════════════
   ⚠ 종전 입력칸의 포커스 표시는 `box-shadow 0 0 0 3px rgba(255,255,255,.14)` 하나였다.
     유리 위 알파 0.14 는 비포커스 배경(0.06)과 사실상 구분되지 않아, 키보드로 지금
     어디에 있는지 알 수 없었다. 흰 실선으로 올린다(WCAG 2.2 비텍스트 대비 3:1 기준).
   ⚠ :focus 가 아니라 :focus-visible 이다 — 마우스 클릭에는 링이 뜨지 않으므로
     기존 화면 인상은 그대로고, 키보드 사용자만 표시를 얻는다.
   ⚠ 유리(.g) 요소에는 box-shadow 를 쓰지 않는다 — 그 속성이 유리의 안쪽 하이라이트를
     담고 있어서 덮어쓰면 재질이 깨진다. 링은 outline 으로만 그린다. */
.cta:focus-visible,
.quick a:focus-visible,
.notice a:focus-visible,
.reset:focus-visible,
.fcard:focus-visible,
.who:focus-visible{
  outline:2px solid rgba(255,255,255,.95);outline-offset:3px
}
/* ★ 입력칸 포커스 = **강조색 링 하나** (2026-08-09 변경).
   ⛔ 종전은 `흰 2px + 검정 halo 5px` 두 겹이었다. 두 겹짜리 링은 면적·명도 모두
      CTA 를 눌러, autofocus 로 켜지는 순간 화면의 시선 1순위가 **빈 입력칸**이 됐다
      (위계 역전). 링은 "지금 여기"만 알리면 되고 주 행동보다 무거울 이유가 없다.
   ⚠ 흰 링을 CI Main 으로 바꿔도 대비는 유지된다 — 입력칸 면(rgba(255,255,255,.06) 위
     어두운 유리)에서 #00A4EB 는 비텍스트 3:1 기준을 넘는다. 밝은 구름이 뒤에 와도
     링 자체가 유채색이라 흰 배경에 묻히지 않는다(오히려 흰 링보다 안전하다). */
.fld input:focus-visible,
.chk input:focus-visible{
  outline:2px solid #00A4EB;outline-offset:2px;box-shadow:none
}

@media (prefers-reduced-motion: no-preference){
  .lin > *{opacity:0;transform:translateY(14px);
    animation:rise .7s var(--ease) forwards}
  .tag{animation-delay:.05s}
  .form{animation-delay:.16s}    /* 슬로건이 먼저, 카드가 뒤따라 — 읽는 순서와 같게 */
  @keyframes rise{to{opacity:1;transform:none}}
}
@media (prefers-reduced-motion: reduce){
  /* 허브 팝업도 모션 축소 설정을 존중 — 즉시 나타나게 */
  .hub{transition:none}
  .hub .fcard{opacity:1;transform:none;animation:none}
}

/* ═══ 좁은 화면 ══════════════════════════════════════════════════════════
   ⚠ 변위맵은 고정 픽셀이라 폭이 바뀌면 굴절이 밀린다 → 폼이 가변폭이 되는
     좁은 화면에선 렌징을 끄고 일반 블러로 폴백한다. */
@media (max-width:1080px){
  .left{margin:0 auto;padding:0 24px;width:100%;max-width:448px}
  .lnForm{backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px)}
  .form{width:100%;height:auto;gap:18px;padding-bottom:24px}
  /* 허브는 4장이 한 줄에 안 들어가므로 2×2 로 접힌다(.hub 가 flex-wrap) */
}
@media (max-width:640px){
  .cta{width:100%;max-width:200px}
  /* ⛔ 여기 있던 `.fcard{width:100%;max-width:264px;height:104px}`·`.fcard h4/p`·`.lnCard`
     4줄도 유리 시절 잔재였다 → 삭제(2026-08-09). 허브 타일 치수는 [B] 블록의
     `.hub .fcard`(grid 자동폭)가 정한다. 특히 `max-width:264px` 는 모바일에서 카드가
     화면 폭을 못 채우게 만드는 지뢰라 [B] 에서 일부러 `max-width:none` 으로 되돌리고
     있었다 — 원인을 지우는 쪽이 맞다. */
}

/* 작성자 · 문의 — 공지(.notice)와 같은 줄 문법을 쓰고 **글자만 한 단 작게** 한다.
   ⚠ 화면 하단 고정(fixed)이었던 것을 이 자리로 옮겼다(사용자 지시 2026-08-05) →
     `.lnfoot` 규칙은 지웠다. 우하단 CI 락업과 끝을 맞추는 문제도 함께 사라졌다.
   ⚠ 배경이 영상이라 밝기가 계속 바뀐다 → 공지와 같은 그늘을 그대로 물려받는다(.notice). */
.notice.by{margin-top:8px;font-size:11.5px;color:var(--w60);align-items:flex-start;line-height:1.6}
.notice.by i{margin-top:7px}                    /* 두 줄로 접히면 점이 첫 줄에 붙어야 한다 */
/* ★ 줄바꿈 수리 (2026-08-09). 390px 에서 "…유준현 대 / 리" 로 **단어 한가운데가 갈렸다**.
   원인은 두 가지이고 둘 다 고친다 —
     ① CSS 의 기본 줄바꿈 규칙은 한글을 **글자 단위**로 끊는다 → `word-break:keep-all`
        (한국어에서는 어절 단위로 끊는 이 값이 맞다. 영문 동작에는 영향이 없다)
     ② 문장이 한 덩어리라 어디서 접힐지 운에 맡겨져 있었다 → 마크업에서 **두 줄로 못박는다**
        (index.html: 1줄 = 담당자 / 2줄 = 메일주소 + 안내). 메일주소는 nowrap 이라 안 갈린다. */
.notice.by{word-break:keep-all}
.notice.by span{display:block}
.notice.by a{color:rgba(255,255,255,.9);text-decoration:none;white-space:nowrap;
  border-bottom:1px solid rgba(255,255,255,.35)}
.notice.by a:hover{border-bottom-color:rgba(255,255,255,.85)}

/* ═══ 낮은 화면 = **가로로 든 폰** (2026-08-09 신설) ═══════════════════════════
   증상(844×390 실측): `.left` 의 top 이 **−30px** 이라 슬로건 윗줄이 화면 위로 잘려
   나가 있었고, `body{overflow:hidden}` 때문에 **스크롤로도 볼 수 없었다** — 영구히 못 보는
   내용이 생긴 상태다. 콘텐츠(약 452px)가 뷰포트(390px)보다 큰데 `.stage` 가
   `height:100%` + `align-self:center` 로 세로 중앙에 고정하니 위아래가 같이 잘린 것이다.
   → 낮은 화면에서만 **세로 스크롤을 연다.** 데스크톱·세로폰 치수는 건드리지 않는다.
   ⛔ `maximum-scale` 이나 요소 삭제(display:none)로 때우지 말 것 — 기능이 사라진다. */
@media (max-height:560px){
  html,body{height:auto;min-height:100%}
  body{overflow-y:auto;overflow-x:hidden}
  .stage{height:auto;min-height:100%;align-items:flex-start}
  /* 상단 줄(.corner, 고정)에 가리지 않게 위를 비우고, 아래는 문의 줄이 잘리지 않게 둔다 */
  .left{align-self:flex-start;margin-top:74px;margin-bottom:28px}
  .tag{margin-bottom:18px}
  .notice.by{margin-top:6px}
  /* 우하단 CI 락업은 스크롤과 함께 움직이면 서명이 아니라 부유물이 된다 → 고정 유지.
     다만 낮은 화면에서 문의 줄과 겹치지 않도록 한 단 작게. */
  .brand-corner{height:15px;bottom:12px;right:20px}
}

/* ═══ iOS 자동 확대 차단 (2026-08-06) ═══════════════════════════════════════
   증상(사용자 지적): "모바일에서 로그인 화면 및 로그인 후 메뉴 화면이 꽉찬 화면이 아니라,
   자꾸 가운데 부분을 확장한 것처럼 보여서 손으로 다시 화면을 좁혀서 딱 맞게 세팅해야한다."

   원인: **iOS Safari 는 글자 크기가 16px 미만인 입력칸을 탭하면 화면을 확대한다.**
   그리고 **스스로 되돌리지 않는다.** 확대 상태는 그 탭에 남으므로, 로그인하면서 확대된 채
   허브·앱 화면까지 그대로 이어진다. 실측: 로그인 입력칸 9개 전부 14.5px 이하.

   대응: 좁은 폭에서만 입력칸 글자를 16px 로 올린다. 데스크톱 치수는 건드리지 않는다.
   ⛔ `maximum-scale=1`(확대 금지)로 막지 말 것 — 시력이 약한 사람이 손으로 확대하는 것까지
      막힌다. 원인을 없애는 쪽(16px)이 맞다.
   ⚠ 체크박스·라디오는 글자 크기와 무관하므로 제외한다(크기를 건드리면 정렬이 깨진다). */
@media (max-width:720px){
  input:not([type="checkbox"]):not([type="radio"]),
  select, textarea{ font-size:16px }
}

/* ═══════════════════════════════════════════════════════════════════════════
   [A] 카드 밖 글자 대비 수리  +  [B] 허브 = 앱과 같은 판 (D안)
   2026-08-09 디자이너. login.css 에 넣을 내용 (삽입 위치는 보고 참조)
   ═══════════════════════════════════════════════════════════════════════════ */

/* ───────────────────────────────────────────────────────────────────────────
   [A] 진입 화면 — 카드 "밖" 글자
   실측(1920 영상 · 17.5초 루프를 7프레임 훑어 **가장 불리한 프레임** 채택, 1024px):
     .over 1.29 / .quick 1.37 / .notice.by 1.86 / .tag 1.82  (기준 4.5 · 큰 글자 3.0)
   원인 두 가지 —
     ① `#bgshade` 의 그늘이 **로그인 카드 자리(19% 50%)만** 덮어, 그 위(라벨·슬로건)와
        아래(문의 줄), 그리고 우상단 바로가기는 구름 밝기에 그대로 노출됐다.
     ② 카드 밖 글자가 **반투명 흰색**(50~80%)이었다. 반투명 글자는 밝은 배경과 섞여
        아무리 그늘을 깔아도 4.5 에 닿지 못한다(수식상 한계 — 알파 .5 는 배경이 RGB 40
        수준이어야 겨우 통과).
   → 고치는 방향: **글자는 불투명 흰색으로, 위계는 크기·굵기·자간으로.**
     그늘은 '전면 스크림'으로 돌아가지 않고 **글자가 앉는 두 자리**에만 둔다 —
     ① 화면 위 가장자리(바로가기 줄) ② 왼쪽 글자 기둥(라벨·슬로건·카드·문의).
   ⛔ 화면 전체를 덮는 가로 그라디언트를 다시 넣지 말 것("너무 탁하다" 2026-07-30). */

#bgshade{
  background:
    /* ① 위 가장자리 — 영상 위 자막의 표준 기법(가장자리 그라디언트). 150px 아래로는 0. */
    linear-gradient(180deg, rgba(0,0,0,.64) 0, rgba(0,0,0,.52) 56px,
                            rgba(0,0,0,.16) 108px, transparent 152px),
    /* ② 왼쪽 글자 기둥 — 종전 820×660 을 세로로 늘려 라벨·슬로건·카드·문의 한 줄을
          **하나의 그늘**로 받는다(모양을 늘린 것이지 새 모양을 더한 것이 아니다). */
    radial-gradient(620px 860px at 20% 50%,
      rgba(0,0,0,.58) 0%, rgba(0,0,0,.52) 44%, rgba(0,0,0,.23) 70%, transparent 86%);
}
/* ⛔ 종전의 '우측 가장자리 그늘'은 **없앴다** — 그건 우측 세로 레일(맨텍스트)이 있던
   시절의 것이고, 그 레일은 이미 사라졌다. 지금은 오른쪽을 눌러 얻는 것이 없다.
   이 삭제분이 위 ①② 로 늘어난 어둠의 상당 부분을 되돌려 준다(실측치는 보고 참조). */

/* 카드 밖 글자 = 불투명 흰색. 위계는 크기(12 / 48 / 11.5)·굵기·자간이 만든다. */
.over{color:#FFFFFF}
.quick a,.quick .ico{color:#FFFFFF}
.quick a:hover,.quick .ico:hover{color:#FFFFFF;opacity:.82}
.notice,.notice.by{color:#FFFFFF}
.notice.by a{color:#FFFFFF;border-bottom-color:rgba(255,255,255,.55)}
/* 슬로건 둘째 줄 — 큰 글자(기준 3.0)라 한 단 낮춰도 되지만 **불투명**으로 둔다 */
.tag span{color:#DDE1E5}
/* 점 표식도 같이 올린다(작은 비텍스트 요소 기준 3:1) */
.notice i{background:rgba(255,255,255,.85)}

/* 좁은 화면 — 레이아웃은 1080px 부터 **가운데 정렬**로 바뀌는데(login.css .left),
   그늘만 720px 기준이라 721~1080 구간에서 글자와 그늘이 어긋나 있었다(실측: 1024 가 가장 나쁨).
   두 기준을 맞춘다. */
@media (max-width:1080px) and (min-width:721px){
  #bgshade{
    background:
      linear-gradient(180deg, rgba(0,0,0,.64) 0, rgba(0,0,0,.52) 56px,
                              rgba(0,0,0,.16) 108px, transparent 152px),
      radial-gradient(470px 860px at 50% 50%,
        rgba(0,0,0,.58) 0%, rgba(0,0,0,.52) 44%, rgba(0,0,0,.23) 70%, transparent 86%);
  }
}
@media (max-width:720px){
  #bgshade{
    background:
      linear-gradient(180deg, rgba(0,0,0,.62) 0, rgba(0,0,0,.44) 52px,
                              rgba(0,0,0,.12) 100px, transparent 144px),
      radial-gradient(430px 820px at 50% 50%,
        rgba(0,0,0,.54) 0%, rgba(0,0,0,.49) 44%, rgba(0,0,0,.22) 70%, transparent 88%);
  }
}

/* ───────────────────────────────────────────────────────────────────────────
   [B] 상단 줄 — 진입에서는 투명, 허브에서는 앱 셸의 띠
   `.quick`(바로가기)과 `.who`(나)를 각각 화면 모서리에 고정하던 것을 한 줄로 묶었다
   (index.html 의 <header class="corner">). 진입 화면의 자리·크기는 그대로다. */
.corner{
  position:fixed;top:0;left:0;right:0;z-index:7;
  display:flex;align-items:flex-start;gap:20px;
  /* 진입 화면의 바로가기 좌표를 그대로 유지한다(종전 .quick{top:30;right:40}) —
     실측으로 before/after 좌표가 1px도 달라지지 않게 맞춘 값이다.
     ⚠ 2026-08-09: 바로가기 링크의 타깃을 15 → 28px 로 키우면서 padding-top 을
       30 → 23.5 로 내렸다(= 30 - (28-15)/2). 링크 **글자**의 세로 중심을 종전 자리에
       그대로 두기 위한 보정이다. 둘 중 하나만 고치면 상단 줄이 6~7px 내려앉는다. */
  padding:23.5px 40px;pointer-events:none;background:transparent;
  transition:background .45s ease, padding .45s ease, height .45s ease}
.corner > *{pointer-events:auto}
.corner-sp{flex:1}
.corner-logo{height:18px;width:auto;display:none}
/* 고정 좌표를 쓰던 두 요소를 줄 안으로 되돌린다 */
.corner .quick{position:static;top:auto;right:auto}
.corner .who{position:static;top:auto;left:auto;margin:0;padding:4px 8px}

/* ───────────────────────────────────────────────────────────────────────────
   [B] 색 토큰 — **출처: sop_v5/app.css** (허브가 앱과 같은 판이 되게)
   ⚠ 값이 그쪽에서 바뀌면 여기도 같이 바꿀 것. 이름만 `--h-` 로 구분해 진입 화면의
     무채색 계단(--w*)과 섞이지 않게 했다. */
:root{
  --h-shell:#00263F; --h-shell-txt:#F7F7F9; --h-shell-txt-lo:#C9CDD3;
  --h-canvas:#F4F5F7; --h-panel:#FFFFFF; --h-line:#E3E5E9;
  --h-ink:#16181D; --h-ink-2:#454B55; --h-ink-3:#545A64; --h-ink-4:#6E747D;
  --h-accent:#0079B0; --h-accent-strong:#00669B;
}
/* ★ 다크 (2026-08-09 수정) — 두 가지를 고쳤다.
   ① **면이 세 개로 갈린다.** 종전엔 셸·캔버스·패널이 전부 `#15181D` 한 색이라
      허브에서 카드가 **면으로는 존재하지 않았다**(패널↔캔버스 명도차 0.00 L*).
      카드를 카드로 만들던 것은 1px 테두리 하나뿐인데 그 대비가 1.25:1 이었다.
      → 캔버스 `#101418` · 패널 `#171C22` 로 갈라 **명도차 3.91 L\*** 를 준다.
        라이트 테마의 흰 패널(#FFFFFF) ↔ 회색 캔버스(#F4F5F7) 차이가 3.49 L\* 이므로
        두 테마가 **같은 세기의 위계**를 갖게 된다(반복 원칙).
      ⚠ 어두운 UI 의 정석은 '테두리를 진하게'가 아니라 '면을 띄우는 것'이다 —
        테두리로 3:1 을 만들려면 #5E6874 급이 필요하고, 그건 라이트(1.26:1)와
        전혀 다른 화면이 된다. 실측 근거는 이번 보고 참조.
   ② **셸이 CI Navy 언더톤으로 돌아온다.** 종전 `#15181D` 는 순회색이라 상단 띠에서
      브랜드가 사라졌다. `#0A1B27` 은 Navy(#00263F) 계열이면서 캔버스보다 **어둡다**
      (2.84 L\* 아래) → "레일만 남색이면 그 면만 떠 보인다"는 우려가 생기지 않는다.
   ⚠ 라이트의 `--h-*` 는 sop_v5/app.css 와 같은 값이다. 다크는 **포털 허브 전용**으로
     갈라져 있다 — 앱 셸(app.css)의 다크는 사용자 지시(2026-08-04 "다크는 검정 그대로")로
     `#15181D` 를 유지하므로, 그쪽을 맞출지는 별도 결정 사항이다(보고 참조). */
:root[data-theme="dark"]{
  --h-shell:#0A1B27; --h-shell-txt:#EDF2F5; --h-shell-txt-lo:#9FB0BC;
  --h-canvas:#101418; --h-panel:#171C22; --h-line:#262D35;
  --h-ink:#E9EBEF; --h-ink-2:#B7BDC6; --h-ink-3:#A3ABB6; --h-ink-4:#8F97A2;
  --h-accent:#00A4EB; --h-accent-strong:#4FC3F7;
}

/* ───────────────────────────────────────────────────────────────────────────
   [B] 허브 = 하나의 화면(판). 영상 위 유리 타일이 아니다.
   ⛔ `.hub.show{background:rgba(3,7,13,.55)}` 로 **영상을 눌러** 대비를 얻던 방식으로
      되돌리지 말 것 — 그게 "지구가 회색 안개"의 원인이었다. */
.hub{
  position:fixed;inset:0;z-index:6;display:block;overflow:auto;
  padding:0;gap:0;background:var(--h-canvas);color:var(--h-ink);
  /* ⚠ align-content 를 반드시 되돌린다. 종전 규칙의 `align-content:center` 는 flex 용으로
     넣은 것인데, 요즘 Chromium 은 **블록 컨테이너에도 align-content 를 적용**한다
     → display:block 으로 바꿔도 내용이 세로 가운데로 밀려 있었다(실측). */
  align-content:normal;align-items:normal;flex-wrap:nowrap;
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .55s var(--ease), visibility 0s linear .55s}
.hub.show{opacity:1;visibility:visible;pointer-events:auto;
  /* ⛔ 종전 규칙의 `background:rgba(3,7,13,.55)`(영상을 눌러 대비를 얻던 스크림)을
     **반드시 덮어쓴다** — `.hub.show` 가 `.hub` 보다 특이도가 높아, 위에서 캔버스색을
     줘도 이 선언이 이긴다(실측으로 확인). login.css 원본에서 지우는 것이 정석이다. */
  background:var(--h-canvas);
  transition:opacity .55s var(--ease)}

/* 로그인(어두운 영상) → 허브(밝은 판) 의 밝기 점프 완화 =
   허브가 떠오르는 0.55초 동안 **영상·그늘이 같이 잦아든다**(교차 페이드).
   영상은 그 뒤 index.html 스크립트가 정지·해제한다(합성 부하를 로그인 순간에만 둔다). */
body.hubon #bg,
body.hubon #bgposter,
body.hubon #bgshade,
body.hubon .vignette,
body.hubon .brand-corner{opacity:0;transition:opacity .55s ease;pointer-events:none}

.hubwrap{max-width:824px;margin:0 auto;padding:96px 32px 56px}

/* ═══ A안 「정돈」 — 구조는 그대로, 위계만 바로잡는다 (2026-08-09 사용자 채택) ═══
   ① 인사말 강등: `29px 단독 제목` → **날짜와 한 줄, 15px**
      ⛔ 되돌리지 말 것. 종전엔 화면에서 가장 큰 글자가 "안녕하세요, ○○ 님" 이었다.
        매일 여는 화면에서 1순위 자리를 **아무 일도 시키지 않는 문장**이 차지하고,
        정작 사람이 하러 온 일(앱 고르기)은 그 아래 작은 카드였다 — 위계 역전.
        인사는 지우지 않는다(사람이 쓰는 화면이다). 자리만 내린다.
   ② 관리자 격하: 카드 동급 → **구분선 아래 유틸리티 행** (아래 `.hubutil`)
   ③ 카드가 일을 한다: 제목만 있던 카드 안에 **하위 진입점**을 실제 링크로 (아래 `.hubsub`) */
/* ⛔ `.hubhello`(날짜 · ○○ 님) 규칙은 **삭제됐다** — 2026-08-18 마크업째 걷었다
   (index.html 참조: 날짜·시각은 시계 위젯이, 날씨는 날씨 위젯이 말한다).
   ⚠ 되살리지 말 것. 규칙만 남아 있으면 다음 사람이 "숨겨 둔 것"으로 읽는다. */
/* 날씨 현황 (2026-08-12) — 인사줄 꼬리의 한 조각. 기상청 초단기실황을 워커가 중계한다.
   ⚠ 구분점은 `::before` 다. 값을 못 받아 요소가 감춰지면 점도 함께 사라진다
     (이름 자리처럼 `<i>·</i>` 를 따로 두면 점만 덩그러니 남는 상태를 또 다뤄야 한다).
   ⚠⚠ `[hidden]` 짝을 **반드시** 함께 둔다. author 의 display 선언은 UA 의
      `[hidden]{display:none}` 을 이긴다 → 짝이 없으면 날씨를 못 받은 사람에게
      **빈 조각이 계속 떠 있다**(이 파일 `.hubempty` 에서 같은 함정을 이미 밟았다). */
.hubwx{display:inline;color:var(--h-ink-4)}
.hubwx[hidden]{display:none}
.hubwx::before{content:"·";margin-right:9px;opacity:.6}
.hubsec{font-size:11.5px;font-weight:600;letter-spacing:.14em;color:var(--h-ink-4);
  margin-bottom:11px}
/* 카드 수에 따라 폭이 스스로 맞는다 — **1장인 사람**(권한이 하나뿐)이 화면 한가운데
   264px 타일 하나만 보던 문제를 여기서 푼다: 1장이면 한 칸이 통째로 그 카드다. */
/* ⚠ 최소폭 310 → **220px** (2026-08-09). 카드가 4장에서 3장으로 줄면서(관리자가 아래
   유틸리티 행으로 내려갔다) 310 기준으로는 **2 + 1** 로 접혀 세 번째 카드만 아랫줄에
   혼자 남았다 — "내 업무"가 한 세트로 안 읽힌다(근접·정렬).
   220 이면 같은 폭(max-width:760) 안에서 **한 줄에 3장**이 들어간다. 화면 폭·여백은
   그대로다(hubwrap 824px 불변) → 허브의 전체 인상은 유지된다.
   ★ 카드가 1장뿐인 사람(권한이 하나)은 여전히 그 한 칸을 통째로 쓴다(auto-fit + 1fr). */
.hubcards{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));
  gap:14px;max-width:760px}

/* ⚠ 카드는 이제 `<a>` 가 아니라 `<article>` 이다 (A안).
   카드 전체를 `<a>` 로 감싼 채 안에 하위 링크를 넣으면 **앵커 중첩**이라 브라우저가
   앵커를 강제로 쪼갠다(HTML 파서 규칙 — 목업 첫 시안에서 실제로 카드가 두 동강 났다).
   → 제목만 링크로 두고, 카드 **상단 띠 전체**를 그 링크의 확장 클릭영역으로 만든다
     (`.fcard h4 a::after`). 하위 링크 영역은 침범하지 않는다. */
.hub .fcard{
  width:auto;max-width:none;height:auto;min-height:84px;border-radius:10px;
  padding:16px 20px 12px;position:relative;
  display:flex;flex-direction:column;justify-content:flex-start;
  background:var(--h-panel);border:1px solid var(--h-line);
  backdrop-filter:none;-webkit-backdrop-filter:none;
  /* 라이트에서 패널(#FFF)과 캔버스(#F4F5F7)의 차가 3.49 L* 뿐이라 테두리만으로는
     카드가 '떠 있는 면'으로 안 읽혔다 → 아주 옅은 그림자 한 겹. 다크는 면 자체가
     갈라져 있으므로(3.91 L*) 그림자를 쓰지 않는다. */
  box-shadow:0 1px 2px rgba(16,24,40,.05);
  color:var(--h-ink);text-decoration:none;
  opacity:0;transform:translateY(10px);
  transition:box-shadow .18s ease, border-color .18s ease}
:root[data-theme="dark"] .hub .fcard{box-shadow:none}
.hub .fcard::before{display:none}          /* 유리 테두리(::before) 제거 */
.hub .fcard h4{font-size:19px;font-weight:600;letter-spacing:-.02em;line-height:1.2;
  color:var(--h-ink);text-shadow:none;display:flex;align-items:center;gap:9px}
.hub .fcard h4 a{color:inherit;text-decoration:none}
/* 제목 링크의 클릭 영역을 카드 상단 띠 전체로 넓힌다(하위 링크 줄은 건드리지 않는다) */
.hub .fcard h4 a::after{content:"";position:absolute;left:0;right:0;top:0;height:50px}
/* 제목 앞 표식 — 앱마다 다른 색을 주지 않는다(장식색 금지). 강조 1색으로 통일. */
.hub .fcard h4::before{content:"";width:3px;height:15px;border-radius:2px;flex:none;
  background:var(--h-accent)}
.hub .fcard p{font-size:12.5px;line-height:1.5;color:var(--h-ink-3);margin-top:4px;
  text-shadow:none}

/* ── ③ 하위 진입점 ──────────────────────────────────────────────────────
   ★ 새 데이터 배관 없이 **이미 있는 화면**으로만 보낸다. 카드에 "오늘 출하 N건" 같은
     상태 한 줄을 붙이지 않는 것은 종전 결정 그대로다(넣을 실데이터가 없다).
   ⚠ 주소는 `앱주소#화면키` 형태다. 화면키는 각 앱 index.html 의 `data-nav` 값에서
     그대로 가져왔다(지어낸 값 없음). **셸이 아직 hash 를 읽지 않으므로** 지금은
     앱의 기본 화면으로 열린다 — 링크가 깨지는 게 아니라 '한 단계 덜 가는' 것이다.
     core.js 가 부팅 때 location.hash 를 읽으면 그대로 정확히 착지한다(보고의 요구 명세). */
.hubsub{margin-top:11px;border-top:1px solid var(--h-line);padding-top:5px;
  display:flex;flex-direction:column}
.hubsub a,.hubsub span{font-size:12.5px;line-height:1.35;color:var(--h-ink-3);
  text-decoration:none;padding:6px 0;min-height:28px;
  display:flex;align-items:center;justify-content:space-between;gap:10px}
.hubsub a:hover{color:var(--h-accent-strong)}
.hubsub span{color:var(--h-ink-4)}            /* 링크가 아닌 항목(준비 중) */
.hubsub em{font-style:normal;font-size:11px;color:var(--h-ink-4);flex:none}

/* ⚠ hover 를 transform 으로 주지 않는다 — 등장 애니메이션이 fill-mode:forwards 로
   transform 을 붙잡고 있어 먹지 않는다(같은 함정이 .left/.lin 주석에도 적혀 있다).
   면 위 카드의 hover 는 그림자·테두리로 충분하다. */
.hub .fcard:hover{box-shadow:0 4px 14px rgba(16,24,40,.10);border-color:var(--h-accent)}
:root[data-theme="dark"] .hub .fcard:hover{box-shadow:0 6px 18px rgba(0,0,0,.5)}
/* 포커스 링 — 진입 화면의 흰 링은 밝은 판 위에서 보이지 않는다. 강조색 링으로 바꾼다. */
.hub .fcard h4 a:focus-visible,
.hubsub a:focus-visible,
.hubutil a:focus-visible{outline:2px solid var(--h-accent);outline-offset:3px;border-radius:4px}
.hub .fcard[aria-disabled] h4{color:var(--h-ink-3)}

.hub.show .fcard{animation:hubRise .5s var(--ease) forwards}
@keyframes hubRise{to{opacity:1;transform:none}}
.hubempty{grid-column:1/-1;text-align:left;font-size:14px;line-height:1.7;
  color:var(--h-ink-2);text-shadow:none}
/* ── ② 유틸리티 행 — 카드가 아니다 ────────────────────────────────────────
   ★ '관리자'를 여기로 내렸다. 매일 여는 대시보드·MES·CRM 과, 계정 하나 만들 때 한 번
     들어가는 관리 도구가 **같은 무게일 이유가 없다**. 지우는 게 아니라 무게를 맞춘다.
   ⚠ 권한 필터(login.js applyMenuPermissions)는 `.fcard` 가 아니라 `[data-menu]` 를
     본다 — 이 링크에도 `data-menu="admin"` 이 있어야 권한 없는 사람에게서 사라진다.
     둘 중 하나만 고치면 관리자 링크가 전원에게 보인다. */
/* ★ 문의 줄 = **오른쪽 아래** (2026-08-18 사용자 지시: "문의 저건 좀 더 오른쪽 아래로
   내려도 될 거 같은데?"). 인사줄이 쓰던 자리를 그대로 받는다 — 화면 오른쪽 하단은
   '읽고 끝나는 것'의 자리이고, 문의처가 정확히 그것이다. */
.hubutil{margin-top:26px;border-top:1px solid var(--h-line);padding-top:13px;
  display:flex;align-items:center;justify-content:flex-end;text-align:right;
  flex-wrap:wrap;gap:14px 22px;font-size:12.5px}
.hubutil > a{color:var(--h-ink-3);text-decoration:none;
  min-height:28px;display:inline-flex;align-items:center}
.hubutil > a:hover{color:var(--h-accent-strong)}
.hubutil .sp{flex:1}
.hubfoot{font-size:11.5px;color:var(--h-ink-4)}
.hubfoot a{color:var(--h-accent-strong);text-decoration:none;white-space:nowrap;
  border-bottom:1px solid var(--h-line)}
.hubfoot a:hover{border-bottom-color:var(--h-accent-strong)}

/* ── 허브의 상단 줄 = 앱 셸의 띠 ── */
body.hubon .corner{background:var(--h-shell);padding:0 32px;height:56px;align-items:center}
body.hubon .corner-logo{display:block}
body.hubon .quick{gap:8px;margin-right:-6px}   /* 시각 간격 8+6+6 = 20 (종전과 같다) */
body.hubon .quick a,body.hubon .quick .ico{color:var(--h-shell-txt-lo);text-shadow:none}
body.hubon .quick a:hover,body.hubon .quick .ico:hover{color:var(--h-shell-txt);opacity:1}
/* 나 — 앱과 같은 자리(우측 상단). 팀은 이름 뒤 한 단 낮은 밝기로 잇는다. */
body.hubon .who{flex-direction:row;align-items:center;gap:7px;border-radius:8px}
body.hubon .who:hover{background:rgba(255,255,255,.08)}
body.hubon .who-name{order:1;font-size:13px;color:var(--h-shell-txt);text-shadow:none}
body.hubon .who-team{order:2;font-size:12px;font-weight:400;letter-spacing:0;
  color:var(--h-shell-txt-lo);text-shadow:none}
body.hubon .who-team::before{content:"· "}
body.hubon .who-caret{order:3;width:14px;height:14px;margin:0;color:var(--h-shell-txt-lo)}
body.hubon .who:hover .who-caret{transform:none;color:var(--h-shell-txt)}

/* ── 내 정보 패널 — 허브(밝은 판) 위에서는 유리가 아니라 판이어야 한다 ──
   ⚠ 이 손질이 없으면 라이트 테마에서 **흰 판 위 흰 글자**가 된다(유리가 뒤의 밝은
     캔버스를 그대로 통과시키기 때문). 다크에서도 판·선이 없어 경계가 사라진다. */
body.hubon .sheet-bd{background:rgba(8,12,18,.42)}
body.hubon .sheet-card{background:var(--h-panel);border:1px solid var(--h-line);
  backdrop-filter:none;-webkit-backdrop-filter:none;
  box-shadow:0 18px 48px rgba(0,0,0,.18);color:var(--h-ink)}
body.hubon .sheet-card::before{display:none}
body.hubon .sheet-card h3{color:var(--h-ink)}
body.hubon .sheet-sep{color:var(--h-ink-2);border-top-color:var(--h-line)}
body.hubon .sheet-sep span{color:var(--h-ink-4)}
body.hubon .fld span{color:var(--h-ink-3)}
body.hubon .fld span em{color:var(--h-ink-4)}
body.hubon .fld input,body.hubon .fld select{background:var(--h-canvas);border:1px solid var(--h-line);
  color:var(--h-ink)}
/* select 화살표도 라이트 톤으로(위 어두운 배경용 SVG는 여기선 안 보인다) */
body.hubon .fld select{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 12px center;background-size:14px}
body.hubon .fld select option{background:var(--h-panel);color:var(--h-ink)}
body.hubon .fld input::placeholder{color:var(--h-ink-4)}
body.hubon .fld input:focus,body.hubon .fld select:focus{background:var(--h-panel);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--h-accent) 28%, transparent)}
body.hubon .fld input:focus-visible,body.hubon .fld select:focus-visible{outline:2px solid var(--h-accent);outline-offset:1px;
  box-shadow:none}
body.hubon .fld input:disabled{background:var(--h-canvas);color:var(--h-ink-4)}
/* ★ 비밀번호 눈 아이콘 — 내 정보 패널은 **라이트 판**이라 흰색 계열이 안 보인다
     (2026-08-18). 도형·크기·터치 표적은 로그인 화면과 같고 색만 판을 따라간다. */
body.hubon .pw-eye{color:var(--h-ink-4)}
body.hubon .pw-eye:hover{background:var(--h-canvas);color:var(--h-ink-2)}
body.hubon .pw-eye[aria-pressed="true"]{color:var(--h-ink)}
body.hubon .pw-eye:focus-visible{outline:2px solid var(--h-accent);outline-offset:2px}
body.hubon .sheet .cta{background:var(--h-accent);color:#FFFFFF;
  backdrop-filter:none;-webkit-backdrop-filter:none;box-shadow:none}
body.hubon .sheet .cta::before{display:none}
body.hubon .sheet .cta i{background:rgba(255,255,255,.22)}
/* ⚠ CTA 를 유리로 되돌리면서(2026-08-10) 위쪽 `.cta.g.strong:hover/:disabled`(0,4,0)가
   이 블록의 면 지정(0,3,1)을 **이겨 버린다** → 허브에서는 한 번 더 못 박는다.
   안 하면 '내 정보' 저장 버튼이 마우스만 올려도 흰 유리로 바뀐다(밝은 판 위 흰 버튼). */
body.hubon .sheet .cta:hover,
body.hubon .sheet .cta:disabled{background:var(--h-accent);box-shadow:none}
body.hubon .sheet .cta:disabled{opacity:.5}
body.hubon .lnk{color:var(--h-ink-3);text-decoration-color:var(--h-line)}
body.hubon .lnk:hover{color:var(--h-ink)}
body.hubon .err.ok{color:var(--h-ink-2)}
:root[data-theme="dark"] body.hubon .sheet .cta{color:#08202C}

/* ── 좁은 화면 ──
   ⚠ 기능을 없애지 않는다(display:none 금지). 상단 줄이 **두 줄로 접힌다** —
     1줄: 로고 + 나 / 2줄: 바로가기. */
@media (max-width:720px){
  /* ⚠ 104 → **120px** (2026-08-09). 폰에서 상단 줄이 두 줄로 접히는데, 바로가기 링크를
     28px 타깃으로 키우면서 그 줄의 높이가 84 → 96px 이 됐다. 104 그대로 두면 인사 줄이
     셸 바로 밑 8px 에 붙는다(실측) → 셸 높이 + 24px 여백으로 다시 맞춘다.
     ⛔ 둘은 한 쌍이다. 타깃 크기를 바꾸면 이 값도 같이 확인할 것. */
  .hubwrap{padding:120px 18px 40px}
  /* 인사 줄은 폰에서 두 줄로 접힐 수 있다(flex-wrap) — 크기는 데스크톱과 같게 둔다.
     ⚠ 반응형에서 글자 크기를 개별 요소만 바꾸지 않는다(타이포 규범: 토큰·세트 단위). */
  .hubcards{grid-template-columns:1fr;max-width:none}
  /* 유틸리티 행은 폰에서 세로로 — 관리자 링크와 문의가 한 줄에 안 들어간다 */
  /* 폰에서도 오른쪽 정렬을 지킨다(2026-08-18) — 세로로 쌓되 오른쪽 끝에 붙는다 */
  .hubutil{flex-direction:column;align-items:flex-end;gap:10px;margin-top:22px}
  .hubutil .sp{display:none}
  body.hubon .corner{flex-wrap:wrap;height:auto;padding:9px 16px 10px;align-items:center}
  body.hubon .corner-sp{display:none}
  body.hubon .corner-logo{order:1;height:16px}
  body.hubon .who{order:2;margin-left:auto}
  body.hubon .quick{order:3;flex-basis:100%;justify-content:flex-start;gap:18px;
    padding-top:6px}
}
/* 모션 축소 — 허브는 즉시 나타난다 */
@media (prefers-reduced-motion: reduce){
  .hub{transition:none}
  .hub .fcard{opacity:1;transform:none;animation:none}
}

/* ══ 홈 셸 v2 — 영상 배경 + 반투명 좌측 셸 (2026-08-11 확정 스펙) ═══════════════
   사용자 확정: ① 우주 배경 **계속 재생** ② 사이드바 **반투명**(배경이 비침)
                ③ 모듈 진입 후에는 배경 없음(문서가 바뀌므로 저절로)
                ④ 로고=띠 맨 위 · ☰ 복원 · 상단 Navy 띠 제거

   ★★ 되돌리기 스위치 = `<html data-shell="v2">` — 이 블록 전체와 DOM 구성 스크립트가
      그 속성 아래에 있다. 지우면 종전 카드 화면으로 완전 복귀한다.
   ★★ 두 번째 조건 = `body.hubon`. 이 문서는 **로그인 화면과 같은 장**이므로, 인증 후
      허브 상태에서만 그려진다. ⛔ 이 조건을 빼면 로그인 화면이 바뀐다.

   ══ 대비 계산 (영상 위에서 글자가 읽혀야 한다) ═══════════════════════════════
   영상은 프레임마다 밝기가 변한다 → **가장 밝은 프레임**이 아니라 그보다 강한
   **최악값(순백 #FFFFFF 배경)** 으로 계산했다. 이보다 밝은 프레임은 존재할 수 없다.
     유리면 = rgba(11,11,11,.68) 을 순백 위에 합성 → sRGB 0.349 → L=0.1000
       · 메뉴 글자 #FFFFFF   → (1.05)/(0.15)   = **7.00:1** ✓ AA(4.5) 통과
       · 보조 글자 #C8C8C8   → (0.7548)/(0.15) = **4.70:1** ✓ AA 통과
       · 워드마크 #00A4EB    → (0.3754)/(0.15) = 2.50:1 (로고=비텍스트, 3:1 대상 아님.
         실제로는 아래 #bgshade 가 영상을 눌러 이보다 높다)
     ⛔ 보조 글자를 #C8C8C8 보다 어둡게 내리지 말 것 — 최악 프레임에서 AA 가 깨진다.
   ⚠ `backdrop-filter` 미지원 브라우저는 유리가 성립하지 않는다 → 아래 @supports 가
     불투명에 가까운 면(.92)으로 올린다. 대비는 그때 더 좋아진다(안전한 방향).
   ⚠ 앱(/mes/ 등)의 사이드바는 **불투명 그대로**다. 반투명은 홈에서만 — 앱에는 비칠
     배경이 없다(문서가 다르다).
   ══════════════════════════════════════════════════════════════════════════ */
:root[data-shell="v2"]{
  /* 홈은 배경이 **영상**이라 앱의 흰 캔버스 토큰을 쓰지 않는다(그 위에 흰 판을 얹으면
     영상이 안 보인다). 대신 유리면 + 밝은 글자 한 벌만 둔다. 값의 근거는 위 계산. */
  --hs-glass:rgba(11,11,11,.68);
  --hs-ink:#FFFFFF;        /* 7.00:1 (최악 프레임) */
  --hs-ink-lo:#C8C8C8;     /* 4.70:1 */
  --hs-hover:rgba(255,255,255,.10);
  /* 구조 경계선 — 유리면 위라 색이 아니라 **밝기 차**로 긋는다.
     ⚠ 홈은 배경이 영상이라 배경색으로는 영역을 나눌 수 없다 → 선이 유일한 수단이다. */
  --hs-line:rgba(255,255,255,.22);
  /* ★★ 활성 면 = **인접 유리면 대비 3:1** (WCAG 1.4.11, 2026-08-11 표준 반영).
     종전 .18 은 유리면 대비 1.56:1 로 미달이었다. 계산(최악=순백 배경):
       유리면 합성 sRGB 0.349 → L=0.1000 / 필요한 면 L=0.35 → sRGB 0.6262
       → 흰색 알파 α=(0.6262-0.349)/(1-0.349)= **0.43**
     실제 대비: 흰 배경 위 **3.00:1** · 검정 배경 위 **4.12:1** — 영상 밝기와 무관하게 통과. */
  /* ★ 활성 = 옅은 면 + **닫힌 윤곽**(사방 1px). 면만으로 3:1 을 만들면 흰 덩어리가 된다.
     윤곽 rgba(255,255,255,.65) 의 대비(유리면 기준):
       최악(순백 배경) 합성 sRGB 0.772 → L=0.5575 → (0.6075)/(0.15) = **4.05:1** ✓
       최암(검정 배경) 합성 sRGB 0.660 → L=0.3927 → (0.4427)/(0.0523) = **8.46:1** ✓
     영상 밝기가 어떻든 3:1 을 넘는다. */
  --hs-active:rgba(255,255,255,.14);
  --hs-active-line:rgba(255,255,255,.65);
}
/* ⛔ 기본은 숨김 — 스크립트가 셸을 만들어도 로그인 화면에는 나타나지 않는다(두 겹 방어). */
:root[data-shell="v2"] .hshell{display:none}

/* ── ① 배경이 실제로 보이게 ────────────────────────────────────────────────
   ⚠ 허브 판(.hub)이 불투명하면 영상이 돌아도 **가려서 안 보인다.** 투명으로 둔다.
   ⚠ `body.hubon` 이 배경 층들을 **페이드아웃시키던 규칙을 취소**한다(login.css 위쪽).
     `.brand-corner`(진입 화면 서명)는 그대로 감춘다 — 로고는 이제 띠에 있다. */
:root[data-shell="v2"] body.hubon .hub,
:root[data-shell="v2"] body.hubon .hub.show{background:transparent}
:root[data-shell="v2"] body.hubon #bg,
:root[data-shell="v2"] body.hubon #bgposter,
:root[data-shell="v2"] body.hubon #bgshade,
:root[data-shell="v2"] body.hubon .vignette{opacity:1;pointer-events:none}

/* ── ④ 상단 Navy 가로 띠 제거 ──────────────────────────────────────────────
   로고가 좌측 띠로 갔으므로 그 줄이 남을 이유가 없다. 면색을 걷고 로고만 감춘다.
   ⚠ `.corner` 는 **로그인 화면에서도 쓰는 요소**다 → 반드시 `body.hubon` 안에서만 바꾼다.
     (로그인 화면에서 이 줄은 원래 투명이다 — 그 모습이 기준이고, 건드리지 않았다) */
:root[data-shell="v2"] body.hubon .corner{background:transparent}
:root[data-shell="v2"] body.hubon .corner-logo{display:none}

/* ── ② 반투명 셸 ──────────────────────────────────────────────────────────── */
:root[data-shell="v2"] body.hubon .hshell{
  display:flex;position:fixed;left:0;top:0;bottom:0;width:304px;z-index:7;
  background:var(--hs-glass);
  /* 셸 ↔ 본문(영상) 경계 = **셸 전체 높이를 덮는 세로선 1줄.** 주인은 셸이다. */
  border-right:1px solid var(--hs-line);
  -webkit-backdrop-filter:blur(18px) saturate(120%);
  backdrop-filter:blur(18px) saturate(120%)
}
/* 띠 ↔ 레일 경계 = 띠 전체 높이. 접히면 레일이 없으므로 이 선도 없다(경계가 사라진다). */
:root[data-shell="v2"] body.hubon .hstrip{border-right:1px solid var(--hs-line)}
:root[data-shell="v2"] body.hubon.rail-collapsed .hstrip{border-right:0}
@supports not ((backdrop-filter:blur(2px)) or (-webkit-backdrop-filter:blur(2px))){
  /* 유리가 성립하지 않는 브라우저 — 배경은 못 비치지만 **글자는 반드시 읽혀야 한다** */
  :root[data-shell="v2"] body.hubon .hshell{background:rgba(11,11,11,.92)}
}
/* 접힘 = 앱과 **같은 클래스·같은 저장키**(viol-rail-collapsed). 레일 폭만 0 이 된다. */
/* 접힘 = 앱과 같은 클래스·같은 저장키(viol-rail-collapsed). 레일 폭만 0 이 된다. */
:root[data-shell="v2"] body.hubon.rail-collapsed .hshell{width:var(--hs-stripw)}
:root[data-shell="v2"] body.hubon.rail-collapsed .hrail{width:0;padding-left:0;padding-right:0}
:root[data-shell="v2"] body.hubon.rail-collapsed .hubwrap{padding-left:104px}

/* ══ hover = 레일이 **잠깐 펼쳐진다** (사용자 지시 2026-08-11) ═════════════════
   원문: "이모티콘에 마우스오버 시 메뉴 펼쳐짐"
   ★★ **덮어쓴다** — 셸 폭을 실제로 늘리지 않는다. 늘리면 스치기만 해도 본문이 밀린다.
   ★ 저장된 접힘 상태는 그대로다(JS 가 localStorage 를 건드리지 않는다) — 마우스를 빼면 원래대로.
   ⚠ 유리면·경계선 값은 펼친 레일과 **같은 토큰**을 쓴다(다른 값을 쓰면 두 벌이 된다).
   ⚠ `.hshell` 이 `position:fixed` 라 그것이 곧 이 절대위치의 기준 상자다. */
:root[data-shell="v2"] body.hubon.rail-collapsed.rail-peek .hrail{
  position:absolute;left:var(--hs-stripw);top:0;bottom:0;width:240px;z-index:6;
  padding:var(--hs-pad) 8px 24px;
  /* ⚠ 앱에서 이 자리에 `visibility:hidden` 을 안 되돌려 "펼쳐도 안 보이는" 회귀가 났다
     (app.css 회귀 주석 참조) → 홈에도 방어적으로 못 박는다. */
  visibility:visible;
  background:var(--hs-glass);border-right:1px solid var(--hs-line);
  -webkit-backdrop-filter:blur(18px) saturate(120%);
  backdrop-filter:blur(18px) saturate(120%)}
@supports not ((backdrop-filter:blur(2px)) or (-webkit-backdrop-filter:blur(2px))){
  :root[data-shell="v2"] body.hubon.rail-collapsed.rail-peek .hrail{background:rgba(11,11,11,.92)}
}

/* ══ 행 맞춤 — 띠 아이콘 n번째와 레일 모듈명 n번째가 **같은 y 중심** ═════════════
   원문: "메뉴를 펼치면 이모티콘 옆에 메뉴명이 와야하는데 간격 배열이 맞지 않음"
   ⚠ 어긋난 원인은 값이 두 곳에 따로 적혀 있던 것이다:
       띠   = 아이콘 44 + gap 2 (한 줄 46) · 위 여백 52(☰ 자리) + 로고 44 + 6
       레일 = 모듈명 32 + margin 6 (한 줄 38) · 위 여백 10 + 워드마크 44
     → 한 줄이 46 vs 38, 시작점이 102 vs 54 로 **둘 다** 달랐다.
   ★ 그래서 **토큰 한 벌**을 만들고 띠·레일이 같은 값을 읽게 한다. 한쪽만 바꾸지 말 것. */
:root[data-shell="v2"]{
  /* ⛔ `--hs-stripw/--hs-row/--hs-gap/--hs-pad/--hs-head-gap` 정의를 **걷었다**(2026-08-14).
     그 다섯은 앱 셸(app.css)의 `--stripw/--sh-*` 와 **같은 뜻·같은 값인데 이름만 달랐다** —
     그래서 app.css 가 머리 칸을 44 → 48 로 올렸을 때 홈만 44 로 남아 폰에서 첫 아이콘이
     58 vs 54 로 어긋났다(실측).
     ★ 이제 `portal/shell_tokens.css` 가 `--hs-* : var(--sh-*)` 별칭으로 잇는다 →
       **이 파일의 var(--hs-*) 사용처는 한 곳도 안 고쳤다**(변경 면적 최소).
     ⚠ 아래 --hs-col-pad·--hs-block·--hs-row-m 은 홈 **고유** 값이라 여기 남는다
       (셸 기하가 아니라 홈 화면의 단 배치 값이다). */
  /* 폰 전용 (2026-08-14) — 오른쪽 칸의 좌우 여백·블록 간격·터치 행 높이.
     ⚠ --hs-col-pad 는 **모든 블록이 공유하는 좌측 기준선**이다. 블록마다 다른 값을 주면
       기준선이 다시 갈라져 이번 개편의 목적 자체가 무너진다. */
  --hs-col-pad:16px;
  --hs-block:20px;
  --hs-row-m:48px;     /* 44 최소 + 여유 4 */
}
/* 모듈 띠 — ☰ 가 없어졌으므로 위 여백이 52px 일 이유도 없다(그건 ☰ 자리였다). */
/* 메뉴 묶음을 로고 아래로 얼마나 내릴지 — **한 값으로 묶어 둔다**.
   ⛔ 2026-08-18 오전에 9vh(1440에서 81px)를 줬더니 "너무 내려갔다"는 지적을 받았다.
     그 전에는 아이콘만 내려 이름과 81px 어긋나기도 했다. 두 사고 모두 **값이 두 군데
     흩어져 있었기 때문**이다 → 토큰 하나로 모으고 아이콘·이름이 같이 읽게 한다.
   현재 0 = 로고 바로 아래(원래 자리). 다시 내리고 싶으면 이 한 줄만 고친다. */
:root[data-shell="v2"]{--hs-menu-drop:0px}
:root[data-shell="v2"] body.hubon .hstrip{
  width:var(--hs-stripw);flex:none;display:flex;flex-direction:column;align-items:center;
  padding:var(--hs-pad) 0;gap:var(--hs-gap);overflow-y:auto;scrollbar-width:none}
:root[data-shell="v2"] body.hubon .hstrip::-webkit-scrollbar{display:none}
/* ★ 아이콘 묶음을 **아래로 내린다** (2026-08-18 사용자 지시: "왼쪽 사이드바 메뉴도
   그렇고" — 문의 줄을 오른쪽 아래로 내리라는 말에 이어진 것이다).
   ⛔ 종전엔 로고 바로 아래(=화면 맨 위)에서 시작해 아래쪽 세로 여백이 통째로 비었다.
   ★ 로고는 **그대로 맨 위에 둔다** — 레일 머리의 워드마크와 세로 중심이 맞아야 하고
     (사용자가 이미 한 번 지적한 자리다), 로고를 같이 내리면 그 정렬이 깨진다.
     그래서 로고와 첫 아이콘 **사이만** 벌린다.
   ⚠ 화면 높이에 비례하는 값(vh)을 쓴다 — 큰 화면에서는 넉넉히 내려오고 낮은 화면에서는
     저절로 덜 내려온다. 고정 px 로 박으면 노트북에서 아이콘이 톱니까지 밀린다.
   ⚠ 아래쪽 빈 칸(.hstrip-sp)이 남은 높이를 먹으므로 톱니·접기는 그대로 맨 아래다. */
/* ⚠⚠ **아이콘과 이름은 같이 움직인다.** 2026-08-18 에 아이콘만 9vh 내렸다가
     펼친 상태에서 **아이콘 n번째와 이름 n번째의 y 가 81px 어긋났다**(사용자 지적:
     "왼쪽 이모티콘 메뉴아이콘 위치가 어긋나있어"). 이 짝맞춤은 이 셸의 불변식이고
     (아래 `.hrail .hubcards [data-menu]` 의 행 높이 주석이 같은 이야기다),
     한쪽만 내리면 그 자리에서 바로 깨진다.
   → 띠(아이콘)와 레일(이름)에 **같은 값**을 준다. 값을 바꿀 땐 반드시 두 줄을 함께.
   ⚠ 화면 높이에 비례하는 값이라 낮은 화면에서는 저절로 덜 내려온다. */
:root[data-shell="v2"] body.hubon .hstrip-logo + button{margin-top:var(--hs-menu-drop)}
:root[data-shell="v2"] body.hubon .hrail .hubcards{margin-top:var(--hs-menu-drop)}
:root[data-shell="v2"] body.hubon .hstrip-sp{flex:1;min-height:8px}
/* ★ 로고 = 아이콘들 바로 위. 날개는 fill 도형이라 stroke 규칙과 분리한다.
   ★★ 워드마크(레일 머리)와 **세로 중심이 같아야 한다** (사용자 지적: "로고와 사명의 높이가
      맞지 않아보임") → 두 칸 모두 높이 --hs-row, 위 여백 --hs-pad 로 묶었다.
   색은 양 테마·양 배경 모두 CI #00A4EB 고정. */
:root[data-shell="v2"] body.hubon .hstrip-logo{
  width:var(--hs-row);height:var(--hs-row);flex:none;display:grid;place-items:center;
  /* ⚠⚠ flex `gap`(--hs-gap)이 때 자식 사이에 들어가므로 그만큼 미리 버린다 —
     안 버리면 때가 레일보다 2px 내려오며 모듈명과 어긋난다(실증값 때 82 vs 레일 80). */
  margin-bottom:calc(var(--hs-head-gap) - var(--hs-gap));color:#00A4EB}
:root[data-shell="v2"] body.hubon .hstrip-logo svg{
  /* ⚠ 날개가 자글자글하다는 지적(2026-08-17, 두 번째). 벡터인데도 거친 이유 둘:
     ① 22×20 은 원본 비율(91:80 = 1.1375)과 달라 세로로 눌린다 → 얇은 깃 사이 틈이
        서브픽셀로 뭉개진다. ② 사용자 화면이 112.5% 배율이라 20px 가 22.5 실제
        픽셀에 걸친다 — 정수로 안 떨어지면 가장자리가 흐려진다.
     → 비율을 원본대로 두고(27.3 : 24) 실제 픽셀에 맞게 키운다. 24 × 1.125 = 27 (정수). */
  width:27.3px;height:24px;fill:currentColor;stroke:none;
  shape-rendering:geometricPrecision}
:root[data-shell="v2"] body.hubon .hstrip button{
  width:var(--hs-row);height:var(--hs-row);flex:none;border:0;background:none;border-radius:0;
  display:grid;place-items:center;color:var(--hs-ink-lo);cursor:pointer}
:root[data-shell="v2"] body.hubon .hstrip button:hover{
  background:var(--hs-hover);color:var(--hs-ink)}
/* ⚠ 메뉴 아이콘 전용이다 — **날개는 제외**한다(2026-08-17).
   종전엔 `.hstrip svg` 가 날개까지 잡아 20×20 정사각·선긋기로 덮었다. 날개는
   면을 채우는 도형이고 비율이 91:80 이라, 정사각에 눌리면서 얇은 깃 사이가
   뭉개졌다 — "자글자글하다"의 정체가 이것이다. 위에서 `.hstrip-logo svg` 로
   따로 잡아 둔 값이 **뒤에 오는 이 규칙에 계속 덮이고 있었다.**
   ⛔ 여기에 `.hstrip-logo` 를 다시 포함시키지 말 것. */
:root[data-shell="v2"] body.hubon .hstrip button svg{
  width:20px;height:20px;fill:none;stroke:currentColor;
  stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
/* ⛔ `[aria-expanded="true"]` 활성 면 규칙을 **걷었다** — 홈의 띠는 이제 아코디언 선택기가
   아니라 **링크 묶음**이다(누르면 그 모듈로 이동한다). 홈에서는 "현재 모듈"이라는 것이 없다. */

/* 펼침·접힘 버튼 = 띠 맨 아래(레퍼런스와 같은 자리).
   ⚠ 아이콘은 **사이드 패널 픽토그램 하나이고 상태에 따라 모양이 바뀌지 않는다**
     (사용자 지적 2026-08-11: 레퍼런스 3종 모두 열고 닫는 아이콘이 같았다).
     상태는 면색 + aria-expanded 로만 알린다. 아래 폐기 주석 참조. */
:root[data-shell="v2"] body.hubon .hstrip-tg{
  margin-top:var(--hs-gap);color:var(--hs-ink-lo)}
/* ⛔ 방향 화살표 반전(scaleX(-1))을 **폐기**했다 (사용자 지적 2026-08-11: "열고 닫는 게 다
   똑같은 이모티콘이었음"). 아이콘은 사이드 패널 픽토그램 하나이고 모양이 바뀌지 않는다.
   ★ 상태는 aria-expanded + **펼쳐져 있을 때 면색**으로 알린다(앱 셸과 같은 문법). */
:root[data-shell="v2"] body.hubon:not(.rail-collapsed) .hstrip-tg{
  background:var(--hs-active);color:var(--hs-ink);
  box-shadow:inset 0 0 0 1px var(--hs-active-line)}

/* 레일 240px — **모듈명만** 들어간다(하위 메뉴는 그 모듈 안에서 본다) */
:root[data-shell="v2"] body.hubon .hrail{
  width:240px;flex:none;overflow:hidden;padding:var(--hs-pad) 8px 24px;
  display:flex;flex-direction:column}
/* 워드마크 칸 = 띠의 날개 칸과 **같은 높이·같은 위 여백** → 세로 중심이 일치한다 */
:root[data-shell="v2"] body.hubon .hbrand{
  display:flex;align-items:center;height:var(--hs-row);margin-bottom:var(--hs-head-gap);
  padding:0 8px;flex:none}
:root[data-shell="v2"] body.hubon .hbrand img{height:15px;width:auto;display:block}
/* ⛔ ☰(.hburger) 는 폐기됐다 — 혹시 남은 노드가 있어도 자리를 먹지 않게 못 박는다. */
:root[data-shell="v2"] body.hubon .hburger{display:none}
:root[data-shell="v2"] body.hubon .hrail .hubcards{
  display:block;max-width:none;gap:0;overflow-y:auto}

/* ⛔ 카드 모양(면·테두리·그림자·라운드·떠오르는 애니메이션)을 전부 끈다.
   ★★ 그리고 **모든 모듈명이 보인다** — 감추지 않는다(홈 = 모듈 목록).
   ⚠ 한 줄 높이 = 아이콘 한 줄과 같은 --hs-row + --hs-gap. 이 짝을 깨면 다시 어긋난다. */
:root[data-shell="v2"] body.hubon .hub .fcard{
  display:block;background:none;border:0;box-shadow:none;border-radius:0;
  min-height:0;padding:0;margin:0 0 var(--hs-gap);opacity:1;transform:none;animation:none}
/* ⛔ 하위 메뉴는 레일에 **나오지 않는다**(사용자 지시). DOM 은 남기고 화면에서만 뺀다 —
   노드를 지우면 권한 필터(login.js keep · pre-hub __VIOL_STASH)와 뒤엉킨다.
   ⚠ JS 가 `hidden` 까지 걸어 **탭 순서에서도** 뺀다. CSS 만으로 감추면 키보드로 들어간다. */
:root[data-shell="v2"] body.hubon .hub .fcard .hubsub{display:none}
:root[data-shell="v2"] body.hubon .hub .fcard:hover{box-shadow:none;border-color:transparent}
/* ⛔ 카드 제목 앞 3px 세로 막대 — 액센트 막대 전폐(앱 셸과 같은 규칙) */
:root[data-shell="v2"] body.hubon .hub .fcard h4::before{content:none;display:none}
/* 모듈명 = **진입 링크**. 글자 크기는 앱 레일(app.css .nav = 14px)과 **같은 값**이다
   (사용자 지적: "상위 메뉴와 하위 메뉴간의 폰트 크기가 다름" → 한 값으로 통일).
   ⚠ 종전 12px + 자간 .08em 이었다 — 하위(13px)보다 작아 위계가 거꾸로였다. */
:root[data-shell="v2"] body.hubon .hub .fcard h4{
  display:block;font-size:14px;font-weight:600;letter-spacing:0;margin:0}
:root[data-shell="v2"] body.hubon .hub .fcard h4 a{
  display:flex;align-items:center;height:var(--hs-row);padding:0 12px;position:static;
  font-size:14px;color:var(--hs-ink);text-decoration:none}
:root[data-shell="v2"] body.hubon .hub .fcard h4 a:hover{
  background:var(--hs-hover);color:var(--hs-ink)}
/* ⛔ 꺾쇠(.grpbtn)·머리 래퍼(.grphead) 규칙은 **폐기**했다 (사용자 지시 2026-08-11:
   "드롭다운 표시를 그냥 없애고 메뉴명만 나오고 메뉴를 눌러서 들어갈 수 있게").
   JS 도 그 노드를 만들지 않고, 남아 있으면 DOM 에서 제거한다. */

/* 아이콘 툴팁 — 접힘 상태에서 이름을 알리는 통로다(펼쳐지면 뜨지 않는다).
   ⚠ `title` 을 쓰지 않는 이유는 지연 800ms · 스타일 불가 · 키보드 포커스에서 안 뜸. */
:root[data-shell="v2"] .htip{
  position:fixed;z-index:10;pointer-events:none;white-space:nowrap;
  padding:5px 9px;font-size:12px;line-height:1.3;
  background:#FFFFFF;color:#16181D;box-shadow:0 6px 18px rgba(0,0,0,.4)}
/* ⚠ 카드 상단 띠를 넓히던 `::after` 확장 클릭영역은 목록에서 **다음 항목을 덮는다** → 끈다. */
:root[data-shell="v2"] body.hubon .hub .fcard h4 a::after{content:none}
:root[data-shell="v2"] body.hubon .hub .hubsub{display:block;margin:0;border:0;padding:0}
:root[data-shell="v2"] body.hubon .hub .hubsub a,
:root[data-shell="v2"] body.hubon .hub .hubsub span{
  display:flex;align-items:center;height:34px;padding:0 12px;
  font-size:13px;color:var(--hs-ink);text-decoration:none;border:0}
:root[data-shell="v2"] body.hubon .hub .hubsub a:hover{background:var(--hs-hover)}
:root[data-shell="v2"] body.hubon .hub .hubsub span{color:var(--hs-ink-lo)}
:root[data-shell="v2"] body.hubon .hub .hubsub em{
  font-style:normal;margin-left:auto;font-size:11px;color:var(--hs-ink-lo)}
:root[data-shell="v2"] body.hubon .hub .hubempty{
  display:block;padding:0 12px;font-size:13px;color:var(--hs-ink-lo);text-align:left}
/* ⚠⚠ **짝 규칙을 반드시 함께 둔다.** author `display:block` 은 UA 의 `[hidden]{display:none}`
   을 이긴다 → 이 규칙만 두면 권한이 멀쩡한 사람에게도 "접근 권한이 아직 설정되지
   않았습니다"가 항상 떠 있다(실측으로 그 상태를 재현했다: 메뉴 5개 + 안내문 동시 표시).
   같은 함정이 app.css `.mb-burger[hidden]` 주석에도 적혀 있다 — 두 번째로 밟았다. */
:root[data-shell="v2"] body.hubon .hub .hubempty[hidden]{display:none}

/* ── 본문 = 인사 + 유틸리티. 영상 위이므로 **밝은 글자**로 뒤집는다 ──────────
   ⚠ 이 글자들은 유리 뒤가 아니라 **영상 위**에 바로 얹힌다 → 로그인 화면이 쓰는 것과
     같은 처리(밝은 글자 + 그늘 층 #bgshade)에 기댄다. 로그인 화면에서 이미 성립하는
     조합이다. 유리면 글자(7.00:1)처럼 최악 프레임까지 계산으로 보장되지는 않는다. */
:root[data-shell="v2"] body.hubon .hubwrap{
  margin:0;max-width:none;padding:64px 40px 56px 344px;color:var(--hs-ink)}
/* 날씨도 같은 판(영상 위)이다 — 밝은 글자로 뒤집지 않으면 어두운 배경에서 안 읽힌다. */
:root[data-shell="v2"] body.hubon .hubwx{color:var(--hs-ink-lo)}
:root[data-shell="v2"] body.hubon .hubutil{border-top-color:rgba(255,255,255,.18)}
:root[data-shell="v2"] body.hubon .hubutil > a,
:root[data-shell="v2"] body.hubon .hubfoot{color:var(--hs-ink-lo);text-shadow:0 1px 3px rgba(0,0,0,.55)}
:root[data-shell="v2"] body.hubon .hubfoot a{color:var(--hs-ink);border-bottom-color:rgba(255,255,255,.4)}
/* ⛔ '내 업무' 라벨은 카드 묶음의 제목이었다 — 카드가 없으므로 걷는다. */
:root[data-shell="v2"] body.hubon .hubsec{display:none}
/* ⛔ 플라이아웃(.hfly) CSS 를 **삭제**했다 (사용자 지시 2026-08-11:
   "이모티콘에 마우스 오버 시 해당 메뉴의 하위 메뉴는 나올 필요 없음").
   JS(index.html) 쪽 flyOpen/flyBind 도 함께 폐기했다 — 한쪽만 지우면 죽은 규칙이 남는다.
   hover 의 뜻은 이제 "레일이 잠깐 펼쳐진다"(위 .rail-peek) 하나뿐이다. */

/* ── 좁은 폭 ─────────────────────────────────────────────────────────────
   ⛔ display:none 으로 셸을 없애지 않는다(상시 지침). 고정 사이드바를 풀어 위로 올린다. */
@media (max-width:900px){
  /* ⚠ 상단 줄(.corner — 바로가기·나)이 여기서는 **두 줄로 접힌다**(login.css 폰 블록).
     셸을 0 에서 시작하면 그 위에 겹쳐 올라가 바로가기 링크를 덮는다(실측으로 확인:
     "홈페이지·더존" 위에 아이콘 줄이 포개졌다) → 그만큼 아래에서 시작한다. */
  /* ★★ 2026-08-14 — **메뉴를 다시 세로로 세운다** (사용자 지시 원문:
     "모바일에선 로그인 후 메뉴가 상단에 가로 형태로 있던데 이거 세로로 바꾸고").
     ⛔ 무엇이 문제였나 (390×844 실측):
        · `.hstrip{flex-direction:row;overflow-x:auto}` → 모듈 아이콘이 **가로 줄**(389×48,
          내용 412px 이라 옆으로 밀린다)
        · `.hrail{flex-basis:100%}` + `.rail-collapsed .hrail{height:0}` → 모듈명 세로 목록이
          **높이 0 으로 통째 클리핑**(실적 대시보드·MES·CRM·데이터·인허가 5줄 전부 안 보임)
        · 접힘이 기본(index.html applyCollapse)이라 폰에서는 **늘 그 상태로 들어온다**
        → 결과적으로 폰의 홈 메뉴가 "가로 아이콘 줄 하나"였다. 사용자가 본 것이 이것이다.
     ★ 처방 = 셸을 화면 위로 떼어 놓되(position:static) **안쪽 배치는 데스크톱 그대로** —
       띠(세로 아이콘 칸) | 레일(세로 모듈명 칸). 새 컴포넌트를 만들지 않는다.
     ⚠ **감추는 것이 없다.** 접힘/펼침 토글(.hstrip-tg)도 뜻이 그대로다:
       접힘 = 세로 아이콘 칸 · 펼침 = 아이콘 + 이름. **두 상태 다 세로**다.
     ⚠ 폰에서 처음 들어올 때 이름까지 보이도록 **기본값만** 펼침으로 바꿨다
       (index.html applyCollapse — 저장된 선택이 있으면 그 값이 이긴다).
     ⚠ 되돌리려면 이 절의 `.hshell`·`.hrail` 세 규칙을 종전 값으로 되돌린다:
       `.hshell{flex-wrap:wrap;align-items:flex-start}` ·
       `.hstrip{width:auto;flex:1;flex-direction:row;justify-content:flex-start;
                padding:0 10px;gap:4px;overflow-x:auto}` ·
       `.hstrip-sp{flex:none;min-height:0;width:8px}` ·
       `.hrail{flex-basis:100%;width:auto;padding:6px 8px 0}` ·
       `.rail-collapsed .hrail{width:auto;height:0;padding:0}` */
  /* ★★★ 2026-08-14 2차 — **한 칸으로 모은다** (사용자 반려: "배치가 너무 이상해.
     메뉴는 오른쪽에 꽉차야하고, 며칠 유준현님 이런 건 오른쪽에 두고, 문의 내용 등도
     오른쪽에… 오른쪽에 홈페이지, 더존 등을 넣어야 할 거 같은데?" · 디자이너 자문 반영)

     ⛔ 진짜 원인은 "메뉴가 왼쪽에 있어서"가 아니라 **세로 기준선이 3개**였다는 것이다:
        ① 화면 오른쪽 끝(프로필 칩)  ② 화면 맨 왼쪽(바로가기·날짜·관리자·문의)
        ③ 셸 안쪽 x=72(모듈명)
       기준선이 셋이면 여백을 어떻게 만져도 정돈돼 보이지 않는다(정렬 원칙).
       게다가 셸(높이=내용)과 본문(.hubwrap, 셸 **밖**)이 서로 다른 상자라
       셸 아래 빈 공간과 화면 하단 검정이 **동시에** 생겼다.

     ★ 처방 = `.hub` 를 2열 그리드로 만들고 `.hshell` 을 `display:contents` 로 **풀어서**
       띠 · 레일 · 본문 셋을 같은 그리드에 태운다. 그러면
         · 오른쪽 칸의 좌측 기준선이 **모든 블록에 강제**된다(기준선 2개로 줄어든다)
         · 본문이 셸 안으로 들어와 **셸 높이 = 화면 높이**가 된다(하단 검정 소멸)
       ⚠ **DOM 은 하나도 안 옮긴다** — index.html 의 build() 무수정이다.

     ⚠⚠ `display:contents` 는 상자를 없앤다 → `.hshell` 의 유리면(배경·blur)이 **사라진다.**
       그래서 유리를 세 아이템에 각각 다시 깐다(아래). 이 안의 유일한 실질 비용이다. */
  :root[data-shell="v2"] body.hubon .hub{
    display:grid;
    grid-template-columns:var(--hs-stripw) 1fr;
    grid-template-rows:auto 1fr;
    /* ★ 2026-08-14 3차 — 유리를 **최상단까지** 덮는다 (사용자 지시:
       "최상단까지 검은색 필터가 씌워져야 하고").
       종전 96px 은 고정 상단 줄(.corner)이 차지하던 자리를 비워 둔 것이었는데,
       그 띠만 영상이 그대로 비쳐 판이 중간에서 시작하는 것처럼 보였다.
       → 0 으로 내리고, 상단 줄은 아래에서 **판 위에** 얹는다(자리를 비우지 않는다).
       ⚠ 노치·상태바는 safe-area 로만 피한다 — 고정 px 로 비우면 기기마다 어긋난다. */
    padding-top:0}
  /* 위 여백은 띠·레일이 각자 안전영역만큼만 갖는다(유리면은 0 에서 시작) */
  :root[data-shell="v2"] body.hubon .hstrip,
  :root[data-shell="v2"] body.hubon .hrail,
  :root[data-shell="v2"] body.hubon.rail-collapsed .hrail{
    padding-top:calc(var(--hs-pad) + env(safe-area-inset-top,0px))}
  :root[data-shell="v2"] body.hubon .hshell{display:contents}
  :root[data-shell="v2"] body.hubon .hburger{display:none}

  /* 띠 = 1열, 두 줄에 걸쳐 세로로 이어진다(본문 옆에서도 아이콘에 닿는다) */
  :root[data-shell="v2"] body.hubon .hstrip{
    grid-column:1;grid-row:1/-1;width:auto;height:auto;
    background:var(--hs-glass);border-right:1px solid var(--hs-line)}
  /* 레일 = 2열 첫 줄. 폭은 남는 것 전부(데스크톱 고정 240px 을 쓰면 오른쪽이 빈다) */
  :root[data-shell="v2"] body.hubon .hrail{
    grid-column:2;grid-row:1;width:auto;height:auto;min-width:0;
    padding:var(--hs-pad) var(--hs-col-pad) 0;background:var(--hs-glass)}
  /* 본문(인사·관리자·문의) = 2열 둘째 줄. **레일과 같은 좌측 기준선**을 쓴다.
     세로 flex + .hubutil{margin-top:auto} 로 유틸을 바닥에 붙인다(남는 높이를 여기서 흡수). */
  :root[data-shell="v2"] body.hubon .hubwrap,
  :root[data-shell="v2"] body.hubon.rail-collapsed .hubwrap{
    grid-column:2;grid-row:2;max-width:none;margin:0;
    /* ⚠ 아래 여백은 **바로가기 줄(고정, 오른쪽 하단) 높이만큼** 비운다.
       안 비우면 문의 줄과 겹친다(실측: 문의 우측 340 vs 바로가기 좌측 206 — 겹침).
       바로가기 34px + 아래 12px + 사이 12px = 58px. 바로가기 치수를 바꾸면 여기도 바꾼다. */
    padding:var(--hs-block) var(--hs-col-pad)
            calc(58px + env(safe-area-inset-bottom,0px));
    display:flex;flex-direction:column;background:var(--hs-glass)}
  :root[data-shell="v2"] body.hubon .hubutil{margin-top:auto}

  /* 상단 줄도 오른쪽 칸 위에 얹는다 — 바로가기(홈페이지·더존…)가 띠 위가 아니라
     **모듈명과 같은 기준선**에서 시작한다. 프로필 칩은 그대로 오른쪽 끝. */
  /* ★ 2026-08-14 3차 — 상단 줄(.corner)을 **줄로 쓰지 않는다.**
     사용자 지시 둘을 동시에 만족시켜야 한다:
       ① "유준현 · 경영기획팀"을 **사명과 같은 줄, 사명 오른편**에
       ② 홈페이지·더존·유튜브·인스타는 **오른쪽 하단**에, 서로 간격은 더 좁게
     두 자식이 서로 반대편 끝으로 가므로 한 줄로 묶어 둘 이유가 없어졌다.
     → `.corner` 는 자리를 안 먹는 껍데기로 두고 **자식 둘을 각각** 고정 배치한다.
     ⚠ DOM 은 그대로다(index.html 무수정) — `.corner` 는 로그인 화면과 공용이라
       마크업을 옮기면 그쪽이 깨진다. 그래서 CSS 로만 떼어 놓는다.
     ⚠ `.corner` 자체의 배경·padding 을 반드시 지운다. 안 지우면 유리 위에 또 한 겹이
       얹혀 최상단 색이 달라 보인다(①의 목적이 무너진다). */
  :root[data-shell="v2"] body.hubon .corner{
    left:0;right:0;top:0;bottom:0;height:auto;
    display:block;padding:0;background:transparent}

  /* ① 프로필 = 사명(.hbrand)과 **같은 줄**의 오른쪽 끝.
     ⚠ top 은 레일의 위 여백과 **같은 식**을 쓴다 — 한쪽만 고치면 줄이 어긋난다.
     ⚠ 높이를 --hs-row 로 맞춰 사명 칸과 세로 중심이 일치한다(.hbrand 와 같은 값). */
  :root[data-shell="v2"] body.hubon .corner .who{
    position:fixed;right:var(--hs-col-pad);
    top:calc(var(--hs-pad) + env(safe-area-inset-top,0px));
    height:var(--hs-row);display:flex;align-items:center;margin:0}

  /* ② 바로가기(홈페이지·더존·유튜브·인스타) = **폰에서는 내린다**
     (2026-08-14 4차 사용자 지시: "모바일에선 그냥 홈페이지, 더존, 유튜브, 인스타 링크
      없어도 됨").
     ⛔ 상시 지침("폰 대응은 접기이지 삭제가 아니다")의 **명시적 예외**다 —
       사용자가 직접 "없어도 된다"고 판단한 항목이라 대체 입구를 두지 않는다.
       ⚠ 다른 항목에 이 예외를 확대 적용하지 말 것. 데스크톱에는 그대로 남는다.
     ⚠ 그 자리(오른쪽 하단)는 아래 인사줄이 대신 쓴다. */
  :root[data-shell="v2"] body.hubon .corner .quick{display:none}

  /* ③ 날짜·인사 = 오른쪽 **하단** (2026-08-14 4차 사용자 지시:
       "2026년 8월 14일 유준현님 이 문구는 오른쪽 하단에 배치").
     ⚠ 바로가기가 비운 자리를 그대로 받는다 — 같은 좌표 식을 쓴다(둘이 겹칠 일은 없다).
     ⚠ 오른쪽 정렬이므로 줄바꿈도 오른쪽 기준이다(flex-end). */
  /* ⛔ 인사줄 고정 배치(08-14 4차: "오른쪽 하단에 배치")는 **삭제됐다** — 2026-08-18
     사용자가 그 문구 자체를 없앴다("아래 날짜 + 000님 문구는 이제 없어도 돼").
     그 자리는 아래 문의 줄이 받는다. */

  /* ④ 관리자·문의 위의 **가로선 삭제** (2026-08-14 4차 사용자 지시:
     "메뉴를 펼쳤을 때 가로선이 나오는 거 그냥 삭제").
     ⚠ 선이 하던 '구획' 역할은 위 여백(margin-top:auto)이 이미 하고 있다 — **단, 그 전제는
       내용이 화면에 다 들어갈 때만 성립한다.** 위젯 판이 생기면서(2026-08-17) `.wb` 가
       폰에서 **자기 스스로 스크롤하는 상자**가 됐다(`@media(max-width:900px){.wb{
       overflow-y:auto;min-height:0}}`, board.css). 위젯이 판 높이보다 많아지면
       `margin-top:auto` 가 흡수할 '남는 공간'이 0이 되어 버리고, `padding-top:0`
       까지 곁들여 있으면 **판을 끝까지 내렸을 때 마지막 타일이 문의 줄에 그대로
       맞닿는다**(2026-08-26 사용자 신고 — 실측: 하네스 재현 시 gap = 0px, 데스크톱은
       35px). 가로선을 없앤 결정은 그대로 두고, **바닥 안전 여백만 되살린다.** */
  :root[data-shell="v2"] body.hubon .hubutil{border-top:0;padding-top:16px}

  /* 「관리자 · 계정 · 권한」 줄을 폰에서 숨기던 규칙은 **필요 없어졌다** —
     2026-08-17 에 마크업에서 통째로 지웠다(index.html 참조). 데스크톱·폰 모두 없다.
     ⛔ 되살리지 말 것. 왼쪽 아이콘 띠 맨 아래 톱니바퀴가 관리자 입구다. */

  /* 모듈 행 = 칸 전폭.
     ⛔ 여기 있던 오른쪽 끝 화살표(`::after{content}`)를 **걷었다** (2026-08-14 4차, 사용자
       지적: "메뉴를 펼치면 > 표시가 있어서 이모티콘 메뉴와 메뉴명의 가로 라인이 일치하지
       않음"). 화살표가 행 높이를 키워 왼쪽 아이콘 줄(44px)과 y 가 어긋났다.
       affordance 보다 **아이콘↔이름 정렬**이 우선이라는 사용자 결정이다.
     ★ 그래서 행 높이를 아이콘 줄과 **같은 값**(--hs-row)으로 묶는다 — 이름 줄 n번째와
       아이콘 n번째의 y 가 같아야 한다. 종전 --hs-row-m(48)은 4px 씩 밀리는 원인이었다.
     ⚠ 화살표를 되살리려면 행 높이를 유지한 채(안쪽에서) 넣을 것. */
  :root[data-shell="v2"] body.hubon .hrail .hubcards [data-menu]{
    min-height:var(--hs-row);height:var(--hs-row)}

  /* ══ 접힘 — 폰에서는 **뜻을 좁힌다** ═══════════════════════════════════════
     ⛔ 데스크톱 규칙(`.rail-collapsed .hrail{width:0}`)을 그대로 두면 오른쪽 칸이 통째로
       사라져 날짜·바로가기·관리자·문의까지 없어진다. 그건 **삭제**이고 반려 사유다.
     ★ 폰에서 접힘 = "모듈 **이름 5줄만** 접힌다". 나머지는 다 남는다.
       근거: 폰에서 오른쪽 칸을 0 으로 만들면 남는 게 64px 띠뿐이라 화면의 88%가 빈다 —
       접기로 얻는 것이 없다. 폰에서 접기의 실익은 "이름 5줄만큼 위로 당기기"뿐이다.
     ⚠ 이름 칸은 `display:none` 이다(visibility 가 아니라). 탭 순서에서도 빠져야 한다.
       토글을 다시 누르면 즉시 돌아온다 — 되돌릴 수 있으므로 삭제가 아니다. */
  :root[data-shell="v2"] body.hubon.rail-collapsed .hrail{
    width:auto;height:auto;padding:var(--hs-pad) var(--hs-col-pad) 0}
  :root[data-shell="v2"] body.hubon.rail-collapsed .hrail .hubcards{display:none}

  /* ══════════════════════════════════════════════════════════════════════════
     ★★★ 2026-08-18 — 위젯 판이 들어온 뒤 **셸 첫 줄이 0 으로 짜부라졌다**
     ──────────────────────────────────────────────────────────────────────────
     사용자 지적(폰 실화면): "사이드바 메뉴 열리지도 않고, 메뉴바가 영역 침범하여 짤리고".

     ⛔ 무엇이 일어났나 (390×844 실측, 로그인 상태 재현):
          .hub  grid-template-rows 선언 = `auto 1fr`   ·  **계산값 = `0px 844px`**
          .hrail  rect = [64, 0, 326, **0**]  ·  overflow:hidden  ·  scrollHeight 54
          .hubcards(모듈 5줄) rect = [80, 54, 294, **0**]  ← 통째로 클리핑
          .hubwrap rect = [64, **0**, 326, 844]  ← 둘째 줄이 화면 맨 위에서 시작
        → ① 레일이 높이 0 이라 **모듈명이 한 줄도 안 보인다**(펼침 토글을 눌러도 화면이
             그대로다 = "열리지 않는다"의 정체. 띠 아이콘 클릭은 원래대로 이동한다 —
             실측: dashboard 아이콘 클릭 → /dash/#summary 로 이동됨)
          ② 본문(위젯 판)이 y=0 에서 시작해 **고정 배치된 프로필 칩(.who, y 8~52)** 과
             같은 자리를 쓴다(실측 겹침: #whoTeam[287,23,345,37] × #wbEdit[311,34,374,56]
             = 34×3px). 사용자가 본 "유준현 · 경영기획팀 과 02:00 기준이 포개짐"이 이것이다.

     ★ 원인은 **한 줄**이다 — `.hubwrap` 의 `min-height` 가 폰에서 100vh 로 남아 있었다.
       board.css 는 그것을 폰에서 풀려고 `body.hubboard .hubwrap{min-height:0}`(0,2,1)을
       ≤900 블록에 넣었지만, 같은 파일 위쪽의
       `:root[data-shell="v2"] body.hubon.hubboard .hubwrap{min-height:100vh}`(0,5,1)이
       **특이도로 이긴다**(실측 계산값 min-height:844px). 미디어쿼리는 특이도를 올려주지
       않는다 → 폰 해제가 조용히 무효였다.
       둘째 줄(1fr)의 자동 최소치가 100vh 로 못박히니 첫 줄(auto)에 남는 높이가 0 이 된다.

     ⚠ 그래서 여기서 **`#hub`(id)로 특이도를 올려** 되돌린다. board.css 는 자동 생성물이라
       손대지 않는다(규격본 대조 게이트가 있다) → 고칠 자리는 홈 CSS 쪽이 맞다.
     ⚠ `min-height:0` 만으로 충분하다 — 줄 높이는 `.hrail` 의 내용이 정하고(접힘 54 /
       펼침 274), 둘째 줄이 나머지를 1fr 로 받는다. `.hub` 자체가 화면 크기 고정이라
       문서가 늘어나지도 않는다.
     ⚠ 데스크톱은 이 블록 밖이라 무변경이다(1920·1440 좌표 diff 0 으로 확인). */
  :root[data-shell="v2"] body.hubon.hubboard #hub .hubwrap{
    min-height:0;
    /* ⛔ 종전 아래 여백 58px 은 **오른쪽 하단 바로가기 줄**(홈페이지·더존…) 자리였다.
       그 줄은 2026-08-14 4차 지시로 폰에서 내려갔고(display:none), 인사줄도 아래에서
       흐름으로 되돌리므로 그 예약 공간은 남아 있을 이유가 없다 → 보통 여백으로. */
    padding-bottom:calc(12px + env(safe-area-inset-bottom,0px))}

  /* ★ 인사줄(날짜 · ○○ 님)을 **흐름으로 되돌린다** — 판이 선 화면에서만.
     ⛔ 무엇이 문제였나: 2026-08-14 4차 지시("오른쪽 하단에 배치")를 `position:fixed` 로
       구현했는데, 2026-08-17 에 그 자리 위로 **위젯 판 + 문의 줄**이 들어왔다.
       고정 요소는 자리를 먹지 않으므로 board.css 가 정한 순서(인사 → 판 → 문의)가
       무효가 되고, 화면 바닥에서 문의 줄·판 꼬리·인사줄이 서로 포개져 보였다
       (실측: .hubutil 772~786 / .hubhello 814~832 / .wb 꼬리 타일이 772 에서 잘림 —
        사용자가 "한 줄에 뒤엉켜"라고 한 그 자리다).
     ★ 자리는 그대로 **오른쪽 하단**이다 — 다만 고정이 아니라 문의 줄 **바로 아래 줄**로
       내려온다(order:5). 두 줄이 위아래로 나뉘므로 겹칠 수가 없다.
     ⚠ 판이 서지 않은 화면(hubboard 없음)에서는 종전 고정 배치가 그대로 유지된다. */
  /* (인사줄을 흐름으로 되돌리던 규칙도 함께 삭제 — 요소가 없다) */

  /* ★ 프로필 칩의 세로 중심을 **머리 칸(--sh-head)** 중심에 맞춘다.
     ⛔ 종전 `top:--hs-pad(8)` 은 레일의 위 여백을 따라 적은 값이었는데, shell_tokens.css
       ≤1024 블록이 `.hrail{padding-top:var(--safe-t)}`(=0) 로 **같은 특이도·뒤 순서**로
       덮어써서 근거가 사라졌다 → 칩 중심 30 vs 날개·워드마크 중심 24 로 6px 어긋났다(실측).
     ★ 식으로 못박는다: top = (머리칸 48 − 칩 높이 44)/2 = 2. 토큰이 바뀌면 함께 움직인다. */
  :root[data-shell="v2"] body.hubon .corner .who{
    top:calc((var(--sh-head) - var(--hs-row)) / 2 + env(safe-area-inset-top,0px))}
}

/* ══════════════════════════════════════════════════════════════════════════
   번쩍임 대책 — 문서 전환 뷰 트랜지션 (2026-08-11)
   ──────────────────────────────────────────────────────────────────────────
   사용자 지적: "메뉴마다 이동할 때 화면이 번쩍이면서 새로고침하는 느낌"
   ⚠ 크로스도큐먼트 전환은 **떠나는 문서와 도착 문서가 모두 옵트인**해야 성립한다.
     그래서 같은 선언이 sop_v5/app.css 끝에도 있다 — 한쪽만 넣으면 조용히 무효다.
   ⚠ at-rule 이라 `data-shell` 로 스코프할 수 없다. 되돌리려면 이 블록을 지운다.
   ⚠ 미지원 브라우저는 지금과 완전히 같다(점진적 향상).
   ══════════════════════════════════════════════════════════════════════════ */
/* ⚠ 2026-09-03 — 모션 축소에서는 지속시간을 줄이는 게 아니라 **전환을 끈다.**
   종전 `.001s` 는 ① 모션을 끄겠다는 설정을 무시하고 유사 전환을 돌렸고
   ② headless 에서 스크립트 개시 이동 뒤 문서가 영구 render-block 되는 원인이었다
   (rAF 0회 · 스크린샷 타임아웃 → 검증 하네스가 눈을 잃는다).
   짝이 되는 선언은 sop/app.css 끝에 있다 — **두 파일을 항상 같이 고친다**(한쪽만
   고치면 그 방향 전환에서 조용히 어긋난다). 상세는 릴리스 런북 「함정 7」. */
@view-transition{navigation:auto}
@media (prefers-reduced-motion:reduce){@view-transition{navigation:none}}
