/* ======================================================================
 * WIZON widget card shell — 공용 셸 + 카드 토큰 (차별화 축① 1차 슬라이스)
 * ======================================================================
 *
 * 왜 이 파일이 있는가
 * -------------------
 * 포털 위젯 카드가 10벌 넘게 있는데 공유하는 카드 토큰이 0개였다. 같은 «카드»를
 * 그리면서 서로 다른 낱말을 쓴다:
 *
 *   역할        commerce-price-card          doc-summary-card
 *   표면        --wizon-ai-surface           --cg-surface
 *   실선        --wizon-ai-popover-border    --cg-menu-border
 *   본문색      --wizon-ai-text-primary      --cg-text
 *   모서리      --wizon-ai-radius-popover    --ds-radius-sm
 *   여백        --wds-space-5                14px 16px  (스케일 밖 raw)
 *   캡션        --wds-font-size-caption      --ds-text-caption-12
 *   강조무게    --wds-font-weight-medium     600        (raw)
 *
 * 이름만 다른 게 아니라 «값»이 다르다. 그래서 카드마다 모서리도 그림자도 여백도
 * 제각각이었다. 이 파일은 그 결정을 한 곳으로 모은다.
 *
 * ★ 다만 타이포는 «이름만» 갈렸다. 실측하면 정본이 하나로 만난다:
 *     --ds-text-caption-12 = --wds-font-size-caption  (0.75rem)
 *     --ds-text-caption-13 = --wds-font-size-body-s   (0.8125rem)
 *   (shared/design-tokens/primitive.css:151-152 — ds 쪽이 wds 를 그대로 참조한다.)
 *   그래서 이번 이관에서 «글자 크기는 한 픽셀도 안 움직인다». 두 낱말이 같은 값을
 *   가리키고 있었을 뿐이고, 그게 이 작업이 위험하지 않은 이유다.
 *
 * 이미 있던 것을 먼저 찾았다 (기각 기록)
 * --------------------------------------
 * ① `.ds-card` + `--ds-card-*` — docs/product/71-component-rulebook.md §4.3,
 *    design-system/components/card/RULES.md. header/body/footer 3-slot 해부까지
 *    이미 있고 tests/test_design_system_contract.py 가 셀렉터와 토큰 7종을 핀으로
 *    잡고 있다. 그런데 «포털에서 사용처가 0»이다(app.js·index.html 에 ds-card 0건).
 *    실제 소비자는 admin-console 뿐.
 *    → 그대로 쓰지 않고 «별칭»으로 얹은 이유 셋:
 *      (a) 같은 계약이 admin-console 도 묶고 있어, 포털 사정으로 넓히면 관제
 *          콘솔이 같이 움직인다. 첫 슬라이스가 지기엔 너무 센 결합이다.
 *      (b) 밀도 축이 없다. 우리 카드 둘은 지금 서로 다른 밀도로 «의도적으로»
 *          살고 있다(대화형 20px vs 문서형 14px). 한 값으로는 못 덮는다.
 *      (c) --ds-card-radius = --ds-radius-sm = 10px 인데 commerce 는 16px 이다.
 *          단일 값이 두 카드를 동시에 만족시킬 수 없다.
 *    → 그래서 --ds-card-* 가 있는 자리는 반드시 그 위에 별칭으로 올린다. 정본은
 *      계속 --ds-card-* 이고, 이 파일은 새 «원색»을 하나도 만들지 않는다.
 * ② `--cg-*` — 이미 --wizon-ai-* 의 별칭 층이다. 하지만 :root 가 아니라
 *    body.chatgpt-reference-v1 에 스코프돼 있어서, 그 클래스 밖에서는 조용히 빈
 *    값이 된다(styles.css 22000행대 주석에 실측 기록 있음: 클래스를 떼면 테두리가
 *    rgb(13,13,13) 으로 떨어진다). 위젯 셸은 어디에 붙을지 모르므로 --cg-* 를
 *    타지 않고 :root 에 사는 --wizon-ai-* / --wds-* / --ds-* 만 참조한다.
 *
 * 반영 범위 — 이번 슬라이스는 파일럿 2장뿐
 * ----------------------------------------
 * commerce-price-card(대화형·넉넉) 와 doc-summary-card(문서형·조밀). 성격이 반대인
 * 둘을 먼저 태워서 밀도 축이 진짜 필요한지부터 확인한다. 나머지 위젯은 후속.
 *
 * 이 파일이 styles.css 안에 있지 않은 이유
 * ----------------------------------------
 * styles.css 는 38,636줄이다(파일 상한 800줄의 48배). 그리고 지금 다른 세션이
 * 같은 파일의 commerce 블록을 고치고 있다. 새 층을 그 안에 밀어 넣으면 충돌만
 * 만든다. travel-route-widget.css 가 이미 같은 선례다.
 *
 * anti-drift 램칫 주의
 * --------------------
 * 이 파일도 tests/test_design_system_contract.py 의 스캔 대상이다(경로가
 * apps/ai-portal/app/static 아래다). 램칫은 «정확히 같아야» 통과하고 현재 여유가
 * 0이다. 그래서 여기서는 box-shadow / border-radius / font-size 의 값이 «반드시»
 * var() 를 포함해야 하고, raw hex 와 rgb() 는 (주석 밖에) 한 개도 있으면 안 된다.
 * 새 리터럴 하나가 곧바로 빨강이다.
 * ====================================================================== */

/* ★별칭은 :root 가 아니라 «.wiz-card» 에 선언한다. 이게 이 파일에서 가장 미묘한 결정이라
   근거를 남긴다.

   처음엔 :root 에 뒀다가 실측에서 되돌렸다. 포털은 WDS 타입 스케일을 자기 타이포그래피
   계약으로 다시 가리킨다 — styles.css 35053행:

       --wds-font-size-body-2: var(--cg-text-label-size);
       --wds-font-size-caption: var(--cg-text-meta-size);

   그리고 ≤520px 에서 --cg-text-label-size 가 17px 로 올라간다(doc 160 R3, 한글 모바일
   가독성. GPT 모바일 실측 정합). 이 재지정은 «body» 에 걸린다.

   커스텀 프로퍼티의 var() 는 «선언된 그 요소»에서 치환된다. :root 는 body 보다 위라
   :root 에서 var(--wds-font-size-body-2) 를 읽으면 body 의 재지정이 안 보이고 데스크톱
   값(0.875rem)이 그대로 굳어, 그 굳은 값이 아래로 상속된다. 실측 결과 390 에서 부제가
   17px→14px, eyebrow 가 13px→12px 로 «작아졌다» — 별칭 한 층을 잘못 둔 것만으로 한글
   모바일 업스케일이 통째로 꺼진 것이다.

   .wiz-card 는 body 아래라 재지정을 정상적으로 본다. --cg-* 층이 :root 가 아니라
   body.chatgpt-reference-v1 에 사는 것도 같은 이유일 것이다. */
.wiz-card {
  /* ── 표면과 층 (depth) ───────────────────────────────────────────────
     카드는 «캔버스 위에 뜬 판»이다. 판(surface) → 실선(hairline) → 가라앉은
     영역(sunken: 툴바·타일)의 3층으로만 깊이를 만든다. 그라디언트 금지는
     card/RULES.md 의 Forbidden Patterns 그대로다. */
  /* ★--ds-card-border 를 «쓰지 않는다». 별칭 후보로 먼저 넣었다가 실측에서 되돌렸다:
     --ds-card-border 는 --ds-color-line(#ececec)으로 풀리는데, 이관 대상 두 카드는
     «둘 다» #e3e3e3(--wizon-ai-popover-border)로 서 있었다. commerce 는 직접,
     doc-summary 는 --cg-menu-border 를 거쳐 같은 값에 닿는다. --ds-card-* 를 쓰면
     두 카드의 테두리가 동시에 옅어진다 — 통일은 되지만 «지금 화면»과 달라진다.
     이 슬라이스는 추출이지 재디자인이 아니므로 실측값 쪽을 정본으로 삼는다. */
  --wiz-card-surface: var(--wizon-ai-surface);
  --wiz-card-surface-sunken: var(--wizon-ai-hover-soft-bg);
  --wiz-card-canvas: var(--wizon-ai-canvas);
  --wiz-card-hairline: var(--wizon-ai-popover-border);
  --wiz-card-hairline-strong: var(--wizon-ai-sidebar-border);

  /* ── 모서리와 그림자 ─────────────────────────────────────────────────
     바깥 모서리는 밀도 축이 덮어쓴다(아래 density 블록). 안쪽 요소(타일·칩)는
     바깥보다 항상 작아야 «안에 들어있다»고 읽힌다. */
  --wiz-card-corner: var(--wizon-ai-radius-popover);
  --wiz-card-corner-inner: var(--ds-radius-sm);
  --wiz-card-corner-chip: var(--ds-radius-xs);
  --wiz-card-corner-pill: var(--ds-radius-pill);
  --wiz-card-elevation: var(--wizon-ai-popover-shadow);

  /* ── 잉크 사다리 (hierarchy) ─────────────────────────────────────────
     세 단계로 고정한다. 네 번째를 만들면 아무도 어디에 쓸지 모른다. */
  --wiz-card-ink: var(--wizon-ai-text-primary);
  --wiz-card-ink-soft: var(--wizon-ai-text-secondary);
  --wiz-card-ink-muted: var(--wizon-ai-text-muted);

  /* ── 리듬 (rhythm) ───────────────────────────────────────────────────
     «전부 같은 padding» 을 피하려고 방향을 나눠 둔다. 값은 밀도 축이 준다. */
  --wiz-card-pad-block: var(--wds-space-5);
  --wiz-card-pad-inline: var(--wds-space-5);
  --wiz-card-gap: var(--wds-space-4);
  --wiz-card-gap-tight: var(--wds-space-2);
  --wiz-card-stack: var(--wds-space-3);
  --wiz-card-detach: var(--wds-space-4);

  /* ── 타입 사다리 ─────────────────────────────────────────────────────
     eyebrow(맥락) → title(무엇) → subtitle(조건) → body → meta(언제·어디서).
     크기 대비로 위계를 만든다. 굵기만으로 만들지 않는다. */
  --wiz-card-type-eyebrow: var(--wds-font-size-caption);
  --wiz-card-type-title: var(--wds-font-size-title-l);
  --wiz-card-type-subtitle: var(--wds-font-size-body-2);
  --wiz-card-type-body: var(--wds-font-size-body-2);
  --wiz-card-type-meta: var(--wds-font-size-caption);
  --wiz-card-weight-title: var(--wds-font-weight-semibold);
  --wiz-card-weight-emphasis: var(--wds-font-weight-medium);

  /* ── 한글 타이포그래피 ────────────────────────────────────────────────
     영어권 카드 토큰셋(OpenAI Apps SDK, Thesys Crayon)이 안 다루는 축이다.
     라틴 문자는 공백에서 자연히 끊기지만 한글은 «어절» 단위로 끊어야 읽힌다.

     ★ break 와 overflow 는 «반드시 한 쌍»이다. keep-all 만 걸면 어절 안에서
       절대 안 끊기므로, 끊을 자리가 없는 긴 토큰(URL·제품코드·「EPA+DHA」 같은
       라틴 혼용 덩어리)이 컨테이너를 뚫고 나간다. 반대로 anywhere 만 걸면 한글이
       낱말 한가운데서 잘린다. 그래서 이 파일은 두 값을 따로 쓰지 못하게 항상
       같이 적용한다(.wiz-card__prose / __title / __subtitle 참고).
     ★ 자간: Pretendard 는 큰 글자에서 살짝 조여야 덩어리로 읽히고, 본문에서
       조이면 오히려 뭉갠다. 그래서 제목만 음수, 본문 0, 라벨은 양수다.
       값은 --wds-letter-spacing-* 정본을 그대로 빌린다.
     ★ 줄높이: 한글은 디센더가 적어 라틴과 같은 leading 이면 빽빽해 보인다.
       본문은 넉넉하게, 제목은 두 줄 이상 갈 때 뭉치도록 좁게 간다. */
  --wiz-card-hangul-break: keep-all;
  --wiz-card-hangul-overflow: anywhere;
  --wiz-card-leading-title: var(--wds-line-height-tight);
  --wiz-card-leading-body: var(--wds-line-height-normal);
  --wiz-card-leading-label: var(--wds-line-height-label);
  --wiz-card-tracking-title: var(--wds-letter-spacing-h3);
  --wiz-card-tracking-body: var(--wds-letter-spacing-normal);
  --wiz-card-tracking-label: var(--wds-letter-spacing-label);

  /* 숫자열은 자리가 흔들리면 비교가 안 된다. 가격비교·지표 타일의 전제다. */
  --wiz-card-numeral: tabular-nums;

  /* ── 상태색 (semantic, 별칭만) ───────────────────────────────────────
     새 색을 만들지 않는다. 경고와 위험은 «다른 단계»라 반드시 나눠 둔다 —
     둘을 한 토큰으로 묶었다가 화면에서 같은 빨강으로 뭉갠 전례가 있다. */
  --wiz-card-state-accent: var(--wds-color-wizon-accent);
  --wiz-card-state-warn: var(--wizon-ai-warning-text);
  --wiz-card-state-bad: var(--wizon-ai-danger-text);
  --wiz-card-state-ok: var(--ad-green);
  --wiz-card-focus-ring: var(--wizon-ai-focus-visible-ring);
  --wiz-card-focus-width: 2px;
  --wiz-card-focus-offset: 2px;
}

/* ── 밀도 축 (density) ──────────────────────────────────────────────────
   «전부 같은 여백» 을 막는 장치다. 두 값은 발명한 게 아니라 지금 화면에서
   측정한 것이다.

   comfortable = 대화 흐름에 얹히는 카드(commerce). 답변 옆에서 «판»으로 읽혀야
     하므로 여백 20px · 모서리 16px · 그림자 있음.
   compact = 문서에 딸린 밀집 요약(doc-summary). 근거가 많고 촘촘해야 하므로
     여백 16px · 모서리 10px · 그림자 없음(본문에 붙어 사는 표면이라 뜨면 안 된다).

   ★ compact 의 세로 여백은 원래 14px 이었다. 스케일에 없는 값이라 --wds-space-4
     (16px)로 «올려» 맞췄다. 위아래 2px 늘어난다 — 스크린샷 대조에서 유일하게
     의도적으로 달라지는 지점이고, 토큰 스케일에 올리는 게 이 작업의 목적이다. */
.wiz-card[data-wiz-density="comfortable"] {
  --wiz-card-pad-block: var(--wds-space-5);
  --wiz-card-pad-inline: var(--wds-space-5);
  --wiz-card-corner: var(--wizon-ai-radius-popover);
  --wiz-card-gap: var(--wds-space-4);
  --wiz-card-stack: var(--wds-space-3);
}

.wiz-card[data-wiz-density="compact"] {
  --wiz-card-pad-block: var(--wds-space-4);
  --wiz-card-pad-inline: var(--wds-space-4);
  --wiz-card-corner: var(--ds-radius-sm);
  --wiz-card-gap: var(--wds-space-2);
  --wiz-card-stack: var(--wds-space-2);
  --wiz-card-type-title: var(--wds-font-size-body-s);
  --wiz-card-type-subtitle: var(--wds-font-size-body-s);
  --wiz-card-type-body: var(--wds-font-size-body-s);
  --wiz-card-elevation: none;
}

/* ── 셸 컨테이너 ────────────────────────────────────────────────────────
   컨테이너 쿼리로 스스로 반응한다. 뷰포트 브레이크포인트가 아니다 — 같은
   카드가 대화 폭·사이드바 폭 어디에 놓일지 모르기 때문이다. */
.wiz-card {
  container-type: inline-size;
  min-width: 0;
  margin-top: var(--wiz-card-detach);
  overflow: hidden;
  border: 1px solid var(--wiz-card-hairline);
  border-radius: var(--wiz-card-corner);
  background: var(--wiz-card-surface);
  box-shadow: var(--wiz-card-elevation);
  color: var(--wiz-card-ink);
  font-variant-numeric: var(--wiz-card-numeral);
}

/* ── 헤더 ───────────────────────────────────────────────────────────────
   왼쪽에 정체(eyebrow/title/subtitle), 오른쪽에 출처·시각(meta). 두 덩어리를
   양끝으로 밀어 시선의 시작과 끝을 만든다. */
/* align-items 는 center 다. 발명이 아니라 실측 — 이관 대상 두 카드가 «둘 다» 지금
   center 로 서 있다. baseline 이 다중행 헤더에는 더 견고하다는 통념이 있지만, 여기서는
   좁아지면 컨테이너 쿼리가 세로 스택으로 바꾸므로 baseline 이 이득을 볼 구간이 없고,
   commerce 의 meta 는 왼쪽에 강조 막대를 달고 있어 center 라야 막대가 가운데 선다. */
.wiz-card__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--wiz-card-gap);
  padding: var(--wiz-card-pad-block) var(--wiz-card-pad-inline);
}

/* 헤더와 본문 사이 실선은 «선택»이다. 넉넉한 카드는 구획이 필요하고, 조밀한
   카드는 선이 하나 더 늘면 답답해진다. 그래서 기본값이 아니라 명시 옵션이다. */
.wiz-card__head[data-wiz-rule="on"] {
  border-bottom: 1px solid var(--wiz-card-hairline);
}

.wiz-card__headings {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: var(--wiz-card-gap-tight);
  min-width: 0;
}

.wiz-card__eyebrow {
  display: block;
  color: var(--wiz-card-ink-muted);
  font-size: var(--wiz-card-type-eyebrow);
  letter-spacing: var(--wiz-card-tracking-label);
  line-height: var(--wiz-card-leading-label);
}

.wiz-card__title {
  margin: 0;
  overflow-wrap: var(--wiz-card-hangul-overflow);
  color: var(--wiz-card-ink);
  font-size: var(--wiz-card-type-title);
  font-weight: var(--wiz-card-weight-title);
  letter-spacing: var(--wiz-card-tracking-title);
  line-height: var(--wiz-card-leading-title);
  word-break: var(--wiz-card-hangul-break);
}

.wiz-card__subtitle {
  margin: 0;
  overflow-wrap: var(--wiz-card-hangul-overflow);
  color: var(--wiz-card-ink-soft);
  font-size: var(--wiz-card-type-subtitle);
  letter-spacing: var(--wiz-card-tracking-body);
  line-height: var(--wiz-card-leading-label);
  word-break: var(--wiz-card-hangul-break);
}

/* 출처·시각 슬롯. 헤더가 좁아지면 제목 아래로 내려가되 오른쪽 정렬은 유지한다. */
.wiz-card__meta {
  flex: 0 0 auto;
  max-width: 100%;
  overflow-wrap: var(--wiz-card-hangul-overflow);
  color: var(--wiz-card-ink-muted);
  font-size: var(--wiz-card-type-meta);
  letter-spacing: var(--wiz-card-tracking-label);
  line-height: var(--wiz-card-leading-label);
  text-align: right;
  word-break: var(--wiz-card-hangul-break);
}

/* ── 본문 ───────────────────────────────────────────────────────────── */
.wiz-card__body {
  display: block;
  min-width: 0;
  padding: var(--wiz-card-pad-block) var(--wiz-card-pad-inline);
  color: var(--wiz-card-ink);
  font-size: var(--wiz-card-type-body);
}

/* 헤더 바로 아래에 붙는 본문은 위 여백을 헤더가 이미 냈으므로 겹쳐 내지 않는다. */
.wiz-card__head + .wiz-card__body {
  padding-top: 0;
}

.wiz-card__head[data-wiz-rule="on"] + .wiz-card__body {
  padding-top: var(--wiz-card-pad-block);
}

/* 카드 안의 산문. 한글 줄바꿈 정책이 «쌍»으로 적용되는 자리다. */
.wiz-card__prose {
  margin: 0;
  overflow-wrap: var(--wiz-card-hangul-overflow);
  color: var(--wiz-card-ink-soft);
  font-size: var(--wiz-card-type-body);
  letter-spacing: var(--wiz-card-tracking-body);
  line-height: var(--wiz-card-leading-body);
  word-break: var(--wiz-card-hangul-break);
}

/* ── 푸터 (액션 칩 슬롯) ─────────────────────────────────────────────── */
.wiz-card__foot {
  display: flex;
  flex-wrap: wrap;
  gap: var(--wiz-card-gap-tight);
  align-items: center;
  padding: var(--wiz-card-pad-block) var(--wiz-card-pad-inline);
  border-top: 1px solid var(--wiz-card-hairline);
}

.wiz-card__body + .wiz-card__foot {
  padding-top: var(--wiz-card-pad-block);
}

/* 포커스는 셸이 한 번만 정의한다. 카드마다 다른 링이 뜨면 «디자인된» 상태가
   아니라 사고로 보인다. */
.wiz-card :focus-visible {
  outline: var(--wiz-card-focus-width) solid var(--wiz-card-focus-ring);
  outline-offset: var(--wiz-card-focus-offset);
}

/* 좁은 컨테이너에서는 헤더의 좌우 배치를 포기하고 세로로 쌓는다. meta 의 오른쪽
   정렬도 같이 푼다 — 한 줄짜리 카드에서 오른쪽에 홀로 붙은 시각은 미아가 된다. */
@container (max-width: 30rem) {
  .wiz-card__head {
    flex-direction: column;
    align-items: stretch;
    gap: var(--wiz-card-stack);
  }

  .wiz-card__meta {
    text-align: left;
  }
}
