/* ==========================================================================
   가나전산 웹주문시스템 — 레이아웃 & 컴포넌트
   구조: GEOWEB(order.geoweb.kr) 정보구조  /  스타일: Design.md
   ========================================================================== */

/* ==========================================================================
   1. RESET & BASE
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html {
  font-size: 62.5%;              /* 1rem = 10px 앵커 */
  -webkit-text-size-adjust: 100%;

  /* 스크롤바 자리를 항상 비워둔다.
     안 그러면 세로 스크롤이 생기는 화면만 뷰포트가 ~15px 좁아져서
     메뉴를 옮길 때마다 전체 폭이 어긋난다. */
  scrollbar-gutter: stable;
  overflow-y: scroll;            /* scrollbar-gutter 미지원 브라우저 대비 */
}

body {
  margin: 0;
  /* 세로 flex — 푸터를 항상 화면 맨 아래에 붙이고,
     남는 세로 공간은 전부 .app(목록)이 가져간다 */
  /* ★★ min-height 만으로는 부족하다 (2026-07-30). min-height 는 '이만큼은
     된다' 일 뿐 상한이 아니라, 목록이 길면 body 가 그만큼 늘어난다. 그러면
     .app 이 **줄어들 이유가 없어져** shrink 가 아무 일도 안 한다 —
     실제로 shrink 를 1 로 고치고도 문서가 그대로 98px 넘쳤다.
     높이를 확정해야 넘침이 생기고, 그래야 .app 이 줄어 푸터가 화면 안에 남는다.
     ※ dvh 는 주소창이 접히는 브라우저 대비. 못 읽으면 위의 vh 가 남는다. */
  min-height: 100vh;
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--canvas);      /* ★ 순백 아님 — 웜 크림이 시그니처 */
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--text-4);
  line-height: var(--leading);
  letter-spacing: var(--tracking);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, p, figure, ul, ol, dl, dd { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img { max-width: 100%; display: block; }
button, input, select, textarea { font: inherit; letter-spacing: inherit; color: inherit; }
button { cursor: pointer; }
a { color: inherit; text-decoration: none; }
table { border-collapse: collapse; width: 100%; }

/* 스크롤바 — 크림 캔버스에 맞춘 웜 톤
   overflow 는 x/y 둘 다. 표가 넓어지면 페이지를 밀어내는 대신
   패널 안에서 가로 스크롤되게 해야 화면별 전체 폭이 어긋나지 않는다. */
.scroll-y { overflow: auto; overscroll-behavior: contain; }
.scroll-y::-webkit-scrollbar { width: 8px; height: 8px; }
.scroll-y::-webkit-scrollbar-track { background: transparent; }
.scroll-y::-webkit-scrollbar-thumb {
  background: rgba(0, 0, 0, 0.16);
  border-radius: 4px;
}
.scroll-y::-webkit-scrollbar-thumb:hover { background: rgba(0, 0, 0, 0.28); }

/* .panel 등이 display:flex 를 갖고 있어 hidden 속성을 덮어쓰므로 강제 */
[hidden] { display: none !important; }

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ==========================================================================
   2. APP SHELL
   ========================================================================== */

.app {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr) var(--banner-w);

  /* ★ 고정폭 중앙정렬 — max-width 가 아니라 width.
     해상도와 무관하게 항상 같은 폭이라 컬럼 폭이 흔들리지 않는다.
     좌우로 남는 공간은 캔버스 여백이 된다. */
  width: var(--shell-w);
  margin-inline: auto;
  /* 콘텐츠와 푸터 사이 여백.
     ★ 좌우는 --gutter(16)가 아니라 4px 이다 (2026-07-29 전역 승격).
        원래 3패널 전용 옵션(body[data-tight])이었는데, 화면마다 손으로 붙이다 보니
        조회 화면에 빠져서 **주문 화면 본문 1108 / 조회 화면 1072** 로 36px 이
        어긋났다. 메뉴를 오갈 때 좌우 여백이 출렁인다.
        옵션으로 두는 한 언젠가 또 빠진다 — 기본값으로 올려 빠질 자리를 없앴다.
        (같은 뿌리의 사고를 WIS_HREF 에서도 겪었다) */
  /* ★★ **사이드바와 본문 사이를 8px 로** (2026-08-12 요청 — "2mm 이상").
       2mm = 7.56px(96dpi) 이라 4px 로는 모자랐다.
     ★ **좌우 패딩에서 꺼내 왔다.** 셸 폭(1340)이 고정이라 gap 을 그냥 늘리면
       그만큼 본문이 줄고, 본문이 줄면 **제품목록 548 이 깨진다** — 그 값은
       grid 메타(avail 540)와 묶여 있어 두 곳을 같이 고쳐야 한다(함정 ⑰).
       패딩 4→2 로 좌우 4px 을 내주면 gap 4→8 을 흡수하고도 폭이 그대로다.
           2 + 220 + 8 + 1108 + 2 = 1340   (실측으로 확인)
     ⚠ 사이드바를 줄이는 길은 막혀 있다 — 내용폭 204px 에 「주문 통계」가
       202px 이라 **여유가 2px 뿐이다.** 4px 만 줄여도 글자가 잘린다.
     ⚠ 배너를 켤 때의 셸 폭 계산(아래 @media 1520)에도 이 gap 이 들어간다.
       거기 `--space-1` 을 같이 올리지 않으면 배너를 켠 순간 본문이 4px 줄어든다. */
  padding: var(--space-4) 2px var(--space-7);
  gap: var(--space-2);

  /* ★ 행 높이를 명시한다.
     auto 로 두면 행 높이 = max(사이드바, 본문, 배너) 가 되어
     사이드바 메뉴를 접었다 펴는 것만으로 페이지 전체 높이가 출렁인다.
     minmax(최소, 1fr) → 최소는 보장하고 남는 화면 공간은 목록이 흡수. */
  /* ★★ shrink 를 1 로 (2026-07-30). 전에는 `1 0 auto` 라 **줄어들 수가 없어서**
     목록이 길면 셸이 뷰포트보다 커지고 배송안내·푸터가 화면 밖으로 밀렸다.
     전체화면인데도 푸터가 잘린다는 지적이 이것이다 — 실측 1920×1080 기준
     문서 1049 / 뷰포트 951 로 **98px 넘쳤다.**
     ★ 그런데 그냥 줄이면 안 된다. 하한이 없으면 짧은 화면에서 목록이 두세 줄로
       뭉개진다 → `min-height: var(--page-min-h)` 가 바닥을 잡는다.
       그 아래로 좁아지면 그때는 페이지가 스크롤되는 편이 맞다.
     ★ 남는 높이는 여전히 목록이 먹는다(grow 1). 모자랄 때만 .scroll-y 안에서
       행 수가 줄어든다 — 페이지가 아니라 패널이 스크롤되는 원래 설계 그대로다. */
  flex: 1 1 auto;
  min-height: var(--page-min-h);
  grid-template-rows: minmax(var(--page-min-h), 1fr);
  align-items: stretch;
}

/* 배너가 필요 없는 화면 */
.app--nobanner { grid-template-columns: var(--sidebar-w) minmax(0, 1fr); }

/* ★★ 배너를 켜도 본문이 안 줄어든다 — 셸을 오른쪽으로 넓혀 붙인다 (2026-07-31)
   ---------------------------------------------------------------------------
   전에는 배너 컬럼(152 + gap 4)이 1340 **안에서** 자리를 가져갔다. 켜는 순간
   목록의 가변 컬럼(상품명)이 그만큼 좁아지고 검색바 가용 폭도 같이 줄어서,
   'CS 가 배너를 켜면 표가 좁아진다' 를 늘 계산에 넣고 살아야 했다.
   → 화면이 넉넉하면 **셸 자체를 배너 몫만큼 키운다.** 본문·사이드바는 그대로고
     배너만 오른쪽에 덧붙는다. 그리드 메타의 avail(배너 꺼짐 기준)이 켠 상태에서도
     그대로 맞는다.

   ★ --shell-w 를 body 에서 한 번 바꾼다. .app · .foot__inner · .delivery ·
     .policy 넷이 전부 이 토큰 하나를 보고 margin-inline:auto 로 가운데 놓이므로,
     넷이 **함께** 움직여야 왼쪽 끝선이 안 어긋난다 (사이드바 47 / 푸터 47).
     .app 에만 걸면 셸만 왼쪽으로 76px 밀리고 푸터는 제자리에 남는다.

   ⚠ **좁은 화면에서는 예전 그대로다.** 1340 은 1366×768 구형 사무용 모니터에서
     가로 스크롤이 안 생기는 최대치라, 무조건 넓히면 그 모니터가 깨진다.
     하한 1520 = 1340 + 152 + 8(gap) + 좌우 여백 10씩. 그 아래에서는 배너가 다시
     안으로 들어오므로 **검색바 872 기준(5절)은 계속 유효하다.** */
@media (min-width: 1520px) {
  body[data-banner-col="on"] {
    /* ⚠ 마지막 항은 **배너 앞의 gap 몫**이다. `.app` 의 gap 과 반드시 같아야
         한다 — 2026-08-12 에 gap 을 4→8 로 올리며 여기도 같이 올렸다.
         한쪽만 고치면 배너를 켠 순간 본문이 4px 줄어 제품목록 548 이 깨진다. */
    --shell-w: calc(var(--shell-base) + var(--banner-w) + var(--space-2));
  }
}

/*
   ---------------------------------------------------------------------------
   전에는 주문화면이 배너를 하드 거부했다(data-banner="off"). 자리가 없어서다.
   그런데 **주문이 메인 메뉴**라 거기에 안 나오면 배너를 켤 이유가 거의 없다.

   → 위 '셸을 넓혀 바깥에 붙이는' 장치를 그대로 쓰되, **안으로는 절대 안
     들어오게** 한다. 주문화면 3패널은 폭이 고정(548/280/264)이라 168px 을
     빼앗기면 그 자리에서 무너진다 — 조회 화면처럼 '가변 컬럼이 줄어드는'
     여유가 없다.

     "작은 창에서는 배너가 안 보이거나 짤려도 무관하다".
     ★ 본문은 어떤 폭에서도 지금과 똑같다. 배너를 켜든 끄든 3패널은 안 움직인다.

   ⚠ 넓은 화면용 규칙은 **따로 없다.** .app 의 기본이 이미 3컬럼이고 셸을
     넓히는 것은 위 data-banner-col 규칙이 한다. 여기에 3컬럼을 또 쓰면
     관리자가 배너를 껐을 때 붙는 .app--nobanner 를 덮어써서 빈 자리가 남는다. */
/* ★★ 아래 한계(1100.02)를 **반드시 같이 둔다.** 없으면 주문화면이 좁은 화면에서
     통째로 깨진다 — 18절이 1100 이하에서 `.app { grid-template-columns: 1fr }` 로
     1단을 만드는데, 여기 선택자는 `body[…] .app` 라 특정도가 (0,2,1) 이다.
     **순서와 무관하게 18절을 이긴다.** 그러면 `--sidebar-w` 가 100% 인 채로
     2컬럼이 남아 `100% + 0` 이 되고 **본문이 폭 0 이 된다.**
     실측 — 386px 에서 .main 이 0px, 내용이 전부 화면 밖으로 나갔다.
     ⚠ 특정도로 이기는 규칙을 미디어쿼리에 넣을 때는 **반대쪽 끝도 막아야 한다.**
       한쪽만 적으면 다른 분기와 겹치는 구간에서 조용히 이겨 버린다. */
@media (min-width: 1100.02px) and (max-width: 1519.98px) {
  body[data-banner="outside"] .app {
    grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  }
  body[data-banner="outside"] .banners { display: none; }
}

/* ★ data-tight 는 없어졌다 (2026-07-29). 위 .app 의 기본값이 되었다.
   ★ 패널 사이 gap(.workspace)은 8 로 둔다. 여기까지 줄이면 카드 셋이
     한 덩어리로 붙어 보여서, 벌어 온 폭보다 잃는 것이 크다.
   ※ 아직 남아 있는 <body data-tight> 는 아무 일도 하지 않는다. 무해하다. */

/* 내용이 길어 잘리면 안 되는 화면(입력 폼 등)은 행 높이 고정을 푼다 */
/* 내용 길이가 화면마다 다른 글 화면(공지 상세 · 회원정보)은 행 높이 고정을 푼다.
   ★ 여기서는 **줄이면 안 된다** — 줄이면 본문이 셸 밖으로 넘쳐 푸터와 겹친다.
     body 높이가 확정돼 있으므로 shrink 를 0 으로 두면 넘친 만큼 페이지가
     스크롤되고 푸터는 그 아래에 정상으로 놓인다. */
.app--auto { grid-template-rows: auto; flex-shrink: 0; }
.app--auto .sidebar { min-height: var(--page-min-h); }

.main {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
  min-height: 0;   /* 높이는 .app 의 고정 행이 결정한다 */
}

/* ==========================================================================
   3. SIDEBAR  (House Green — Design.md의 다크 밴드 역할)
   ========================================================================== */

.sidebar {
  /* align-self 기본값(stretch)로 행 높이를 그대로 채운다.
     메뉴가 길어져도 .nav 가 스크롤될 뿐 행 높이를 밀지 않는다. */
  background: var(--sidebar-bg);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: var(--space-3) var(--space-2) var(--space-2);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-height: 0;
  overflow: hidden;               /* 좁아져도 내용이 카드 밖으로 새지 않게 */
}

/* --- 로고 --- */
.sidebar__brand {
  display: block;
  text-align: center;
  padding: 0 var(--space-2) var(--space-1);
  color: var(--sidebar-fg);
}
.sidebar__brand-sub {
  display: block;                       /* inline이면 세 줄이 한 덩어리로 줄바꿈됨 */
  font-size: 1.0rem;
  color: var(--sidebar-fg-soft);
  letter-spacing: var(--tracking-loose);
  white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.sidebar__brand-name {
  display: block;
  font-size: var(--text-6);
  font-weight: 700;
  line-height: var(--leading-compact);
  letter-spacing: -0.02em;
  white-space: nowrap;
}
.sidebar__brand-tag {
  display: block;
  font-size: var(--text-1);
  color: var(--sidebar-accent);
  margin-top: var(--space-0);
  white-space: nowrap;
}

/* --- 프로필 카드 --- */
.profile {
  background: var(--sidebar-tile);
  border: 1px solid var(--sidebar-line);
  border-radius: var(--radius-card);
  padding: var(--space-2);
  position: relative;
  text-align: center;
  color: var(--sidebar-fg);
}
.profile__avatar {
  width: 4.4rem; height: 4.4rem;
  margin: 0 auto var(--space-1);
  border-radius: var(--radius-round);
  background: var(--green-light);
  color: var(--house-green);
  display: grid; place-items: center;
  font-size: 2rem;
}
.profile__name {
  font-size: var(--text-2);
  font-weight: 600;
  line-height: 1.35;
}
.profile__meta {
  font-size: var(--text-1);
  color: var(--sidebar-fg-soft);
  margin-top: var(--space-0);
}
/* 카드 우측 위 아이콘 묶음 — 회원정보 · 로그아웃.
   ★ 로그아웃이 여기로 올라왔다 (2026-07-29). 사이드바 맨 아래 '버튼1 버튼2'
     옆에 붙어 있어 뜬금없었다 — '누구로 로그인해 있나' 를 말하는 카드에서
     '그만두기' 를 누르는 편이 자연스럽다.
   ※ 아이콘이 하나일 수도 둘일 수도 있어서 absolute 로 하나씩 두지 않고
     flex 묶음으로 둔다. 개수가 변해도 오른쪽 끝이 안 흔들린다.

     글리프 ⚙ · ⏻ 였는데 두 가지가 걸렸다.
       · ⏻(U+23FB)은 **하드웨어 전원 기호**다. '기기 끄기' 로 읽히지
         로그아웃으로 읽히지 않는다. 재 보니 폰트 대체 문제는 아니었고
         (둘 다 Pretendard 로 14px 정상 렌더) 모양과 뜻의 문제였다.
       · 글리프는 굵기를 우리가 못 정한다. 가는 원+선(⏻) 옆에 꽉 찬 톱니(⚙)가
         붙으니 무게가 안 맞아 한쪽이 튀어 보였다 (실측 폭 11.7 / 13.9).
     SVG 는 stroke 를 1.6px 로 맞출 수 있어 둘이 같은 무게로 보인다.
   ※ 색은 currentColor 다 — hover·팔레트 전환이 글자색 규칙 하나로 따라온다
     (함정 ⑱ : 사이드바 위의 색을 고정 hex 로 쓰면 한 팔레트에서 반드시 깨진다). */
.profile__acts {
  position: absolute; top: var(--space-1); right: var(--space-1);
  display: flex; gap: 2px;
}
.profile__icon {
  width: 2.4rem; height: 2.4rem;
  border: 0; border-radius: var(--radius-round);
  background: transparent;
  color: var(--sidebar-fg-soft);
  display: grid; place-items: center;
  transition: var(--ease);
}
.profile__icon svg {
  width: 1.6rem; height: 1.6rem;
  display: block;                 /* inline 이면 baseline 여백이 생겨 1px 내려앉는다 */
  fill: none; stroke: currentColor;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
.profile__icon:hover { background: var(--sidebar-hover); color: var(--sidebar-fg); }
.profile__icon:active { transform: scale(var(--press-scale)); }

/* --- 거래처 자리 (관리자·본사직원만 누를 수 있다) ----------------------
   프로필 카드가 곧 "지금 어느 거래처로 일하는 중인가" 를 말하는 자리다.
   그래서 거래처 전환도 여기서 한다 — 본문에 배너를 띄우면 그 화면에서만
   보이는데, 사이드바는 전 화면에 있다.
   약국(54040)은 거래처가 고정이라 <p> 로 그려져 이 규칙이 안 걸린다. -- */
.profile__ven {
  display: block; width: 100%;
  padding: var(--space-0) var(--space-1);
  /* ★ 이 두 줄이 없으면 '거래처 선택됨' 상태만 글자가 안 보인다.
     <button> 은 UA 기본값으로 background:buttonface(#f0f0f0) 와
     border:2px outset 을 갖는다. 어두운 사이드바에 밝은 회색 상자가 생기고
     그 위에 흰 글자가 얹혀 명암비 1.14 — 사실상 안 보인다.
     이 결함이 오래 안 잡힌 이유 : 나머지 두 상태는 스스로 배경을 정한다.
       미선택   .is-empty 가 골드 틴트를 깔아 UA 배경을 덮는다
       약국     <div> 라서 UA 스타일이 아예 없다
     → '기본 상태' 를 마지막에 재는 습관이 만든 사각지대였다. */
  border: 0;
  background: none;
  border-radius: var(--radius-field);
  color: inherit;
  text-align: center;
  transition: background var(--ease), transform var(--ease);
}
button.profile__ven { cursor: pointer; font: inherit; }
/* ★ hover 를 '배경 밝히기' 로 하면 안 된다.
   어두운 사이드바에서 배경이 밝아지면 흰 글자의 명암비가 같이 떨어진다
   (평소 5.2~15.4 → hover 4.0~4.5). 눌러 보라고 만든 상태에서 글자가
   더 안 읽히는 셈이다. 테두리로 알리면 대비는 평소 값 그대로 유지된다. */
button.profile__ven:hover  { box-shadow: inset 0 0 0 1px var(--sidebar-fg-soft); }
button.profile__ven:active { transform: scale(var(--press-scale)); }

/* 눌러서 바꿀 수 있다는 표시.
   ★ 색을 흐리게(--sidebar-fg-soft) 두면 안 된다. 12px 에서 명암비가
     평소 3.5~4.3, 마우스를 올리면(--sidebar-hover 가 배경을 밝힌다) 2.9~3.3
     까지 떨어져 사실상 안 읽힌다. 실측으로 확인했다.
     보조로 보이게 하는 일은 '색'이 아니라 '크기와 굵기'가 한다 —
     이름은 13px/600, 이 줄은 12px/400 이라 색이 같아도 위계가 선다. */
.profile__swap {
  display: block;
  margin-top: var(--space-0);
  font-size: var(--text-1);
  font-weight: 400;
  color: var(--sidebar-fg);
}
.profile__swap::before { content: "⇄ "; }

/* ★ 거래처 미선택 — 관리자·본사직원은 로그인 직후 이 상태다.
   이때 화면의 장바구니·재고·단가가 전부 빈 거래처 기준이라 조용히 두면 안 된다.

   ★ 주의를 끄는 일은 골드 테두리·틴트가 하고, 글자색은 건드리지 않는다.
     처음엔 글자도 --gold-light 로 칠했는데 흰 사이드바 팔레트(light)에서
     명암비가 1.46 까지 떨어져 사실상 안 보였다 (다른 넷은 3.1~3.6).
     사이드바 배경은 팔레트마다 다르므로, 그 위에 얹는 글자는 반드시
     같이 움직이는 --sidebar-fg 를 써야 한다. 고정 hex 를 쓰면 한 팔레트에서
     반드시 깨진다 — 함정 ③ 과 뿌리가 같다. */
.profile__ven.is-empty {
  background: rgba(203, 162, 88, 0.22);
  box-shadow: inset 0 0 0 1px var(--gold);
}
/* 미선택 상태의 hover 는 골드 테두리를 굵히는 것으로 (위 규칙보다 구체적이어야 이긴다) */
button.profile__ven.is-empty:hover { box-shadow: inset 0 0 0 2px var(--gold); }
.profile__ven.is-empty .profile__swap { font-weight: 700; }
.profile__ven.is-empty .profile__swap::before { content: "▸ "; }

/* --- 아코디언 메뉴 (GEOWEB 구조) --- */
/* 메뉴가 남는 공간을 흡수 → 유틸 버튼은 항상 사이드바 하단에 붙는다 */
.nav { flex: 1; min-height: 0; overflow-y: auto; }
.nav__group + .nav__group { margin-top: var(--space-0); }

.nav__head {
  width: 100%;
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-2);
  background: transparent; border: 0;
  border-radius: 8px;
  color: var(--sidebar-fg);
  font-size: var(--text-3);
  font-weight: 600;
  text-align: left;
  transition: var(--ease);
}
.nav__head:hover { background: var(--sidebar-hover); }
.nav__head:active { transform: scale(var(--press-scale)); }
.nav__head .ico { width: 1.8rem; text-align: center; opacity: .85; }
.nav__head[aria-expanded="true"] { color: var(--sidebar-accent); }

.nav__sub {
  overflow: hidden;
  max-height: 40rem;
  transition: max-height var(--ease-expander);
}
.nav__head[aria-expanded="false"] + .nav__sub { max-height: 0; }

.nav__link {
  display: block;
  padding: var(--space-1) var(--space-2) var(--space-1) 3.6rem;
  font-size: var(--text-2);
  color: var(--sidebar-fg-soft);
  border-radius: 8px;
  transition: var(--ease);
}
.nav__link:hover { color: var(--sidebar-fg); background: var(--sidebar-hover); }
.nav__link.is-active {
  color: var(--house-green);
  background: var(--green-light);
  font-weight: 600;
}

/* ★ 아직 ASP 로 옮기지 않은 화면 — 자리는 두되 누를 수 없다 (shell.js 의 WIS_HREF).
   메뉴에서 지우지 않는 이유 : 무엇이 남았는지 보여야 한다.
   색을 직접 칠하지 않고 투명도만 낮춘다 — 팔레트 5종이 사이드바 배경을
   뒤집는데 hex 를 박으면 한 팔레트에서 반드시 깨진다 (wis-pitfalls 함정 ⑱).

   ★ .45 로 시작했다가 .65 로 올렸다. 팔레트 5종에서 명암비가 1.89~2.16 이라
     12px 글자가 배경에 묻혔다. .65 면 2.6~2.9 로 읽히면서 살아 있는 메뉴
     (4.0~5.3)와는 확실히 갈린다. 비활성 항목은 AA 대상이 아니지만
     '무슨 메뉴인지' 는 읽혀야 한다. */
.nav__link.is-todo,
.nav__solo.is-todo {
  opacity: .65;
  cursor: default;
}
.nav__link.is-todo:hover,
.nav__solo.is-todo:hover { background: none; }

/* 권한 구분선 — 관리자 메뉴 앞에 들어간다 */
.nav__divider {
  display: flex; align-items: center; gap: var(--space-2);
  margin: var(--space-2) var(--space-2) var(--space-1);
  font-size: 1.0rem;
  font-weight: 700;
  letter-spacing: var(--tracking-loose);
  color: var(--sidebar-fg-soft);
  white-space: nowrap;
}
.nav__divider::after {
  content: ""; flex: 1; height: 1px; background: var(--sidebar-line);
}

/* 단독 메뉴 (하위 없음) */
.nav__solo {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2);
  border-radius: 8px;
  color: var(--sidebar-fg);
  font-size: var(--text-3);
  font-weight: 600;
  transition: var(--ease);
}
.nav__solo:hover { background: var(--sidebar-hover); }
.nav__solo.is-active { color: var(--house-green); background: var(--green-light); }
.nav__solo .ico { width: 1.8rem; text-align: center; opacity: .85; }

/* --- 사이드바 하단 유틸 ---
   2026-07-31 에 자리표시 버튼 셋을 뺐고, 2026-08-06 에 **한 칸**을 되살렸다
   (관리자 '주문 통계' · shell.js 의 UTILS).
   ★ 지금 들어오는 것은 <button> 이 아니라 <a> 다 — 화면 이동이라 링크여야 한다
     (새 탭·주소 복사). 그래서 밑줄을 지우고 .is-active 를 같이 둔다.
   ⚠ 한 칸이라 .util-row(3등분)는 안 쓴다. 그 규칙은 아래 그대로 남겨 둔다. */
.sidebar__utils {
  flex: none;                     /* 항상 온전히 표시 — 잘리지 않는다 */
  border-top: 1px solid var(--sidebar-line);
  padding-top: var(--space-2);
  display: grid;
  gap: var(--space-1);
}
.util {
  display: flex; align-items: center; gap: var(--space-2);
  width: 100%;
  padding: var(--space-2);
  border: 1px solid var(--sidebar-line);
  border-radius: 8px;
  background: var(--sidebar-tile);
  color: var(--sidebar-fg);
  font-size: var(--text-2);
  font-weight: 600;
  text-align: left;
  transition: var(--ease);
}
.util:hover { background: var(--sidebar-hover); border-color: var(--sidebar-accent); text-decoration: none; }
.util:active { transform: scale(var(--press-scale)); }
.util .ico { font-size: 1.6rem; }
/* 지금 보고 있는 화면 — nav__link.is-active 와 같은 방식(연한 바탕 + 진한 글자).
   따로 색을 만들지 않는다. */
.util.is-active {
  background: var(--green-light);
  border-color: var(--green-light);
  color: var(--house-green);
}
.util--gold {
  border-color: rgba(203, 162, 88, 0.5);
  background: rgba(203, 162, 88, 0.14);
  color: var(--gold-light);
}
.util--gold:hover { background: rgba(203, 162, 88, 0.24); }

.util-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-1); }
.util-row .util { justify-content: center; padding: var(--space-2) 0; }

/* --- 페이지 안내줄 ---
   큰 제목은 두지 않는다. 현재 위치는 좌측 메뉴의 활성 표시로 알 수 있고,
   업무 화면에서는 목록에 쓸 세로 공간이 더 중요하다. */
.page-head {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  padding: 0 var(--space-1) var(--space-1);
  min-height: 2rem;
}
.page-title {              /* index.html 표지 등 예외적으로만 사용 */
  font-size: var(--text-7);
  font-weight: 700;
  color: var(--brand-green);
  letter-spacing: -0.03em;
  line-height: var(--leading-compact);
}
.page-desc { font-size: var(--text-3); color: var(--ink-soft); }
.page-desc b { color: var(--green-accent); font-weight: 600; }
.page-head__actions { margin-left: auto; display: flex; gap: var(--space-2); }

/* ★ 처리결과가 들어가는 빈 자리는 자리를 먹지 않는다.
   .main 이 flex column + gap 8 이라 **빈 <div> 도 flex 아이템**이다 —
   gap 이 한 번 더 들어가 검색바가 8px 아래에서 시작한다.
   주문서조회에는 이 슬롯이 있고 원장조회에는 없어서, 두 조회 화면의
   display:none 이면 flex 아이템에서 빠져 gap 도 안 먹는다.
   ※ 토스트가 들어오면 :empty 가 풀려 저절로 다시 보인다. */
#resSlot:empty { display: none; }

/* --- 탭 ---------------------------------------------------------------
   두 가지로 쓴다. 모양이 같아야 하므로 **한 벌만 둔다.**
     ① 화면 안 전환 — <button class="tab">
     ② 형제 화면으로 건너가기 — <a class="tab">  (관리자 하위 탭)
   ②는 shell.js 의 buildSubtabs() 가 만든다. 사이드바 한 칸에 화면이 둘씩
   묶인 곳(알림 관리 · 기본 설정 · 문구 관리)에서 본문 맨 위에 붙는다.
   ★ <a> 는 기본이 inline 이라 padding 이 세로로 안 먹고 가운데 정렬도 안 된다 —
     아래 셋(display·text-align·text-decoration)이 그 차이를 메운다. */
.tabs {
  display: flex;
  background: var(--white);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  overflow: hidden;
  /*
     `.main` 은 세로 flex 이고, `.app` 에 `app--auto` 가 없는 화면에서는 행 높이가
     화면에 **고정**된다. 그러면 내용이 넘칠 때 flex 가 자식들을 눌러 줄이는데,
     `flex-shrink` 기본값이 1 이라 이 띠가 **0px 까지 찌부러졌다**(실측).
     `overflow:hidden` 이 걸려 있어 글자도 같이 사라져 '탭이 없어진' 것처럼 보인다.
     ⚠ 짧은 문서(회사소개 312자)에서는 안 넘쳐서 멀쩡했다 — 그래서 문서를
       옮겨 다니면 탭이 나타났다 사라졌다 했다. 길이에 따라 달라지는 버그였다.
     ★ 눌릴 자리는 목록·본문이지 길잡이가 아니다. 길잡이가 먼저 사라지면
       지금 어디에 있는지조차 알 수 없게 된다. */
  flex-shrink: 0;
}
.tab {
  flex: 1;
  display: block;
  text-align: center;
  text-decoration: none;
  padding: var(--space-3) var(--space-2);
  border: 0; background: transparent;
  border-bottom: 2px solid var(--line);
  color: var(--ink-soft);
  font-size: var(--text-4);
  font-weight: 600;
  transition: var(--ease);
}
.tab:hover { background: var(--neutral-cool); color: var(--ink); }
.tab.is-on { color: var(--green-accent); border-bottom-color: var(--green-accent); }

/* --- 작업 영역 그리드 (주문/반품 화면: 목록 + 정보 컬럼) ---
   두 컬럼의 높이를 강제로 맞추고(stretch), 남는 공간은
   .panel--fill 로 지정된 패널이 흡수한다. 좌/우 하단선이 정렬된다. */
.workspace {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--info-col-w);
  gap: var(--space-2);
  align-items: stretch;
  flex: 1;          /* .main 안에서 남는 세로 공간을 전부 차지 */
  min-height: 0;
}
/*
   [제품목록] [상품정보 / 과거주문] [장바구니]

   왜 이렇게 가나 : 제품목록을 세로로 길게 쓰려는 것이다.
   2단 배치에서는 제품목록이 장바구니와 세로를 나눠 써서 절반만 쓴다
   (실측 344px / 12행). 장바구니를 옆으로 빼면 통째로 쓴다.

   폭 예산 (1340 고정 · data-tight 기준)
     1340 − 4(패딩) − 220(사이드바) − 4(gap) − 4(패딩) = 1108  본문
     1108 − 8 − 8 = 1092                                      패널 3개 몫
     1092 = 제품목록 548 + 상품정보/과거주문 244 + 장바구니 300

   ★ 장바구니를 350 → 300 으로 줄이고 그 50px 을 제품목록에 줬다.
     장바구니를 두 줄짜리(.cart-item)로 바꾸고 나니 폭이 남아서다 —
     이름 줄 여유 195px · 숫자 줄 여유 106px 이었다. 남는 폭을 그냥 두면
     '제품목록을 길게' 라는 이 개편의 목적을 반만 달성한다.
   ※ 이 값을 바꾸면 grid-config.js 의 ORDER_GOODS_V2.avail 도 같이 고칠 것. */
.workspace--3 {
  /* ★ 280 — 244 에서 36px 늘렸다 (2026-07-29). 장바구니에서 가져왔다.
     300 이 하한이었던 이유는 2행에 삭제 버튼이 있었기 때문인데(함정 ⑳-b)
     그 버튼을 1행으로 올려 34px 을 돌려받았다. 금액 한 줄 상한은 그대로다.
     제품목록 548 은 안 건드렸다 — 건드리면 grid 메타의 avail(540) 도
     두 곳(grid-config.js · app/inc/grid.asp) 을 같이 고쳐야 한다(함정 ⑰). */
  --info-col-w: 28rem;     /* 280px — 상품정보 · 과거주문 */
  --cart-col-w: 26.4rem;   /* 264px — 장바구니 (삭제 ✕ 를 1행으로 올린 뒤) */
  grid-template-columns: minmax(0, 1fr) var(--info-col-w) var(--cart-col-w);
}

/* 반품신청 — 같은 3패널인데 배분이 다르다. 셋 다 실측으로 잡았다.
   · 상품정보 240 : 중간에 과거주문내역이 없어 컬럼을 통째로 쓴다.
     제조번호·유효기간 입력과 담기 전 안내까지 들어 있어 236px 을 요구한다.
   · 반품내역 328 : 2행(수량·제조번호·유효기간·삭제)이 298px 을 쓴다.
     + 항목 좌우패딩 16 + 스크롤바 8 = 322 가 하한이라 6px 만 남겼다.
   · 매출내역 512 : 나머지. 표 504 중 고정 342 를 빼면 상품명이 162 다
     (가장 긴 '라미실정125mg 98T (PTP)' 볼드 156 + 여유 6).
   1080 = 매출내역 512 + 상품정보 240 + 반품내역 328 */
.workspace--ret {
  --info-col-w: 24rem;
  --cart-col-w: 32.8rem;
}

.ws-col {
  display: flex; flex-direction: column;
  gap: var(--space-2);
  min-width: 0; min-height: 0;
  /* 폭 조절 손잡이(.wsgrip)의 기준점. ※ .ws-col--anchor 가 같은 값을 갖고
     있는데 그건 실패 토스트를 세우는 자리라 뜻이 다르다 — 둘 다 남긴다. */
  position: relative;
}

/* --- 패널 폭 조절 손잡이 (2026-08-14 · 프로토타입 시험) -------------------
   ★ 셸 총폭은 한 픽셀도 안 바뀐다. 그리드가
     `minmax(0,1fr) var(--info-col-w) var(--cart-col-w)` 라서 뒤의 두 값만
     고치면 제품목록(1fr)이 나머지를 흡수한다.
   ★ 손잡이는 컬럼 **왼쪽 gap(8px) 위에 얹는다** — 자리를 새로 안 먹는다.
     그래서 손잡이를 붙였다고 폭 예산이 1px도 안 줄어든다.
   ★ 평소에는 **선을 안 그린다.** 늘 보이는 세로선을 넣으면 패널 사이에
     없던 칸막이가 생겨 3패널이 표처럼 읽힌다 — 마우스를 올렸을 때만 나온다.
   ⚠ 1100px 미만은 세로 1단이라 조절할 것이 없다. 아래 미디어쿼리가 감춘다. */
.wsgrip {
  position: absolute; top: 0; bottom: 0;
  left: calc(-1 * var(--space-2) - 2px);
  width: 12px; z-index: 4;
  cursor: col-resize;
  touch-action: none;
}
.wsgrip::after {
  content: '';
  position: absolute; left: 5px; top: 0; bottom: 0;
  width: 2px; border-radius: 1px;
  background: transparent;
  transition: background var(--ease);
}
.wsgrip:hover::after,
.wsgrip.is-drag::after { background: var(--green-accent); }

/* --- 목록 칸 너비 손잡이 (2026-08-14 · 프로토타입 시험) -------------------
   ★ 모양·동작은 관리자 「목록 컬럼」 미리보기의 `.colresize` 를 **그대로** 쓴다.
     새로 그리면 같은 것이 두 벌이 된다.
   ⚠ **자리만 다르다.** 미리보기 th 는 잘라내기가 없어 칸 밖(-4px)에 둬도
     보이지만, 목록 th 는 `overflow:hidden` 이라 밖에 두면 손잡이가 통째로
     잘려 아예 안 잡힌다 → 칸 **안쪽 오른쪽 끝**에 붙인다. */
.tbl thead th > .colresize { right: 0; }

/*
   내용이 넘치면 패널 안에서 스크롤된다 — 밖으로 밀지 않는다. */
.ws-col--split > .panel { flex: 1 1 0; min-height: 0; }
.ws-col--split > .panel > .panel__body { overflow: auto; scrollbar-gutter: stable; }

/* 남는 세로 공간을 비율로 나눠 갖는 패널.
   flex-basis:0 이라 "고정 높이 + 나머지" 가 아니라 순수 비율 분배 →
   화면이 아무리 작아져도 컬럼 합계가 컨테이너를 넘지 않는다.
   (고정 높이를 쓰면 좁은 화면에서 최소 높이가 튀어 화면 간 높이가 어긋난다) */
.panel--fill { flex: 1 1 0; }
/* 목록 패널 비중. 페이저를 헤더로 올려 확보한 공간을 장바구니에 몰아주기
   위해 1.1 까지 낮췄다 — 둘이 거의 같은 높이를 갖는다. */
.panel--fill--2x { flex-grow: 1.1; }

/* 높이 하한은 반드시 부모(.table-wrap)에 건다.
   부모를 min-height:0 으로 두고 자식(.scroll-y)에만 하한을 걸면
   부모가 자식보다 작아져 테이블이 밖으로 삐져나온다 (메모줄과 겹침). */
.panel--fill .table-wrap {
  flex: 1 1 auto;
  min-height: 9rem;                 /* 헤더 + 최소 2행 */
  display: flex; flex-direction: column;
}
.panel--fill .scroll-y { flex: 1 1 auto; min-height: 0; }

@media (max-width: 1100px) {
  .app { grid-template-rows: auto; }        /* 세로로 쌓이므로 고정 해제 */
  .workspace { grid-template-columns: 1fr; flex: none; }
  .panel--fill { flex: none; }
  .panel--fill .scroll-y { flex: none; height: 22rem; }
  .banners { max-height: none; }
  .wsgrip { display: none; }                /* 세로 1단에서는 조절할 경계가 없다 */
  .tbl thead th > .colresize { display: none; }
}

/* ==========================================================================
   4. PANEL (카드)
   ========================================================================== */

.panel {
  background: var(--white);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
}

.panel__head {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--line);
  min-height: 4.2rem;
}
.panel__title {
  font-size: var(--text-5);
  font-weight: 700;
  color: var(--brand-green);          /* Design.md: 제목은 Starbucks Green */
  letter-spacing: -0.02em;
  white-space: nowrap;
}
.panel__note {
  font-size: var(--text-1);
  color: var(--ink-soft);
}
.panel__actions { margin-left: auto; display: flex; align-items: center; gap: var(--space-1); }
.panel__body { padding: var(--space-2) var(--space-3) var(--space-3); }
.panel__body--flush { padding: 0; }

/* 헤더 아래 보조 한 줄.
   패널이 좁아 .panel__note 를 제목 옆에 못 붙일 때 쓴다 —
   붙이면 제목이 밀려 잘리거나 헤더가 두 줄이 되어 높이가 튄다.
   (3패널 장바구니 350px 의 '출고예정일' 이 이 경우다) */
.panel__sub {
  padding: var(--space-1) var(--space-3);
  border-bottom: 1px solid var(--line);
  background: var(--neutral-cool);
  font-size: var(--text-1);
  color: var(--ink-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ==========================================================================
   5. BUTTONS  (Design.md — 50px 풀필 + scale(0.95) 예외 없음)
   ========================================================================== */

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--space-1);
  height: var(--control-h);
  padding: 0 var(--space-3);
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  background: transparent;
  font-size: var(--text-3);
  font-weight: 600;
  line-height: var(--leading-compact);
  white-space: nowrap;
  transition: all var(--ease);
}
.btn:active { transform: scale(var(--press-scale)); }

/* 잠긴 버튼 — 눌러도 아무 일이 없다는 것이 **보여야** 한다.
   ★ 전에는 disabled 를 걸어도 겉모습이 그대로였다. 눌러 보고서야 안 되는 줄
     아는 버튼은 잠근 것이 아니다 (반품요청서의 '범위 밖' 두 개와 조회 두 화면의
     '자료 없음' 엑셀·출력이 그랬다).
   ★ 흐리게만 하지 않고 **커서까지** 바꾼다. 왜 잠겼는지는 title 이 말한다.
   ⚠ :hover 와 :active 도 같이 꺼야 한다 — 안 그러면 반응은 하는데 결과만
     없는, 가장 헷갈리는 상태가 된다. */
.btn:disabled,
.btn[disabled] {
  opacity: .45;
  cursor: not-allowed;
}
.btn:disabled:hover,
.btn[disabled]:hover {
  border-color: var(--line-strong); color: var(--ink-soft); background: var(--white);
}
.btn:disabled:active,
.btn[disabled]:active { transform: none; }

/*
   ★★ **잠그기만 하면 '고장' 으로 보인다.** 바로 위 :disabled 는 흐려지고
     회색이 되는데 그건 "지금은 못 누른다" 지 "하는 중" 이 아니다. 로그인은
     1~2초가 걸려서 그 사이 아무 표식이 없으면 **엔터를 계속 친다** — 그것이
     이 클래스를 만든 이유다.
   ★ 두 번 눌리는 것을 막는 것은 여전히 disabled 다. 여기는 **보이는 것만**
     맡는다 — 그래서 위의 :disabled 겉모습(흐림·회색·not-allowed)을 되돌린다.
   ★ 고리 색은 currentColor 다. 초록 버튼이든 흰 버튼이든 따라온다 —
     색을 박으면 팔레트를 바꿨을 때 여기만 안 따라온다.
   ⚠ 움직임을 줄이는 설정이면 안 돌린다. 그래도 글자가 「로그인 중…」으로
     바뀌므로 상태는 그대로 전달된다. */
.btn--busy,
.btn--busy:disabled,
.btn--busy[disabled] { opacity: 1; cursor: progress; }
.btn--busy:disabled:hover,
.btn--busy[disabled]:hover {
  border-color: inherit; color: inherit; background: inherit;
}
.btn--busy::before {
  content: "";
  width: 1em; height: 1em;
  flex: none;                          /* 글자가 길어도 고리는 안 찌그러진다 */
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: wis-spin .7s linear infinite;
}
@keyframes wis-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .btn--busy::before { animation: none; }
}

/* 버튼에 붙는 단축키 표식 (2026-08-12) — 「주문하기 F9」 · 「제품검색 Esc」
   ★★ **키를 붙였으면 화면이 그 키를 말해야 한다.** 안 적으면 아는 사람만
     쓰는 기능이 된다. ※ 제품검색은 F5 였다가 Esc 로 바뀌었다(2026-08-13).

   ★★ **처음엔 `opacity:.62` 짜리 작은 글자였는데 안 보였다**(2026-08-12
     읽히지 않는다. → 흐림을 걷어내고 **키캡 모양(네모 테두리)** 으로 바꿨다.
     모양이 뜻을 말한다. 크기는 그대로 둔다(이름이 주인이고 이건 곁들이).

   ★★ **색을 박지 않는다 — 전부 `currentColor` 다.** 이 표식이 붙는 버튼
     둘이 팔레트마다 배경이 뒤집힌다 : 「주문하기」는 `--onDark-bg` 를 정의한
     팔레트(emerald)에서만 초록 알약 + 흰 글자이고, **나머지 15개에서는 흰
     알약 + 진초록 글자**다. 흰색을 박으면 그 15개에서 흰 바탕에 흰 키캡이
     된다. 테두리·글자·바탕을 전부 글자색에서 끌어오면 어느 쪽이든 맞는다.
   ※ `color-mix` 는 못 읽는 브라우저에서 그 줄만 버려진다 — 테두리는 남는다.
   ※ .btn 이 inline-flex + gap 이라 사이 간격은 저절로 붙는다. */
.btn__key {
  font-size: var(--text-1);
  font-weight: 700;
  line-height: 1;
  padding: 3px 5px 4px;
  border: 1px solid currentColor;
  border-radius: 4px;
  background: color-mix(in srgb, currentColor 14%, transparent);
  letter-spacing: .02em;
}

.btn--primary { background: var(--green-accent); border-color: var(--green-accent); color: var(--white); }
.btn--primary:hover { background: var(--brand-green); border-color: var(--brand-green); }

.btn--outline { border-color: var(--green-accent); color: var(--green-accent); background: var(--white); }
.btn--outline:hover { background: var(--green-light); }

.btn--quiet { border-color: var(--line-strong); color: var(--ink-soft); background: var(--white); }
.btn--quiet:hover { border-color: var(--ink-faint); color: var(--ink); }

.btn--dark { background: var(--house-green); border-color: var(--house-green); color: var(--white); }
.btn--dark:hover { background: var(--green-uplift); border-color: var(--green-uplift); }

.btn--danger { border-color: var(--red); color: var(--red); background: var(--white); }
.btn--danger:hover { background: var(--red); color: var(--white); }

.btn--sm { height: 2.6rem; padding: 0 var(--space-2); font-size: var(--text-1); }
.btn--lg { height: var(--control-h-lg); padding: 0 var(--space-4); font-size: var(--text-4); }
.btn--block { width: 100%; }

/* 아이콘 전용 원형 */
.btn-icon {
  width: 2.6rem; height: 2.6rem;
  display: grid; place-items: center;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-round);
  background: var(--white);
  color: var(--ink-soft);
  font-size: var(--text-2);
  transition: var(--ease);
  /* ★ <a> 와 <button> 둘 다 이 클래스를 쓴다 (주문취소가 2026-08-11 에 POST
     폼 버튼이 됐다). 버튼은 글꼴·여백·커서를 상속하지 않으므로 맞춰 준다 —
     안 맞추면 취소 ✕ 만 글꼴이 다르고 원이 찌그러진다. <a> 에는 무해하다. */
  padding: 0; font: inherit; line-height: 1; cursor: pointer;
}
.btn-icon:hover { border-color: var(--green-accent); color: var(--green-accent); }
.btn-icon:active { transform: scale(var(--press-scale)); }
.btn-icon--danger:hover { border-color: var(--red); color: var(--red); }
/* 주문취소 폼 — 표 칸 안에서 자리를 차지하면 안 된다. 버튼만 보인다. */
.cancel-form { display: inline; }

/* 텍스트 링크 액션 */
.link-action {
  font-size: var(--text-1);
  font-weight: 600;
  color: var(--green-accent);
  transition: var(--ease);
}
.link-action:hover { text-decoration: underline; }

/* ==========================================================================
   6. FORM CONTROLS
   ========================================================================== */

.field {
  height: var(--control-h);
  padding: 0 var(--space-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-field);
  background: var(--white);
  font-size: var(--text-3);
  color: var(--ink);
  transition: var(--ease);
  min-width: 0;
}
.field::placeholder { color: var(--ink-faint); }
.field:focus {
  outline: none;
  border-color: var(--green-accent);
  box-shadow: 0 0 0 3px rgba(0, 117, 74, 0.12);
}
.field:disabled { background: var(--ceramic); color: var(--ink-faint); }
.field--sm { height: 2.8rem; font-size: var(--text-2); }
.field--num { text-align: right; font-variant-numeric: tabular-nums; }

/* ★ 숫자칸의 스피너(위아래 화살표)를 없앤다.
   없애지 않으면 '수량' placeholder 가 잘린다 — 폭이 모자라서가 아니라
   .field--num 이 오른쪽 정렬이라 글자가 시작되는 자리가 곧 스피너 자리다.
   (프로토타입은 type=text 라 멀쩡하고 앱만 type=number 여서 앱에서만 잘렸다)
   주문수량은 화살표로 하나씩 올리는 값이 아니라 쳐 넣는 값이다.
   type=number 는 그대로 두므로 모바일 숫자 키패드와 방향키 증감은 남는다. */
.field--num[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.field--num[type="number"]::-webkit-outer-spin-button,
.field--num[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  appearance: none;
  margin: 0;
}

select.field {
  appearance: none;
  padding-right: 2.6rem;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'><path d='M2 4l4 4 4-4' fill='none' stroke='rgba(0,0,0,.5)' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right var(--space-2) center;
}

.field-label {
  font-size: var(--text-1);
  font-weight: 600;
  color: var(--ink-soft);
  white-space: nowrap;
}

/* 라디오 / 체크 */
.choice { display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--text-3); cursor: pointer; }
.choice input { accent-color: var(--green-accent); width: 1.5rem; height: 1.5rem; }

/* 검색바 */
.searchbar {
  background: var(--white);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: var(--space-2) var(--space-3);
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--space-2);
}
.searchbar__group { display: flex; align-items: center; gap: var(--space-2); }
.searchbar__spacer { flex: 1; }

/* 기간 퀵버튼 그룹 */
.quick-range { display: inline-flex; gap: var(--space-1); flex: none; }
.quick-range .btn { min-width: 6.4rem; }
.quick-range .btn.is-on { background: var(--green-accent); border-color: var(--green-accent); color: var(--white); }

/* 검색바가 빠듯한 화면용 — 라벨 폭에 맞춰 줄인다 (오늘/일주일/한달은 3글자면 충분) */
.quick-range--sm .btn { min-width: 0; padding-inline: var(--space-2); }

/* 원장조회 집계 단위 — 구버전 director_list.asp:381-386 의 flag 라디오(1/2/3).
   바로 옆에 기간 퀵버튼이 있어서 같은 버튼형으로 만들면 둘이 구분되지 않는다.
   무엇이 기간이고 무엇이 집계 단위인지 형태로 구분되게 진짜 라디오로 둔다. */
.radio-group { display: inline-flex; align-items: center; gap: var(--space-2); flex: none; }
.radio-group label {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--text-2); color: var(--ink);
  white-space: nowrap; cursor: pointer;
}
.radio-group input { accent-color: var(--green-accent); margin: 0; cursor: pointer; }

/* ==========================================================================
   7. DATA TABLE  (고밀도 — Design.md에 없는 B2B 확장)
   ========================================================================== */

.table-wrap { position: relative; }
.table-wrap .scroll-y { border-top: 1px solid var(--line); }

/* ★ 스크롤바 자리를 항상 비워 둔다.
   행이 적어 스크롤이 안 생기는 패널만 표가 8px 넓어져, 위아래로 붙은 두 패널의
   컬럼 경계가 어긋나 보인다 (제품목록 608 / 장바구니 616 인 상태였다).
   페이지 전체에는 html 에 이미 같은 처리를 해 뒀다 — 함정 ⑥ 과 같은 문제다.
   이 값이 곧 grid-config.js 의 avail(608/896) 기준이기도 하다. */
.scroll-y { scrollbar-gutter: stable; }

/* ★ table-layout: fixed
   기본값(auto)에서는 <col width> 가 권장값일 뿐이고 실제 컬럼 폭을
   "데이터 내용"이 결정한다 → 같은 화면이라도 데이터가 다르면 폭이 달라진다.
   fixed 로 두면 colgroup 값이 절대 기준이 되어 어떤 데이터에서도 동일하게 렌더링된다. */
.tbl {
  table-layout: fixed;
  width: 100%;
  font-size: var(--text-2);
  font-variant-numeric: tabular-nums;
}

.tbl thead th {
  position: sticky; top: 0; z-index: 2;
  background: var(--ceramic);
  color: var(--ink-soft);
  font-weight: 600;
  font-size: var(--text-1);
  text-align: center;
  height: var(--row-h-tight);
  padding: 0 var(--space-1);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

.tbl tbody td {
  height: var(--row-h);
  padding: 0 var(--space-1);
  border-bottom: 1px solid var(--line);
  color: var(--ink);
  vertical-align: middle;
  /* ★ 행 높이를 상태와 무관하게 고정한다.
     선택 시 font-weight 가 600 으로 바뀌면 글자가 미세하게 넓어지는데,
     고정폭 셀에서 그 폭이 콘텐츠 박스를 넘으면 줄바꿈이 일어나 행이 32→40px 로 튄다.
     (매출일자 84px → 콘텐츠 76px, 볼드 날짜 75.7px — 여유 0.3px 이었다)
     nowrap 이면 넘쳐도 말줄임으로 degrade 될 뿐 높이는 절대 변하지 않는다.
     thead th 는 이미 같은 이유로 nowrap 이다. */
  white-space: nowrap;
  text-overflow: ellipsis;
}

.tbl tbody tr { transition: background 0.12s ease; }
.tbl--click tbody tr { cursor: pointer; }
.tbl--click tbody tr:hover { background: var(--row-hover); }
.tbl tbody tr.is-selected { background: var(--green-light); }
.tbl tbody tr.is-selected td { font-weight: 600; }

/* 목록 '안에' 섞여 있는 합계행 — 원장조회의 이월잔액 · 소계 · 기간계 · 누계.
   잔액이 위에서 아래로 이어져야 해서 tfoot 로 몰 수가 없다.
   ★ 배경만 주고 굵기는 건드리지 않는다. 볼드는 고정폭 셀에서 줄바꿈을 일으켜
     행 높이를 32 → 40px 로 밀어 올린다 (함정 ⑭). 합계행은 숫자가 제일 큰
     행이라 하필 거기서 터진다. 눈에 띄게 하는 일은 배경이 이미 한다. */
.tbl tbody tr.is-total > td {
  background: var(--ceramic);
  border-top: 1px solid var(--line);
}
.tbl--click tbody tr.is-total { cursor: default; }
.tbl--click tbody tr.is-total:hover > td { background: var(--ceramic); }

/* 원장조회의 **입금행** — 매출 사이에 날짜순으로 섞여 있고 잔액이 그 순서대로
   이어진다. 금액 방향이 반대인데 목록에서는 구분이 안 됐다.
   ★ 합계행과 다른 색이어야 한다 — 둘 다 회색이면 '특별한 행' 이 한 종류로
     보인다. 입금은 돈이 들어온 것이라 파랑 계열로 나눈다.
   ★ 배경만 준다. 볼드는 고정폭 셀에서 줄바꿈을 일으켜 행 높이를 밀어 올린다
     (합계행과 같은 이유 · 함정 ⑭).
   ⚠ 아주 옅게 둔다. 매출행 사이에 끼는 행이라 진하면 표가 얼룩덜룩해진다.
   ※ 새 색을 만들지 않고 이미 있는 --tint-blue 를 쓴다 (약가 인하 배지 배경).
     팔레트를 바꾸면 같이 따라온다 — 색을 박아 두지 않는다. */
.tbl tbody tr.is-pay > td { background: var(--tint-blue); }
.tbl--click tbody tr.is-pay:hover > td { background: var(--tint-blue); }


.tbl tfoot td {
  height: var(--row-h);
  padding: 0 var(--space-1);
  background: var(--ceramic);
  font-weight: 700;
  border-top: 1px solid var(--line);
}

.tbl .num { text-align: right; padding-right: var(--space-2); }
.tbl .ctr { text-align: center; }
/* table-layout:fixed 에서는 max-width 트릭이 필요 없다 */
.tbl .ell { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tbl td, .tbl th { overflow: hidden; }

/*
   `.num`·`.ctr` 은 위 두 줄이 th 에도 그대로 걸린다(`.tbl .num` 이 아래
   `.tbl thead th` 의 가운데 정렬을 특이도로 이긴다). `.ell` 만 정렬을 안 갖고
   있어서 — 원래 값칸에서는 td 기본이 왼쪽이라 필요가 없었다 — 여기서 준다.
   ⚠ 왼쪽 정렬 컬럼(제조사 · 상품명/규격)만 움직인다. 코드칸은 가운데,
     금액·수량칸은 오른쪽 그대로다. */
.tbl thead th.ell { text-align: left; }

.tbl__empty {
  text-align: center;
  color: var(--ink-faint);
  font-size: var(--text-2);
  padding: var(--space-6) 0;
}

/* 규격 — 상품명 뒤에 붙는다
     12px 이었는데 "너무 흐려서 안 읽힌다" 는 지적을 받았다. 규격(30T · 100ml)은
     장식이 아니라 **어느 포장을 담을지 고르는 값**이라 상품명과 같은 무게로 읽혀야
     한다. → 색·크기를 상품명과 같게(--ink · 13px) 맞춘다.
   ⚠ 구분은 색이 아니라 **띄어쓰기 한 칸**이 한다. 상품명 뒤에 붙는 자리라는 것이
     이미 그 값이 규격이라고 말해 준다.
   ⚠ 12 → 13px 이라 같은 칸에서 **글자가 그만큼 더 잘린다**(상품명/규격 칸은
     가변 컬럼이라 폭이 안 늘어난다). 장바구니(264px)에서도 같은 클래스를 쓴다 —
     잘리는 것은 title 툴팁이 받아 준다(CellTitle 이 규격까지 넣는다). */
.spec { color: var(--ink); font-size: var(--text-2); }

/* ==========================================================================
   8. VERTICAL SPEC TABLE  (상품정보 패널)
   ========================================================================== */

/* 세로형 표도 fixed. auto 로 두면 셀 안의 입력창 기본 폭(~177px)을
   줄이지 못해 표가 패널 밖으로 커지고 '담기' 버튼이 잘린다. */
.spec-tbl { table-layout: fixed; width: 100%; font-size: var(--text-2); }
.spec-tbl th {
  width: 8rem;
  background: var(--neutral-cool);
  color: var(--ink-soft);
  font-weight: 600;
  text-align: left;
  padding: var(--space-1) var(--space-2);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.spec-tbl td {
  padding: var(--space-1) var(--space-2);
  border-bottom: 1px solid var(--line);
  font-variant-numeric: tabular-nums;
}
.spec-tbl tr:last-child th,
.spec-tbl tr:last-child td { border-bottom: 0; }

/* 담기 입력 영역 */
.add-row { display: flex; align-items: center; gap: var(--space-1); }
.add-row .field { flex: 1 1 0; min-width: 0; }   /* min-width:0 없으면 버튼이 밀려 넘침 */
.add-row .btn { flex: none; }
.add-hint {
  display: block;
  font-size: var(--text-1);
  color: var(--ink-soft);
  margin-top: var(--space-1);
}

/* ==========================================================================
   9. BADGES
   ========================================================================== */

.badge {
  display: inline-flex; align-items: center;
  height: 1.8rem; padding: 0 var(--space-1);
  border-radius: var(--radius-pill);
  border: 1px solid currentColor;
  font-size: 1.1rem;
  font-weight: 700;
  letter-spacing: 0;
  white-space: nowrap;
  vertical-align: middle;
}
/* 골드 — Design.md 규칙에 따라 '특가/기획' 한정 */
.badge--special { color: var(--gold); background: var(--gold-lightest); }
.badge--down    { color: var(--blue);  background: var(--tint-blue); }  /* 약가 인하 */
.badge--up      { color: var(--red);   background: var(--tint-red); }   /* 약가 인상 */
.badge--out     { color: var(--ink-soft); background: var(--ceramic); } /* 품절 */
.badge--new     { color: var(--green-accent); background: var(--green-light); }

/* --- 제품 구분 (특수약품 코드 00027) ------------------------------------
   구버전은 목록 제품명에 DB 가 직접 <FONT COLOR> 를 붙였다.
     향정(00002) 빨강 · 생물(00006) 파랑 — 이 둘뿐이었고
     마약·오남용·독극약은 상품정보 배지 이미지로만 구분됐다.
   리뉴얼은 넷으로 갈라 목록에서도 한눈에 보이게 한다.
   ★ 글자색이 아니라 배지다. 12px 본문 글자색을 팔레트마다 바꾸면
     대비가 흔들린다 (함정 ⑱). 배지는 자기 배경을 갖고 다닌다.
   ※ '기타'(00001)는 배지가 없다 — 데모 DB 1,462건 중 1,317건이라
     붙이면 목록이 배지로 뒤덮인다. 구버전도 안 붙였다. */
.badge--psy   { color: var(--red);       background: var(--tint-red); }     /* 향정신성의약품 */
.badge--narc  { color: var(--purple);    background: var(--tint-purple); }  /* 마약 · 한외마약 */
.badge--bio   { color: var(--blue);      background: var(--tint-blue); }    /* 생물학적제제 */
.badge--care  { color: var(--orange);    background: var(--tint-orange); }  /* 오남용우려약품 · 독극약 */
.badge--plain { color: var(--ink-soft);  background: var(--ceramic); }      /* 전문/일반 · 보험/비보험 */

/* --- 구매이력 동그라미 — '이 거래처가 전에 한 번이라도 주문한 상품' ---------
   그리는 곳은 inc/grid.asp 의 EverMark() 하나다 (거기에 '왜 원인가' 가 있다).
   ★★ **지름 14px 은 눈대중이 아니다.** 직전 안이 ▲(15.5×19)였는데 세모는 자기
     사각형의 절반만 채운다 — 잉크 147px². 그것과 같아지는 원의 지름이 14px 이다
     (π·7² = 154). 처음에 8px 로 잡았다가 "눈에 안 띈다" 로 되돌아온 자리라,
     **보이는 양을 맞춰 놓고** 시작한다.
     ※ 세모를 버린 이유는 크기가 아니라 뜻이다 — 방향이 있는 도형은 색과 무관하게
       '오름' 으로 읽히고, 이 화면에는 약가 인상 배지가 실재한다. 원은 방향이 없다.
   ★ **글꼴이 아니라 CSS 로 그린다.** ▲(U+25B2)는 Pretendard 에 없어 대체 글꼴이
     그렸다 — 글꼴이 다른 배포에서 크기가 흔들린다. border-radius 는 안 흔들린다.
   ★ 색은 --green-accent. 골드는 특가 전용(Design.md).
   ★ line-height 를 타지 않는 inline-block + vertical-align 이라 행 높이(32px)를
     안 건드린다. 더 키워야 하면 width/height 한 쌍만 고치면 된다. */
.ever-mark {
  display: inline-block;
  width: 1.4rem; height: 1.4rem;
  margin-right: var(--space-1);
  border-radius: 50%;
  background: var(--green-accent);
  vertical-align: middle;
  flex: none;
}

/* 상품정보의 '상품분류' 칸 — 배지가 여러 개라 줄바꿈을 허용한다 */
.badge-row { display: flex; flex-wrap: wrap; gap: var(--space-0); }

/* 상태 텍스트 */
.state { font-size: var(--text-1); font-weight: 600; }
.state--done    { color: var(--green-accent); }
.state--pending { color: var(--ink-soft); }
.state--reject  { color: var(--red); }

/* ==========================================================================
   9-b. CART LIST  (두 줄짜리 장바구니 — 좁고 긴 컬럼용)
   --------------------------------------------------------------------------
   3패널에서 장바구니 컬럼이 350px 이다. 구버전 장바구니는 638px 였고
   (cupharm·gnhds 둘 다 638) 그 폭에 상품명 340 + 수량·단가·금액·삭제가 들어갔다.
   350px 에 같은 5컬럼을 한 줄로 넣으면 상품명이 104px 밖에 안 남아
   규격은커녕 이름도 잘린다.

   → 한 줄을 두 줄로 쪼갠다. 세로는 남고 가로가 모자란 상황이라 맞바꾸는 것이다.
       1행  제품명 (폭을 다 쓴다 → 규격 병기가 살아난다)
       2행  수량 · 단가 · 금액 · 삭제

   ★ 표(.tbl)를 안 쓴다. 한 항목이 두 줄이면 이미 표가 아니다.
     <tr> 두 개로 흉내내면 colgroup·정렬 규칙이 전부 어긋나고,
     table-layout:fixed 의 폭 계산도 의미가 없어진다.
   ★ 숫자에 라벨을 붙인다. thead 가 없어서 무엇이 단가고 무엇이 금액인지
     알려줄 자리가 그것뿐이다. (.summary__item 과 같은 방식)
   ========================================================================== */

.cart-list { display: block; }

.cart-item {
  padding: var(--space-1) var(--space-2) var(--space-2);
  border-bottom: 1px solid var(--line);
  transition: background 0.12s ease;
}
.cart-item:hover { background: var(--row-hover); }

/* 1행 — 제품명 · (반품은 매출일자) · 삭제. 넘치면 말줄임.
   행 높이는 변하지 않는다(함정 ⑭)

     2행이 34px(버튼 26 + gap 8)을 돌려받아 장바구니 컬럼을 300 → 264 로 줄였고
     그 36px 이 전부 상품정보로 갔다 (244 → 280). 제품목록 548 은 그대로다.
     대신 제품명이 34px 을 잃는다 — 맞바꾼 것이지 공짜가 아니다. */
.cart-item__nm {
  font-size: var(--text-2);
  font-weight: 600;
  line-height: 1.9;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* 삭제 ✕ — 원형 버튼(.btn-icon 26px)이 아니라 글자만 남긴 빨간 ✕ 다.
   원형 버튼을 그대로 올리면 1행 높이가 25 → 26px 로 커지고 테두리가
   제품명과 같은 줄에서 시선을 끌어 이름보다 버튼이 먼저 보인다.
   여기서는 삭제가 주인공이 아니다 — 18px 히트영역에 12px 글자면 충분하다. */
.cart-item__del {
  flex: none; align-self: center;
  width: 1.8rem; height: 1.8rem;
  display: grid; place-items: center;
  padding: 0; border: 0; border-radius: var(--radius-round);
  background: none;
  color: var(--red);
  font-size: var(--text-1);
  line-height: 1;
  transition: var(--ease);
}
.cart-item__del:hover  { background: var(--tint-red); }
.cart-item__del:active { transform: scale(var(--press-scale)); }

/* 2행 — 수량 · 단가 · 금액
   gap 이 6px 인 이유 — 8 이면 자연폭이 242px 이라 240 을 2px 넘긴다
   (수량 9,999 · 단가 99,999 · 금액 99,999,999 · 실측). 2px 때문에 금액이
   접혀 항목이 57 → 75px 로 뛴다. 마지막 값은 auto 마진으로 오른쪽 끝에
   붙으므로 실제로 좁아져 보이는 곳은 수량↔단가 한 군데뿐이다. */
.cart-item__row {
  display: flex; align-items: center; gap: 0.6rem;
}
.cart-item__row .field--num { width: 5.6rem; flex: none; }
/* 삭제 버튼이 빠지면서 오른쪽 끝이 비었다. 마지막 값(금액 · 유효기간)을
   그 자리로 밀어 세로로 줄을 맞춘다 — 항목마다 자릿수가 달라도 끝이 같다. */
.cart-item__row > :last-child { margin-left: auto; }

.cart-item__cell {
  display: flex; align-items: baseline; gap: var(--space-1);
  font-size: var(--text-2);
  font-variant-numeric: tabular-nums;
}
.cart-item__label { font-size: var(--text-1); color: var(--ink-faint); }
.cart-item__amt   { font-weight: 700; }

/* --------------------------------------------------------------------------
   장바구니 수량 입력칸 (주문 · 다빈도 전용 — 반품은 글자 그대로)
   ★ 2행 전체가 240px 안에 들어가야 한다. 넘치면 접혀서 항목 높이가
     57 → 66px 로 뛴다 (함정 ⑳-b). 그래서 폭을 고정으로 잡는다.
   ★ .field 를 안 쓴다 — 그건 높이 32px 이라 항목이 통째로 커진다.
     여기는 글자와 같은 줄에 얹히는 22px 짜리 작은 칸이다.
   ★ 스피너를 안 만든다(type=text + inputmode=numeric). type=number 면
     스피너가 오른쪽을 먹어 숫자가 잘린다 (함정 ⑳).
   -------------------------------------------------------------------------- */
.cart-item__qty {
  width: 4.4rem; height: 2.2rem; flex: none;
  padding: 0 var(--space-1);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-field);
  background: var(--white);
  font: inherit; font-size: var(--text-2);
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: inherit;
}
.cart-item__qty:focus {
  outline: none;
  border-color: var(--green-accent);
  box-shadow: 0 0 0 1px var(--green-accent);
}
/* 보내는 중에는 잠근다 — 연타로 두 번 보내면 나중 것이 이긴다는 보장이 없다 */
.cart-item__qty[disabled] { opacity: .55; }

/* 1행 오른쪽 끝에 붙는 보조값 (반품내역의 매출일자).
   2행에 넣으면 제조번호·유효기간과 함께 340px 을 넘긴다. */
.cart-item__nm { display: flex; align-items: baseline; gap: var(--space-2); }
.cart-item__nm > .cart-item__main {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis;
}
.cart-item__side {
  flex: none;
  font-size: var(--text-1);
  font-weight: 400;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   10. SUMMARY BAR  (합계)
   ========================================================================== */

.summary {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2) var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: var(--house-green);
  color: var(--ink-white);
  border-radius: 0 0 var(--radius-card) var(--radius-card);
}
/* 요청사항 입력 — 별도 줄을 두지 않고 합계바 안에 넣어 세로 공간을 아낀다 */
.summary__memo {
  flex: 1 1 14rem;
  min-width: 0;
  height: 3.2rem;
  padding: 0 var(--space-2);
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: var(--radius-field);
  background: rgba(255, 255, 255, 0.10);
  color: var(--ink-white);
  font-size: var(--text-2);
  transition: var(--ease);
}
.summary__memo::placeholder { color: var(--ink-white-faint); }
.summary__memo:focus {
  outline: none;
  border-color: var(--green-light);
  background: rgba(255, 255, 255, 0.18);
}

.summary__item { display: flex; align-items: baseline; gap: var(--space-1); }
.summary__label { font-size: var(--text-1); color: var(--ink-white-soft); }
.summary__value { font-size: var(--text-5); font-weight: 700; font-variant-numeric: tabular-nums; }
.summary__value small { font-size: var(--text-2); font-weight: 400; margin-left: 2px; }
.summary__actions { margin-left: auto; display: flex; gap: var(--space-2); }

/* --------------------------------------------------------------------------
   배송비 안내 한 줄 (2026-08-10)
   ★ 구버전 상길이 장바구니 아래에 두던 문구다(주석 처리되어 있다).
     자리를 **합계 바로 아래**로 옮겼다 — 버튼 아래는 시선이 이미 버튼에 가 있어
     늦고, 금액 옆이라야 "얼마 더 담을까" 로 이어진다.
   ★★ **한 줄을 넘기면 안 된다.** 이 줄은 장바구니 높이를 그대로 먹는다 —
     실측 24px = 장바구니 **한 행**. 두 줄이 되면 두 행이다.
     그래서 `white-space: nowrap` 으로 못박고, 넘치면 말줄임으로 자른다
     (자를 만큼 길어졌다면 문구가 잘못된 것이지 칸이 좁은 게 아니다).
   ★ 무료 도달은 초록이 아니라 **밝기**로 알린다. 바탕이 이미 초록이라
     초록 글자는 안 보인다 (함정 ⑱-b 와 같은 자리).
   -------------------------------------------------------------------------- */
.summary__ship-fee {
  width: 100%;
  margin: calc(var(--space-1) * -1) 0 0;
  font-size: var(--text-1);
  line-height: 1.5;
  color: var(--ink-white-soft);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.summary__ship-fee b { font-weight: 700; color: var(--ink-white); }
.summary__ship-fee.is-free { color: var(--ink-white); font-weight: 700; }
/* ★ 합계바의 확정 버튼(주문하기 · 반품신청) — 배경·글자·테두리를 토큰으로 뺐다.
   기본은 '초록 바 위의 흰 알약' 이고 글자가 --green-accent 다.
   ⚠ 그런데 팔레트에 따라 **글자와 바가 같은 색**이 된다 —
     에메랄드는 --green-accent 와 --house-green 이 둘 다 #009476 이다.
     거기에 잠금(.btn:disabled 의 opacity .45)이 걸리면 버튼이 한 덩어리로
     합성되면서 글자 픽셀이 **정확히 바 배경색**으로 떨어진다(실측 0,148,118).
     알약 대비 1.87 — 0건일 때가 늘 그 상태라 '주문하기' 가 거의 안 보였다.
   → 그런 팔레트는 themes.css 에서 --onDark-* 를 뒤집는다. 색은 여기 박지 않는다.
   ⚠ 테두리를 배경과 따로 둔 이유 : 바와 같은 계열로 알약을 칠하면 **버튼 모양이
     안 읽힌다**(에메랄드 실측 1.37 · 흰 알약이던 때는 3.82였다). 배경을 뒤집는
     팔레트는 테두리로 윤곽을 되찾는다.
   ⚠ fallback 은 var() 안에 적어야 한다. :root 에 --onDark-fg: var(--green-accent)
     로 두면 :root 에서 값이 확정돼 팔레트 전환이 안 먹는다 (함정 ③). */
.summary .btn--onDark {
  background:   var(--onDark-bg, var(--white));
  border-color: var(--onDark-border, var(--onDark-bg, var(--white)));
  color:        var(--onDark-fg, var(--green-accent));
}
.summary .btn--onDark:hover {
  background:   var(--onDark-hover, var(--green-light));
  border-color: var(--onDark-border, var(--onDark-hover, var(--green-light)));
}
/* 잠긴 버튼에 마우스를 올려도 색이 튀지 않게 한다.
   공용 .btn:disabled:hover 는 흰 바탕·회색 글자로 되돌리는데, 이 자리는 바탕이
   초록이라 그 조합이 그대로 보이면 눌리는 것처럼 깜빡인다. */
.summary .btn--onDark:disabled:hover,
.summary .btn--onDark[disabled]:hover {
  background:   var(--onDark-bg, var(--white));
  border-color: var(--onDark-border, var(--onDark-bg, var(--white)));
  color:        var(--onDark-fg, var(--green-accent));
}
.summary .btn--ghostDark { background: transparent; border-color: rgba(255,255,255,.6); color: var(--white); }
.summary .btn--ghostDark:hover { background: rgba(255,255,255,.12); }

/* ★ 좁은 컬럼용 — 3패널의 장바구니(350px)에서 쓴다.
   한 줄짜리 합계바는 최소 700px 은 있어야 메모·품목·합계·버튼이 들어간다.
   350px 에서 그대로 쓰면 flex-wrap 이 제멋대로 접혀 높이가 들쭉날쭉해진다
   → 애초에 세로로 쌓아 높이를 확정한다. */
.summary--stack {
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-2);
  /* ⚠⚠ nowrap 이 **반드시** 있어야 한다 (2026-08-06 실측).
     .summary 는 가로 배치용이라 flex-wrap: wrap 이다. 그대로 세로로 눕히면
     '감싸는 세로 플렉스' 가 되는데, 그때 줄의 **가로폭은 컨테이너가 아니라
     가장 넓은 자식의 max-content** 로 정해진다. align-items:stretch 도 그
     줄 폭에 맞춰 늘일 뿐이라 컨테이너를 넘긴다.
     배송 줄(select 84 + 요청사항 195 = 287)을 넣자마자 300px 칸에서 39px 이
     밖으로 나가 「변경」이 패널 바깥에 그려졌다. 자식이 좁을 때는 안 드러난다. */
  flex-wrap: nowrap;
}
/* 줄 안에서 줄어들 수 있어야 한다. min-width:auto 면 입력칸 기본폭이 바닥이 된다. */
.summary--stack > * { min-width: 0; }
.summary--stack .summary__memo { flex: none; }
.summary--stack .summary__row {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-2);
}
.summary--stack .summary__actions { margin-left: 0; }
.summary--stack .summary__value { font-size: var(--text-4); }

/* --- 배송 (배송지를 쓰는 납품처에서만 그려진다) -----------------------------
   ★ 합계바에는 **고른 결과만** 둔다. 우편번호·주소·상세주소·연락처 둘·수령인·
     저장된 배송지 목록은 전부 모달(.modal--ship)에서 그린다.
     300px 칸에 그 일곱 칸을 밀어 넣으면 구버전(638px)보다 못한 화면이 되고,
     주소를 그리는 곳이 두 곳이 된다 (제1 규칙).
   ★ 방문이면 주소 줄을 아예 안 그린다 — 구버전과 같다(order_basket.asp:293).
     빈 칸을 회색으로 남겨 두면 "왜 못 쓰지" 를 묻게 된다.
   ★ 색은 .summary__memo 와 같은 계열이다. 초록 바 위의 반투명 필드 —
     여기만 흰 바탕을 쓰면 합계바가 두 덩어리로 쪼개져 보인다. */
.summary__ship { display: flex; flex-direction: column; gap: var(--space-1); }

/* 배송방법 + 요청사항을 한 줄에 둔다.
   ⚠ 줄을 따로 주면 1366×768 에서 장바구니가 3개→2개로 준다(실측 2026-08-06).
     장바구니 항목 하나는 34px 짜리 select 한 줄보다 비싸다.
   ★ 배송을 안 쓰는 납품처에는 select 가 없다. 그때는 요청사항이 폭을 다 쓴다 —
     빈 자리가 남지 않게 flex 로만 나눈다(고정폭 배분 금지). */
.summary__sel {
  flex: 1 1 auto; min-width: 0;
  height: 2.8rem;
  padding: 0 var(--space-1);
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: var(--radius-field);
  background: rgba(255, 255, 255, 0.10);
  color: var(--ink-white);
  font-size: var(--text-2);
  transition: var(--ease);
}
.summary__sel:focus { outline: none; border-color: var(--green-light); background: rgba(255, 255, 255, 0.18); }
/* 펼친 목록은 OS 가 흰 바탕에 그린다. 흰 글자를 물려주면 안 읽힌다. */
.summary__sel option { color: var(--ink); background: var(--white); }

/* ⚠ 이 세 줄은 **.summary__sel 뒤에** 있어야 한다. 앞에 두면 특정도가 같아
     나중에 오는 .summary__sel 의 flex:1 1 auto 가 이기고, 고정폭이 무시되면서
     select 가 84 → 68px 로 쪼그라든다 (실측 2026-08-06). */
.summary__memorow { display: flex; align-items: center; gap: var(--space-2); }
.summary__memorow .summary__memo { flex: 1 1 auto; }
.summary__memorow .summary__sel { height: 3.2rem; }   /* 요청사항 칸과 높이를 맞춘다 */
.summary__sel--way { flex: none; width: 7.2rem; }

.summary__addr {
  display: flex; align-items: center; gap: var(--space-2);
  width: 100%;
  padding: var(--space-1) var(--space-2);
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: var(--radius-field);
  background: rgba(255, 255, 255, 0.10);
  color: var(--ink-white);
  font-size: var(--text-2);
  text-align: left;
  transition: var(--ease);
}
.summary__addr:hover { background: rgba(255, 255, 255, 0.18); }
.summary__addr-txt {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.summary__addr-act { flex: none; font-size: var(--text-1); color: var(--ink-white-soft); text-decoration: underline; }
.summary__addr.is-empty .summary__addr-txt { color: var(--ink-white-faint); }

/* ==========================================================================
   11. PAGINATION
   ========================================================================== */

/* 목록 아래 고정으로 자리를 먹는 영역이라 최대한 낮게 유지한다.
   여기서 아낀 세로 공간은 그대로 목록·장바구니로 간다. */
.pager {
  display: flex; align-items: center; justify-content: center;
  gap: var(--space-0);
  padding: var(--space-1) 0;
}
/* ★ 프로토타입은 <button>, 앱은 <a class="btn btn--quiet btn--sm"> 이다.
   앱은 페이지 이동이라 링크여야 하고(새 탭·주소 복사), 프로토타입은 갈 곳이 없다.
   두 선택자를 같이 적어 두 화면이 똑같이 보이게 한다.
   이걸 안 해서 앱은 '현재 페이지' 표시(.is-on)가 아예 안 먹고 있었다 —
   .pager button.is-on 이 <a> 에는 걸리지 않는다. */
.pager button,
.pager .btn {
  min-width: 2.4rem; height: 2.4rem;
  padding: 0 var(--space-1);
  border: 0; border-radius: var(--radius-round);
  background: transparent;
  color: var(--ink-soft);
  font-size: var(--text-1);
  font-weight: 600;
  transition: var(--ease);
}

/* ★ 마우스를 올리면 반전한다 — 배경을 채우고 글자를 흰색으로.
   전에는 회색 틴트(--ceramic)뿐이라 눌러도 되는 자리인지 잘 안 보였다.
   현재 페이지(.is-on)는 더 밝은 --green-accent 라 hover 와 구분된다.
     hover  --house-green  #35604F   흰 글자 대비 7.15
     현재   --green-accent #00754A   흰 글자 대비 5.76 */
.pager button:hover,
.pager .btn:hover {
  background: var(--house-green);
  border-color: var(--house-green);
  color: var(--white);
}
.pager button.is-on,
.pager .btn.is-on {
  background: var(--green-accent);
  border-color: var(--green-accent);
  color: var(--white);
}
.pager button:active,
.pager .btn:active { transform: scale(var(--press-scale)); }
.pager__info { margin-left: var(--space-2); font-size: var(--text-1); color: var(--ink-soft); }

/* 목록 아래에 붙는 페이저 자리.
   ★ 3패널로 바뀌면서 제품목록이 세로를 통째로 쓰게 됐다(8행 → 19행).
     100건을 훑고 다음 장으로 넘어가려면 시선이 목록 끝에 있는데
     페이저가 헤더 우측에 있으면 되돌아 올라가야 한다.
     스크롤은 .scroll-y 안에서 돌므로 이 줄은 늘 보인다 (같이 안 밀린다).
   ※ 세로 한 줄(약 34px = 목록 1행)을 쓴다. 2단 배치 때 헤더로 올렸던 이유가
     그것이었는데, 지금은 19행이라 1행을 내줄 만하다. */
/* 목록을 조각으로 갈아 끼우는 동안 (검색 · 페이지 이동).
   ★ 스피너를 띄우거나 내용을 비우지 않는다 — 그러면 그게 곧 깜빡임이다.
     옛 목록을 그대로 두고 살짝 흐리게만 해서 '바뀌는 중' 을 알린다. */
.scroll-y.is-loading { opacity: .55; transition: opacity var(--ease); }

/* --- 즉시 필터에서 찾은 글자 (제품목록 전체 보기 · 2026-08-12) --------------
   ★ 브라우저 기본 <mark> 는 배경만 바꾸고 여백이 없어 글자가 붙어 보인다.
     좁은 칸(상품명 150px)이라 **가로 여백을 안 준다** — 주면 그만큼 이름이
     더 잘린다. 대신 아주 얕은 라운드로 덩어리를 구분한다.
   ⚠ `color` 를 반드시 명시한다. 기본값이 브라우저마다 달라 팔레트를 바꾸면
     노란 배경에 흰 글자가 되는 조합이 나온다. */
mark.hl {
  background: var(--mark-bg);
  color: var(--ink);
  padding: 0;
  border-radius: 2px;
  font-weight: 700;
}

/* --- 화살키로 목록을 훑을 때 (주문 화면) ---------------------------------
   포커스를 받는 것은 행이 아니라 목록 상자다. 행마다 tabindex 를 붙이면
   탭 순서에 100개가 끼어들어 키보드로 화면을 빠져나갈 수가 없다.
   ★ 그래서 상자에 기본 테두리를 그리지 않는다 — '지금 어디' 는 선택행
     (.is-selected · 배경 + 볼드)이 이미 말하고 있고, 테두리까지 그리면
     목록 전체가 한 번 더 둘러싸여 시선이 분산된다.
   ⚠ 포커스 표시를 그냥 지우는 것이 아니다. 대신할 표시가 화면에 있는지
     확인하고 지운 것이다 — 없으면 '잠근 것은 잠겨 보여야 한다' 의 반대가 된다. */
#goodsBox:focus { outline: none; }

.panel__foot {
  border-top: 1px solid var(--line);
  background: var(--neutral-cool);
  border-radius: 0 0 var(--radius-card) var(--radius-card);
}

/* 패널 헤더 안에 넣는 축소판 — 목록 아래 한 줄을 통째로 없앤다.
   3패널 주문화면은 목록 아래로 되돌렸고, 조회 화면들이 아직 이걸 쓴다. */
.pager--inline { padding: 0; }
.pager--inline button,
.pager--inline .btn { min-width: 2.2rem; height: 2.2rem; font-size: 1.1rem; }
.pager--inline .pager__info { margin-left: var(--space-1); white-space: nowrap; }

/* ==========================================================================
   12. BANNER COLUMN
   ========================================================================== */

.banners {
  align-self: start;
  max-height: 100%;        /* 행 높이를 넘겨 푸터를 밀지 않게 */
  overflow-y: auto;
  display: grid; gap: var(--space-2);
}
.banners::-webkit-scrollbar { width: 0; }
.banner {
  display: block;
  width: 100%;
  border-radius: var(--radius-card);
  overflow: hidden;
  box-shadow: var(--shadow-card);
  background: var(--white);
  transition: var(--ease);
}
.banner:hover { box-shadow: var(--shadow-float); }
.banner:active { transform: scale(var(--press-scale)); }
/* ★ 주소가 없는 배너는 <div> 다 (눌러도 아무 일이 없는 링크를 안 만든다).
     그때는 눌리는 시늉도 하지 않는다 — 반응만 하고 결과가 없는 것이
     가장 헷갈린다 (잠긴 버튼과 같은 판단). */
div.banner { cursor: default; }
div.banner:hover { box-shadow: var(--shadow-card); }
div.banner:active { transform: none; }

/* ★★ 업로드한 그림 (2026-07-31). 그림이 있으면 색+문구 대신 이것만 그린다.
   ★ 권장 규격은 152 x 200 이다 — 컬럼 폭이 152px 고정이라 가로가 그보다
     크면 세로만 길어진다. 관리자 화면이 그 숫자를 안내한다.
   ⚠ 세로는 안 자른다. 잘라 버리면 관리자가 올린 그림의 아래쪽이 사라지는데
     화면은 그 사실을 말하지 않는다 — 규격이 다르면 **길어지는 편**이 낫다.
     대신 .banners 가 세로 스크롤을 가지므로 푸터를 밀지 않는다. */
.banner__img { display: block; width: 100%; height: auto; }

/* 프로토타입용 플레이스홀더 */
.banner__ph {
  aspect-ratio: 128 / 166;
  display: grid; place-items: center;
  text-align: center;
  padding: var(--space-2);
  font-size: var(--text-1);
  font-weight: 600;
  line-height: 1.4;
}

/* ==========================================================================
   13. FLOATING ACTION  (Design.md의 Frap — 주문하기 상시 노출)
   ========================================================================== */

.fab {
  position: fixed;
  right: calc(var(--gutter) - 0.8rem);
  bottom: calc(var(--gutter) - 0.8rem);
  width: 5.6rem; height: 5.6rem;
  border: 0; border-radius: var(--radius-round);
  background: var(--green-accent);
  color: var(--white);
  display: grid; place-items: center;
  box-shadow: var(--shadow-float);
  z-index: 40;
  transition: all var(--ease);
}
.fab:hover { background: var(--brand-green); }
.fab:active { transform: scale(var(--press-scale)); box-shadow: var(--shadow-float-active); }
.fab__count {
  position: absolute; top: -2px; right: -2px;
  min-width: 2.1rem; height: 2.1rem;
  padding: 0 5px;
  border-radius: var(--radius-pill);
  background: var(--gold);
  color: var(--house-green);
  font-size: 1.1rem; font-weight: 700;
  display: grid; place-items: center;
  border: 2px solid var(--white);
}

/* ==========================================================================
   14. FOOTER
   ========================================================================== */

/* 배경은 전체 너비, 내용은 셸과 같은 폭으로 정렬 (GEOWEB 방식) */
.foot {
  background: var(--house-green);
  color: var(--ink-white-soft);
  font-size: var(--text-1);
  line-height: 1.6;
}
.foot__inner {
  width: var(--shell-w);
  max-width: 100%;
  margin-inline: auto;
  /* 좌우는 셸과 같은 4px. 16 으로 두면 푸터 글자가 사이드바보다 12px 안쪽으로
     들어가 왼쪽 끝선이 어긋난다 (실측 사이드바 47 / 푸터 59). */
  padding: var(--space-6) var(--space-1);
  display: flex; align-items: center; gap: var(--space-4);
}
.foot a { color: var(--ink-white); }
.foot a:hover { text-decoration: underline; }
.foot__links { margin-left: auto; display: flex; align-items: center; gap: var(--space-4); white-space: nowrap; }

/* --------------------------------------------------------------------------
   푸터 '화면 색' — 약관 링크와 구별되어야 한다
   ★ 처음에는 같은 <a> 였는데 셋이 똑같이 생겨서 문서 링크로 읽혔다.
     테두리로 **누르는 것**임을 알리고, 칩으로 지금 색을 보여준다.
   ★ 칩 안쪽 흰 테두리가 핵심이다 — 대부분의 팔레트에서 --sidebar-bg 와
     푸터 배경(--house-green)이 **같은 색**이라, 링이 없으면 칩이 통째로 사라진다.
     (그린 1·블루 1·청록·보라·와인·뉴트럴이 전부 그렇다)
   ★ hover 는 배경이 아니라 밝기로 준다. 어두운 면에서 배경을 밝히면 글자 대비가
     같이 떨어진다 (함정 ⑱-b).
   -------------------------------------------------------------------------- */
.foot__skin {
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 3px var(--space-2) 3px 5px;
  border: 1px solid var(--ink-white-soft);
  border-radius: var(--radius-pill);
  line-height: 1.4;
}
.foot__skin:hover { border-color: var(--ink-white); text-decoration: none; }
.foot__skin:active { transform: scale(var(--press-scale)); }
.foot__skin-chip {
  width: 1.4rem; height: 1.4rem;
  border-radius: var(--radius-round);
  background: var(--sidebar-bg);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .65);
}

/* --------------------------------------------------------------------------
   푸터 「공식 홈페이지」 — 주소가 있을 때만 그려진다 (2026-08-10)
   ★ 구버전(상길) 푸터 오른쪽의 연두 알약 자리다. 회사 홈페이지가 따로 있는
     도매만 쓴다 — 「문구 관리 › 홈페이지 주소」가 비면 마크업이 아예 없다.
     그래서 이 규칙은 **켜고 끄지 않는다.** 없으면 없는 것이다.
   ★ 약관 링크·'화면 색' 과 **달라야 한다.** 넷이 같은 <a> 면 문서 링크로
     읽혀 눈에 안 띈다 (바로 위 foot__skin 과 같은 근거).
   ★★ 글자를 --green-accent 가 아니라 **--ink** 로 둔다. 연한 바탕 위의
     accent 는 팔레트에 따라 명암비가 3.15 까지 떨어진다
     (초록6 #009476 on #d4ede8 · 계산). '특가' 배지가 이미 겪고 있는 문제를
     새 자리에 복사하지 않는다.
   ★ 기본 규칙에 `.foot` 을 안 붙인다 — 문구 관리의 푸터 미리보기(.pv__foot)가
     같은 클래스를 쓴다. 거기서만 다르게 생기면 CS 가 저장하기 전에 보는 것과
     저장한 뒤 나오는 것이 갈라진다.
   ⚠ 색만 `.foot a.foot__home` 으로 한 번 더 눌러 준다 — 위의
     `.foot a { color: 흰색 }` 이 클래스 하나짜리 규칙을 이긴다(0,1,1 > 0,1,0).
   -------------------------------------------------------------------------- */
.foot__home {
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 3px 4px 3px var(--space-3);
  background: var(--green-light);
  color: var(--ink);
  border-radius: var(--radius-pill);
  font-weight: 600; line-height: 1.4;
  white-space: nowrap;
}
.foot a.foot__home { color: var(--ink); }
.foot a.foot__home:hover { background: var(--white); text-decoration: none; }
.foot a.foot__home:active { transform: scale(var(--press-scale)); }
/* 화살표 칩 — 팔레트 16종 실측 (2026-08-10)
     알약 글자 : 11.13 ~ 13.44   (--ink on --green-light)
     알약 ↔ 푸터 : 3.11 ~ 6.12   (구분되는가)
     칩 화살표 : 3.82 ~ 7.75     (최저는 emerald · --house-green 이 #009476)
   ★ 화살표는 aria-hidden 인 **장식**이다 — 링크가 무엇인지는 글자가 말한다.
     그래서 최저 3.82 를 글자 기준(4.5)이 아니라 UI 요소 기준(3.0)으로 본다.
     ⚠ 여기에 --green-accent 를 쓰면 안 된다. 연한 바탕 위에서 2점대까지
       떨어지는 팔레트가 있다 ('특가' 배지가 그 상태다). */
.foot__home-go {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.8rem; height: 1.8rem;
  border-radius: var(--radius-round);
  background: var(--house-green); color: var(--ink-white);
  font-size: var(--text-2); line-height: 1;
}

/* ==========================================================================
   14b. FORM LAYOUT  (회원정보 등 입력 화면)
   ========================================================================== */

.form-grid {
  display: grid;
  grid-template-columns: 12rem minmax(0, 1fr);
  align-items: center;
  gap: var(--space-2) var(--space-3);
}
.form-grid > .lbl {
  font-size: var(--text-3);
  font-weight: 600;
  color: var(--ink-soft);
  text-align: right;
}
.form-grid > .lbl .req { color: var(--red); margin-left: 2px; }
.form-grid > .val { display: flex; align-items: center; gap: var(--space-1); flex-wrap: wrap; }
.form-grid > .val .field { min-width: 0; }
/* ★ `.form-grid` 밖에서도 쓴다 (2026-08-12). 칸이 하나뿐인 패널은 2열 그리드를
     안 쓰는데, 선택자가 `.form-grid .hint` 로 묶여 있어 그런 자리에서는 설명
     글이 본문 크기·본문 색으로 나왔다. 뜻은 어디서나 같으니 선택자를 넓힌다. */
.hint { font-size: var(--text-1); color: var(--ink-faint); }
/* 칸이 하나뿐인 패널의 설명 줄 — <p> 기본 여백을 죽이고 위만 띄운다 */
.hint--under { display: block; margin: var(--space-2) 0 0; }
.form-grid .sep { grid-column: 1 / -1; height: 1px; background: var(--line); margin: var(--space-2) 0; }
.form-actions {
  display: flex; justify-content: center; gap: var(--space-2);
  padding-top: var(--space-4);
}
@media (max-width: 720px) {
  .form-grid { grid-template-columns: 1fr; }
  .form-grid > .lbl { text-align: left; }
}

/* ==========================================================================
   14c. LOGIN  (독립 화면 — 셸 없음)
   ========================================================================== */

/* ★★ 이제 한 장짜리 카드가 아니라 **랜딩**이다 (2026-08-01).
   비주얼 + 네 칸이 세로로 쌓이므로 가운데정렬(place-content:center)을 쓰면
   내용이 100vh 를 넘는 순간 **위쪽이 화면 밖으로 밀려난다** — 실측에서
   비주얼이 y=-21 로 잘려 나갔다. 위에서부터 쌓고, 남으면 아래가 빈다. */
.login {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* ★ 남는 세로 자리를 **위아래로 나눈다.** 아래에만 몰리면 화면이
     덜 채워진 것처럼 보이고, 카드를 늘려 채우면 그림 비율이 흔들린다.
     ⚠⚠ **`safe` 가 반드시 있어야 한다** (2026-08-07 실측).
       그냥 `center` 는 내용이 상자보다 커지면 **넘치는 만큼을 위로도 밀어낸다.**
       메인 그림이 전체 폭이 되면서 내용 965 > 상자 920 이 됐고,
       카드 위쪽이 y = **-21px** 로 잘려 위쪽 패딩 40px 이 통째로 사라졌다.
       `safe center` 는 넘칠 때만 위쪽 정렬로 떨어져 패딩을 지킨다.
       ※ 두 줄인 이유 : `safe` 를 모르는 브라우저는 둘째 줄을 통째로 버리고
         첫 줄(center)로 돈다. 한 줄만 적으면 그런 브라우저에서 정렬이 풀린다.
       ※ 이 파일 1616행 주석이 같은 증상을 이미 적어 뒀는데 코드에는 안 들어가
         있었다 — 그래서 다시 나타났다. */
  justify-content: center;
  justify-content: safe center;
  gap: var(--space-3);
  /* ★ 위아래 여백 40 → 24 (2026-08-07). safe center 로 위쪽 패딩이 되살아나자
     이번엔 위만 넓고 아래가 잘려 보였다 — 내용이 화면보다 커서 위에서 준 만큼
     그대로 아래로 밀린다. 좌우는 그대로 24. */
  padding: var(--space-4);
}
/* ★★ **세로 flex + `min-height:100vh` 는 항목을 눌러 버린다.**
   내용이 화면보다 길어지면 `flex-shrink:1`(기본값) 때문에 카드가 제 높이보다
   작아진다 — 그리드 행은 163.5+220 인데 카드 높이가 **0** 으로 잡혔다(실측).
   전에 `.login__panel` 하나만 막아 뒀는데, 메인을 여러 블록으로 나눈 뒤
   `.lp-hero` 가 같은 함정에 빠졌다 → **자식 전부**를 막는다. */
.login > * { flex: 0 0 auto; }

/* ★ 하나의 패널 위에 브랜드 소개 + 로그인 폼을 나란히 올린다.
   배경 사진은 패널 전체에 옅게 깔고, 글자는 그 위로 올린다. */
/* ★ 로그인 카드와 그 아래 정보칸이 **같은 폭**을 쓴다 (2026-08-01).
   구버전 gnhds 메인이 비주얼과 상자 줄을 같은 폭으로 놓은 것과 같다 —
   폭이 다르면 한 화면이 아니라 두 화면처럼 보인다. */
:root { --login-w: 98rem; }

.login__panel {
  position: relative;
  overflow: hidden;
  /* ⚠ `min(98rem, 100%)` 로 쓰면 안 된다 — 이 카드는 내용 크기로 잡히는
     그리드 칸 안에 있어서 `100%` 가 자기 내용폭을 되참조한다. 실측 730px 로
     접혀 있었다(의도 980). 뷰포트 기준으로 못 박는다.
     `.join__panel` 에서 같은 자리를 밟았다. */
  width: min(var(--login-w), calc(100vw - 3.2rem));
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  background: var(--white);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-float);
}
/* ★ --login-bg — 관리자가 올린 메인 배경. 없으면 기본 그림이 그대로 나온다.
   ⚠⚠ **이 변수는 지금 아무도 안 넣는다** (2026-08-26). login.asp 이 넣던 것을
     걷어냈다 — 넣는 순간이 곧 `.lp-hero--pic::before{content:none}` 인 순간이라
     켜지는 경우가 없으면서 404 만 났다. 이 카드(.login__panel)는 join.asp 것이라
     기본 그림으로 잘 돌아간다.
   ⚠⚠ **되살릴 때는 루트부터 시작하는 주소로 넣을 것.** 인라인 커스텀 속성에
     상대주소를 넣으면 브라우저가 **문서가 아니라 이 파일(assets/app.css) 기준**
     으로 풀어 `assets/` 가 한 번 더 붙는다. `src="…"` 는 멀쩡한데 CSS 변수에
     들어가는 순간 어긋난다 — 실측으로 잡았다.
   ★★ **주소만 바꾼다.** opacity·크기·위치는 여기 그대로 둔다 —
     인라인으로 background 를 통째로 쓰면 그 셋이 화면 파일로 끌려 나와
     두 벌이 된다. 그리고 opacity 를 화면에서 정하게 두면 언젠가 진한 사진이
     올라와 **글자가 안 읽힌다.** 읽히는 것은 코드가 지킨다. */
.login__panel::before {
  content: "";
  position: absolute; inset: 0;
  background: var(--login-bg, url("img/pharmacy_bg.png")) center / cover no-repeat;
  opacity: 0.2;
}

/* 올린 회사 로고. 글자 워드마크(.brandmark) 자리를 그대로 쓴다.
   ★ 높이로 맞춘다 — 가로로 긴 로고든 정사각이든 같은 줄에 앉는다. */
.login__logo {
  display: block;
  max-width: 100%;
  max-height: 6.4rem;
  margin-inline: auto;
  object-fit: contain;
}

.login__intro,
.login__form {
  position: relative;                 /* 배경 위로 */
  display: flex; flex-direction: column; justify-content: center;
  padding: var(--space-7) var(--space-5);
  min-width: 0;
}
.login__intro { text-align: center; }
.login__form  { border-left: 1px solid var(--line); }
.login__inner { width: 100%; max-width: 30rem; margin-inline: auto; }

.login__eyebrow {
  font-size: var(--text-1); font-weight: 700;
  color: var(--ink-soft); letter-spacing: var(--tracking-loose);
}
.login__brand {
  font-size: 4.4rem; font-weight: 800;
  color: var(--brand-green); letter-spacing: -0.03em;
  line-height: var(--leading-compact);
  margin-top: var(--space-1);
}

/* --------------------------------------------------------------------------
   로그인 워드마크 — 획을 따라 그려지는 상호 (2026-07-30)
   ★ **자바스크립트가 없다.** CSS 애니메이션 하나다. 외부 런타임(Lottie 등)을
     안 쓰는 이유는 앱이 자기 완결이어야 하고, 파일이 하나 늘면 캐시·경로·버전을
     또 관리해야 하기 때문이다 (함정 ⑲ · 아이콘을 인라인 SVG 로 둔 것과 같은 판단).
   ★ **글자는 진짜 글자다.** <text> 에 스트로크를 걸어 윤곽을 따라 그린다 —
     패스로 변환하지 않으므로 상호가 바뀌면 글자만 고치면 된다.
     ⚠ 그래서 '사람이 쓰는 획 순서' 는 아니다. 그건 손글씨 패스를 직접 그려야 한다.
   ★ **그라데이션이 팔레트를 따라간다.** stop-color 가 토큰이라 사용자가 고른
     화면 색으로 같이 바뀐다. 고정 hex 를 쓰면 팔레트를 바꿨을 때 여기만 옛 색으로
     남는다 (함정 ③ · ⑱ 과 같은 뿌리).
   ★ 애니메이션이 끝나면 **원래 모습 그대로**다 — 색·크기·자간이 h1 과 같다.
     움직임을 못 보는 환경에서도 잃는 것이 없어야 한다.
   -------------------------------------------------------------------------- */
/* ★ width:100% 로 두면 컨테이너 폭에 맞춰 늘어나 글자 크기가 h1 과 달라진다.
   높이를 고정하고 폭을 auto 로 둬야 44px 그대로 그려진다 (실측 배율 1.00). */
/* ★ margin-inline:auto 가 있어야 가운데로 온다. 부모(.login__intro)의
   `text-align: center` 는 **인라인 내용**만 옮긴다 — 글자였을 때는 그것으로
   가운데 정렬됐지만, SVG 는 display:block 이라 안 따라온다. 실제로 왼쪽에 붙었다.

   ★★ **가운데 정렬을 viewBox 폭에 기대지 않는다** (2026-07-30 · 실측으로 잡았다).
     상호를 관리자가 고칠 수 있게 되면서 viewBox 폭을 글자 수로 계산했는데,
     그 식(37.2px/자)은 **한글에서만 맞는다.** 폰트가 멀쩡한 상태에서도
       SEOUL CU      viewBox 298 / 실제 205 → 가운데서 **46px 왼쪽**
       한글6자 CU  viewBox 335 / 실제 289 → **23px 왼쪽**
     로 어긋났다. 폰트 폴백(맑은 고딕 +15%)에서는 반대로 넘쳐 흘렀다.
     → <text> 를 `x="50%" text-anchor="middle"` 로 두면 **글자가 제 박스의
       가운데**에 놓이고, 박스는 margin-inline:auto 로 카드 가운데에 놓인다.
       viewBox 가 얼마든 가운데 정렬은 항상 맞는다 — 폭 추정 오차가
       **양옆 여백**으로만 남는다(안 보인다).
   ★ max-width 로 카드 밖으로 나가는 길을 막는다. 추정이 좁아 넘치더라도
     SVG 가 줄어들 뿐 글자가 잘리거나 삐져나오지 않는다. */
.brandmark {
  display: block; height: 5.4rem; width: auto;
  max-width: 100%;
  margin-inline: auto; overflow: visible;
}
.brandmark__t {
  font-family: inherit;
  font-size: 44px; font-weight: 800; letter-spacing: -0.03em;
  fill: url(#bmGrad);
  stroke: url(#bmGrad);
  stroke-width: 0.7;
  stroke-linejoin: round;
  /* 총 길이는 <text> 에서 못 잰다(getTotalLength 가 없다). 넉넉히 잡고
     그리는 시간으로 속도를 맞춘다 — 값이 커도 끝 상태는 같다. */
  stroke-dasharray: 1600;
  stroke-dashoffset: 1600;
  fill-opacity: 0;
  animation:
    bmDraw 1.6s cubic-bezier(.65, 0, .35, 1) .15s forwards,
    bmFill  .7s cubic-bezier(.65, 0, .35, 1) 1.2s forwards;
}
@keyframes bmDraw { to { stroke-dashoffset: 0; } }
@keyframes bmFill { to { fill-opacity: 1; } }

/* ★ 움직임을 줄이는 설정이면 **처음부터 완성된 모습**으로 둔다.
   애니메이션만 끄면 dashoffset 이 남아 상호가 통째로 안 보인다 — fail-open. */
@media (prefers-reduced-motion: reduce) {
  .brandmark__t {
    stroke-dashoffset: 0; fill-opacity: 1; animation: none;
  }
}
.login__lead { font-size: var(--text-6); font-weight: 600; margin-top: var(--space-4); }
.login__lead em { color: var(--green-accent); font-style: normal; }
.login__sub { font-size: var(--text-4); color: var(--ink-soft); margin-top: var(--space-2); white-space: pre-line; }
.login__stat { margin-top: var(--space-4); font-size: var(--text-5); font-weight: 600; }
.login__stat b { color: var(--green-accent); font-size: var(--text-6); }
.login__promo {
  margin-top: var(--space-5);
  display: block;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-card);
  background: var(--house-green);
  color: var(--ink-white);
  box-shadow: var(--shadow-card);
  transition: var(--ease);
}
.login__promo:hover { box-shadow: var(--shadow-float); }
.login__promo:active { transform: scale(var(--press-scale)); }
.login__promo b { color: var(--gold); }

.login__lock {
  width: 5.6rem; height: 5.6rem;
  margin: 0 auto var(--space-4);
  border-radius: var(--radius-round);
  background: var(--green-light);
  color: var(--brand-green);
  display: grid; place-items: center;
  font-size: 2.4rem;
}
.login__form .field { width: 100%; height: var(--control-h-lg); margin-bottom: var(--space-2); }
.login__row { display: flex; align-items: center; justify-content: space-between; margin: var(--space-2) 0 var(--space-3); }
.login__links {
  display: flex; justify-content: center; gap: var(--space-3);
  margin-top: var(--space-3); padding-top: var(--space-3);
  border-top: 1px solid var(--line);
  font-size: var(--text-2); color: var(--ink-soft);
}
.login__links a:hover { color: var(--green-accent); text-decoration: underline; }

/* --- 다른 화면이 넘겨준 안내 (지금은 가입 완료) ---------------------------
   ★ 오류(붉은 글씨)와 **다른 색**이어야 한다. 가입에 성공했는데 붉게 뜨면
     실패한 줄 안다. */
.login__note {
  font-size: var(--text-2);
  text-align: center;
  color: var(--green-accent);
  background: var(--tint-green, rgba(27, 94, 75, 0.07));
  border-radius: var(--radius-btn, 10px);
  padding: var(--space-2);
  margin-bottom: var(--space-2);
  white-space: pre-line;
}

/* ==========================================================================
   14c-2. JOIN  (회원가입 — 로그인과 같은 셸 밖 카드)
   --------------------------------------------------------------------------
   ★ 로그인 카드(.login__panel)를 그대로 쓰되 **한 칸짜리**다. 좌측 브랜드
     소개가 없다 — 가입은 읽을 것이 아니라 채울 것이라 폭을 다 준다.
   ★ 입력칸이 여덟이라 세로가 길다. 화면 높이에 맞춰 늘이지 않는다
     (늘이면 내용이 잘린다 — 19절 로그인에서 겪은 것과 같은 자리다).
   ========================================================================== */
.join__panel {
  grid-template-columns: minmax(0, 1fr);
  /* ⚠ `min(52rem, 100%)` 로 쓰면 안 된다 — 이 카드는 내용 크기로 잡히는
     그리드 칸 안에 있어서 `100%` 가 자기 내용폭을 되참조한다. 실측 365px 로
     접혔다(의도는 520). 폭을 확정하고 좁은 화면만 max-width 로 막는다. */
  /* ⚠ `max-width: 100%` 도 같은 함정이다 — 100% 가 또 자기 칸을 참조한다.
     좁은 화면에서 카드가 화면보다 넓어졌다. **뷰포트 기준**으로 못 박는다
     (3.2rem = .login 의 좌우 여백 몫). */
  width: min(76rem, calc(100vw - 3.2rem));
  /* ⚠ `.login` 이 플렉스 세로 배치로 바뀌면서(2026-08-01 메인 화면 개편)
     `align-self` 의 뜻이 **가로**로 바뀌었다 — start 를 두었더니 카드가
     왼쪽에 붙었다(실측 왼쪽 24 / 오른쪽 830).
     세로로 안 늘어나게 하려던 것이므로 이제 `flex: none` 이 맞는 말이다. */
  align-self: center;
  flex: 0 0 auto;
}
/* ★★ **두 칸으로 짠다.** 한 칸으로 쌓았더니 카드가 923px 이 되어 작은 창에서
   비밀번호/확인 · 성명/회사명 · 전화/휴대전화.
   ⚠ 좁은 화면에서는 한 칸으로 되돌린다. 두 칸을 고집하면 칸이 반토막 난다. */
.join__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--space-3);
}
.join__f--wide { grid-column: 1 / -1; }
@media (max-width: 620px) {
  .join__grid { grid-template-columns: minmax(0, 1fr); }
}
/* ★ 로그인 폼은 안쪽을 300px 로 좁혀 둔다(입력칸이 둘뿐이라 넓으면 허전하다).
   가입은 여덟이라 그 폭이면 세로만 길어진다 — 카드 폭을 그대로 쓴다. */
.join__panel .login__inner { max-width: none; }
.join__title {
  font-size: var(--text-5);
  font-weight: 800;
  text-align: center;
  margin-bottom: var(--space-1);
}
.join__lead {
  font-size: var(--text-2);
  color: var(--ink-soft);
  text-align: center;
  line-height: 1.6;
  margin-bottom: var(--space-3);
}
.join__lbl {
  display: block;
  font-size: var(--text-2);
  font-weight: 700;
  margin: var(--space-2) 0 4px;
}
.join__lbl .req { color: var(--red); }

/* 아이디 칸 + 중복확인 버튼 한 줄 */
.join__row { display: flex; gap: var(--space-1); }
.join__row .field { flex: 1 1 auto; min-width: 0; }
.join__row .btn { flex: 0 0 auto; }

/* 중복확인 결과. ★ 자리를 늘 잡아 둔다 — 글이 생길 때 아래가 밀리면
   버튼 위치가 움직여 두 번 누르게 된다. */
.join__hint {
  min-height: 1.8rem;
  font-size: var(--text-1);
  color: var(--ink-soft);
  padding-top: 4px;
}
.join__hint.is-ok   { color: var(--green-accent); }
.join__hint.is-warn { color: var(--red); }

/* --- 1단계 : 개인정보 수집·이용 동의 --------------------------------------
   ★ 스스로 스크롤한다. 약관이 길다고 카드가 화면 밖으로 자라면 [동의합니다]
     버튼이 안 보인다 — 눌러야 다음으로 가는 화면에서 그건 막다른 길이다. */
.join__terms {
  max-height: 42rem;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-btn, 10px);
  padding: var(--space-3);
  margin-bottom: var(--space-3);
  font-size: var(--text-2);
  line-height: 1.7;
  color: var(--ink-soft);
  background: var(--canvas);
}
.join__terms h2 {
  font-size: var(--text-2);
  font-weight: 700;
  color: var(--ink);
  margin: var(--space-3) 0 var(--space-1);
}
.join__terms h2:first-child { margin-top: 0; }
.join__terms p { margin-bottom: var(--space-1); }
.join__terms .ind { padding-left: var(--space-3); }

.join__err {
  color: var(--red);
  font-size: var(--text-2);
  text-align: center;
  margin-bottom: var(--space-2);
  white-space: pre-line;
}
/* 별표 범례. ★ 칸마다 * 를 달아 놓고 뜻을 안 적으면 처음 보는 사람은
   그게 필수인지 강조인지 모른다. */
.join__legend {
  font-size: var(--text-1);
  color: var(--ink-soft);
  margin-bottom: var(--space-2);
}
.join__legend .req { color: var(--red); }

@media (max-width: 860px) {
  .login__panel { grid-template-columns: 1fr; }
  .login__form { border-left: 0; border-top: 1px solid var(--line); }
}

/* ==========================================================================
   14d. ARTICLE  (공지사항 상세 — 구버전 company/notic_view.asp)
   --------------------------------------------------------------------------
   본문 길이가 글마다 다르므로 이 화면은 .app--auto 로 행 높이 고정을 푼다.
   (고정하면 짧은 글에서 본문이 늘어나 붕 뜨고, 긴 글은 잘린다)
   ========================================================================== */

.article__head {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--line);
  background: var(--neutral-cool);
}
.article__title {
  font-size: var(--text-6);
  font-weight: 700;
  color: var(--brand-green);
  letter-spacing: -0.02em;
  line-height: var(--leading-compact);
  word-break: keep-all;          /* 한글은 음절이 아니라 어절 단위로 끊는다 */
}
.article__title .badge { vertical-align: 0.35rem; margin-right: var(--space-1); }

.article__meta {
  display: flex; flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  margin-top: var(--space-2);
  font-size: var(--text-2);
  color: var(--ink-soft);
}
.article__meta i {                /* 라벨 — 값보다 한 단계 옅게 */
  font-style: normal;
  color: var(--ink-faint);
  margin-right: var(--space-1);
}

/* --- 첨부파일 ---
   구버전은 notic.filename 을 읽어만 두고 화면에 뿌리지 않았다(미구현).
   자리를 미리 잡아둔다 — 첨부가 없으면 이 블록째로 감춘다. */
.article__files {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-bottom: 1px solid var(--line);
  font-size: var(--text-2);
}
.article__files > .field-label { flex: none; }
.file-link {
  display: inline-flex; align-items: center; gap: var(--space-1);
  height: 2.6rem;
  padding: 0 var(--space-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-pill);
  background: var(--white);
  color: var(--green-accent);
  font-weight: 600;
  transition: var(--ease);
}
.file-link:hover { border-color: var(--green-accent); background: var(--green-light); }
.file-link:active { transform: scale(var(--press-scale)); }
.file-link .sz { color: var(--ink-faint); font-weight: 400; }

/* --- 본문 ---
   목록과 달리 '읽는' 영역이므로 행간을 1.5 → 1.75 로 넓힌다. */
.article__body {
  padding: var(--space-4);
  min-height: 16rem;
  font-size: var(--text-4);
  line-height: 1.75;
  word-break: keep-all;
}
.article__body > * + * { margin-top: var(--space-3); }
/* ★ 편집기의 「큰 제목」(2026-08-07). h3 보다 한 단계 위다.
     ⚠ 이 규칙이 없으면 공지 화면에서만 크기가 안 붙는다 — 편집기와 문서 화면에는
       h2 스타일이 있어서 **거기서는 멀쩡해 보인다.** 그런 어긋남이 제일 늦게 잡힌다. */
.article__body h2 {
  font-size: var(--text-6);
  font-weight: 700;
  color: var(--house-green);
  margin-top: var(--space-5);
  line-height: var(--leading-compact);
}
.article__body h3 {
  font-size: var(--text-5);
  font-weight: 700;
  color: var(--house-green);
  margin-top: var(--space-5);
}
.article__body ul { list-style: disc; padding-left: 2rem; }
.article__body ol { list-style: decimal; padding-left: 2rem; }
/* ★ 그림만 든 목록에는 점도 들여쓰기도 주지 않는다 (2026-08-07).
     편집기에서 커서가 목록 줄에 있을 때 그림을 넣으면 `<ul><li><img></li></ul>` 이
     되는데, 그러면 그림 왼쪽에 점 하나가 찍히고 2rem 만큼 밀린다. 실제로 회사소개가
     그 상태였다 — 본문에 뜻이 있어 목록으로 만든 것이 아니라 **넣다 보니 걸린 것**이다.
   ⚠ `:has()` 를 모르는 브라우저에서는 이 두 줄이 통째로 무시된다.
     그때는 점이 남을 뿐 레이아웃은 안 깨진다 (안전한 쪽으로 떨어진다). */
.article__body ul:has(> li > img) { padding-left: 0; list-style: none; }
.article__body li:has(> img) { list-style: none; }
.article__body li + li { margin-top: var(--space-1); }
.article__body strong { font-weight: 700; color: var(--brand-green); }
.article__body a { color: var(--green-accent); text-decoration: underline; }
/* 관리자가 붙여넣은 표가 패널을 밀어내지 않게 */
.article__body table {
  table-layout: fixed;
  font-size: var(--text-3);
  border: 1px solid var(--line);
}
.article__body th, .article__body td {
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--line);
  text-align: center;
}
.article__body th { background: var(--ceramic); color: var(--ink-soft); font-weight: 600; }
/* ★ 본문 이미지는 편집기가 넣은 것이라 폭·높이가 인라인 style 로 박혀 있다.
     실측 : 데모 DB 에 `style="width: 1263px"` 짜리가 있다 (본문 폭은 1076).
     그대로 두면 **화면이 가로로 밀린다** — max-width 로 가둔다.
   ⚠ !important 를 쓴다. 인라인 style 은 클래스 규칙보다 강해서 이것 말고는
     이길 방법이 없다. 본문은 우리가 안 쓰는 HTML 이라 예외로 둔다.
   ※ height:auto 를 같이 줘야 비율이 안 깨진다 (width 만 줄이면 세로가 그대로다). */
.article__body img {
  display: block; margin-inline: auto;
  max-width: 100% !important; height: auto !important;
}

/* --- 문서 (이용약관 · 개인정보처리방침) -------------------------------------
   ★ 공지 본문(.article__body)에 얹는다. 읽는 영역이라는 점이 같으니
     새 규칙을 처음부터 만들지 않는다 — 다르게 두면 두 벌이 된다.
   ★ 법률 문서라 **조·항·호의 계층이 눈에 보여야** 한다. 구버전은 400px 짜리
     중첩 테이블로 그 계층을 표현했는데, 표를 지운 자리를 들여쓰기가 대신한다. */
.doc-body { max-width: 82rem; margin-inline: auto; }
.doc-body h2 {
  font-size: var(--text-5); font-weight: 700;
  color: var(--ink); margin-top: var(--space-6);
  padding-bottom: var(--space-2); border-bottom: 2px solid var(--line);
}
.doc-body h3 { font-size: var(--text-4); margin-top: var(--space-4); }
/* 항(①) · 호(1.) — 번호를 왼쪽에 걸고 본문을 그만큼 들여 쓴다.
   ⚠ 번호와 본문이 원문에서 서로 다른 줄에 있어서, 그냥 이어 붙이면
     '① 회사는…' 이 아니라 '①' 다음 줄에 본문이 오는 모양이 된다. */
.doc-body .doc-p, .doc-body .doc-ol li {
  position: relative; padding-left: 2.2rem;
}
.doc-body .doc-no {
  position: absolute; left: 0; top: 0;
  color: var(--ink-soft); font-weight: 600;
}
.doc-body .doc-ol { list-style: none; padding-left: 1.6rem; }
.doc-body .doc-dash { padding-left: 2.2rem; }

/* --- 랜딩 (로그인 전 첫 화면) ------------------------------------------------
   ★ 셸을 안 쓴다. 로그인 전이라 사이드바에 그릴 메뉴도, 프로필도 없다.
     로그인 화면과 같은 자리다 — **이 앱에서 셸 밖 화면이 이제 둘이다.**
   ★ 폭은 셸과 같은 기준값(--shell-base)을 쓴다. 로그인하면 바로 그 폭의 화면이
     나오는데 첫 화면만 다른 폭이면 '다른 사이트' 처럼 보인다.
   ⚠ 여기는 --shell-w 가 아니라 --shell-base 다. 랜딩에는 배너 컬럼이 없으므로
     배너를 켠 상태에서 넓어진 값을 따라가면 안 된다 (2절). */
.landing {
  width: var(--shell-base);
  max-width: 100%;
  margin-inline: auto;
  padding: var(--space-5) var(--space-4) var(--space-8);
  display: flex; flex-direction: column; gap: var(--space-5);
}
.landing__top {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4);
}
.landing__eyebrow {
  font-size: var(--text-2); font-weight: 600;
  letter-spacing: var(--tracking-looser); color: var(--ink-faint);
}
.landing__brand { line-height: 1; }
.landing__brand .brandmark { height: 5.4rem; max-width: 100%; }

/* --- 히어로 ---
   ★ 배경은 **팔레트 그라데이션**이다. 이미지를 소스에 박으면 납품처마다 교체를
     잊고, 화면 색을 바꿔도 그림만 옛 색으로 남는다 (함정 ③ 과 같은 뿌리).
     사진이 필요하면 회사소개 본문에 에디터로 넣는다 — 그 경로는 이미 있다. */
.hero {
  border-radius: var(--radius-card);
  background:
    radial-gradient(120% 140% at 12% 0%, var(--green-light) 0%, transparent 58%),
    radial-gradient(100% 120% at 92% 8%, var(--gold-lightest) 0%, transparent 52%),
    var(--white);
  box-shadow: var(--shadow-card);
  padding: var(--space-8) var(--space-6);
  text-align: center;
  overflow: hidden;
}
.hero__inner { max-width: 62rem; margin-inline: auto; }
.hero__brand { line-height: 1; margin-bottom: var(--space-4); }
.hero__brand .brandmark { height: 6.4rem; max-width: 100%; }
.hero__lead {
  font-size: var(--text-7); font-weight: 700; line-height: 1.3;
  word-break: keep-all;
}
.hero__lead em { color: var(--green-accent); font-style: normal; }
.hero__sub {
  margin-top: var(--space-3);
  font-size: var(--text-4); color: var(--ink-soft); line-height: 1.7;
  word-break: keep-all;
}
.hero__stat {
  display: inline-block; margin-top: var(--space-4);
  padding: var(--space-1) var(--space-4);
  border-radius: var(--radius-pill);
  background: var(--ceramic); color: var(--ink-soft);
  font-size: var(--text-3);
}
.hero__stat b { color: var(--house-green); }
.hero__cta {
  margin-top: var(--space-6);
  display: flex; gap: var(--space-2); justify-content: center; flex-wrap: wrap;
}

/* 기획전 — 로그인 화면의 카드와 같은 값에서 온다 (PW_TEXT) */
.hero-promo {
  display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap;
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-card);
  background: var(--house-green); color: var(--ink-white);
}
.hero-promo__sub { font-size: var(--text-2); color: var(--ink-white-soft); }
.hero-promo__main { font-size: var(--text-4); }
.hero-promo__main b { color: var(--gold-light); }

/* --- 등장 애니메이션 ---
   ★ 업무 화면이다. '살아 있다' 만 알리고 기다리게 하지 않는다 — 0.5초 안에 끝난다.
   ⚠ prefers-reduced-motion 에서는 **아예 안 움직인다.** 매일 여는 사람이 있고,
     멀미를 느끼는 사용자가 실제로 있다. 끄는 것이 아니라 '처음부터 제자리'다. */
@media (prefers-reduced-motion: no-preference) {
  .hero, .hero-promo, .landing__doc, .landing__cta {
    animation: landing-up 0.5s var(--ease) both;
  }
  .hero-promo   { animation-delay: 0.06s; }
  .landing__doc { animation-delay: 0.12s; }
  .landing__cta { animation-delay: 0.18s; }
}
@keyframes landing-up {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}
/* 문서 화면(로그인 전)은 글자 상호만 — 워드마크는 첫 화면 것이다 */
.landing__brand--sm {
  font-size: var(--text-6); font-weight: 700; color: var(--house-green);
}
.landing__main { display: flex; flex-direction: column; gap: var(--space-4); }
.landing__cta { text-align: center; }
.landing__foot {
  border-top: 1px solid var(--line);
  padding-top: var(--space-4);
  font-size: var(--text-2); color: var(--ink-faint); line-height: 1.7;
}
.landing__links { margin-top: var(--space-2); display: flex; gap: var(--space-4); }
.landing__links a { color: var(--ink-soft); text-decoration: underline; }


/* --- 비주얼 (로고 + 메인 이미지) -------------------------------------------
   ★★ 구버전 main.asp 의 `.visual` 자리다 — 왼쪽 로고, 오른쪽 큰 그림.
     둘 다 관리자가 올린다(관리자 > 문구 관리 > 메인 화면).
   ★ 그림이 없으면 **글자로 채운다.** 빈 자리를 보여주지 않는다 —
     로고가 없으면 워드마크, 메인 이미지가 없으면 홍보 문구다.
   ★ 배경은 그림을 안 올렸을 때만 은은하게 깔린다(기본 그림 pharmacy_bg).
     그림을 올리면 `.lp-hero--pic::before` 가 content:none 이라 안 그린다. */
.lp-hero {
  position: relative;
  overflow: hidden;
  width: min(var(--login-w), calc(100vw - 3.2rem));
  margin-inline: auto;
  display: grid;
  grid-template-columns: 26rem minmax(0, 1fr);
  align-items: center;
  gap: var(--space-5);
  background: var(--white);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: var(--space-5);
}
.lp-hero::before {
  content: "";
  position: absolute; inset: 0;
  background: var(--login-bg, url("img/pharmacy_bg.png")) center / cover no-repeat;
  opacity: 0.14;
}
.lp-hero__brand,
.lp-hero__main { position: relative; min-width: 0; }
.lp-hero__brand { text-align: center; }
.lp-hero__tag {
  font-size: var(--text-2);
  color: var(--ink-soft);
  margin-top: var(--space-1);
}
/* --- 그림을 올렸을 때 (.lp-hero--pic) ---------------------------------------
   ★★ **한 파일이 두 가지 일을 하면 둘 다 어중간해진다.** 전에는 올린 그림을
     뒤에 흐리게(::before) 한 번, 오른쪽 네모로 또 한 번 그렸다 — 같은 그림이
   → 그림이 있으면 **오른쪽을 여백 없이 채우고**, 뒤 배경은 안 그린다.
     기본 그림(pharmacy_bg)은 아무것도 안 올렸을 때만 은은하게 남는다. */
.lp-hero--pic {
  padding: 0;
  gap: 0;
  align-items: stretch;      /* 그림이 카드 높이를 끝까지 쓴다 */
  /* ★★ **칸의 비율을 정하지 않는다 — 올린 그림이 정한다.**
     여기를 두 번 틀렸다(2026-08-03) :
       ① 칸이 남는 세로 자리를 먹게 두었더니 **창 높이마다 비율이 달라져**
          4:3 그림의 좌우가 240px 씩 잘렸다.
       ② 그래서 4:3 으로 못박았더니, 이번엔 **가로 배너(2.22:1)의 좌우가
          477px** 잘렸다.
     어느 비율로 고정하든 다른 비율은 잘린다. 도매마다 갖고 있는 그림이
     4:3 사진이기도 하고 가로 배너이기도 하니 **고정이 답이 아니다.**
     → 폭만 채우고 높이는 그림 비율대로 따라간다. 어떤 그림도 안 잘린다.
     ⚠ 대신 카드 높이가 그림마다 달라진다. 남는 자리는 `.login` 이
       위아래로 나눠 가지므로(justify-content:center) 빈 티가 안 난다. */
  min-height: 0;
}
.lp-hero--pic { align-items: center; }

/* ★★ **가로로 긴 그림은 카드 전체 폭을 쓴다.**
   오른쪽 칸(720)에만 넣으면 배너가 작아 보이고 왼쪽에 빈 자리만 남는다.
   ⚠ 판정은 **그림이 다 받아진 뒤** login.asp 의 스크립트가 한다 —
     서버는 올린 그림의 비율을 모른다(DB 에 크기가 없다).
   ※ 스크립트가 안 돌면 오른쪽 칸 배치 그대로다. 잘리지 않으므로 못 쓰게
     되지는 않는다. */
.lp-hero--wide { grid-template-columns: 1fr; }
.lp-hero--wide .lp-hero__brand { padding: var(--space-4) var(--space-5); }
/*
   .lp-hero--pic 이 padding:0 인 것은 '오른쪽 칸을 여백 없이 채운다' 는 뜻이었는데,
   전체 폭이 되면서 그림이 카드 네 변에 그대로 붙어 답답해 보였다.
   ⚠ 위쪽은 안 준다 — 상호 줄(.lp-hero__brand)의 아래 패딩이 이미 그 자리다.
     양쪽에서 주면 상호와 그림 사이만 두 배로 벌어진다. */
.lp-hero--wide .lp-hero__main { padding: 0 var(--space-4) var(--space-4); }
/* ★ `background: none` 을 같이 준다 (2026-08-26) — `content:none` 만으로는
   브라우저가 **기본 그림(pharmacy_bg)을 여전히 받아 온다**(실측 200 OK).
   안 그릴 그림을 로그인 화면마다 받고 있었다. 그리지 않기로 했으면 받지도
   않는 것이 맞다. */
.lp-hero--pic::before { content: none; background: none; }
.lp-hero--pic .lp-hero__brand {
  padding: var(--space-5);
  display: flex; flex-direction: column; justify-content: center;
}

/*
   구버전 상길 메인이 이 모양이다 — 왼쪽 로고 · 가운데 상호 · 오른쪽 홈페이지 배너.
   ★★ **자리가 있는 것은 가로로 긴 그림일 때뿐이다**(.lp-hero--wide).
     그때만 상호 줄이 카드 전체 폭(980)을 쓴다 — 아니면 왼쪽 26rem 칸에 갇혀
     좌우에 넣을 자리가 없다(실측 : 980 일 때 한쪽 257px · 260 일 때 64px).
     → 좁을 때는 3칸을 안 만들고 **위아래로 쌓는다**(위 flex 세로 배치 그대로).
   ★ 양쪽 칸을 **같은 폭**으로 잡는다. 한쪽만 주면 가운데 상호가 반대쪽으로
     밀려 가운데 정렬이 깨진다 — 로고를 안 올린 도매에서도 그렇게 된다.
     그래서 그림이 없어도 화면 파일이 빈 칸을 그대로 그린다.
   ⚠ 가운데가 좁아지는 만큼 워드마크(SVG)가 **통째로 줄어든다**(max-width:100%).
     실측 : 좌우 없을 때 상호 466px · 좌우 19rem 씩일 때 340px. */
.lp-hero--pic.lp-hero--wide .lp-hero__brand {
  display: grid;
  grid-template-columns: 19rem minmax(0, 1fr) 19rem;
  align-items: center;
  gap: var(--space-4);
}
.lp-hero__side { display: flex; align-items: center; min-width: 0; }
.lp-hero__side--r { justify-content: flex-end; }
/* 왼쪽 로고 · 오른쪽 배너 — 둘 다 **높이로 맞춘다**. 가로로 긴 로고든 네모난
   배너든 같은 줄에 앉는다. ★ 원본보다 크게는 안 늘린다 — 작은 GIF 를 늘리면
   테두리가 뭉개진다(구버전 그림은 대개 작다 · 실측 160×50 · 184×86). */
.lp-hero__logo,
.lp-hero__home img {
  display: block;
  max-width: 100%;
  max-height: 8.8rem;
  object-fit: contain;
}
.lp-hero__home { display: block; max-width: 100%; }
/* 그림을 안 올렸을 때 대신 나오는 단추 (2026-08-26).
   ★ 푸터에서 쓰던 그 단추 그대로다 — 모양을 새로 만들지 않았다.
   ⚠ 190px 칸이라 **글자를 안 접는다**(상호가 길면 두 줄이 되어 줄이 출렁인다). */
.lp-hero__homebtn { white-space: nowrap; max-width: 100%; }
.lp-hero__id { min-width: 0; }

/* 올린 메인 이미지. ★ **폭만 채우고 높이는 비율대로 따라간다.**
   전에는 `absolute` + `cover` 로 칸을 덮었는데, 그러면 칸 비율과 그림 비율이
   어긋나는 만큼 그대로 잘려 나간다. 자르지 않는 쪽을 택했다.
   ※ 카드에 `overflow:hidden` 이 걸려 있어 모서리는 저절로 둥글게 잘린다. */
.lp-hero__img {
  display: block;
  width: 100%;
  height: auto;
  /* ★★ **로그인 칸을 화면 밖으로 밀어내지 않는다.**
     정사각형(1024×1024)을 올렸더니 카드가 720 이 되어 로그인 버튼이
     화면 아래(935 > 900)로 나갔다 — 첫 화면에서 제일 중요한 것이 안 보였다.
     빼는 값 33rem = 위아래 여백(8) + 카드 사이(3.2) + 아래 네 칸(21.8).
     ⚠ `object-fit` 이 함께 있어야 한다. 상한이 걸리는 순간 상자는
       720×상한이 되는데, 이게 없으면 그림이 **찌그러진다**.
     ★ `cover` 다. `contain` 은 안 자르는 대신 **좌우에 흰 여백**을 남긴다
       ※ 상한이 안 걸릴 때는 상자가 이미 그림 비율이라 **`cover` 여도 안
         잘린다.** 자르는 것은 딱 '높이를 제한한 만큼' 이다. */
  max-height: max(28rem, calc(100vh - 33rem));
  object-fit: cover;
}
/* ★★ 전체 폭일 때는 **예산을 다시 잡는다** (2026-08-07).
   33rem 은 그림이 오른쪽 칸에 있을 때 잡은 값이다 — 그때는 상호가 그림 **옆**
   이라 세로를 안 먹었다. 전체 폭이 되면 상호 줄(16.4rem)이 그림 **위**로 올라와
   그만큼이 더 든다. 그대로 두면 1366×768 에서 로그인 버튼이 접힌 아래로 갔다
   (실측 824 > 768).
   ★★ 예산의 기준이 '로그인 **버튼**' 이 아니라 **로그인 칸 전체**다.
     처음엔 39rem(버튼 기준)으로 잡았더니, 버튼은 보이는데 그 아래
     아이디저장·회원가입·모바일 링크가 잘려 **아래가 잘린 화면**이 됐다.
     48rem 은 로그인 칸 바닥까지 넣은 값이다.
   실측 (그림 여백 24px 이 들어가 그림 폭이 932 다)
     1520×920  932×440  잘림  85(16%) · 위 24 / 아래 19  **로그인 칸 전부 보임**
     1366×768  932×340  잘림 185(35%) · 버튼은 보이고 마지막 링크 한 줄이 접힌다
   ⚠ 잘림을 줄이려면 그만큼 로그인 칸이 아래로 내려간다. **한 화면에 그림도 크고
     로그인 칸도 다 보이게 하는 값은 없다** — 920 에서 균형을 맞추고 768 은
     '버튼까지' 를 지키는 선으로 잡았다.
   ⚠ 34rem 하한이 있는 이유 : 하한이 없으면 아주 낮은 창에서 그림이 몇십 px 로
     쪼그라들어 무슨 그림인지 못 알아본다. 그때는 차라리 스크롤이 낫다. */
.lp-hero--wide .lp-hero__img { max-height: max(34rem, calc(100vh - 48rem)); }

/* --- 기본 메인 그림이 여러 장일 때 — 슬라이드 (2026-08-07) -----------------
   ★★ **첫 장만 흐름 안에 두고 나머지를 그 위에 겹친다.**
     전부 absolute 로 두면 상자 높이가 0 이 되어 카드가 납작해진다.
     첫 장이 높이를 정하므로 장이 바뀌어도 카드가 출렁이지 않는다
     (세 장이 같은 크기라는 전제가 아니라, 첫 장 기준이라는 규칙이다).
   ★ 넘김은 **투명도만** 바꾼다. 밀거나 돌리면 그림마다 잘리는 곳이 생기고,
     .lp-hero__img 가 이미 cover 라 잘림을 한 번 더 얹는 셈이 된다.
   ★ `is-on` 은 서버가 첫 장에 붙여 보낸다 — 자바스크립트가 없어도 한 장은 보인다.
   ⚠ 움직임을 줄이는 설정(prefers-reduced-motion)에서는 넘기지 않는다.
     그때는 첫 장이 그대로 남는다 — 못 쓰게 되지는 않는다. */
.lp-hero__slides { position: relative; }
.lp-hero__slides > .lp-hero__img {
  opacity: 0;
  transition: opacity 0.9s ease;
}
.lp-hero__slides > .lp-hero__img.is-on { opacity: 1; }
.lp-hero__slides > .lp-hero__img:not(:first-child) {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  max-height: none;
}
@media (prefers-reduced-motion: reduce) {
  .lp-hero__slides > .lp-hero__img { transition: none; }
}

/* --- 로그인 칸 (네 번째 상자) ----------------------------------------------
   ★★ 구버전도 로그인이 아래 한 칸이었다. 위 카드 안에 두었다가 되돌렸다
     */
.lp-info__box--login { background: var(--surface, var(--white)); }
/* ★ 줄 간격을 6px 로 좁힌다(기본 리듬 8보다 작다). 칸이 여덟이라
   2px 씩만 줄여도 눈에 띄게 낮아진다 — 입력칸 자체는 안 건드린다
   (`.field` 는 전 화면 공용이라 여기서 줄이면 다른 화면까지 따라 줄어든다). */
.lp-login { display: flex; flex-direction: column; gap: 6px; }

/* ★★ 아이디·비밀번호 두 줄 **옆에** 버튼을 세운다.
   버튼을 아래에 쌓으면 한 줄(34px + 간격)을 더 쓴다 — 네 칸의 높이를
   정하는 것이 이 칸이라 그 한 줄이 줄 전체를 키운다. */
.lp-login__rows {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 6px;
  align-items: center;
}
.lp-login__lb {
  font-size: var(--text-1);
  color: var(--ink-soft);
  white-space: nowrap;
}
/* 두 줄을 통째로 덮는다. ⚠ 자리를 **명시**해야 한다 — 자동 배치에 맡기면
   DOM 순서상 '비밀번호' 이름표가 오른쪽 칸으로 올라간다. */
/*
   ⚠ **높이는 안 줄인다.** 두 줄을 덮는 것이 이 버튼의 자리라 높이를 줄이면
     입력칸 두 줄과 아래위가 어긋난다. 줄일 수 있는 것은 폭뿐이다(70 → 56).
   ★ 여기서 아낀 폭은 그대로 **공지사항 칸**으로 갔다 (아래 .lp-info 그리드). */
.lp-login__go {
  grid-column: 3;
  grid-row: 1 / 3;
  height: 100%;
  padding-inline: var(--space-2);
  font-size: var(--text-2);
}
/* 로그인 중 (2026-08-12) — 이 버튼에는 **글자를 못 넣는다.**
   ★★ 「로그인 중…」으로 갈면 버튼이 51 → 93px 로 벌어지고, 가운데 칸이
     `minmax(0,1fr)` 이라 **입력칸이 그만큼 줄어든다**(실측 123 → 81).
     알려주려고 넣은 표시가 사용자가 보고 있는 바로 그 순간 폼을 출렁이게
     만들면 "뭔가 잘못됐다" 로 읽힌다. → 글자를 비우고 **고리만** 남긴다.
     ⚠ 폭을 눌러 두는 것은 스크립트다(login.asp) — 잰 값이라야 정확하다.
   ★ 대신 고리를 키운다. 두 줄을 덮는 74px 짜리 세로 버튼이라 세로는 남아돈다.
     무엇을 하는 중인지는 aria-label 이 말한다. */
.lp-login__go.btn--busy::before { width: 1.7em; height: 1.7em; border-width: 3px; }

.lp-login .field { width: 100%; }
.lp-login__err {
  color: var(--red);
  font-size: var(--text-1);
  white-space: pre-line;
}
.lp-login__join,
.lp-login__m {
  font-size: var(--text-1);
  color: var(--ink-soft);
  text-align: center;
}
.lp-login__join a,
.lp-login__m a { color: var(--green-accent); text-decoration: underline; }

/* --- 로그인 카드 아래 정보칸 (배송안내 · 주문문의 · 공지) ------------------
   ★★ 구버전 gnhds 메인(main/main.asp)의 네 상자를 옮긴 것이다.
     거기서는 [휴무][전화][공지][로그인] 이 240px 씩 한 줄로 있었고,
     **넷 다 GIF** 였다 — 전화번호 하나 바꾸려면 그림을 다시 만들어야 했다.
     여기서는 전부 문구 관리에서 오고, 로그인은 위 카드가 맡으므로 셋이다.

   ★ 로그인 패널과 **같은 폭**을 쓴다. 폭이 다르면 두 화면처럼 보인다.
   ★ 칸 하나가 비면(배송 문구를 안 쓰는 곳) 자동으로 둘이 된다 — 빈 상자를
     남기지 않는다. `auto-fit` 이 그 일을 한다. */
.lp-info {
  width: min(var(--login-w), calc(100vw - 3.2rem));
  margin: var(--space-3) auto 0;
  display: grid;
  /* ★ 네 칸. 구버전(ntpm·gnhds)이 240px 씩 넷을 한 줄로 놓았다.
     좁아지면 둘 → 하나로 접힌다 — 넷을 고집하면 칸이 반토막 난다. */
  grid-template-columns: repeat(auto-fit, minmax(22rem, 1fr));
  gap: var(--space-3);
  /* ★ 넷이 한 줄로 설 때만 **로그인 칸을 넓힌다.**
     넷을 똑같이 나누면 233px 이고, 이름표(41)+버튼(70)을 빼면 입력칸이
     **62px** 밖에 안 남는다(실측) — 아이디가 안 보이는 칸이 된다.
     ⚠ 좁아져서 칸이 접히면 이 규칙은 꺼진다. auto-fit 이 정한 줄 수를
       고정 4칸으로 덮으면 좁은 화면에서 네 칸이 그대로 뭉개진다. */
  /* ★ 넷의 **높이를 맞춘다**(기본 stretch). 전에 `align-items:start` 를 뒀더니
     내용 길이대로 제각각이 돼 계단처럼 보였다 — 공지 5줄과 배송 2줄이 나란히
     서면 그 차이가 그대로 드러난다. 카드 테두리는 정렬의 근거라 맞춰야 한다. */
}
/* ⚠⚠ **`.lp-info` 뒤에 와야 한다.** 미디어쿼리는 특정도를 안 올린다 —
   앞에 두면 뒤따르는 기본 규칙이 그대로 이겨서 아무 일도 안 일어난다
   (함정 ㉔-ⓑ. 이 파일에서 두 번째로 밟았다). */
@media (min-width: 960px) {
  /* ★★ 칸마다 몫이 다르다. **두 번 다시 잡았다.**
       ① 2026-08-07 아침 — 공지사항이 좁아 제목 말줄임이 잦았다
            1fr  1fr  1.25fr  1.35fr   →  203 · 203 · 253 · 274
       ② 2026-08-07 저녁 — 안내 카드 둘이 **그림이 아니라 글자**가 됐다
         (그림 카드는 비율만 맞으면 되니 폭이 문제가 아니었다).
         203px 짜리 칸에서 배송 문구가 다섯 줄로 늘어지고 낱말이 갈라졌다.
            1.35fr  0.95fr  0.95fr  1.35fr  →  274 · 192 · 192 · 274

       ③ 2026-08-07 밤 — **카드 전용 문구가 생겨 배송을 넓힐 이유가 없어졌다.**
         긴 공용 문구를 좁은 칸에 우겨넣던 것이 ②의 원인이었는데, 이제 그 칸을
         보고 쓴 짧은 문구를 따로 넣을 수 있다(PW_TEXT 의 delivery.card).
         되찾은 폭은 **공지로 보낸다** — 제목 말줄임이 넷 중 가장 잦은 자리다.
            1.05fr  1fr  1.45fr  1.47fr   →  197 · 188 · 272 · 276

     ★ 공지 제목 폭 : ① 133 → ② 72 → ③ **152px**. 셋 중 제일 넓다(실측).

     ⚠⚠ **로그인 칸을 더 줄이면 안 된다.** 아이디 입력칸이 120px 아래로
       떨어지면 아이디가 안 보인다(실측 하한). 지금 123px 이고, 공지를 281 까지
       넓힌 안에서는 **119px** 이 되어 버렸다 — 거기가 벽이다.
     ⚠ 배송 197px 은 「1차 10:30 · 2차 13:30 마감」이 **한 줄로 남는 하한**이다.
       193px 로만 줄여도 「2차」와 「13:30」이 갈린다(실측).
     ⚠ 문의 188px 은 전화번호가 한 줄로 남는 선이다(13자리까지 확인).

     ★ ②의 근거는 지웠다 — '배송 문구가 길다' 는 전제 자체가 없어졌다.
       다만 **카드 전용 문구를 안 채운 곳**에서는 그 전제가 그대로 살아 있다.
       그때는 공용 문구가 ` / ` 자리에서 줄을 바꿔 들어간다(login.asp). */
  .lp-info {
    grid-template-columns:
      minmax(0, 1.05fr) minmax(0, 1fr) minmax(0, 1.45fr) minmax(0, 1.47fr);
  }
}

.lp-info__box {
  background: var(--white);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  /* ★ 세로 상자다 — 안쪽에서 '남는 자리를 누가 먹을지' 를 정할 수 있어야 한다.
     넷의 높이는 가장 긴 칸이 정하므로 짧은 칸에는 늘 남는 자리가 생긴다.
     */
  display: flex;
  flex-direction: column;
  /* ★ 위아래만 좁힌다(16) — 좌우는 그대로(24).
     줄 넷의 높이는 **가장 긴 칸(로그인)** 이 정한다. 로그인 칸은 내용이
     꽉 차 있어 남는 자리가 패딩뿐이었다(내용 260 + 패딩 48 = 284, 실측). */
  padding: var(--space-3) var(--space-4);
  min-width: 0;
}
.lp-info__t {
  font-size: var(--text-2);
  font-weight: 800;
  color: var(--brand-green);
  padding-bottom: var(--space-2);
  margin-bottom: var(--space-2);
  border-bottom: 1px solid var(--line);
}
/* 카드 안의 두 번째 제목 (문의 전화 칸의 「영업시간」 · 2026-08-07).
   ★ 카드 제목과 **같은 모양**을 쓴다. 이름표를 값 앞에 붙여 두었더니
     188px 짜리 칸에서 「영업 월~금 09:00」 / 「~ 18:00」로 갈렸다 —
     줄로 세우면 값이 온전히 제 폭을 쓴다.
   ⚠ 밑줄은 **위로 옮긴다.** 카드 제목처럼 아래에 두면 줄이 둘이 되어
     (윗 항목과 이 제목 사이) 칸이 답답해지고 높이도 그만큼 더 든다. */
.lp-info__t--sub {
  margin-top: var(--space-2);
  padding-bottom: 0;
  margin-bottom: 2px;
  border-bottom: 0;
  border-top: 1px solid var(--line);
  padding-top: var(--space-2);
}
.lp-info__b {
  font-size: var(--text-2);
  color: var(--ink-soft);
  line-height: 1.7;
  /* ★ CS 가 입력칸에서 친 Enter 를 그대로 살린다 — <br> 를 알아야만
     줄을 나눌 수 있게 두면, Enter 를 친 사람은 저장은 됐는데 화면은
     안 바뀌는 것을 보게 된다(값에는 들어 있다. 실측). `<br>` 도 그대로 먹는다. */
  white-space: pre-line;
  /* ★ 마지막 줄에 한 단어만 남는 것을 줄인다. 203px 짜리 좁은 칸이라
     한 글자 남는 줄이 자주 생겼다. 모르는 브라우저는 그냥 무시한다. */
  text-wrap: pretty;
  /*
     브라우저 기본값은 한글을 **음절 아무 데서나** 자른다 — 203px 짜리 좁은
     칸에서 「일 / 요일」 「차수 / 로」 처럼 낱말이 반으로 갈렸다(실측).
     이 프로젝트는 이미 일곱 곳에서 같은 값을 쓰고 있었고 여기만 빠져 있었다.
     ⚠ 짝으로 overflow-wrap 이 있어야 한다. keep-all 만 두면 띄어쓰기 없는
       긴 덩어리(URL·영문 제품명)가 칸 밖으로 삐져나간다. */
  word-break: keep-all;
  overflow-wrap: break-word;
}

/*
     `delivery.msg` 는 주문화면 배송바와 **같은 문자열**인데, 강조가 이 카드에서만
     죽어 있었다 (실측) :
       주문바  `.delivery__msg b` → 초록   · `em` → 빨강 굵게
       이 카드 `.lp-info__b   b` → 본문색 · `em` → 브라우저 기본 이탤릭
     CS 가 굵게 친 마감시각이 아무 표시도 안 나서 다섯 줄이 통째로 글자벽이 됐다.
   ★ **문구를 따로 만들지 않는다.** 카드용 짧은 문구를 새로 두면 마감시각이
     두 곳에 생기고, 언젠가 한쪽만 고쳐진다 (CLAUDE.md 2절). 고치는 것은 표시뿐이다. */
.lp-info__b b { font-weight: 700; color: var(--green-accent); }

/* ★★ 남는 자리를 이 본문이 먹는다 (2026-08-07).
   ⚠⚠ **`display:flex` 를 쓰면 안 된다.** 한 번 그렇게 했다가 글이 낱말마다
     갈렸다(실측 3줄 → 9줄). flex 상자에서는 **자식 요소 하나하나가 각각
     항목**이 되므로 문구 안의 `<b>1차 10:30</b>` 들이 전부 제 줄을 차지한다.
     "잇닿은 인라인은 한 덩어리로 묶인다" 는 **글자 조각(text run)** 얘기지
     요소 얘기가 아니다 — 여기서 헷갈렸다.
   ★ 그래서 블록 그대로 두고 높이만 늘린다. 각주를 카드 **바닥**에 붙이려면
     문구를 본문/각주 둘로 쪼개야 하는데, 그건 DB 문자열 한 벌을 깨는 일이라
     안 한다(주문화면 배송바가 같은 문자열을 쓴다). */
.lp-info__b--fill { flex: 1 1 auto; }

.lp-info__b em {
  /* ★ 덧붙임은 **아래로 내린다.** 주문바는 한 줄짜리라 색만으로 갈렸지만,
     203px 짜리 좁은 칸에서는 본문에 이탤릭으로 섞이면 어디까지가 마감시각이고
     어디부터가 설명인지 안 보인다 — 자리로 가르는 편이 낫다. */
  display: block;
  margin-top: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px dashed var(--line);
  font-style: normal;
  font-size: var(--text-1);
  line-height: 1.6;
}
.lp-info__tel {
  /* ★ 이 카드의 주인공이다. 16px 일 때 아래가 81px 비어 있었다(실측) —
     번호를 키워 그 자리를 쓰는 편이, 억지 문구로 채우는 것보다 낫다.
     ⚠ 더 키우지 말 것. 24px 이면 `02-2248-0254` 가 칸(155px)을 넘긴다. */
  font-size: var(--text-5);
  font-weight: 800;
  color: var(--ink);
  letter-spacing: -0.02em;
  margin: var(--space-1) 0 var(--space-2);
}
/* 팩스·영업시간. ★ 붙여 놓으면 번호에 딸린 부속처럼 보여 읽히지 않는다 —
   줄 사이를 벌려 항목들이 같은 무게로 서게 한다.
     주소는 길이에 상한이 없어 넣으면 이 칸이 넷 중 제일 길어진다 — 네 칸의
     높이는 가장 긴 칸이 정하므로 거래처가 바뀔 때마다 로그인 칸이 아래로
     밀린다(실측 : 데모 DB 의 실제 주소가 세 줄이 되어 218 → 237px). */
.lp-info__tel ~ .lp-info__b { line-height: 1.5; }
.lp-info__tel ~ .lp-info__b + .lp-info__b { margin-top: var(--space-2); }
.lp-info__none { color: var(--ink-soft); }

/* --- 안내 카드를 그림으로 갈아 끼운 경우 (2026-08-07) -----------------------
   ★ 구버전(상길)이 이 자리를 디자인된 그림 판으로 썼다. 글자·표로는 못 만드는
     배치라 CS 가 그대로 옮겨 붙일 수 있어야 한다. 제목까지 그림 안에 있으므로
     카드에는 **그림만** 남는다 → 패딩 0.
   ★★ `object-fit: cover` 가 아니라 **contain** 이다.
     cover 는 카드 비율과 다른 그림을 **잘라낸다.** 이 카드에 들어갈 것은
     전화번호·마감시각 같은 '읽어야 하는 값' 이라, 잘려서 사라지는 쪽이
     여백이 남는 쪽보다 훨씬 나쁘다. 권장 크기(420×440 · 표시 209×218 의 2배)를
     지키면 여백이 안 생긴다.
   ⚠ 카드 높이는 **로그인 칸**이 정한다(실측 218px). 그림이 높이를 정하지 않는다 —
     그림마다 줄 높이가 달라지면 넷이 어긋난다.
   ★ 주소가 있을 때만 <a> 다. 없으면 <div> 라 커서도 안 바뀐다. */
.lp-info__box--img {
  padding: 0;
  overflow: hidden;
  display: block;
  background: var(--white);
  /* ★★ **카드는 다른 칸과 같은 높이다. 그림은 위에 붙인다.** (2026-08-07)
     세 번 바꾼 자리라 근거를 다 남긴다.
       ① 카드=줄높이 + contain + 가운데 → 상길 배너(240×141 · 비율 1.70)에서
          위아래 47px 씩 비어 카드 한가운데 띠가 떠 있는 꼴이었다.
       ② 카드가 그림 높이를 따라가게 → 여백은 사라졌지만 **네 칸 높이가
       ③ 지금 — 카드는 줄 높이(218)를 쓰고, 그림은 **위쪽 정렬**로 넣는다.
     ★ 남는 자리가 안 보이는 이유 : **그림의 바탕이 순백(255,255,255)이고
       카드 바탕도 흰색**이라 경계가 없다(실측 — 네 모서리 전부 흰색).
       배경이 다른 색인 그림을 올리면 그 색과 카드 사이에 경계가 보인다.
     ★ 위쪽 정렬인 이유 : 가운데로 두면 그림 속 제목줄이 옆 칸(공지사항·로그인)
       제목보다 아래로 내려가 줄이 안 맞는다.
     ⚠⚠ **늘리지도 자르지도 않는다.** 이 칸에 들어오는 것은 전화번호·마감시각
       처럼 **읽어야 하는 값**이다.
         fill(늘리기) → 세로 1.55배로 글자가 찌그러진다
         cover(채우기) → 좌우 68%가 잘려 전화번호가 사라진다
       칸을 완전히 채우려면 그림을 **칸 비율(가로:세로 ≈ 1:1.05)로 다시** 만든다. */
}
a.lp-info__box--img { transition: var(--ease); }
a.lp-info__box--img:hover  { box-shadow: var(--shadow-float); }
a.lp-info__box--img:active { transform: scale(var(--press-scale)); }
.lp-info__img {
  display: block;
  width: 100%; height: 100%;
  object-fit: contain;
  object-position: top center;
}

/* 공지 — 제목과 날짜. ★ 누를 수 없다(구버전과 같은 수준으로만 연다).
   제목이 길면 말줄임하고 `title` 로 전체를 보여준다. */
.lp-info__list { list-style: none; }
.lp-info__list li {
  display: flex; align-items: baseline; gap: var(--space-2);
  padding: 4px 0;
  font-size: var(--text-2);
}
.lp-info__list li + li { border-top: 1px dashed var(--line); }
.lp-info__nt {
  flex: 1 1 auto; min-width: 0;
  color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.lp-info__nd { flex: none; color: var(--ink-soft); font-size: var(--text-1); }

/* --- 로그인 화면 아래 회사소개 (A안 하단 블록) -------------------------------
   ★ 로그인 패널과 **같은 폭**(--login-w)을 쓴다. 폭이 다르면 두 화면처럼 보인다.
     ⚠ 전에는 --shell-base(1340)라 카드(980)보다 넓었다 — 2026-08-01 에 맞췄다.
   ★ 좌측 라벨은 붙어 따라온다 — 본문이 길어도 지금 무엇을 읽는지 안 잃는다. */
/* ★★ 그림이 들어간 회사소개 — **틀은 그대로 두고 폭만 되찾는다** (2026-08-07)
   ---------------------------------------------------------------------------
   실측(1366px) — 본문 칸이 591px 이라 원본 920px 그림이 **571px 로 줄어** 있었다.
   범인이 셋이었다.
     ⓐ 제목 칸 26rem — 제목 세 줄만 들어가고 그 아래는 sticky 라 통째로 빈다
     ⓑ `.lp-about__b { max-width: 62ch }` — **글줄 폭**인데 그림에까지 걸렸다
     ⓒ 편집기가 그림을 `<ul><li>` 안에 넣으면 점 + 들여쓰기 2rem
   → 2단 틀·섹션 폭(--login-w)은 **하나도 안 건드리고** 셋만 푼다.
     제목 칸 26→20rem · 글줄만 62ch · 그림 목록은 마커 제거. **571 → 684px**(실측).
   ⚠ 원본이 920px 이라 그 위로는 못 간다 — 더 키우려면 큰 파일을 올려야 한다.
   ⚠ 20rem 아래로는 줄이지 말 것. 제목이 24px 이고 납품처마다 길이가 달라서
     (`doc_head` 는 관리자가 정한다) 더 좁히면 두세 줄로 접힌다. 실측 200px 에서
     "회사소개" 는 한 줄에 들어간다. */
.lp-about {
  width: min(var(--login-w), calc(100vw - 3.2rem));
  margin: var(--space-7) auto 0;
  padding: var(--space-7) var(--space-4);
  display: grid; grid-template-columns: 20rem minmax(0, 1fr); gap: var(--space-7);
  border-top: 1px solid var(--line);
}
.lp-about__l {
  align-self: start; position: sticky; top: var(--space-5);
  display: flex; flex-direction: column; gap: var(--space-2);
}
.lp-about__kicker {
  font-size: var(--text-1); letter-spacing: var(--tracking-looser); color: var(--ink-faint);
}
.lp-about__t { font-size: var(--text-6); font-weight: 700; word-break: keep-all; }
.lp-about__rule { width: 4rem; height: 2px; background: var(--brand-green); }
/* ★ 칸 전체를 쓴다. 읽기 폭(62ch)은 **글줄에만** 건다 — 그림·목록까지 묶으면
     위 ⓑ 로 되돌아간다. `> ` 로 직계만 잡아야 <li> 안의 그림이 안 걸린다. */
.lp-about__b { padding: 0; min-height: 0; max-width: none; }
.lp-about__b > p,
.lp-about__b > h2,
.lp-about__b > h3 { max-width: 62ch; }

.lp-foot {
  width: min(var(--login-w), calc(100vw - 3.2rem));
  margin: 0 auto; padding: var(--space-5) var(--space-4) var(--space-7);
  display: flex; flex-direction: column; gap: var(--space-4);
}
.lp-foot__top {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4); flex-wrap: wrap;
  padding-top: var(--space-5); border-top: 1px solid var(--line);
}
/* ★ 버튼이 하나일 수도 둘일 수도 있다 — 「공식 홈페이지」는 주소를 채운
     도매에서만 생긴다. 하나뿐일 때도 지금과 같은 자리에 놓여야 하므로
     묶음 자체는 늘 그린다 (빈 묶음은 폭이 0 이라 흔적이 없다). */
.lp-foot__cta { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.lp-foot__brand { display: flex; flex-direction: column; gap: 2px; }
.lp-foot__brand b { font-size: var(--text-4); word-break: keep-all; }
.lp-foot__brand span {
  font-size: var(--text-1); letter-spacing: var(--tracking-looser); color: var(--ink-faint);
}
.lp-foot__grid {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-4);
  padding-top: var(--space-4); border-top: 1px solid var(--line);
  font-size: var(--text-2); line-height: 1.8; color: var(--ink-soft);
}
.lp-foot__grid > div { display: flex; flex-direction: column; min-width: 0; }
.lp-foot__grid i { font-style: normal; color: var(--ink-faint); }
.lp-foot__links { align-items: flex-start; gap: 2px; }
.lp-foot__links a { color: var(--ink-soft); text-decoration: underline; }

@media (max-width: 900px) {
  /* ★ 26rem 고정 칸을 좁은 화면까지 끌고 가면 오른쪽 그림이 손톱만 해진다.
     위아래로 쌓고, 그림은 높이를 정해 준다(absolute 라 부모 높이가 필요하다). */
  .lp-hero { grid-template-columns: 1fr; }
  /* ★ 좌·우 그림도 같이 접는다. 3칸을 좁은 화면까지 끌고 가면 가운데 상호가
     손톱만 해진다 — 위 .lp-hero 를 1단으로 만든 것과 같은 이유다. */
  .lp-hero--pic.lp-hero--wide .lp-hero__brand { grid-template-columns: 1fr; }
  .lp-hero__side, .lp-hero__side--r { justify-content: center; }
  /* 위아래로 쌓여도 비율은 그대로 4:3 이다 — 폭만 화면 폭으로 바뀐다 */
  .lp-about { grid-template-columns: 1fr; gap: var(--space-4); }
  .lp-about__l { position: static; }
  .lp-foot__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* --- 문구 관리의 그림 칸 (메인 화면 로고 · 메인 이미지) ----------------------
   ★★ 여기에 **CSS 가 아예 없었다.** 미리보기에 크기 제한이 없어서 1254px 짜리를
     올리면 관리자 목록 한가운데 원본 크기로 박힌다(2026-08-03).
   ★ 권장 규격을 버튼 옆에 적고, 올린 그림의 **실제 크기**를 같이 보여준다.
     숫자 하나만 적어 두면 CS 가 자기 그림과 견줄 수가 없다. */
.timg {
  border: 1px solid var(--line);
  border-radius: var(--radius-btn, 10px);
  background: var(--white);
  padding: var(--space-2);
  min-height: 6rem;
  display: flex; align-items: center; justify-content: center;
}
.timg img { max-width: 100%; max-height: 14rem; object-fit: contain; display: block; }
.timg__none { font-size: var(--text-1); color: var(--ink-soft); }
.timg__acts {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
}
.timg__rec { font-size: var(--text-1); color: var(--ink-soft); }
.timg__rec b { color: var(--ink); font-weight: 700; }
/* 표시 크기보다 작은 그림 — 늘려 그리므로 뭉개진다. 색으로 말한다. */
.timg__rec--warn, .timg__rec--warn b { color: var(--red); }

/* --- 관리자 문서 탭 ---
   ★ 문서가 둘(회사소개까지 셋)뿐이라 목록 표를 그리지 않는다.
     탭이 곧 목록이고, 글자 수·숨김 여부를 눌러 보기 전에 알 수 있다. */
.doc-tabs { display: flex; gap: var(--space-2); margin-bottom: var(--space-2); }
.doc-tab {
  display: flex; flex-direction: column; gap: 2px;
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--line); border-radius: var(--radius-field);
  background: var(--white);
  transition: border-color 0.12s ease, background 0.12s ease;
}
.doc-tab:hover { border-color: var(--green-accent); }
.doc-tab.is-on { border-color: var(--green-accent); background: var(--green-light); }
.doc-tab__nm { font-size: var(--text-4); font-weight: 600; }
.doc-tab__meta { font-size: var(--text-2); color: var(--ink-faint); }
.doc-tab__off { color: var(--red); font-weight: 600; }
.doc-tab__home { color: var(--green-accent); font-weight: 600; }

/* --- 이전글 / 다음글 --- */
.article-nav { border-top: 1px solid var(--line); }
.article-nav__row {
  display: flex; align-items: center; gap: var(--space-3);
  height: 4rem;
  padding: 0 var(--space-4);
  transition: background 0.12s ease;
}
.article-nav__row + .article-nav__row { border-top: 1px solid var(--line); }
.article-nav__row:hover { background: var(--row-hover); }
.article-nav__dir {
  flex: none; width: 6.4rem;
  display: flex; align-items: center; gap: var(--space-1);
  font-size: var(--text-2); font-weight: 600;
  color: var(--ink-soft);
}
.article-nav__title {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: var(--text-3);
}
.article-nav__date { flex: none; font-size: var(--text-1); color: var(--ink-faint); }
/* 첫 글 / 마지막 글 — 이동할 대상이 없는 줄 */
.article-nav__row.is-none { cursor: default; }
.article-nav__row.is-none:hover { background: transparent; }
.article-nav__row.is-none .article-nav__title { color: var(--ink-faint); }

.article-actions {
  display: flex; align-items: center; justify-content: center;
  gap: var(--space-2);
  padding: var(--space-3);
  border-top: 1px solid var(--line);
}

/* 패널 본문을 세로 스택으로 — 자식에 margin-top:auto 를 주면 바닥에 붙는다.
   (--flush 전체에 걸면 다른 화면의 상품정보 패널까지 영향받으므로 별도 클래스) */
.panel__body--stack { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }

/* 상품정보 패널의 남는 아래 공간 — 담기 직전에 가장 필요한 것만 둔다 */
.ret-tip {
  margin-top: auto;
  padding: var(--space-2) var(--space-3) var(--space-3);
  border-top: 1px solid var(--line);
  background: var(--neutral-cool);
}
.ret-tip__t {
  font-size: var(--text-1);
  font-weight: 700;
  color: var(--ink-soft);
  letter-spacing: var(--tracking-loose);
  margin-bottom: var(--space-1);
}
.ret-tip ul { font-size: var(--text-1); color: var(--ink-soft); line-height: 1.6; }
.ret-tip li { position: relative; padding-left: var(--space-2); }
.ret-tip li::before { content: "·"; position: absolute; left: 2px; }
.ret-tip li b { color: var(--red); }
.ret-tip .btn { margin-top: var(--space-2); }

/* ==========================================================================
   14e. POLICY  (반품정책 안내 — 구버전 /images/반품정책.png 대체)
   --------------------------------------------------------------------------
   구버전은 900×300 PNG 한 장이었다. 내용에 "제약사 정책에 따라 변경될 수 있습니다"
   라고 적혀 있으면서 정작 그림이라 CS 가 고칠 수 없었다 → HTML 표로 푼다.

   위치는 .app 바깥(푸터 바로 위) — 구버전과 같다.
   .main 안에 넣으면 접혀 있어도 목록 높이를 그만큼 빼앗는다.
   ========================================================================== */

.policy {
  width: var(--shell-w);
  max-width: 100%;
  margin-inline: auto;
  padding: 0 var(--space-1) var(--space-4);
}
.policy__box {
  background: var(--white);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

/* 접힌 상태에서도 핵심 수치(36개월 · 8개월)는 보이게 한다 */
.policy__sum {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-3);
  font-weight: 700;
  color: var(--brand-green);
  cursor: pointer;
  list-style: none;                 /* Firefox */
  transition: background 0.12s ease;
}
.policy__sum::-webkit-details-marker { display: none; }
.policy__sum:hover { background: var(--neutral-cool); }
.policy__sum::after {
  content: "▾";
  margin-left: auto;
  color: var(--ink-faint);
  transition: transform var(--ease);
}
.policy__box[open] .policy__sum::after { transform: rotate(180deg); }
.policy__key {
  font-size: var(--text-2);
  font-weight: 600;
  color: var(--ink-soft);
}
.policy__key b { color: var(--green-accent); }

.policy__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  border-top: 1px solid var(--line);
}
.policy__cell {
  display: grid;
  grid-template-columns: 8.4rem minmax(0, 1fr);
  border-bottom: 1px solid var(--line);
}
.policy__cell:nth-child(odd) { border-right: 1px solid var(--line); }
.policy__cell:nth-last-child(-n+2) { border-bottom: 0; }
.policy__h {
  display: grid; place-items: center;
  padding: var(--space-2) var(--space-1);
  background: var(--neutral-cool);
  color: var(--ink-soft);
  font-size: var(--text-2);
  font-weight: 700;
  text-align: center;
}
.policy__b {
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-2);
  line-height: 1.6;
  word-break: keep-all;
}
.policy__b li { position: relative; padding-left: var(--space-2); }
.policy__b li::before { content: "–"; position: absolute; left: 0; color: var(--ink-faint); }
.policy__b li + li { margin-top: var(--space-1); }
/* 하위 항목 (제약사 나열) */
.policy__b li ul { margin-top: 2px; padding-left: var(--space-3); }
.policy__b li ul li::before { content: "ㄴ"; font-size: 1rem; }
.policy__b b { color: var(--red); font-weight: 700; }
.policy__b em { font-style: normal; color: var(--green-accent); font-weight: 600; }
.policy__b a { color: var(--green-accent); text-decoration: underline; }

.policy__note {
  padding: var(--space-1) var(--space-3) var(--space-2);
  border-top: 1px solid var(--line);
  font-size: var(--text-1);
  color: var(--ink-faint);
  text-align: right;
}

@media (max-width: 1100px) {
  .policy { width: auto; }
  .policy__grid { grid-template-columns: 1fr; }
  .policy__cell:nth-child(odd) { border-right: 0; }
  .policy__cell:nth-last-child(2) { border-bottom: 1px solid var(--line); }
}

/* ==========================================================================
   14f. MODAL  (거래처 검색 팝업 — 구버전 popup/pop_vencode02.asp 대체)
   --------------------------------------------------------------------------
   구버전은 .pop-layer 를 ajax 로 받아 화면 위에 얹는 방식이었다. 구조는 같게
   두되(전체화면 스크림 + 가운데 카드) 디자인 토큰으로 다시 그린다.

   ★ 스크림과 카드를 형제로 둔다. 카드를 스크림의 자식으로 넣고 스크림에
     클릭 닫기를 걸면, 카드 안을 눌러도 이벤트가 스크림까지 올라가 닫힌다.
     (data-close 를 스크림에만 붙이는 것으로도 되지만 형제 구조가 덜 위험하다)

   ★ 목록 표는 다른 화면과 같은 .tbl 을 그대로 쓴다. 모달이라고 다른 표를
     만들면 컬럼 폭·행 높이 규칙이 두 벌이 된다.
   ========================================================================== */

.modal[hidden] { display: none; }
.modal {
  position: fixed; inset: 0; z-index: 100;
  display: grid; place-items: center;
  padding: var(--space-4);
}
.modal__scrim {
  position: absolute; inset: 0;
  background: rgba(0, 0, 0, 0.42);
}
.modal__box {
  position: relative;
  display: flex; flex-direction: column;
  width: min(84rem, 100%);
  max-height: min(64rem, calc(100vh - var(--space-7)));
  background: var(--white);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-float);
  overflow: hidden;
}

.modal__head {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--sidebar-bg);
  color: var(--sidebar-fg);
}
/* ★★ **오른쪽으로 미는 일은 제목이 한다** (2026-08-27 고침).
     전에는 .modal__note 의 margin-left:auto 가 그 일을 하고 있었다 — 그래서
     **안내 문구가 없는 모달에서는 ✕ 가 제목 바로 옆에 붙었다**(결제 모달에서
     문구를 빼자 드러났다). 머리글 순서는 어디서나 제목 > (문구) > ✕ 라
     제목에 걸면 문구가 있든 없든 ✕ 가 오른쪽 끝으로 간다.
   ⚠ 둘 다 auto 를 주면 안 된다 — 플렉스에서 auto 여백이 둘이면 남는 폭을
     **나눠 가져** 문구가 가운데로 밀린다. */
.modal__title {
  margin-right: auto;
  font-size: var(--text-5); font-weight: 700; letter-spacing: var(--tracking);
}

/* ── 모달 옮기기 ────────────────────────────────────────────────────────────
   머리글을 잡아 끌면 창이 따라온다 (shell.js 의 mountModalDrag).
   ★ **잡을 수 있다는 것을 커서로 알린다.** 안 알리면 아는 사람만 쓴다
     (F9·Esc 를 버튼에 적어 둔 것과 같은 이유).
   ★ 끄는 동안 글자가 파랗게 긁히지 않게 body 에 표식을 단다 — 상자에만 걸면
     커서가 상자 밖으로 나간 순간 풀린다.
   ⚠ 1100px 이하에서는 스크립트가 아예 안 끄므로 커서도 되돌린다 —
     못 하는 일에 손 모양을 띄우면 고장으로 보인다. */
.modal__head { cursor: move; user-select: none; }
.modal__head .modal__close,
.modal__head button,
.modal__head a,
.modal__head input { cursor: pointer; }
.modal__head input { cursor: text; }
body.is-modaldrag { user-select: none; cursor: move; }
body.is-modaldrag .modal__scrim { cursor: move; }

@media (max-width: 1100px) {
  .modal__head { cursor: default; }
}
.modal__note { font-size: var(--text-2); color: var(--sidebar-fg-soft); }
.modal__close {
  width: 2.8rem; height: 2.8rem;
  border-radius: var(--radius-round);
  color: var(--sidebar-fg);
  font-size: var(--text-3);
  transition: background var(--ease), transform var(--ease);
}
.modal__close:hover  { background: var(--sidebar-hover); }
.modal__close:active { transform: scale(var(--press-scale)); }

.modal__search {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--line);
}

/* 목록 — 남는 높이를 전부 먹고, 넘치면 안에서 스크롤한다.
   min-height 는 부모(.modal__body)에 건다 — 함정 ② */
.modal__body {
  flex: 1; min-height: 16rem;
  overflow: auto;
  scrollbar-gutter: stable;
}

.modal__foot {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-top: 1px solid var(--line);
  background: var(--neutral-cool);
  font-size: var(--text-2);
  color: var(--ink-soft);
}
.modal__foot .pager { margin-left: auto; }

/* --------------------------------------------------------------------------
   화면 색 고르기 (푸터 '화면 색' → 모달)
   ★ 견본 색은 CSS 에 안 적는다. shell.js 의 SKINS 가 인라인 background 로 준다 —
     여기 또 적으면 색 하나 더할 때 두 곳을 고쳐야 한다.
   ★ 칩은 --sidebar-bg 를 그대로 보여준다. 사용자가 실제로 보는 면이 사이드바라
     거기 쓰이는 색을 보여야 고르고 나서 "생각한 색이 아닌데" 가 안 생긴다.
   -------------------------------------------------------------------------- */
.skin-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--space-2);
  padding: var(--space-3);
}
.skin {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: var(--space-2) 4px;
  border: 1px solid var(--line);
  border-radius: var(--radius-field);
  background: var(--white);
  cursor: pointer;
  transition: border-color .12s, box-shadow .12s;
}
.skin:hover { border-color: var(--line-strong); }
.skin:active { transform: scale(0.95); }
.skin__chip {
  width: 2.8rem; height: 2.8rem;
  border-radius: var(--radius-round);
  /* 흰 사이드바 팔레트나 아주 밝은 색이 와도 칩 경계가 보이게 */
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12);
}
.skin__name { font-size: var(--text-1); color: var(--ink-soft); }
/* 고른 것은 테두리로 알린다 — 배경을 칠하면 칩 색과 섞여 무슨 색인지 흐려진다 */
.skin[aria-pressed="true"] {
  border-color: var(--green-accent);
  box-shadow: 0 0 0 1px var(--green-accent);
}
.skin[aria-pressed="true"] .skin__name { color: var(--ink); font-weight: 600; }

@media (max-width: 560px) {
  .skin-grid { grid-template-columns: repeat(3, 1fr); }
}

/* --------------------------------------------------------------------------
   되묻는 모달 (errnum = -2 "이미 담긴 상품") — 목록 모달과 상자만 다르다.
   ★ 왜 본문 배너가 아니라 모달인가
     배너로 띄우면 본문 맨 위에 끼어들어 목록·장바구니가 통째로 아래로 밀린다.
     방금 누른 [담기] 자리와도 멀다. 이건 '알림' 이 아니라 '예/아니오' 를
     받아야 끝나는 질문이라, 답할 때까지 앞을 막는 편이 맞다.
   -------------------------------------------------------------------------- */
.modal--confirm .modal__box { width: min(46rem, 100%); }
.modal__msg {
  padding: var(--space-4) var(--space-4) var(--space-2);
  font-size: var(--text-3);
  line-height: 1.55;
}
.modal__msg b { font-weight: 700; }
.modal__actions {
  display: flex; justify-content: flex-end; gap: var(--space-2);
  padding: var(--space-2) var(--space-4) var(--space-4);
}

/* 안내 한 줄 (권한 때문에 목록이 비는 경우를 설명한다) */
.modal__hint {
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-1);
  color: var(--ink-faint);
  border-bottom: 1px solid var(--line);
}

@media (max-width: 1100px) {
  .modal__box { max-height: calc(100vh - var(--space-5)); }
}

/* ==========================================================================
   14h. DELIVERY  (배송 스케줄 안내 — 우측 배너에서 하단으로 내려온 것)
   --------------------------------------------------------------------------
   원래는 우측 배너 컬럼의 세 번째 카드였다. 거기서는
     · 152px 폭에 '배송 / 스케줄 / 안내' 세 줄로 접혀 정작 스케줄이 안 들어간다
     · 배너 컬럼을 끄면(기본 숨김) 통째로 사라진다 — 상시 정보인데
   → 폭이 넉넉한 하단 한 줄로 내린다. 문구는 관리자가 입력한다.

   자리는 .app 바깥 · 푸터 위. 반품정책(.policy)과 같은 규칙이다 —
   .main 안에 넣으면 목록 높이를 그만큼 뺏는다.
   ========================================================================== */

.delivery {
  width: var(--shell-w);
  max-width: 100%;
  margin-inline: auto;
  padding: 0 var(--space-1) var(--space-3);
}
.delivery__inner {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: var(--white);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  border-left: 4px solid var(--green-accent);
}
.delivery__tag {
  flex: none;
  font-size: var(--text-2);
  font-weight: 700;
  color: var(--brand-green);
}
.delivery__msg {
  font-size: var(--text-2);
  color: var(--ink);
  line-height: 1.5;
  white-space: pre-line;   /* Enter 를 살린다 — .lp-info__b 와 같은 이유 */
  /* ★ 같은 문자열이 로그인 카드에도 간다. 끊는 규칙까지 같아야 한 벌이다. */
  word-break: keep-all;
  overflow-wrap: break-word;
}
.delivery__msg b { font-weight: 700; color: var(--green-accent); }
.delivery__msg em { font-style: normal; color: var(--red); font-weight: 600; }

@media (max-width: 1100px) {
  .delivery { width: auto; }
  .delivery__inner { flex-direction: column; align-items: flex-start; gap: var(--space-1); }
}

/* ==========================================================================
   14g. 닫기 버튼 · 토스트
   --------------------------------------------------------------------------
   ★ 여기 있던 '본문 상단 안내 한 줄'(.notice)은 모달로 바뀌었다 (2026-07-29).
     배너는 .main 맨 위에 끼어들어 목록·장바구니를 통째로 아래로 민다.
     "거래처를 먼저 선택해주세요" 는 읽고 넘기는 알림이 아니라 '이걸 먼저
     해야 한다' 는 말이라, 바로 옆에 [거래처 선택] 을 붙여 그 자리에서
     끝내는 편이 맞다 → shell.js 의 showNotice() 가 .modal--confirm 을 만든다.
     남은 .notice__close 는 토스트의 닫기 버튼이 쓴다.

   구버전은 이런 걸 전부 alert() 로 띄운다 (director_list2.asp:150 등).
   OS 대화상자는 화면 밖으로 튀어나오고 스타일도 못 입혀서 여기서는 안 쓴다.
   ========================================================================== */

.notice__close {
  width: 2.6rem; height: 2.6rem;
  border-radius: var(--radius-round);
  color: var(--ink-faint);
  transition: background var(--ease), transform var(--ease);
}
.notice__close:hover  { background: var(--ceramic); color: var(--ink); }
.notice__close:active { transform: scale(var(--press-scale)); }

/* --------------------------------------------------------------------------
   토스트 — 처리 결과(담았습니다 / 실패했습니다)를 얹어서 알린다.
   ★ 왜 본문 배너가 아닌가
     배너는 본문 맨 위에 끼어들어 목록·장바구니를 통째로 아래로 민다.
     담기·삭제는 자주 하는 동작이라, 누를 때마다 화면이 50px 씩 출렁인다.
     토스트는 position:fixed 라 레이아웃을 건드리지 않는다.
   ★ 성공은 스스로 사라지고(4초), 실패는 사용자가 닫을 때까지 남는다.
     성공은 "됐다" 만 알면 되지만 실패는 읽고 판단해야 한다.
   -------------------------------------------------------------------------- */
.toast {
  position: fixed; z-index: 90;
  top: var(--space-3); left: 50%;
  transform: translateX(-50%);
  display: flex; align-items: center; gap: var(--space-3);
  max-width: min(72rem, calc(100vw - var(--space-6)));
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4);
  background: var(--white);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-float);
  border-left: 4px solid var(--green-accent);
  font-size: var(--text-3);
  color: var(--ink);
  transition: opacity var(--ease), transform var(--ease);
}
.toast__text { flex: 1; min-width: 0; }
.toast.is-gone {
  opacity: 0;
  transform: translate(-50%, -0.8rem);
  pointer-events: none;
}

/*
   흰 면에 왼쪽 4px 테두리만 빨강이면, 흰 패널 위에서는 그냥 카드 한 장으로
   읽힌다. 성공은 4초 뒤 스스로 사라지므로 놓쳐도 잃는 것이 없지만 **실패는
   사람이 읽고 고쳐야** 끝난다 — 못 보고 지나가면 그 자리에서 막힌다.
   → 면을 통째로 채운다. 명암비 5.77:1 (#c82014 위 흰 글자 · AA 통과).
   ⚠ 닫기 버튼 색을 같이 바꾼다. 안 바꾸면 --ink-faint(검정 38%)가 빨강 위에
     얹혀 사실상 안 보인다 — 실패 토스트는 사용자가 닫아야 사라지는데
     닫는 단추가 안 보이면 그 규칙이 깨진다. */
.toast--err {
  background: var(--red);
  border-left-color: var(--red);
  color: var(--ink-white);
  font-weight: 600;
}
.toast--err .notice__close { color: var(--ink-white-soft); }
.toast--err .notice__close:hover {
  background: rgba(255, 255, 255, 0.18);
  color: var(--ink-white);
}

/*
   화면 맨 위 가운데는 이 화면에서 눈이 가장 안 가는 자리다. 수량을 치는
   눈은 중간 컬럼(상품정보)에 있고 고칠 곳도 그 칸인데, 토스트는 저 위에
   떴다 — 읽고 나서 다시 내려와 어디를 고칠지 찾아야 했다.

   ★ 중간 컬럼에 붙인 **덧그림**이다. 흐름에 넣으면 상품정보 패널이 그만큼
     높아져 5:5 가 깨지고 담을 때마다 화면이 출렁인다 (토스트를 fixed 로
     둔 이유가 바로 그것이다 · 위 주석).
   ★ 패널 **안**(.panel__body)에는 못 넣는다 — 그 상자는 overflow:auto 라
     남는 자리가 모자라면 토스트가 스크롤 밖으로 밀려 **아예 안 보인다.**
     컬럼에 붙이면 잘릴 데가 없다.
   ★ top:50% 는 상품정보와 과거주문내역이 갈리는 선이다. 수량칸은 상품정보
     표의 **마지막 행**이므로 그 바로 아래가 된다.
   ★ z-index 를 슬롯에 주지 않는다. 주면 슬롯이 쌓임 맥락이 되어, 같이 들어
     있는 되묻기 **모달(position:fixed)이 그 안에 갇힌다.**
   ※ 성공 토스트는 그대로 맨 위에 둔다 — '됐다' 는 눈을 옮길 이유가 없다. */
.ws-col--anchor { position: relative; }
.res-slot--info {
  position: absolute; inset: 0;
  pointer-events: none;            /* 덧그림이 패널을 못 쓰게 만들면 안 된다 */
}
.res-slot--info > * { pointer-events: auto; }
.res-slot--info .toast--err {
  position: absolute;
  top: 50%; left: 0; right: 0;
  max-width: none;
  transform: none;
}
.res-slot--info .toast--err.is-gone { transform: translateY(-0.8rem); }

/* ==========================================================================
   15. UTILITIES
   ========================================================================== */

.row { display: flex; align-items: center; gap: var(--space-2); }
.stack { display: flex; flex-direction: column; gap: var(--space-2); }
.grow { flex: 1; min-width: 0; }
.muted { color: var(--ink-soft); }
.strong { font-weight: 700; }
.nowrap { white-space: nowrap; }

/* ==========================================================================
   16. ADMIN SCREENS  (목록 컬럼 · 메뉴 · 배너 · 문구 관리)
   --------------------------------------------------------------------------
   ★★ 여기 있는 것들은 원래 프로토타입 HTML 의 인라인 <style> 이었다.
     동작(js)은 파일로 빼서 앱과 같이 쓰는데 **CSS 만 프로토타입에 남아 있어서
     앱의 관리자 화면 두 개가 스타일 없이 렌더되고 있었다** (2026-07-30 발견).
     문구 관리를 붙이며 세 번째로 같은 일을 할 참이라 여기로 모았다.

   ⚠ 모으면서 **세 화면의 정의가 서로 달랐던 것**이 드러났다 —
     `.warn-banner` 가 두 곳은 빨강(위험), 한 곳은 골드(주의)였고
     `.chk` 는 한 곳에만 `--err` 이 없었다. 뜻이 다른 것은 수식어로 나누고
     (`.warn-banner--soft`), 같은 것은 한 벌로 합쳤다.
     '두 벌로 만들지 않는다' 가 CSS 에도 그대로 걸린다.
   ========================================================================== */

/* --- 공통 : 저장 전 확인 목록 --------------------------------------------- */
.chk { display: flex; align-items: flex-start; gap: var(--space-2); font-size: var(--text-2); padding: var(--space-1) 0; }
.chk__i { flex: none; width: 1.6rem; text-align: center; font-weight: 700; }
.chk--ok   .chk__i { color: var(--green-accent); }
.chk--warn .chk__i { color: var(--gold); }
.chk--err  .chk__i { color: var(--red); }
.chk--err  { color: var(--red); }

/* --- 공통 : 경고 배너 -----------------------------------------------------
   기본은 빨강(되돌리기 어려운 일). --soft 는 골드(알고는 있어야 하는 일). */
.warn-banner {
  display: flex; align-items: flex-start; gap: var(--space-2);
  padding: var(--space-3);
  border: 1px solid var(--red);
  border-left-width: 4px;
  border-radius: var(--radius-card);
  background: var(--tint-red);
  color: var(--red);
  font-size: var(--text-3);
  line-height: 1.55;
}
.warn-banner__i { flex: none; font-size: 1.8rem; line-height: 1.2; }
.warn-banner b { font-weight: 700; }
.warn-banner small { display: block; margin-top: var(--space-1); font-size: var(--text-2); color: var(--ink-soft); }
.warn-banner--soft {
  border-color: var(--gold-light); border-left-width: 1px;
  background: var(--gold-lightest); color: var(--ink);
  padding: var(--space-2) var(--space-3); font-size: var(--text-2);
  margin-bottom: var(--space-3);
}

/* --- 목록 컬럼 관리 (admin-grid) ------------------------------------------ */
.colrow {
  display: grid;
  grid-template-columns: 34px 56px 108px minmax(0,1fr) 76px 88px 56px 40px;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-3);
  border-bottom: 1px solid var(--line);
  font-size: var(--text-2);
}
.colrow--head {
  background: var(--ceramic);
  color: var(--ink-soft);
  font-size: var(--text-1);
  font-weight: 600;
  border-bottom: 1px solid var(--line);
  padding-top: var(--space-2); padding-bottom: var(--space-2);
}
.colrow.is-off { background: var(--neutral-cool); color: var(--ink-faint); }
.colrow.is-off .colrow__key { opacity: .5; }
.colrow__key { font-family: ui-monospace, monospace; font-size: 1.1rem; color: var(--ink-soft); }
.colrow__move { display: flex; gap: 2px; }
.colrow__move button {
  width: 26px; height: 24px; padding: 0;
  border: 1px solid var(--line-strong); border-radius: 4px;
  background: var(--white); color: var(--ink-soft); font-size: 11px;
  transition: var(--ease);
}
.colrow__move button:hover:not(:disabled) { border-color: var(--green-accent); color: var(--green-accent); }
.colrow__move button:disabled { opacity: .3; cursor: default; }
.colrow .field { height: 2.8rem; font-size: var(--text-2); width: 100%; }
.lockmark { text-align: center; font-size: 1.2rem; }
.meter { display: flex; align-items: baseline; gap: var(--space-1); font-size: var(--text-2); }
.meter b { font-size: var(--text-5); font-variant-numeric: tabular-nums; }

/* 미리보기 컬럼 드래그 리사이즈 */
#preview thead th { position: relative; }
.colresize {
  position: absolute; top: 0; right: -4px;
  width: 9px; height: 100%;
  cursor: col-resize; z-index: 3;
  touch-action: none;
}
.colresize::after {
  content: ''; position: absolute; left: 4px; top: 15%;
  width: 1px; height: 70%;
  background: var(--line-strong);
  transition: var(--ease);
}
.colresize:hover::after,
.colresize.is-drag::after { background: var(--green-accent); width: 2px; top: 0; height: 100%; }
#preview thead th.is-flexcol { color: var(--green-accent); }
body.is-resizing { user-select: none; cursor: col-resize; }
.dragtip {
  position: fixed; z-index: 90;
  padding: 4px 10px; border-radius: 6px;
  background: var(--house-green); color: #fff;
  font-size: 1.2rem; font-weight: 700;
  font-variant-numeric: tabular-nums;
  pointer-events: none; white-space: nowrap;
  box-shadow: var(--shadow-float);
}
/*
   없으면 고장으로 읽힌다. 색이 먼저 눈에 들어오고 글자가 이유를 말한다.
   ⚠ **색만 바꾸면 안 된다** — 색을 못 가리는 사람에게는 아무 일도 안 일어난
     것과 같다. 그래서 문구도 통째로 바뀐다(px 숫자 → "더 줄일 수 없습니다"). */
.dragtip--limit { background: var(--red); }

/* --- 메뉴 노출 관리 (admin-menu) ------------------------------------------ */
.mrow {
  display: grid;
  grid-template-columns: 3.2rem minmax(0, 1fr) 18rem 7rem;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  border-bottom: 1px solid var(--line);
}
.mrow--head {
  background: var(--ceramic);
  color: var(--ink-soft);
  font-size: var(--text-1);
  font-weight: 600;
  text-align: center;
}
.mrow--head > :nth-child(2) { text-align: left; }
.mrow--group { background: var(--neutral-cool); font-weight: 700; }
.mrow--item  .mrow__nm { padding-left: var(--space-4); }
.mrow.is-off { color: var(--ink-faint); }
.mrow.is-off .mrow__nm { text-decoration: line-through; }
.mrow__nm { font-size: var(--text-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mrow__nm .ico { margin-right: var(--space-1); }
.mrow__key { font-family: ui-monospace, monospace; font-size: 1.1rem; color: var(--ink-faint); }
.mrow .field { height: 2.8rem; font-size: var(--text-2); width: 100%; }
.mrow__st { text-align: center; font-size: var(--text-1); font-weight: 600; }
.mrow__st--on  { color: var(--green-accent); }
.mrow__st--off { color: var(--ink-faint); }
.mrow__st--lock { color: var(--gold); }
.mrow input[type="checkbox"] { width: 1.7rem; height: 1.7rem; accent-color: var(--green-accent); margin: 0 auto; display: block; }
.navprev {
  background: var(--sidebar-bg);
  border-radius: var(--radius-card);
  padding: var(--space-2);
  max-height: 30rem;
  overflow: auto;
}
.navprev .nav__head { cursor: default; }

/* --- 배너 관리 (admin-banner) --------------------------------------------- */
.brow {
  display: grid;
  grid-template-columns: 3.2rem 9rem minmax(0, 1fr) 11rem 11rem;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2);
  border-bottom: 1px solid var(--line);
}
.brow--head {
  background: var(--ceramic);
  color: var(--ink-soft);
  font-size: var(--text-1);
  font-weight: 600;
  text-align: center;
}
.brow--head > :nth-child(2),
.brow--head > :nth-child(3) { text-align: left; }
.brow.is-off { color: var(--ink-faint); }
.brow.is-off .brow__nm { text-decoration: line-through; }
.brow__nm { font-size: var(--text-3); font-weight: 600; }
.brow .field { height: 2.8rem; font-size: var(--text-2); width: 100%; }
.brow input[type="checkbox"] { width: 1.7rem; height: 1.7rem; accent-color: var(--green-accent); margin: 0 auto; display: block; }

/* 그림·주소 줄 (2026-07-31) — 배너마다 **둘째 줄**이다.
   ★ 한 줄에 이어 붙이면 문구 칸이 밀려 두 글자밖에 안 보인다. 위 그리드가
     이미 다섯 칸을 쓰고 있어 여유가 없다.
   ★ 위 줄과 한 덩어리로 보이게 경계선을 지우고 살짝 들여쓴다 — 안 그러면
     배너가 네 개인 것처럼 읽힌다. */
.brow--sub {
  grid-template-columns: 3.2rem auto auto minmax(0, 1fr);
  border-bottom: 1px solid var(--line);
  padding-top: 0;
  padding-left: calc(var(--space-2) + 3.2rem);
}
.brow--sub .brow__nm { color: var(--ink-soft); font-weight: 500; }

/* 미리보기에 넣은 그림 — 실물과 같은 폭 비율로 보여준다.
   ⚠ 세로는 안 자른다. 실물도 안 자른다 (app.css 12절 .banner__img). */
.bprev__img { width: 100%; height: auto; display: block; border-radius: var(--radius-sm); }

/* 배너 미리보기 — 실제 본문 폭 비율을 그대로 보여준다.
   "여기선 괜찮은데 실제 화면에선 목록이 좁다" 가 되면 설정 화면의 의미가 없다. */
.bprev { display: flex; gap: var(--space-2); background: var(--ceramic); border-radius: var(--radius-card); padding: var(--space-2); }
.bprev__main {
  flex: 1; min-width: 0;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  display: grid; place-items: center;
  min-height: 16rem;
  font-size: var(--text-1); color: var(--ink-soft); text-align: center;
}
.bprev__side { flex: none; width: 7.6rem; display: flex; flex-direction: column; gap: var(--space-2); }
.bprev__b { border-radius: var(--radius-sm); min-height: 4.8rem; display: grid; place-items: center; font-size: 1.1rem; font-weight: 600; line-height: 1.35; text-align: center; }

/* --- 로그인 직후 팝업 (popup 테이블 · shell.js 의 popupShow) ---------------
   ★ window.open 이 아니라 모달이다 — 요즘 브라우저는 사용자 조작 없는
     새 창을 막는다. 관리자가 등록해도 안 뜨면 기능이 없는 것과 같다. */
.modal--popup .modal__box { width: min(92vw, 720px); }
/* ★ 단추 둘을 양 끝으로 민다 — 왼쪽 「오늘 하루 안 보기」 · 오른쪽 「닫기」.
     둘 다 닫으므로 나란히 두면 무엇이 다른지 안 보인다. 떨어뜨려 놓으면
     "그냥 닫기" 와 "오늘은 그만" 이 서로 다른 결정으로 읽힌다.
   ⚠ 이 space-between 이 다른 모달로 새면 버튼 하나가 왼쪽 끝에 붙는다 —
     실제로 상품 이미지 모달에서 그랬다. `.modal--popup` 밖으로 빼지 말 것. */
.modal--popup .modal__actions { justify-content: space-between; align-items: center; }
.popup__body { max-height: 60vh; overflow: auto; font-size: var(--text-3); line-height: 1.7; }
/* 구버전 팝업 본문은 인라인 style 로 폭이 박혀 있다 — 공지 본문과 같은 규칙 */
.popup__body img { max-width: 100% !important; height: auto !important; display: block; }
.popup__body p { margin: 0 0 var(--space-2); }

/* --- 상품 이미지 (상품정보에서 상품명을 누르면) -----------------------------
   ★ 이미지가 **있는 상품만** 이름이 버튼이 된다. 그래서 눌러도 되는 자리라는
     것이 보여야 한다 — 밑줄로 알린다. 색은 팔레트를 따라간다 (함정 ⑱).
   ⚠ 이 칸은 폭이 좁아 대부분 말줄임된다. 버튼이어도 그 동작은 유지한다. */
.linkish {
  border: 0; background: none; padding: 0; font: inherit; cursor: pointer;
  color: var(--green-accent); text-decoration: underline;
  text-underline-offset: 2px; text-align: left;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.linkish:hover { text-decoration-thickness: 2px; }

/* 그림 표시 — 이미지가 있는 상품임을 **패널 머리에서** 알린다 (2026-08-10).
   ★★ 처음에는 이 아이콘을 상품명 **앞**에 붙였다가 옮겼다(같은 날). 그 칸은
     내용폭이 150px 남짓이라 아이콘 13px + 여백 4px 를 이름에서 빼앗는다 —
     그러잖아도 대부분 말줄임되는 자리다. 머리글에는 제목과 상품코드 말고
     쓰는 것이 없어 **폭 값이 0 인 자리**다.
   ★ 그래서 이름은 예전 그대로 `.linkish` 하나다. 누를 수 있다는 것은 밑줄이,
     무엇이 열리는지는 이 단추가 말한다 — 둘 다 같은 showMedImg 를 부른다.

   ★★ **채움 알약이다. 테두리 아이콘이 아니다** (2026-08-10 · 한 번 고쳤다).
     처음에 `.icon-btn`(회색 테두리 24px)을 재활용했다가 "눈에 안 띈다" 는
     지적을 받았다 — 맞다. 그 모양은 **표 안에 늘 있는 단추**라 눈이 배경으로
     읽는다. 이건 **어쩌다 한 번 나타나는 단추**라 성격이 반대다.
     → 채움 + 글자까지 붙여 "여기에 그림이 있다" 를 한눈에 말하게 했다.
   ★ 아이콘만 두지 않고 **「이미지」 세 글자**를 같이 둔다. 머리글에 노는 폭이
     74px 있었고 이 알약이 63px 다 — 넣을 자리가 있는데 뜻을 아이콘 하나에
     맡길 이유가 없다. 상품코드가 다섯 자리(VARCHAR(5))라 더 늘지 않는다.
   ⚠ 글자색은 흰색 고정이 아니라 `--white` 다. 배경이 `--green-accent` 라
     팔레트를 바꾸면 둘이 같이 움직여야 명암비가 유지된다. */
.medimg-btn {
  display: inline-flex; align-items: center; gap: 3px;
  height: 2.2rem; padding: 0 var(--space-2) 0 6px;
  border: 0; border-radius: 999px;
  background: var(--green-accent); color: var(--white);
  font-size: var(--text-1); font-weight: 700; letter-spacing: -0.02em;
  line-height: 1; white-space: nowrap; cursor: pointer;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.16);
  transition: background var(--ease), transform var(--ease);
}
.medimg-btn:hover  { background: var(--brand-green); }
.medimg-btn:active { transform: scale(var(--press-scale)); }
.medimg-btn svg    { fill: currentColor; display: block; }
/* ⚠ display 를 클래스가 정하므로 브라우저 기본 [hidden] 이 진다. 한 줄이 필요하다. */
.medimg-btn[hidden] { display: none; }

.modal--medimg .modal__box { width: min(92vw, 560px); }
.medimg { display: flex; align-items: center; justify-content: center; min-height: 12rem; }
.medimg img { max-width: 100%; max-height: 60vh; height: auto; display: block; }
/* 등록은 돼 있는데 파일이 없는 경우 — 빈 상자면 고장으로 읽힌다 */
.medimg--none { color: var(--ink-soft); font-size: var(--text-2); }

/* --- 표 안의 아이콘 버튼 (일련번호 보기) -----------------------------------
   ★ 좁은 칸(44px · 내용폭 28px)에 글자 버튼을 넣으면 잘린다. 구버전도
     이 자리는 글자가 아니라 목록 모양 아이콘이었다.
   ★ 색은 currentColor 로 받는다 — 팔레트를 바꾸면 같이 따라온다. */
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; padding: 0;
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--white); color: var(--ink-soft);
  cursor: pointer; transition: all var(--ease);
}
.icon-btn:hover { border-color: var(--green-accent); color: var(--green-accent); }
.icon-btn:active { transform: scale(var(--press-scale)); }
.icon-btn svg { fill: currentColor; display: block; }

/* --- 과거주문내역 '자세히' — 최근 1년 (order_screen.asp) --------------------
   ★ 패널이 10건까지밖에 못 담아서 나머지를 여기서 본다. 화면을 안 떠나는 것이
     요점이라 **모달**이다 — 장바구니·검색조건·스크롤이 그대로 남는다.
   ★ 표 자체는 패널과 같은 .tbl 이다. 같은 데이터를 다른 모양으로 그리면
     "이게 그거 맞나" 를 매번 다시 확인하게 된다.
   ⚠ 1년이면 행이 길어질 수 있다. 모달이 화면을 넘지 않게 표만 스크롤한다 —
     모달 전체가 스크롤되면 머리글과 닫기 버튼이 같이 밀려 올라간다. */
.modal--oldyear .modal__box { width: min(92vw, 460px); }
.oldyear__note {
  margin: 0 0 var(--space-2); color: var(--ink-soft); font-size: var(--text-4);
}
.oldyear__note.is-capped { color: var(--red); }
.oldyear__wrap { max-height: 52vh; overflow-y: auto; }

/* --- 배송지 고르기 (합계바 '변경' → 모달) ----------------------------------
   ★ **배송지를 그리는 곳은 여기 하나뿐이다.** 저장된 목록 · 직접 입력 ·
     목록 추가 · 기본 지정이 전부 이 모달 안에 있다. 합계바에는 고른 결과
     한 줄만 남는다 (.summary__addr).
   ★ 구버전은 장바구니 아래에 '배송지목록' 표를 따로 그리고, 입력칸은 그 위에
     또 그렸다(order_basket.asp:300·381). 같은 값을 두 곳에서 그리니 한쪽만
     고쳐지면 갈라진다 — 실제로 방문/택배 전환 때 칸이 사라졌다 다시 나온다.
   ★ 목록은 라디오다. '지금 이 주문에 쓸 곳' 은 언제나 하나다.
   ⚠ 저장된 주소가 없을 수 있다 (첫 주문). 그때는 목록 자리에 안내를 두고
     직접 입력만 남긴다 — 빈 상자를 보여 주면 고장으로 읽힌다. */
.modal--ship .modal__box { width: min(92vw, 620px); }

.ship-sec { padding: var(--space-3); }
.ship-sec + .ship-sec { border-top: 1px solid var(--line); }
.ship-sec__h {
  font-size: var(--text-2); font-weight: 700; color: var(--ink-soft);
  letter-spacing: var(--tracking);
  margin-bottom: var(--space-2);
}

.ship-list { display: flex; flex-direction: column; gap: var(--space-1); }
.ship-item {
  display: flex; align-items: flex-start; gap: var(--space-2);
  padding: var(--space-2);
  border: 1px solid var(--line); border-radius: var(--radius-field);
  background: var(--white);
  cursor: pointer;
  transition: var(--ease);
}
.ship-item:hover { background: var(--neutral-cool); }
.ship-item.is-on { border-color: var(--green-accent); background: var(--green-light); }
.ship-item__pick { flex: none; margin-top: 2px; }
.ship-item__body { flex: 1 1 auto; min-width: 0; }
.ship-item__addr { font-size: var(--text-3); color: var(--ink); }
.ship-item__sub {
  margin-top: 2px;
  font-size: var(--text-1); color: var(--ink-soft);
}
/* '기본' 표식 — 다음에 열었을 때 먼저 골라져 있을 곳이라는 뜻이다 */
.ship-item__tag {
  display: inline-block; margin-left: var(--space-1);
  padding: 0 var(--space-1);
  border-radius: var(--radius-pill);
  background: var(--green-accent); color: var(--white);
  font-size: var(--text-1); line-height: 1.7;
}
/* 삭제는 목록에서만 한다. 지운 뒤 되돌릴 방법이 없으므로 되묻는다. */
.ship-item__del {
  flex: none;
  width: 2.4rem; height: 2.4rem;
  border-radius: var(--radius-round);
  color: var(--ink-faint); font-size: var(--text-3);
  transition: var(--ease);
}
.ship-item__del:hover { background: var(--ceramic); color: var(--red); }

.ship-empty { font-size: var(--text-2); color: var(--ink-faint); }

/* 직접 입력 — 우편번호 검색이 안 떠도 손으로 칠 수 있어야 한다.
   외부 스크립트(다음 우편번호)가 렌더 경로에 있다. 그것이 죽었다고
   주문을 못 하게 만들 수는 없다. */
.ship-form { display: grid; grid-template-columns: 7.5rem 1fr; gap: var(--space-2); align-items: center; }
.ship-form > .lbl { font-size: var(--text-2); color: var(--ink-soft); }
.ship-form > .val { display: flex; align-items: center; gap: var(--space-1); min-width: 0; }
.ship-form > .val .field { min-width: 0; }
.ship-opt { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: var(--space-3); }
.ship-opt label { display: flex; align-items: center; gap: var(--space-1); font-size: var(--text-2); }

/* --- 관리자 「기본 설정」 스위치 (admin_option.asp) --------------------------
   ★ 켜고 끄는 것만 있는 화면이라 설정 모듈(admin-*.js)을 안 붙였다.
     체크박스 몇 개에 모듈을 두면 프로토타입과 앱에 같은 로직이 두 벌 생긴다.
   ★ 설명이 라벨보다 길다. 무엇이 달라지는지 읽고 켜야 하는 값들이라
     '이름만 있는 스위치' 로 두지 않는다 — 그러면 CS 가 못 켠다. */
.optrow { padding: var(--space-3) 0; border-bottom: 1px solid var(--line); }
.optrow:last-of-type { border-bottom: 0; }
.optrow__sw { display: flex; align-items: center; gap: var(--space-2); cursor: pointer; }
.optrow__lbl { font-size: var(--text-4); font-weight: 700; }
.optrow__hint {
  margin: var(--space-1) 0 0 2.4rem;
  font-size: var(--text-2); color: var(--ink-soft);
}

/* --- 숫자 설정 (OPT_TYPE = NUMBER · 2026-08-10) ----------------------------
   ★ 스위치 행과 **눈에 띄게 달라야 한다.** 같은 자리에 섞여 있는데 생김새가
     같으면 "체크가 없네" 로 읽힌다.
   ★ 왼쪽 들여쓰기(2.4rem)를 안내문과 맞춘다 — 체크박스 자리만큼이라
     둘이 어긋나면 목록이 계단처럼 보인다. */
.optrow--num { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: var(--space-2); }
.optrow__numlb { font-size: var(--text-4); font-weight: 700; }
.optrow--num .field--num { width: 9rem; }
.optrow--num .optrow__hint { grid-column: 1 / -1; margin-left: 0; }

/* --- 공지 · 팝업 편집기 (assets/editor.js) ---------------------------------
   ★ 버튼은 SafeHtml 이 살려 두는 서식만 만든다 — editor.js 주석 참고. */
.editor { border: 1px solid var(--line-strong); border-radius: var(--radius-sm); background: var(--white); }
.editor__bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 2px;
  padding: var(--space-1); border-bottom: 1px solid var(--line);
  background: var(--ceramic); border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}
.editor__btn {
  min-width: 2.8rem; height: 2.6rem; padding: 0 var(--space-1);
  border: 1px solid transparent; border-radius: 4px;
  background: transparent; color: var(--ink-soft);
  font-size: var(--text-2); line-height: 1; transition: var(--ease);
}
.editor__btn:hover { background: var(--white); border-color: var(--line-strong); color: var(--ink); }
.editor__btn:active { transform: scale(var(--press-scale)); }
.editor__sep { width: 1px; height: 1.6rem; background: var(--line-strong); margin: 0 var(--space-1); }
.editor__body {
  min-height: 24rem; max-height: 46rem; overflow: auto;
  padding: var(--space-3);
  font-size: var(--text-3); line-height: 1.7;
}
.editor__body:focus { outline: 2px solid var(--green-accent); outline-offset: -2px; }
.editor__body[data-busy="1"] { opacity: .55; pointer-events: none; }
/* 본문 안에서 그림이 화면을 뚫고 나가지 않게 — 구버전 글은 인라인 style 로
   폭이 박혀 있어 !important 가 필요하다 (notice_view 와 같은 규칙) */
.editor__body img { max-width: 100% !important; height: auto !important; }
.editor__body h2 { font-size: var(--text-6); font-weight: 700; line-height: var(--leading-compact); margin: var(--space-3) 0 var(--space-1); }
.editor__body h3 { font-size: var(--text-5); font-weight: 700; margin: var(--space-3) 0 var(--space-1); }
.editor__body p { margin: 0 0 var(--space-2); }
.editor__body ul, .editor__body ol { margin: 0 0 var(--space-2) var(--space-4); }
.editor__body a { color: var(--green-accent); text-decoration: underline; }

/* --- 문구 관리 (admin-text) ------------------------------------------------ */
.trow {
  display: grid;
  grid-template-columns: 15rem minmax(0, 1fr);
  gap: var(--space-2);
  align-items: start;
  padding: var(--space-2);
  border-bottom: 1px solid var(--line);
}
.trow--head {
  display: block;
  background: var(--ceramic);
  color: var(--ink-soft);
  font-size: var(--text-1);
  font-weight: 700;
  letter-spacing: .02em;
}
/* --- 접을 수 있는 묶음 머리줄 (2026-08-07) ---------------------------------
   ★ `<button>` 이다. <div> 에 클릭만 걸면 키보드로 못 연다.
     그래서 버튼의 기본 모양(테두리·가운데정렬·글꼴)을 여기서 지운다.
   ★ 어느 묶음을 왜 접는지는 shell.js 의 TEXT_GROUPS 에 있다 — 여기는 모양만. */
.trow--fold {
  width: 100%;
  text-align: left;
  border: 0;
  border-bottom: 1px solid var(--line);
  font: inherit;
  font-weight: 700;
  color: inherit;
  cursor: pointer;
  transition: var(--ease);
}
.trow--fold:hover { background: var(--neutral-cool); }
.trow--fold__nm, .trow--head__nm { display: inline-block; }
/* 삼각형은 **글자가 아니라 표식**이다 — 펼침 상태만 CSS 로 돌린다. */
.trow--head__nm::before {
  content: "▾";
  display: inline-block;
  margin-right: var(--space-1);
  color: var(--ink-faint);
  transition: transform var(--dur-fast, .12s) ease;
}
.trow--fold[aria-expanded="false"] .trow--head__nm::before { transform: rotate(-90deg); }
/* 왜 접혀 있는지. ★ 접힌 채로도 보인다 — 이유가 안 보이면 '없어졌다' 가 된다. */
.trow--head__note {
  display: block;
  margin-top: 4px;
  font-size: var(--text-1);
  font-weight: 400;
  letter-spacing: 0;
  color: var(--ink-faint);
  line-height: 1.5;
  word-break: keep-all;
  overflow-wrap: break-word;
}
.trow--head__note b { font-weight: 700; color: var(--ink-soft); }
.trow__lb { font-size: var(--text-2); line-height: 1.45; }
.trow__lb b { font-weight: 600; }
.trow__lb small { display: block; color: var(--ink-faint); font-size: var(--text-1); margin-top: 2px; }
.trow__req {
  display: inline-block; margin-left: 4px; padding: 0 5px;
  border-radius: 3px; background: var(--gold-lightest); color: var(--ink);
  font-size: 1rem; font-weight: 700; vertical-align: 1px;
}
.trow__in { min-width: 0; }
.trow__in .field { width: 100%; font-size: var(--text-2); }
.trow__in textarea.field { height: auto; line-height: 1.5; padding: 6px 8px; resize: vertical; }
.trow__cnt { font-size: var(--text-1); color: var(--ink-faint); }
.trow__cnt.is-over { color: var(--gold); font-weight: 700; }
.trow__tag { font-size: var(--text-1); color: var(--ink-faint); margin-left: var(--space-2); }

/* 문구 미리보기 — 실제 클래스(.sidebar__brand · .delivery · .login__*)를
   그대로 쓴다. 따로 그리면 실물과 갈라진다. 여기 있는 것은 '액자' 뿐이다. */
.pv { margin-bottom: var(--space-3); }
.pv__cap { font-size: var(--text-1); color: var(--ink-faint); margin-bottom: 4px; font-weight: 600; }
.pv__brand {
  background: var(--sidebar-bg); padding: var(--space-3);
  display: flex; flex-direction: column; width: 22rem;
  border-radius: var(--radius-card);
}
/* ★ 실제 푸터와 같은 배치다 — 글자 왼쪽, 「공식 홈페이지」 오른쪽.
     버튼이 없을 때는 <span> 자체가 없어 예전과 똑같이 보인다. */
.pv__foot { background: var(--house-green); color: var(--ink-white); padding: var(--space-2) var(--space-3); font-size: var(--text-1); line-height: 1.6; border-radius: var(--radius-card); display: flex; align-items: center; gap: var(--space-3); }
.pv__foot-l { min-width: 0; }
.pv__foot .foot__home { margin-left: auto; }
.pv__login { background: var(--canvas); padding: var(--space-3); text-align: center; border-radius: var(--radius-card); }
.pv .delivery { margin: 0; }

/* --- 주문 통계 (admin-stats) ------------------------------------------------
   ★★ **차트 라이브러리를 안 쓴다. SVG 를 직접 그린다.**
     ① 색이 팔레트를 따라와야 한다 — 아래 .c-* 는 전부 `fill: var(--…)` 다.
       라이브러리에 색을 넘기는 방식이면 팔레트 16종을 코드가 다시 알아야 하고,
       그건 함정 ③(하드코딩한 색)을 다른 이름으로 다시 만드는 것이다.
     ② 지금 폰트가 CDN 에서 오는 것만으로도 걸려 있는 논점인데(CLAUDE.md 5절)
       렌더 경로에 제3자를 하나 더 놓을 이유가 없다. 막대 두 종류면 SVG 로 충분하다.
   ★ SVG 는 **컨테이너 폭을 재서 1:1 로** 그린다(admin-stats.js). viewBox 로
     늘리면 글자까지 같이 늘어나 축 라벨이 흐려진다.
   --------------------------------------------------------------------------- */

/* 요약 타일 — 칸 수를 CSS 에 박지 않는다. 지표를 하나 더하거나 빼도
   auto-fit 이 알아서 나눈다(지금 5개). */
/* ★ 칸 수를 CSS 에 박지 않는다. 지표를 더하거나 빼도 그대로 나뉜다.
   auto-flow:column + auto-columns:1fr 이라 **span 을 쓴 칸이 있어도** 전체가
   같은 폭 단위로 쪼개진다 (아래 .stat--pair 가 2칸을 쓴다). */
.statgrid {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: var(--space-2);
}
.stat {
  min-width: 0;
  padding: var(--space-3);
  border-radius: var(--radius-card);
  background: var(--white);
  box-shadow: var(--shadow-card);
}
/*
     이름 12→14 · 값 24→32 · 단위 13→14.
     이 화면은 목록처럼 훑는 곳이 아니라 **숫자 다섯 개를 보는 곳**이라
     고밀도 규칙(--text-1/2)을 그대로 쓸 이유가 없었다. */
.stat__k { font-size: var(--text-3); color: var(--ink-soft); }
.stat__v {
  margin-top: 2px;
  font-size: var(--text-7); font-weight: 700;
  line-height: var(--leading-compact);
  color: var(--house-green);
  font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.stat__v small { margin-left: 2px; font-size: var(--text-3); font-weight: 400; color: var(--ink-soft); }

/* --- 값이 둘인 타일 (전체 / 웹) ---
   ★★ 비중만 있고 **분모가 없으면 '뭐에 대한 70%냐' 를 다시 묻게 된다.**
     그렇다고 칸을 하나 더 늘리면, 이 화면에서 웹이 아닌 것을 세는 칸이
     주제와 같은 크기·같은 색으로 나란히 서서 **옆 칸들까지 전체 기준으로 읽힌다.**
     → 한 칸에 둘을 넣되 **크기와 색으로 주·종을 가른다.** 앞이 전체(작고 흐리게),
       뒤가 웹(크고 진하게)이다. 이 화면의 주어는 웹이다.
   ★ 두 칸 폭을 쓴다. 32px 짜리 숫자 둘을 215px 에 넣으면 잘린다(실측). */
.stat--pair { grid-column: span 2; }
.stat__all {
  font-size: var(--text-5); font-weight: 600;
  color: var(--ink-soft);
}
/* ⚠ 구분선 색(--line-strong)을 쓰지 않는다. 흰 바탕에서 **대비 1.4** 라 거의
   안 보이는데, 이 슬래시는 장식이 아니라 **두 숫자를 가르는 뜻**을 갖는다 —
   안 보이면 '30,835 23,346' 이 한 덩어리로 읽힌다. */
.stat__sep {
  margin: 0 var(--space-1);
  font-size: var(--text-5); font-weight: 400;
  color: var(--ink-faint);
}

/* 차트 */
.chart { display: block; width: 100%; }
.chart .c-grid { stroke: var(--line); stroke-width: 1; shape-rendering: crispEdges; }
.chart .c-base { stroke: var(--line-strong); stroke-width: 1; shape-rendering: crispEdges; }
.chart .c-bar  { fill: var(--green-accent); transition: fill var(--dur, .12s) ease; }
.chart .c-bar:hover { fill: var(--brand-green); }
/* 주말·휴무일 — 값이 0 에 가까운 날이 실제로 있다. 흐리게 두면 '자료 없음' 과
   '문 닫은 날' 이 구분된다. 실측(hanbaek_new): 토요일 1~3건 · 일요일 행 자체가 없다. */
.chart .c-bar--off { fill: var(--green-light); }
/* ⚠ 축 글자를 키우면 **세 값이 같이 움직인다** (admin-stats.js) —
   좌측 여백 PL(y 라벨 자리) · 아래 여백 PB · x 라벨을 몇 칸마다 찍을지.
   글자만 키우면 y 라벨이 잘리고 x 라벨이 서로 겹친다. 셋을 같이 고칠 것. */
.chart .c-lbl   { fill: var(--ink-soft);  font-size: 12px; }
.chart .c-lbl--y{ fill: var(--ink-faint); font-size: 12px; }
.chart-empty {
  display: grid; place-items: center;
  height: 16rem;
  color: var(--ink-faint); font-size: var(--text-2);
}

/* 순위표 — 표 안에 막대를 같이 넣는다.
   ★ 그래프와 표를 따로 두지 않는 이유 : 따로 두면 같은 순위를 두 곳에서 읽게
     되고 눈이 오간다. 막대는 '크기 비교', 숫자는 '정확한 값' 이라 한 행에
     둘 다 있는 편이 낫다.
   ⚠ .tbl td 가 overflow:hidden 이라 막대가 칸 밖으로 안 나간다 — 그것에 기댄다. */
.rankbar { position: relative; min-width: 0; }
.rankbar::before {
  content: ''; position: absolute; top: 1px; bottom: 1px; left: 0;
  width: var(--p, 0%);
  border-radius: 3px;
  background: var(--green-light);
}
.rankbar > span {
  position: relative;
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rank-no {
  display: inline-block; min-width: 1.8rem;
  font-weight: 700; color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}
.rank-no--top { color: var(--house-green); }

/* ==========================================================================
   17. PRINT  (조회 화면 🖨 출력)
   --------------------------------------------------------------------------
   ★★ **인쇄 전용 화면을 안 만든다.** 만들면 목록을 그리는 곳이 두 벌이 되고,
     컬럼을 켜고 끌 때마다 둘을 같이 고쳐야 한다 — 이 프로젝트가 되풀이해
     피해 온 자리다. 지금 보고 있는 화면을 그대로 인쇄하고, 인쇄에 뜻이 없는
     것(사이드바·검색바·버튼·페이저)만 감춘다.
   ★ 구버전은 ActiveX(`print/smsx.cab`)라 **크롬·엣지에서 아예 안 된다**
     (보안 ③). 이건 브라우저 기본 인쇄라 어디서나 된다.
   ⚠ 화면에서 보이는 컬럼만 인쇄된다. 관리자가 끈 컬럼은 종이에도 없다 —
     그게 맞다(목록과 파일과 종이가 같아야 한다).
   ========================================================================== */
/* 인쇄 전용 블록 — 평소에는 없는 셈 친다.
   ★ 인쇄 전용 '페이지' 를 만들지 않는 대신, 종이에만 필요한 조각을 화면 안에
     숨겨 둔다. 목록을 그리는 곳은 여전히 한 곳이다. */
.print-only { display: none; }

@media print {
  .print-only { display: block !important; }

  /* --- 매출원장 양식 (구버전 order_list_prn2.asp 의 머리글) --------------- */
  .print-doc { margin-bottom: 6mm; }
  .print-doc__title {
    text-align: center; font-size: 20pt; font-weight: 700;
    letter-spacing: .4em; margin: 0 0 5mm;
  }
  .print-doc__party { width: 100%; border-collapse: collapse; font-size: 9pt; }
  .print-doc__party th,
  .print-doc__party td { border: 1px solid #000; padding: 1.5mm 2mm; }
  .print-doc__party th { background: #f0f0f0; text-align: center; width: 22mm; white-space: nowrap; }
  .print-doc__party td { width: 33%; }
  .print-doc__meta {
    display: flex; gap: 8mm; margin-top: 3mm; font-size: 9pt;
  }
  .print-doc__confirm {
    display: flex; justify-content: space-between; align-items: baseline;
    margin-top: 3mm; font-size: 9pt;
  }
  .print-doc__confirm-txt { font-weight: 700; }

  /* 양식이 붙는 화면에서는 화면용 안내문을 감춘다 — 종이에서는 제목이 곧 안내다 */
  .print-doc ~ .page-head { display: none; }

  /* 색을 살린다. 상태 배지가 회색으로 뭉개지면 구분이 안 된다. */
  * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }

  html, body { height: auto; background: #fff; }
  body { font-size: 10pt; }

  /* 지면에 뜻이 없는 것들 */
  .sidebar, .banners, .searchbar, .pager, .devbar, .foot__links,
  .delivery, .policy, .fab, .modal, .toast, #resSlot,
  .panel__actions, .btn, .btn-icon, .quick-range { display: none !important; }

  /* 셸의 고정 폭·높이를 푼다 — 종이 폭에 맞춘다 */
  .app { display: block; width: auto; min-height: 0; grid-template-columns: 1fr; }
  .main { padding: 0; gap: 0; }
  .panel { box-shadow: none; border: 0; border-radius: 0; }
  .panel__head { border-bottom: 1px solid #000; padding: 0 0 4px; }
  .panel__body, .panel__body--flush { padding: 0; }

  /* 표는 잘리지 않게 — 스크롤 상자를 푼다 */
  .table-wrap, .scroll-y { max-height: none !important; overflow: visible !important; height: auto !important; }
  .tbl { width: 100%; }
  .tbl thead { display: table-header-group; }   /* 쪽마다 머리글 반복 */
  .tbl tr { page-break-inside: avoid; }
  .tbl th, .tbl td { border: 1px solid #999; padding: 2px 4px; }

  /* 푸터는 사업자 정보만 남긴다 (거래명세 성격의 종이라 있는 편이 낫다) */
  .foot { border: 0; background: none; color: #000; }
}

/* ==========================================================================
   18. RESPONSIVE
   ========================================================================== */

/* 폭은 --shell-w 로 고정이므로 해상도별 폭 조정 미디어쿼리는 두지 않는다.
   (두면 다시 "PC마다 컬럼 폭이 다른" 문제가 생긴다)
   1100px 미만에서만 세로 1단으로 전환한다. */
@media (max-width: 1100px) {
  :root { --sidebar-w: 100%; }
  .app { width: auto; grid-template-columns: 1fr; }
  .banners { display: none; }
  .sidebar { max-height: none; }
  .nav { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-1); }
}

/* ==========================================================================
   19. MOBILE
   --------------------------------------------------------------------------
     리뉴얼 안에 반응형 레이어를 만들다가 걷어냈다 — 같은 화면을 두 곳에서
     그리게 되고, 그러면 한쪽만 고쳐지는 날이 온다(제1 규칙).
     되살릴 일이 생기면 git 에 있다 (9b6d293 · db859a0).

   ⚠ 아래 한 줄만 남긴다. 이건 모바일 전용 꾸밈이 아니라 **버그 수정**이다 —
     좁은 화면에서 로그인 패널이 화면 높이에 맞춰 늘어나는데 내용이 더 길어
     `overflow: hidden` 이 **로그인 버튼을 통째로 잘라 먹었다**(실측 386×816).
     누를 버튼이 없으면 로그인 자체가 안 된다.
   ========================================================================== */
@media (max-width: 760px) {
  .login__panel { flex: 0 0 auto; }
}

/* ==========================================================================
   20. 결제 결과 (pay_result.asp)
   --------------------------------------------------------------------------
   ★ 셸 밖 화면이라 기존 .card · .btn 을 그대로 쓴다. 여기서 새로 정하는 것은
     **가운데 놓기와 표식 동그라미 둘뿐**이다 — 새 화면이 생길 때마다 규칙을
     새로 만들면 그만큼 갈라진다.
   ⚠ 색은 전부 토큰이다. 이 파일에 색을 글자로 박지 않는다.
   ========================================================================== */
.paydone {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: var(--space-6);
  background: var(--canvas);
}
/* ★ 흰 면은 .panel 이 맡는다 — 이 앱에 .card 는 없다.
     새 화면이 생길 때마다 면을 새로 정의하면 그만큼 갈라진다. */
.paydone__card {
  width: min(44rem, 100%);
  padding: var(--space-7) var(--space-6);
  text-align: center;
}
/* 표식 — ✓ 는 성공, ! 는 그 밖. 글자와 색을 **같이** 바꾼다.
   ★ 색만 바꾸면 색을 못 가리는 사람에게는 아무 일도 안 일어난 것과 같다
     (폭 조절 말풍선에서 같은 판단을 했다). */
.paydone__mark {
  width: 5.6rem;
  height: 5.6rem;
  margin: 0 auto var(--space-4);
  border-radius: var(--radius-round);
  display: grid;
  place-items: center;
  font-size: 2.8rem;
  font-weight: 700;
  line-height: 1;
}
.paydone__mark.is-ok { background: var(--green-light);   color: var(--green-accent); }
.paydone__mark.is-no { background: var(--gold-lightest); color: var(--red); }

.paydone__msg {
  margin: 0 0 var(--space-5);
  font-size: 2rem;
  font-weight: 700;
  line-height: var(--leading-compact);
  color: var(--ink);
  word-break: keep-all;
}
.paydone__hint {
  margin: 0 0 var(--space-5);
  color: var(--ink-soft);
}
/* 값 네 줄 — 제목 왼쪽 · 값 오른쪽. 금액은 자릿수가 어긋나면 못 읽는다. */
.paydone__list {
  margin: 0 0 var(--space-6);
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-2) var(--space-4);
  text-align: left;
  border-top: 1px solid var(--line);
  padding-top: var(--space-4);
}
.paydone__list dt { color: var(--ink-soft); white-space: nowrap; }
.paydone__list dd {
  margin: 0;
  text-align: right;
  font-family: var(--font-num);
  color: var(--ink);
}
/* ⚠ .panel 은 세로 플렉스라 자식이 폭 전체로 늘어난다. 단추가 하나뿐인
     화면에서 카드 폭만큼 늘어나면 '무엇을 누르는 것인지' 가 흐려진다. */
.paydone .btn { align-self: center; min-width: 16rem; }

/* 같은 결과를 **모달 안에서도** 보여준다 (2026-08-31 · 원장 화면을 안 떠난다).
   ★ 값과 표식은 위 규칙을 그대로 쓴다 — 두 벌로 만들지 않는다.
     여기서 정하는 것은 **가운데 정렬과 글자 크기**뿐이다(모달은 화면보다 좁다). */
/* ⚠⚠ **여백은 .modal__body 가 안 준다** — 내용 클래스가 준다(.pay-form 이 그렇다).
     안 주면 글자가 모달 가장자리에 붙어 **잘린 것처럼 보인다**(2026-08-31 실측 :
     본문 안쪽폭 365 = 값표 폭 365, 좌우 여백 0). .pay-form 과 같은 값을 쓴다. */
.paydone__body {
  text-align: center;
  padding: var(--space-5) var(--space-5) var(--space-4);
}
.paydone__body .paydone__mark { width: 4.4rem; height: 4.4rem; font-size: 2.2rem; }
.paydone__body .paydone__msg  { font-size: 1.6rem; margin-bottom: var(--space-4); }
.paydone__body .paydone__list,
.paydone__body .paydone__hint { margin-bottom: 0; }

/* --- nToss 결제 버튼 (2026-08-27) -------------------------------------------
     여백 때문에 그림이 눌려 **글자를 못 읽었다** — 처음엔 16px 밖에 안 됐다.
     껍데기를 걷어내니 같은 자리에서 그림이 두 배가 된다.
   ★ 로고 PNG 는 **배경이 들어 있는 그림**이다(투명 없음 · colortype 2). 그래서
     흰 알약을 따로 그릴 필요가 없다 — 그림이 곧 단추 면이다.
     ⓐ 팔레트 16종 어디에 놓여도 대비가 안 갈린다(그림이 배경을 갖고 있다)
     ⓑ 모서리는 overflow 로 잘라 둥글게 맞춘다
   ⚠ 껍데기를 걷어내면 **눌리는 것처럼 안 보인다.** 그래서 손 모양 커서 ·
     hover 흐려짐 · 누를 때 눌림 · 키보드 초점 테두리를 직접 준다.
     .btn 이 해 주던 일이라 없어진 만큼 여기서 채운다.
   ⚠ 글자가 없으므로 뜻은 그림이 나른다 — alt · title · aria-label 이 짝이다.
   ---------------------------------------------------------------------------- */
.pay-btn {
  display: inline-flex;
  align-items: center;
  align-self: center;          /* 부모가 세로 플렉스여도 안 늘어난다 */
  padding: 0;
  border: 0;
  background: none;
  border-radius: 4px;
  overflow: hidden;            /* 그림 모서리를 둥글게 자른다 */
  line-height: 0;
  cursor: pointer;
  transition: opacity .12s var(--ease), transform .12s var(--ease);
}
.pay-btn:hover    { opacity: .85; }
.pay-btn:active   { transform: scale(var(--press-scale)); }
.pay-btn:disabled { opacity: .5; cursor: default; }
/* 초점 테두리 — 밝은 면(원장)과 어두운 면(주문 합계바) 둘 다에서 보이게
   흰 테두리 + 바깥 그림자 두 겹으로 준다. */
.pay-btn:focus-visible {
  outline: 2px solid var(--white);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px rgba(0, 0, 0, .35);
}
/* ★ 크기는 놓이는 자리가 정한다 — 옆 단추와 높이를 맞춘다.
     원장 : 옆이 btn--sm 이라 줄 높이가 26px  → 그림 20px
     주문 : 합계바가 40px                     → 그림 30px  (.pay-btn--lg) */
.pay-btn__logo {
  display: block;
  height: 20px;
  width: auto;
}
.pay-btn--lg .pay-btn__logo { height: 30px; }

/* --- 결제 입력 모달 (2026-08-27) ---------------------------------------------
   ★ 세 칸(결제자명 · 이메일 · 금액)은 **구버전 팝업 그대로**다. 다른 것은 뜨는
     방식뿐 — 구버전은 450×600 새 창이고 우리는 모달이다(공지 팝업과 같은 이유).
   ★ 칸은 이미 있는 .field · .field-label 을 쓴다. 화면마다 입력칸 모양을 새로
     정하면 그만큼 갈라진다.
   ⚠ 폭을 380 으로 잡았다 — 구버전 팝업의 내용 폭(450 에서 여백을 뺀 값)과
     같은 자리다. 더 넓히면 세 칸짜리 폼이 허전해 보인다.
   ---------------------------------------------------------------------------- */
.modal--pay .modal__box { width: min(38rem, calc(100vw - 4rem)); }
.pay-form {
  display: grid;
  gap: var(--space-1) 0;
  padding: var(--space-4) var(--space-5) var(--space-2);
}
.pay-form .field-label { margin-top: var(--space-3); }
.pay-form .field-label:first-child { margin-top: 0; }
.pay-form .field { width: 100%; }
/* 금액은 오른쪽 정렬 — 자릿수가 어긋나면 못 읽는다 */
.pay-form .field--num { text-align: right; font-family: var(--font-num); }
.pay-req { color: var(--red); }
/* 막힌 이유 — 색만이 아니라 **글자로** 말한다 */
.pay-err {
  margin: var(--space-3) 0 0;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-field);
  background: var(--gold-lightest);
  color: var(--red);
  font-size: var(--text-2);
}
