@layer site.modules {
  /* santa-here-global — 상세 페이지 섹션 블록 부품.
     tokens.css·components.css 뒤에 로드된다. 섹션 수준 블록(히어로·혜택·스펙·딜·안심)을
     조립하는 데 필요한, components.css(커머스 동선 부품)에 없던 콘텐츠 부품을 sh- 접두로
     추가한다. 나라별 밀도 차이는 여기서 만들지 않는다 — 부품은 중립, 조립(레시피)이 결정. */

  /* ── 섹션 머리글(.sh-head) — 라벨/제목/보조 슬롯을 한 블록으로 통합(EN-052).
     제목을 얹는 옛 클래스 7갈래(블록별 헤더·상세 라벨·그리드 헤더·주문/동의 소제목류
     + 가운데 정렬 변형 하나)를 이 블록과 변이 5종(--center·--split·--label·--group·
     --sub)으로 흡수했다. 기본값은 가장 많이 쓰인 옛 갈래(상세 블록 ×10곳)에서
     옮겼다 — 나머지는 변이 선택자에서 다른 값만 덮어쓴다. 매크로:
     sites/santa-here-global/templates/_head.html의 head(). */
  .sh-head { margin: 0 0 var(--spacing-xl); }
  .sh-head__sub {
    font-family: var(--font-family);
    font-size: var(--m3-body-medium-size); line-height: var(--m3-body-medium-line); font-weight: var(--m3-body-medium-weight); letter-spacing: var(--m3-body-medium-tracking);
    
    color: var(--color-text-muted);
    margin: var(--spacing-sm) 0 0;
  }
  .sh-head__eyebrow {
    font-family: var(--font-family);
    
    font-size: var(--m3-label-small-size); line-height: var(--m3-label-small-line); font-weight: var(--m3-label-small-weight); letter-spacing: var(--m3-label-small-tracking);
    
    text-transform: uppercase;
    /* 상품 테마 색이 아니라 브랜드 잉크를 쓴다 — 상품 액센트는 데이터로 들어와
       tokens.css에 없으므로 scripts/check_contrast.py가 볼 수 없고, 11px 라벨은
       상품 색에 따라 AA 미달이 난다(cosrx의 세이지 #5E7A6B는 아이보리 위 4.26).
       액센트 예산은 채움 표면이 계속 갖는다(EN-049 조각 5). */
    color: var(--color-primary-text);
    margin: 0 0 var(--spacing-sm);
  }
  .sh-head__title {
    font-family: var(--font-family-heading);
    
    font-size: var(--m3-headline-small-size); line-height: var(--m3-headline-small-line); font-weight: var(--m3-headline-small-weight); letter-spacing: var(--m3-headline-small-tracking);
    
    color: var(--color-accent);
    margin: 0;
  }

  /* --split: 제목 + 우측 보조 슬롯(aside) 한 줄 (구 그리드 머리글 클래스) */
  .sh-head--split { display: flex; align-items: baseline; justify-content: space-between; margin: var(--spacing-sm) 0 var(--spacing-lg); }
  .sh-head--split .sh-head__title { font-size: var(--m3-title-large-size); line-height: var(--m3-title-large-line); font-weight: var(--m3-title-large-weight); letter-spacing: var(--m3-title-large-tracking); }

  /* --label: 페이지의 큰 섹션 제목(상품 소개·From Santa·배송·교환환불) — 그 안의
     소제목(.sh-detail-group-head, title-medium)보다 뚜렷이 커야 위계가 선다. */
  .sh-head--label { margin: var(--spacing-3xl) var(--spacing-xs) var(--spacing-md); }
  .sh-head--label .sh-head__title {
    font-family: var(--font-family);
    font-size: var(--m3-title-large-size); line-height: var(--m3-title-large-line); font-weight: var(--m3-title-large-weight); letter-spacing: var(--m3-title-large-tracking);
    color: var(--color-text);
    margin: 0;
  }

  /* --group: 소제목(대문자 + 자간 0.01em) — buyer_order(주문 소제목)와
     checkout_consent(동의 소제목)의 옛 클래스 두 갈래를 통합. 대문자·자간을 order 쪽
     값으로 맞춘 것이 이 리팩터링에서 유일하게 의도한 시각 변화다(체크아웃 동의
     소제목이 대문자가 된다). 색은 옛 두 갈래가 --color-text와 --color-accent로 갈려
     있었으나 둘 다 #3A2A22(ink-12)라 눈에 차이가 없다 — 하나로 합쳤다. */
  /* 여백은 두 옛 갈래(주문 0 / 동의 0 0 xs)를 후자로 통일했다 — 문맥 선택자로
     보존하면 눈에 보이지 않는 차이 때문에 선택자가 늘어난다. */
  .sh-head--group { margin: 0 0 var(--spacing-xs); }
  .sh-head--group .sh-head__title {
    font-size: var(--m3-label-medium-size); line-height: var(--m3-label-medium-line); font-weight: var(--m3-label-medium-weight); letter-spacing: var(--m3-label-medium-tracking);
    
    
    text-transform: uppercase;
    color: var(--color-text);
    margin: 0;
  }

  /* --sub: 하단 테두리형 소제목 (구 동의 상세 제목 클래스) */
  .sh-head--sub {
    margin: 0 0 var(--spacing-sm);
    padding-bottom: var(--spacing-sm);
  }
  .sh-head--sub .sh-head__title { font-size: var(--m3-title-medium-size); line-height: var(--m3-title-medium-line); font-weight: var(--m3-title-medium-weight); letter-spacing: var(--m3-title-medium-tracking); color: var(--color-accent); margin: 0; }

  /* ── 통이미지 상세 — 원본 비율 그대로, 크롭 없음 ── */
  .sh-detail-image {
    display: block;
    width: 100%;
    height: auto;
    border-radius: var(--radius-lg);
  }

  /* ── 별점 + 리뷰 수 — 상품 카드·리뷰 요약 공용 ── */
  .sh-stars {
    display: inline-flex;
    align-items: center;
    gap: var(--spacing-sm);
    font-family: var(--font-family);
  }
  .sh-stars .stars {
    color: var(--color-gold);
    font-size: var(--m3-label-large-size); line-height: var(--m3-label-large-line); font-weight: var(--m3-label-large-weight); letter-spacing: var(--m3-label-large-tracking);
    
  }
  .sh-stars .count {
    font-size: var(--m3-label-medium-size); line-height: var(--m3-label-medium-line); font-weight: var(--m3-label-medium-weight); letter-spacing: var(--m3-label-medium-tracking);
    
    color: var(--color-text-muted);
  }

  /* ═══ 브랜드 모티프 — "한국발 국제 소포"의 시각 언어 ═══ */

  /* ── 항공우편 줄무늬 — 섹션 구분선. red/green 사선이 국제우편 봉투 테두리를 인용.
     슬롯으로 각도·띠폭·높이·색을 열어 다른 변이가 재선언 없이 붙을 수 있게 한다
     (rule/css-guide.md 3절). 기본값은 기존 리터럴과 동일하다. */
  .sh-airmail {
    --airmail-angle: -45deg;
    --airmail-band: 14px;   /* 색 띠 하나의 폭 — a·b 둘 다에 적용된다 */
    --airmail-gap: 8px;     /* 띠 사이 여백 폭 */
    --airmail-height: 8px;
    --airmail-color-a: var(--color-primary);
    --airmail-color-b: var(--color-secondary);
    --airmail-color-between: var(--color-ivory);
    height: var(--airmail-height);
    border: none;
    margin: 0;
    /* 정지점은 band·gap의 누적합이다. 각 항을 리터럴 오프셋으로 적으면 band를 바꿔도
       뒤쪽 띠 폭이 안 따라와 슬롯 이름이 거짓이 된다(첫 매개화에서 그렇게 났다). */
    background: repeating-linear-gradient(
      var(--airmail-angle),
      var(--airmail-color-a) 0 var(--airmail-band),
      var(--airmail-color-between) var(--airmail-band)
        calc(var(--airmail-band) + var(--airmail-gap)),
      var(--airmail-color-b) calc(var(--airmail-band) + var(--airmail-gap))
        calc(var(--airmail-band) * 2 + var(--airmail-gap)),
      var(--airmail-color-between) calc(var(--airmail-band) * 2 + var(--airmail-gap))
        calc(var(--airmail-band) * 2 + var(--airmail-gap) * 2)
    );
    border-radius: var(--radius-full);
    opacity: 0.85;
  }
  /* --blue 변이 — 실제 "Par Avion" 항공우편 줄무늬는 관례상 빨강+파랑이다(빨강+초록은
     크리스마스색을 우편 모티프에 얹은 것). 슬롯 재선언 없이 색 하나만 바꾼다
     (2026-08-15, 브랜드 색 다양화 논의). */
  .sh-airmail--blue {
    --airmail-color-b: var(--color-blue-postal);
  }

  /* ── 소인(postmark) 스탬프 — 정품·한국 발송 인장. 살짝 기울여 도장 느낌.
     슬롯으로 테두리 색·선 스타일·모양·글자색·기울기·크기를 열었다. 기본값은 기존
     리터럴과 동일하다. */
  .sh-postmark {
    --postmark-border-color: var(--color-primary);
    --postmark-border-style: dashed;
    --postmark-shape: 50%;
    --postmark-text-color: var(--color-primary-text);
    --postmark-rotate: -8deg;
    --postmark-size: 92px;
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--spacing-xs);
    width: var(--postmark-size);
    height: var(--postmark-size);
    border: var(--border-width) var(--postmark-border-style) var(--postmark-border-color);
    border-radius: var(--postmark-shape);
    color: var(--postmark-text-color);
    font-family: var(--font-family);
    text-align: center;
    transform: rotate(var(--postmark-rotate));
    opacity: 0.9;
  }
  .sh-postmark .pm-top,
  .sh-postmark .pm-bottom {
    font-size: var(--m3-label-small-size); line-height: var(--m3-label-small-line); font-weight: var(--m3-label-small-weight); letter-spacing: var(--m3-label-small-tracking);
    
    letter-spacing: 0.14em;
    text-transform: uppercase;
  }
  .sh-postmark .pm-main {
    font-family: var(--font-family-heading);
    font-size: var(--m3-title-medium-size); line-height: var(--m3-title-medium-line); font-weight: var(--m3-title-medium-weight); letter-spacing: var(--m3-title-medium-tracking);
    
    
  }
  /* ══════════ 카탈로그(홈) — 히어로·배지·신청바·파트너·편지모달 ══════════ */
  /* width:100% 근거는 .l-product 참조 — body의 세로 flex 아이템이라 margin:0 auto가
     늘어나기를 꺼서, 폭이 뷰포트가 아니라 자식의 콘텐츠 크기를 따른다.
     원래는 어긋나지 않았다. 아래 히어로를 grid에서 flex로 바꾸자 이 컨테이너가 1280px
     화면에서 1080px 대신 681px로 줄었다 — grid의 1fr 트랙과 달리 flex 아이템은 콘텐츠
     크기가 부모의 max-content에 그대로 얹히기 때문이다. 상품 그리드가 뷰포트 기준
     미디어 쿼리를 쓸 때는 컨테이너와 무관하게 열 수가 정해져 이런 축소가 드러나지
     않았을 것이나, 내재적 레이아웃에서는 컨테이너 폭이 곧 열 수라 바로 표면화됐다
     (1280px에서 4열 → 2열).
     넘침 검사(scripts/capture_viewports.py --overflow)로는 못 잡는 종류다 — 컨테이너가
     뷰포트보다 좁아지는 방향이라 가로 스크롤이 생기지 않는다. */
  .sh-catalog { width: 100%; max-width: var(--width-wide); margin-left: auto; margin-right: auto; padding: 0 var(--spacing-xl); }

  /* FAB이 뜨는 폭에서 콘텐츠가 우측 끝까지 붙지 않게 여백을 미리 확보한다(EN-051 3번,
     --fab-inset-right 참조). */
  @media (max-width: 480px) {
    .sh-catalog { padding-right: var(--fab-inset-right); }
  }

  /* 히어로 — grid 대신 flex + flex-wrap.
     두 항의 flex 값이 세 가지 일을 동시에 한다.
     (1) 나란히 설 때 이미지는 260px를 지킨다 — 텍스트에 grow 999를 줘 남는 폭을 사실상
         전부 가져가게 했다. 이미지의 grow 1은 반올림 오차 수준만 먹는다.
     (2) 접히면 이미지가 그 줄을 혼자 쓰므로 전폭으로 커진다 — 원래 grid가 max-780에서
         1fr 한 열이 되며 하던 일이다. flex: 0 0 260px으로 두면 접혀도 260px에 머물러
         모바일에서 이미지가 쪼그라든다(실제로 그렇게 만들었다가 되돌렸다).
     (3) 접히는 지점은 260 + 420 + gap(4xl=40) = 720px 미만일 때다. 텍스트 하한 420px은
         아래 __sub의 max-width와 같은 값이다 — "카피가 420px보다 좁아지면 이미지 옆에
         두지 않는다"는 한 문장으로 정리되고, 옛 780 분기와 접힘 지점이 실측상 일치한다.
         숫자를 임의로 고르지 않고 이미 있는 결정에서 파생시킨 것이 요점이다. */
  /* 위쪽 padding은 뺐다 — body > main의 공통 여백(설계 논의 08-14, components.css)과
     중복되면 카탈로그만 헤더와의 간격이 더 벌어진다. 아래쪽은 히어로 다음 섹션과의
     내부 간격이라 유지한다. */
  .sh-catalog-hero { display: flex; flex-wrap: wrap; gap: var(--spacing-4xl); align-items: center; padding: 0 0 var(--spacing-2xl); background: transparent; color: var(--color-text); text-align: left; }
  .sh-catalog-hero__media { flex: 1 1 260px; aspect-ratio: 1/1; border-radius: var(--radius-card); background: repeating-linear-gradient(45deg, var(--m3-surface-container-high) 0 20px, var(--m3-surface) 20px 40px); position: relative; overflow: hidden; box-shadow: var(--shadow-md); display: flex; align-items: center; justify-content: center; }
  .sh-catalog-hero__media-ph { font-size: var(--m3-headline-large-size); line-height: var(--m3-headline-large-line); font-weight: var(--m3-headline-large-weight); letter-spacing: var(--m3-headline-large-tracking); opacity: .5; }
  .sh-catalog-hero__eyebrow { font-family: var(--font-family); font-size: var(--m3-label-medium-size); line-height: var(--m3-label-medium-line); font-weight: var(--m3-label-medium-weight); letter-spacing: var(--m3-label-medium-tracking); text-transform: uppercase; color: var(--color-primary-text); margin: 0 0 var(--spacing-sm); }
  /* clamp(하한 4xl=32px, 상한 5xl=40px) — 옛 780 분기를 흡수. 400~780px 선형 보간
     (400px는 이 사이트 실측 최소 대응 폭, 780px는 옛 분기 값을 그대로 상한 기준점으로 썼다). */
  .sh-catalog-hero__title { font-family: var(--font-family-heading); font-weight: 700; font-size: clamp(var(--m3-headline-large-size), 1.47rem + 2.1vw, var(--m3-display-small-size)); line-height: 1.05; margin: 0 0 var(--spacing-md); color: var(--color-accent); }
  .sh-catalog-hero__title em { color: var(--color-primary-text); font-style: normal; }
  .sh-catalog-hero__sub { font-size: var(--m3-body-large-size); line-height: var(--m3-body-large-line); font-weight: var(--m3-body-large-weight); letter-spacing: var(--m3-body-large-tracking); color: var(--color-text-muted); margin: 0 0 var(--spacing-xl); max-width: 420px; }
  .sh-catalog-hero__copy { flex: 999 1 420px; min-width: 0; }

  /* 봉투(편지 트리거) */
  .sh-envelope { display: inline-flex; align-items: center; gap: var(--spacing-md); background: var(--color-bg); border: var(--border-width) solid var(--color-sand); border-radius: var(--radius-lg); padding: var(--spacing-md) var(--spacing-lg) var(--spacing-md) var(--spacing-md); cursor: pointer; box-shadow: var(--shadow-sm); animation: sh-float var(--motif-duration-float) var(--m3-easing-standard) infinite, sh-wiggle var(--motif-duration-wiggle) var(--m3-easing-standard) infinite; }
  .sh-envelope:hover { border-color: var(--color-primary); }
  .sh-envelope__stamp { position: relative; width: 42px; height: 34px; border-radius: var(--radius-sm); background: var(--color-primary); flex: none; overflow: hidden; display: flex; align-items: center; justify-content: center; }
  /* 우표 반짝임 — 흰빛이 스치는 기법이다. 색이 아니라 광택이라 M3 역할에 자리가 없다(모티프). */
  .sh-envelope__stamp::after { content: ""; position: absolute; top: 0; left: -60%; width: 40%; height: 100%; background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .7), transparent); animation: sh-shimmer var(--motif-duration-shimmer) var(--m3-easing-standard) infinite; }
  .sh-envelope__stamp svg { width: 22px; height: 22px; }
  .sh-envelope__text b { display: block; font-size: var(--m3-label-large-size); line-height: var(--m3-label-large-line); font-weight: var(--m3-label-large-weight); letter-spacing: var(--m3-label-large-tracking); }
  .sh-envelope__text span { color: var(--color-text-muted); font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); font-weight: var(--m3-body-small-weight); letter-spacing: var(--m3-body-small-tracking); }
  @keyframes sh-float { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }
  @keyframes sh-wiggle { 0%,84%,100% { rotate: 0deg; } 88% { rotate: -4deg; } 92% { rotate: 4deg; } 96% { rotate: -2deg; } }
  @keyframes sh-shimmer { 0%,70%,100% { left: -60%; } 85% { left: 130%; } }
  @media (prefers-reduced-motion: reduce) { .sh-envelope, .sh-envelope__stamp::after { animation: none !important; } }

  /* 편지·Details 등 — 팝업 모달은 공용 .modal-overlay/.modal-panel을 재사용하되, 열림·닫힘을
     is-open 클래스로 직접 토글하는 vanilla JS 소비처(Alpine도, hidden 속성도 아닌 것 — 편지·
     파트너 가입 Details)는 .modal-overlay--js 수식자를 마크업에 추가로 붙인다. .modal-overlay
     하나에 전부 걸면 다른 방식(예: #prOverlay의 hidden 속성 토글, sh-product-request-overlay
     [hidden] 규칙)과 겹쳐 열어도 안 보이는 회귀가 난다(2026-08-14, 처음 시도에서 발견) —
     그래서 opt-in 수식자로 좁혔다. Alpine x-show 쪽은 인라인 style로 display를 직접 제어해
     애초에 이 규칙 자체가 필요 없다(수식자를 안 붙이면 그만). */
  /* .modal-overlay--js:not(.is-open){display:none}와 .modal-panel 애니메이션은 base
     .modal-overlay/.modal-panel의 토글 메커니즘이라 base components.css로 옮겼다(EN-071).
     여기 남는 것은 site 전용 클래스(.sh-letter--modal)에 거는 부분뿐이다. */
  .modal-overlay--js.is-open .sh-letter--modal { animation: sh-unfold var(--m3-duration-medium2) var(--m3-easing-emphasized-decelerate); }
  @media (prefers-reduced-motion: reduce) { .modal-overlay--js.is-open .sh-letter--modal { animation: none; } }
  /* .card의 기본 패딩(--card-padding: spacing-xl, 20px)이 모달 안 텍스트 밀도에 비해 빠듯하다
     — 예전 sh-letter-modal/Details 팝업은 모달 패딩+카드 안쪽 패딩을 이중으로 둬 32~48px이었다
     (2026-08-14, 여백 부족 지적). 모달 전용으로 더 넉넉하게 다시 준다. */
  .modal-overlay .modal-panel { padding: var(--spacing-2xl) var(--spacing-xl); }

  /* 편지 모달 — 액자(바깥 래퍼)를 따로 두지 않는다(2026-08-15). 원래는 스크롤 담당 액자 +
     흰 카드(.sh-letter) + 하단 고정 Close 버튼, 3단 구조였는데, Close를 카드 우상단 X로
     옮기면서 액자가 하던 일(스크롤 틀 + 버튼 자리)이 없어졌다 — .sh-letter 자신이 폭 제약
     (max-width 420px, 원래 .modal-panel 몫)과 스크롤까지 겸한다. 모달이 아닌 단독 섹션
     (product_landing.html)에서는 이 수식자가 안 붙어 원래 표면(overflow:hidden 등)을 그대로
     유지한다. */
  .sh-letter--modal {
    width: 100%;
    max-width: 420px;
    max-height: 80vh;
    position: relative;
  }
  /* base .sh-letter의 overflow:hidden(둥근 모서리 클리핑)을 모달에서만 끈다 — 클리핑이
     스크롤바를 모서리 곡선에서 잘라 위아래 화살표 잔여물처럼 보이게 하는지 확인하기
     위해(2026-08-15). 두 클래스를 동시에 걸어 특이도를 base보다 높인다(선언 순서와
     무관하게 이겨야 함). */
  .sh-letter.sh-letter--modal { overflow: visible; }
  /* 스크롤은 본문(.sh-letter__body)이 직접 맡는다 — 이미 존재하던 요소라 래퍼를 새로
     만들 필요가 없다. flex:1 1 auto만으로는 flex 자식의 기본 최소 크기(auto)가 내용 전체
     높이로 잡혀 부모 max-height를 넘어서도 안 줄어든다 — min-height:0으로 그 기본값을
     풀어야 overflow-y:auto가 실제로 스크롤을 만든다(flexbox 자동 최소 크기 규격 — .sh-letter
     자체가 base .modal-panel처럼 flex-direction:column은 아니지만 이 값들은 스크롤 계산에
     그대로 적용된다). */
  .sh-letter--modal > .sh-letter__body {
    max-height: 80vh;
    overflow-y: auto;
  }
  .sh-letter__close-x {
    position: absolute;
    top: var(--spacing-sm);
    /* 브라우저 기본 스크롤바(보통 15~17px)와 겹치지 않을 만큼 안쪽으로 — spacing-md(12px)는
       스크롤바 폭보다 좁아 카드 모서리에서 X와 스크롤바가 거의 붙어 보였다(2026-08-15,
       사용자 지적). 스크롤바 자체는 커스텀하지 않고 브라우저 기본값 그대로 둔다. */
    right: calc(var(--spacing-lg) + var(--spacing-sm));
    width: 32px;
    height: 32px;
    border: none;
    border-radius: 50%;
    background: var(--color-sand);
    color: var(--color-text);
    font-size: 1.25rem;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    z-index: 1;
  }
  .sh-letter__close-x:hover { background: var(--color-border); }

  /* 하단 고정 텍스트 Close 버튼 — 편지 모달은 우상단 X로 옮겼지만(위), 파트너 가입
     Details 팝업(_signup_terms.html)은 여전히 이 스타일을 쓴다. */
  .sh-letter-modal__close { display: block; width: 100%; margin-top: var(--spacing-md); background: var(--color-bg-dark); color: var(--color-ivory); border: none; border-radius: var(--radius-full); padding: var(--spacing-md); font-size: var(--m3-label-large-size); line-height: var(--m3-label-large-line); font-weight: var(--m3-label-large-weight); letter-spacing: var(--m3-label-large-tracking); cursor: pointer; }

  /* 강점 배지 */
  .sh-badge-row { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--spacing-sm); margin: 0 0 var(--spacing-xl); }
  .sh-badge-row__item { display: inline-flex; align-items: center; gap: var(--spacing-sm); font-size: var(--m3-label-medium-size); line-height: var(--m3-label-medium-line); font-weight: var(--m3-label-medium-weight); letter-spacing: var(--m3-label-medium-tracking); color: var(--color-accent); background: var(--color-bg); border: var(--border-width) solid var(--color-sand); border-radius: var(--radius-full); padding: var(--spacing-sm) var(--spacing-lg); box-shadow: var(--shadow-xs); }
  .sh-badge-row__icon { width: 16px; height: 16px; flex: none; }

  /* 설명 팝오버가 붙은 뱃지 — 클릭하면 짧은 설명이 뜬다(가치 제안 문서 "뱃지" 절).
     .sh-badge-row__item--popover는 위치 기준(relative)만 더하고, 겉모습은 트리거
     버튼(.sh-badge-row__trigger)이 기존 .sh-badge-row__item 스킨을 그대로 이어받는다 —
     버튼 기본 스타일(테두리·배경)을 지우고 뱃지 모양을 복제하는 대신 상속으로 정합을
     유지한다. */
  .sh-badge-row__item--popover { position: relative; padding: 0; border: none; background: none; box-shadow: none; }
  .sh-badge-row__trigger { display: inline-flex; align-items: center; gap: var(--spacing-sm); font: inherit; color: inherit; background: var(--color-bg); border: var(--border-width) solid var(--color-sand); border-radius: var(--radius-full); padding: var(--spacing-sm) var(--spacing-lg); box-shadow: var(--shadow-xs); cursor: pointer; }
  .sh-badge-row__trigger[aria-expanded="true"] { border-color: var(--color-accent); }
  /* 클릭 가능 신호 — 라벨 뒤 정보 아이콘. 라벨과의 간격은 --spacing-xs로 다른 아이콘-라벨
     gap(--spacing-sm)보다 좁게 둬 "라벨에 딸린 보조 신호"로 읽히게 하고, 크기도 본문
     아이콘(16px)보다 작은 13px로 존재감을 낮춘다. */
  .sh-badge-row__info-icon { width: 13px; height: 13px; flex: none; margin-left: calc(var(--spacing-xs) - var(--spacing-sm)); opacity: 0.7; }
  /* z-index는 --z-dropdown(50)을 쓴다 — 채팅·메일 FAB(--z-overlay 200)와 모달(--z-modal 400)
     보다 낮게 둬 겹치는 순간에도 항상 팝오버가 아래로 밀려 FAB·모달을 가리지 않는다. */
  .sh-badge-row__popover { position: absolute; top: calc(100% + var(--spacing-xs)); left: 50%; transform: translateX(-50%); z-index: var(--z-dropdown); width: max(220px, 60vw); max-width: 320px; background: var(--color-bg); color: var(--color-text); border: var(--border-width) solid var(--color-sand); border-radius: var(--radius-md); padding: var(--spacing-md); box-shadow: var(--shadow-md); font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); font-weight: var(--m3-body-small-weight); letter-spacing: var(--m3-body-small-tracking); text-align: left; }
  .sh-badge-row__popover[hidden] { display: none; }

  /* 상품 신청 인라인 바 — flex-wrap·white-space:normal을 상시 적용한다(옛 640 분기 폐기).
     input이 이미 flex: 1 1 12rem이라 자리가 모자라면 스스로 접힌다 — 미디어 쿼리로
     "언제 접을지"를 따로 셈할 필요가 없다. */
  .sh-request-bar { margin: var(--spacing-2xl) 0 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--spacing-lg); border: var(--border-width) dashed var(--color-tan); border-radius: var(--radius-lg); padding: var(--spacing-md) var(--spacing-xl); }
  .sh-request-bar__icon { font-size: var(--m3-title-large-size); line-height: var(--m3-title-large-line); font-weight: var(--m3-title-large-weight); letter-spacing: var(--m3-title-large-tracking); flex: none; }
  .sh-request-bar__text { font-size: var(--m3-label-large-size); line-height: var(--m3-label-large-line); font-weight: var(--m3-label-large-weight); letter-spacing: var(--m3-label-large-tracking); white-space: normal; }
  /* flex-basis를 주어 자리가 모자라면 줄바꿈되게 한다 — flex: 1(basis 0)이면 줄바꿈 대신
     끝없이 찌그러져, 좁은 화면에서 입력창이 원 하나로 사라졌다. */
  .sh-request-bar input { flex: 1 1 12rem; background: var(--color-bg); border: var(--border-width) solid var(--color-sand); border-radius: var(--radius-full); padding: var(--spacing-sm) var(--spacing-lg); font-size: var(--m3-body-medium-size); line-height: var(--m3-body-medium-line); font-weight: var(--m3-body-medium-weight); letter-spacing: var(--m3-body-medium-tracking); font-family: var(--font-family); outline: none; }
  .sh-request-bar input:focus { border-color: var(--color-primary); }
  /* 색·모양·타입은 .btn.btn-primary가 공급한다(EN-060 5-3c) — 여기는 요청 바
     안에서의 자리(줄지 않는 폭)만 잡는다. */
  .sh-request-bar .btn { flex: none; }

  /* 상품 요청 모달 — 오버레이·패널·입력·버튼은 베이스 컴포넌트(.modal-overlay/.modal-panel/
     .input/.btn)를 그대로 쓰고, 여기서는 요청 폼 고유의 레이아웃만 얹는다. */
  .sh-product-request-overlay[hidden] { display: none; }
  .sh-product-request { max-height: 88vh; overflow-y: auto; gap: 0; }
  .sh-product-request__head { padding: var(--spacing-xl) var(--spacing-2xl) var(--spacing-md); }
  .sh-product-request__eyebrow { font-size: var(--m3-label-small-size); line-height: var(--m3-label-small-line); font-weight: var(--m3-label-small-weight); letter-spacing: var(--m3-label-small-tracking); text-transform: uppercase; color: var(--color-primary-text); margin: 0 0 var(--spacing-xs); }
  .sh-product-request__title { font-family: var(--font-family-heading); font-size: var(--m3-title-large-size); line-height: var(--m3-title-large-line); font-weight: var(--m3-title-large-weight); letter-spacing: var(--m3-title-large-tracking); color: var(--color-accent); margin: 0; }
  .sh-product-request__body { padding: var(--spacing-xl) var(--spacing-2xl) var(--spacing-lg); display: flex; flex-direction: column; gap: var(--spacing-lg); }
  .sh-product-request__field label { display: block; font-size: var(--m3-label-medium-size); line-height: var(--m3-label-medium-line); font-weight: var(--m3-label-medium-weight); letter-spacing: var(--m3-label-medium-tracking); color: var(--color-text); margin-bottom: var(--spacing-xs); }
  .sh-product-request__optional { font-size: var(--m3-label-medium-size); line-height: var(--m3-label-medium-line); font-weight: var(--m3-label-medium-weight); letter-spacing: var(--m3-label-medium-tracking); color: var(--color-text-muted); }
  .sh-product-request__hint { font-size: var(--m3-label-small-size); line-height: var(--m3-label-small-line); font-weight: var(--m3-label-small-weight); letter-spacing: var(--m3-label-small-tracking); color: var(--color-text-muted); margin: var(--spacing-xs) 0 0; }
  /* 브랜드·사이즈는 둘 다 선택이라 한 줄로 묶어 시각 무게를 낮춘다. flex-wrap +
     flex-basis(140px, 라벨+입력이 읽기 편한 최소폭)로 자연 줄바꿈시킨다 — 모달 폭이
     440px 고정이라 뷰포트가 아니라 모달 자체의 좁은 폭(작은 화면에서 92vw로 줄어듦)이
     접힘을 결정한다. */
  .sh-product-request__row { display: flex; flex-wrap: wrap; gap: var(--spacing-md); }
  .sh-product-request__row .sh-product-request__field { flex: 1 1 140px; min-width: 0; }
  /* 첨부 자리 — 후속 과제라 비활성으로 두고 자리만 지킨다. */
  .sh-product-request__attach { display: flex; align-items: center; gap: var(--spacing-sm); border: var(--border-width) dashed var(--color-sand); border-radius: var(--radius-md); padding: var(--spacing-sm) var(--spacing-md); color: var(--color-text-muted); font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); font-weight: var(--m3-body-small-weight); letter-spacing: var(--m3-body-small-tracking); opacity: .65; cursor: not-allowed; }
  .sh-product-request__soon { margin-left: auto; font-size: var(--m3-label-small-size); line-height: var(--m3-label-small-line); font-weight: var(--m3-label-small-weight); letter-spacing: var(--m3-label-small-tracking); background: var(--color-sand); color: var(--color-text-muted); border-radius: var(--radius-full); padding: var(--spacing-xs) var(--spacing-sm); }
  .sh-product-request__error { color: var(--color-primary-text); font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); font-weight: var(--m3-body-small-weight); letter-spacing: var(--m3-body-small-tracking); margin: 0; }
  .sh-product-request__error[hidden] { display: none; }
  .sh-product-request__foot { padding: 0 var(--spacing-2xl) var(--spacing-2xl); display: flex; flex-direction: column; gap: var(--spacing-sm); }
  .sh-product-request__submit { width: 100%; border-radius: var(--radius-full); padding: var(--spacing-md); font-size: var(--m3-label-large-size); line-height: var(--m3-label-large-line); font-weight: var(--m3-label-large-weight); letter-spacing: var(--m3-label-large-tracking); }
  /* 취소 버튼은 이제 .btn .btn-sm(base)이 배경·테두리·패딩·커서를 전담한다(메일·채팅
     모달 취소 버튼과 동일 기준, EN-071) — 여기는 이 모달만의 나머지 값이 없으면 빈다. */
  .sh-product-request__done { padding: var(--spacing-3xl) var(--spacing-2xl); text-align: center; }
  .sh-product-request__done[hidden] { display: none; }
  .sh-product-request__mark { font-size: var(--m3-headline-large-size); line-height: var(--m3-headline-large-line); font-weight: var(--m3-headline-large-weight); letter-spacing: var(--m3-headline-large-tracking); display: block; margin-bottom: var(--spacing-md); }
  .sh-product-request__done .sh-product-request__hint { margin-bottom: var(--spacing-xl); font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); font-weight: var(--m3-body-small-weight); letter-spacing: var(--m3-body-small-tracking); }

  /* 파트너 (루돌프 + 말풍선) — flex-wrap + justify-content:center로 자연 줄바꿈시킨다.
     자식이 78px(루돌프)·340px(말풍선, max-width:78vw 상한 있음) 고정폭이라 합(78+340+gap
     24px=442px)이 컨테이너에 안 들어가면 스스로 접힌다 — 옛 720 분기가 필요 없다.
     옛 분기의 gap 축소(2xl→md, 접힌 상태 전용)는 폐기했다 — 접힘이 더 이상 뷰포트가
     아니라 자식 폭 합으로 결정되므로 "접힌 상태"를 가리키는 선택자가 없다. */
  .sh-partner { margin: var(--spacing-5xl) 0 var(--spacing-sm); display: flex; flex-wrap: wrap; justify-content: center; align-items: flex-start; gap: var(--spacing-2xl); }
  .sh-partner__rudolph { position: relative; flex: none; margin-top: -var(--spacing-xs); }
  .sh-partner__rudolph img { display: block; width: 78px; max-width: 100%; }
  .sh-partner__nose-glow { position: absolute; left: 91%; top: 42%; width: 20px; height: 20px; border-radius: 50%; background: radial-gradient(circle, color-mix(in srgb, var(--color-primary) 60%, transparent), transparent 70%); animation: sh-noseglow var(--motif-duration-glow) var(--m3-easing-standard) infinite; }
  @keyframes sh-noseglow { 0%,100% { transform: translate(-50%,-50%) scale(1); opacity: .8; } 50% { transform: translate(-50%,-50%) scale(1.5); opacity: 1; } }
  .sh-partner__bubble { position: relative; flex: none; width: 340px; max-width: 78vw; padding: var(--spacing-xl) var(--spacing-2xl); }
  /* 아래 0·1은 국소 리터럴 — 말풍선 안에서 테두리 SVG를 본문 뒤로 보낼 뿐이라 전역 층과
     무관하다(rule/css-guide.md 3절 예외). */
  .sh-partner__outline { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; z-index: 0; }
  .sh-partner__outline path { fill: none; stroke: var(--color-accent); stroke-width: 2; vector-effect: non-scaling-stroke; stroke-linejoin: round; stroke-linecap: round; }
  .sh-partner__body { position: relative; z-index: 1; }
  .sh-partner__say { font-family: var(--font-family-hand); font-size: var(--m3-headline-small-size); line-height: var(--m3-headline-small-line); font-weight: var(--m3-headline-small-weight); letter-spacing: var(--m3-headline-small-tracking); color: var(--color-accent); margin: 0 0 var(--spacing-sm); }
  .sh-partner__earn { font-size: var(--m3-body-medium-size); line-height: var(--m3-body-medium-line); font-weight: var(--m3-body-medium-weight); letter-spacing: var(--m3-body-medium-tracking); color: var(--color-text-muted); margin: 0 0 var(--spacing-lg); }
  /* 색만 .btn.btn-dark의 슬롯으로 대입한다(EN-060 5-3c) — 이 자리는 초록(secondary)이
     accent 기본값과 갈린다. 그 외(모양·간격·타입)는 .btn-dark를 그대로 받는다. */
  .sh-partner__cta { --btn-dark-bg: var(--color-brown-kraft); --btn-dark-bg-hover: color-mix(in srgb, var(--color-brown-kraft) 85%, black); }  /* 2026-08-15, tan→brown-kraft로 재교체(브랜드 색 다양화 논의) — 리터럴이라 on-*-fixed-variant가 없어 hover는 color-mix로 어둡게(sh-partner__nose-glow와 같은 기법) */
  @media (prefers-reduced-motion: reduce) { .sh-partner__nose-glow { animation: none; } }

  /* ── 상세 이미지 스택 컨테이너 — 상세 섹션(image 블록)의 유일한 렌더 경로다. ── */
  .sh-detail-imgs {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-xs);
  }

  /* ══════════ 상품 상세(PDP) — 페이지 셸·히어로·구매 바·공유·토스트 ══════════ */

  /* 페이지 셸 — 상세는 카탈로그보다 좁은 콘텐츠 단을 쓴다. */
  body.sh-product {
    --page-max: var(--width-content);
    /* 하단 고정 구매 바가 바닥을 점유한다 — FAB은 --anchor-bottom을 읽어 스스로 그 위에
       쌓이고(좌표를 여기서 계산하지 않는다), 본문 여백도 같은 값에서 파생시킨다.
       74px은 폴백이다. 실제 높이는 안에 든 버튼·글꼴·가격 문자열 길이가 정하므로 사람이
       재서 박아 두면 낡는다(실측 69px로 이미 5px 어긋나 있었다) — product_landing.html의
       ResizeObserver가 실측값으로 덮어쓴다. JS가 죽으면 이 값이 최종값이 되므로 넉넉한
       쪽으로 둔다: 작으면 FAB이 바에 겹치고, 크면 조금 더 뜰 뿐이다. */
    --anchor-bottom: 74px;
    padding-bottom: calc(var(--anchor-bottom) + var(--spacing-sm));
  }
  /* width:100%가 필요한 이유 — body가 세로 flex라 main은 flex 아이템이고,
     margin:0 auto(가로 auto 마진)가 늘어나기(stretch)를 끈다. 그러면 폭이 뷰포트가
     아니라 콘텐츠 기준으로 정해져 max-width까지 부풀고, 좁은 화면에서 줄지 않는다
     (375px에서 720px로 넘쳤다). 폭을 뷰포트에 묶고 max-width가 상한만 맡게 한다. */
  .l-product {
    width: 100%;
    max-width: var(--page-max, var(--width-wide));
    margin-left: auto;
    margin-right: auto;
    padding: 0 var(--spacing-lg);
  }

  /* FAB 겹침 방지 — .sh-catalog와 같은 이유(EN-051 4번, --fab-inset-right
     참조). 상품 상세는 --page-max(720px)로 카탈로그와 다른 셸을 쓰므로 여기 따로 걸어야
     한다 — .sh-catalog의 480px 미디어쿼리는 이 페이지에 적용되지 않는다. 768에서 겹침이
     확인돼(--page-max 720 + FAB 폭) 780px까지 넓게 잡는다. */
  @media (max-width: 780px) {
    .l-product { padding-right: var(--fab-inset-right); }
  }

  /* 히어로 이미지 — 1:1 고정비. 이미지가 없으면 플레이스홀더 면이 그대로 보인다.
     4:5였던 것을 카탈로그 썸네일(1/1)에 맞췄다(EN-062) — main.jpg 한 파일이 카드와
     히어로를 함께 섬기는데 상자 비율이 다르면 한쪽에서 cover가 잘라낸다(4:5 상자에
     1:1 이미지를 넣으면 좌우 20%). 대표 이미지는 파일이 하나라 상자를 맞추는 편이 싸다. */
  .sh-product-hero {
    aspect-ratio: 1 / 1;
    max-width: 352px;
    max-height: 352px;
    background: linear-gradient(135deg, var(--product-media), var(--color-ivory));
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-tan);
    border-radius: var(--radius-md);
    /* 위쪽 여백은 body > main의 공통 여백(설계 논의 08-14, components.css)이 대신한다 —
       여기서 또 spacing-xl을 얹으면 헤더와의 간격이 다른 페이지의 두 배가 된다. */
    margin: 0 auto;
    position: relative;
    overflow: hidden;
  }
  .sh-product-hero img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  /* 상품명·가격·배송 한 줄 */
  .sh-product-head { padding: var(--spacing-xl) var(--spacing-xs) var(--spacing-sm); }
  .sh-product-head__brand {
    font-size: var(--m3-title-small-size); line-height: var(--m3-title-small-line); font-weight: var(--m3-title-small-weight); letter-spacing: var(--m3-title-small-tracking);
    color: var(--color-text-muted);
    margin-bottom: var(--spacing-xs);
  }
  .sh-product-head__name {
    font-size: var(--m3-title-large-size); line-height: var(--m3-title-large-line); font-weight: var(--m3-title-large-weight); letter-spacing: var(--m3-title-large-tracking);
    
    
    
    margin: 0;
  }
  .sh-product-head .sh-product-subtitle {
    margin-top: var(--spacing-xs);
    font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); font-weight: var(--m3-body-small-weight); letter-spacing: var(--m3-body-small-tracking);
    color: var(--color-text-muted);
  }
  .sh-product-head__price {
    font-size: var(--m3-headline-small-size); line-height: var(--m3-headline-small-line); font-weight: var(--m3-headline-small-weight); letter-spacing: var(--m3-headline-small-tracking);
    
    margin-top: var(--spacing-md);
    display: flex;
    align-items: center;
    gap: var(--spacing-md);
    flex-wrap: wrap;
  }
  /* 파트너 유치 칩 — 가격 옆 아웃라인 pill */
  .sh-partner-chip {
    font-size: var(--m3-label-medium-size); line-height: var(--m3-label-medium-line); font-weight: var(--m3-label-medium-weight); letter-spacing: var(--m3-label-medium-tracking);
    
    color: var(--color-primary-text);
    border: var(--border-width) solid var(--color-primary);
    border-radius: var(--radius-card);
    padding: var(--spacing-xs) var(--spacing-md);
    white-space: nowrap;
  }
  .sh-partner-chip:hover {
    background: var(--color-primary);
    color: var(--color-text-inverse);
  }

  /* 구매 바 — 버튼 표면은 components.css .btn*이 담당하고 여기는 배치만 갖는다.
     1행 즉시구매 전체폭, 2행 수량 + 담기(성장) + 공유. */
  .sh-buy-bar {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-sm);
    margin: var(--spacing-lg) 0 var(--spacing-sm);
    position: relative;
  }
  .sh-buy-bar .btn-primary { margin: 0; width: 100%; border-radius: var(--radius-md); }
  .sh-buy-bar__row { display: flex; align-items: center; gap: var(--spacing-sm); }
  .sh-buy-bar__cart {
    flex: 1;
    margin: 0;
    display: flex;
    align-items: center;
    gap: var(--spacing-sm);
  }
  /* 변이(.btn-dark 등)가 아니라 .btn을 잡는다 — 세기 단계를 바꿔도 배치가 따라오지
     않게 하기 위해서다. EN-060 회차 3에서 담기를 채움→외곽선으로 내렸을 때 이 규칙이
     .btn-dark에 묶여 있어 폭·모서리가 함께 사라졌다. */
  .sh-buy-bar__cart .btn { flex: 1; margin: 0; border-radius: var(--radius-full); }

  /* 수량 스테퍼 — 단색 잉크 pill. 디바이더·그림자 없음 */
  .sh-qty {
    display: flex;
    align-items: center;
    background: var(--color-accent);
    border-radius: var(--radius-full);
    padding: 0 var(--spacing-xs);
    flex: none;
  }
  .sh-qty button {
    width: 26px;
    height: 36px;
    padding: 0;
    border: none;
    background: none;
    font-size: var(--m3-title-medium-size); line-height: var(--m3-title-medium-line); font-weight: var(--m3-title-medium-weight); letter-spacing: var(--m3-title-medium-tracking);
    
    
    color: var(--color-sand);
    cursor: pointer;
    font-family: inherit;
  }
  .sh-qty button:hover { opacity: 0.75; }
  .sh-qty__input {
    width: 22px;
    flex: none;
    text-align: center;
    font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); font-weight: var(--m3-body-small-weight); letter-spacing: var(--m3-body-small-tracking);
    
    font-family: inherit;
    color: var(--color-sand);
    background: transparent;
    border: none;
    border-radius: 0;
    -moz-appearance: textfield;
  }
  .sh-qty__input::-webkit-outer-spin-button,
  .sh-qty__input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

  /* 공유 — 아이콘 버튼 + 드롭다운 */
  .sh-share-btn {
    width: 44px;
    height: 44px;
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: var(--border-width) solid var(--color-accent);
    border-radius: var(--radius-full);
    padding: 0;
    color: var(--color-accent);
    cursor: pointer;
    white-space: nowrap;
    font-family: inherit;
  }
  .sh-share-btn:hover { background: var(--color-bg-subtle); }
  .sh-share-pop {
    position: absolute;
    right: 0;
    top: calc(100% + var(--spacing-sm));
    background: var(--color-bg);
    border: var(--border-width) solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
    padding: var(--spacing-sm);
    display: none;
    flex-direction: column;
    min-width: 150px;
    z-index: var(--z-dropdown);
  }
  .sh-share-pop.is-open { display: flex; }
  /* 고정 바(.sh-sticky-bar, position:fixed) 안의 팝업 — .sh-sticky-bar 자체가 fixed라
     이미 포지셔닝 컨테이너라서 별도 relative 래퍼는 필요 없다. 다만 히어로 쪽(아래로 열림)과
     달리 화면 맨 아래 고정 바 위에서 열려야 하므로 방향을 뒤집는다. */
  .sh-share-pop--sticky {
    right: var(--spacing-lg);
    top: auto;
    bottom: calc(100% + var(--spacing-sm));
  }
  .sh-share-pop button {
    background: none;
    border: none;
    text-align: left;
    font-size: var(--m3-body-medium-size); line-height: var(--m3-body-medium-line); font-weight: var(--m3-body-medium-weight); letter-spacing: var(--m3-body-medium-tracking);
    padding: var(--spacing-sm) var(--spacing-md);
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-family: inherit;
    color: var(--color-text);
  }
  .sh-share-pop button:hover { background: var(--color-bg-subtle); }
  .sh-share-pop__hint {
    margin-top: var(--spacing-xs);
    padding: var(--spacing-md) var(--spacing-md) var(--spacing-sm);
    font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); font-weight: var(--m3-body-small-weight); letter-spacing: var(--m3-body-small-tracking);
    color: var(--color-text-muted);
    
  }
  .sh-share-pop__hint a { color: var(--color-primary-text); }

  /* 토스트 자리잡기 — 표면(배경·아이콘)은 components.css .sh-toast*가 담당한다 */
  .sh-toast-fixed {
    position: fixed;
    left: 50%;
    bottom: 100px;
    transform: translateX(-50%);
    opacity: 0;
    transition: opacity var(--m3-duration-short4);
    pointer-events: none;
    z-index: var(--z-toast);
    white-space: nowrap;
  }
  .sh-toast-fixed.is-visible { opacity: 1; pointer-events: auto; }
  .sh-toast-fixed a {
    color: var(--color-text-inverse);
    text-decoration: underline;
    margin-left: var(--spacing-sm);
  }

  /* 배송 무게 초과 안내 — 토스트와 달리 자동으로 사라지지 않는다(원인이 해소돼야
     사라짐, 무게가 계속 초과 상태면 계속 떠 있어야 함). 화면 중앙에 고정하고 닫기
     버튼을 둬 사용자가 원하면 직접 치울 수 있게 한다. */
  .cart-shipping-weight-notice {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: var(--z-modal);
    max-width: min(420px, calc(100vw - var(--spacing-xl) * 2));
    display: flex;
    align-items: flex-start;
    gap: var(--spacing-sm);
    box-shadow: var(--shadow-lg);
    margin: 0;
  }
  .cart-shipping-weight-notice__text { margin: 0; flex: 1 1 auto; }
  .cart-shipping-weight-notice__close {
    flex: none;
    width: 28px;
    height: 28px;
    border: none;
    border-radius: 50%;
    background: transparent;
    color: inherit;
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
  }
  .cart-shipping-weight-notice__close:hover { background: rgba(0, 0, 0, 0.08); }

  /* ── 상품 그리드 — 여기는 브레이크포인트를 남긴다(.sh-footer-links와 같은 부류).
     한 줄에 상품을 몇 개 보일지는 "몇 개가 들어가는가"가 아니라 "몇 개를 보이고 싶은가"다.
     375px 폭이면 큰 카드 하나도 작은 카드 둘도 다 들어간다 — 어느 쪽이 옳은지는 공간이
     아니라 장사가 정한다. 밀도 결정이라 내재적 레이아웃에 맡길 수 없다.
     auto-fit minmax(220px)로 바꿨다가 되돌렸다: 모바일이 2열에서 1열로 떨어져 한 화면에
     보이는 상품이 4개에서 2개로 줄고 페이지가 25% 길어졌다.
     방향은 min-width로 통일한다 — 모바일 2열이 기본, 넓어지면 열을 더한다. */
  .sh-product-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--spacing-lg);
    padding-bottom: var(--spacing-sm);
  }
  @media (min-width: 720px) {
    .sh-product-grid { grid-template-columns: repeat(3, 1fr); }
  }
  @media (min-width: 980px) {
    .sh-product-grid { grid-template-columns: repeat(4, 1fr); }
  }

  /* ── 공지 — 배송·교환/환불 등 정적 안내 박스 ── */
  /* 표면(배경·테두리·반경·여백)은 .card 슬롯이 공급한다(EN-052). 여기는 부품 고유의
     활자 규칙만 남긴다. */
  .sh-notice {
    font-size: var(--m3-body-medium-size); line-height: var(--m3-body-medium-line); font-weight: var(--m3-body-medium-weight); letter-spacing: var(--m3-body-medium-tracking);
    
  }
  .sh-notice strong { color: var(--color-primary-text); }

  /* 배송 절차 — 8단계 세로 스테퍼(디자인 핸드오프 11번, 구매 전 안내용 정적 표시라 전
     단계를 동일한 upcoming 톤으로 렌더 — 실제 주문 상태와 연동하지 않는다). 스테퍼·리드타임
     안내문이 서로 다른 배경(카드 vs 페이지 배경)에 떠 있으면 남남처럼 보이므로 하나의
     카드(.sh-journey) 안에 담고, 안내문은 자체 박스 대신 구분선(.sh-journey__note)만 둔다.
     표면은 .card 슬롯이 공급한다(EN-052) — 하단 여백만 좁혀 덮는다. */
  .sh-journey {
    padding-bottom: var(--spacing-xs);
  }
  /* 목록 구조(원·연결선·라벨)는 base의 step-list--number 공유분(components.css)이 그린다 —
     여기서는 브랜드 타이포·색만 재정의한다(설계 논의 08-09, .sh-journey 스코프). */
  .sh-journey .step-list__circle {
    background: var(--color-sand);      /* 미완료 단계 면 */
    color: var(--color-text-muted);     /* 위계는 투명도가 아니라 이름으로 */
    font-size: var(--m3-body-large-size); line-height: var(--m3-body-large-line); font-weight: var(--m3-body-large-weight); letter-spacing: var(--m3-body-large-tracking);
  }
  .sh-journey .step-list__label { font-size: var(--m3-body-large-size); line-height: var(--m3-body-large-line); font-weight: var(--m3-body-large-weight); letter-spacing: var(--m3-body-large-tracking); color: var(--color-text); }
  .sh-journey .step-list__desc {
    font-size: var(--m3-label-large-size); line-height: var(--m3-label-large-line); font-weight: var(--m3-label-large-weight); letter-spacing: var(--m3-label-large-tracking);
    color: var(--color-text-muted);
    margin-top: var(--spacing-xs);
  }
  .sh-journey__note {
    margin-top: var(--spacing-xs);
    padding: var(--spacing-lg) 0 var(--spacing-xl);
    font-size: var(--m3-body-medium-size); line-height: var(--m3-body-medium-line); font-weight: var(--m3-body-medium-weight); letter-spacing: var(--m3-body-medium-tracking);
    
  }
  /* 안내문 라벨-값 리스트 — 부모 클래스(.sh-journey__note, .sh-notice)는 그대로 두고
     하위 구조만 추가한다. santa-here 자식은 이 클래스를 안 써 렌더 불변. */
  .sh-note-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column;
    gap: var(--spacing-md); }
  .sh-note-list__item { display: flex; flex-direction: column; gap: var(--spacing-xs); }
  .sh-note-list__label { font-size: var(--m3-label-small-size); line-height: var(--m3-label-small-line);
    font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
    color: var(--color-primary); }
  .sh-note-list__desc { font-size: var(--m3-body-large-size); line-height: var(--m3-body-large-line);
    font-weight: var(--m3-body-large-weight); letter-spacing: var(--m3-body-large-tracking);
    color: var(--color-text-muted); margin: 0; }

  /* ── 하단 고정 구매 바 — sh-buy-bar(비고정 구매 영역)와 이름이 겹치지 않도록
     sh-sticky-bar로 구분한다. 같은 클래스명을 쓰면 position:fixed가 비고정 구매 바(#cta)에도
     번져 레이아웃이 깨진다. */
  /* 기본값은 숨김(opacity 0 + pointer-events 없음) — #cta(상단 구매 영역)가 보이는 동안은
     고정 바가 겹치지 않아야 한다. IntersectionObserver가 #cta 이탈을 감지하면 is-visible을
     붙여 나타난다. JS가 죽으면 is-visible이 절대 안 붙어 고정 바가 계속 숨어 있지만,
     #cta의 구매 폼은 그대로 살아 있으므로 구매 자체는 막히지 않는다(의도된 실패 모드). */
  .sh-sticky-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    background: var(--color-bg);
    border-top: var(--border-width) solid var(--color-border);
    padding: var(--spacing-md) var(--spacing-lg) calc(var(--spacing-md) + env(safe-area-inset-bottom));  /* env() — 토큰화 불가 */
    z-index: var(--z-sticky);
    opacity: 0;
    transform: translateY(8px);  /* 진입 미세 이동 — sh-review-detail__panel 관례와 동일, 토큰화 불가 */
    pointer-events: none;
    transition: opacity var(--m3-duration-medium1) var(--m3-easing-standard-decelerate), transform var(--m3-duration-medium1) var(--m3-easing-standard-decelerate);
  }
  .sh-sticky-bar.is-visible {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
  }
  .sh-sticky-bar__inner {
    max-width: 720px;
    margin: 0 auto;
    display: flex;
    align-items: center;
    gap: var(--spacing-lg);
    width: 100%;
  }
  .sh-sticky-bar__price { font-size: var(--m3-title-medium-size); line-height: var(--m3-title-medium-line); font-weight: var(--m3-title-medium-weight); letter-spacing: var(--m3-title-medium-tracking); white-space: nowrap; }
  .sh-sticky-bar .btn { margin: 0; flex: 1; padding: var(--spacing-md) 0; }
  /* 담기는 보조 행동이라 주 전환 경로(구매)보다 좁게 — flex 비율로 갈라 폭이 좁아져도 비율이 유지된다. */
  .sh-sticky-bar__cart { flex: 0.55; white-space: nowrap; }

  /* 고정 바 수량 미러 — 상단 .sh-qty와 같은 잉크 pill 스킨을 쓰되, 실제 제출 input이
     아니라 버튼 2개 + aria-live span이다(제출값은 상단 [data-cart-quantity-input]
     하나뿐 — 여기는 표시만 미러링). flex: none으로 담기·구매 버튼의 성장을 뺏지 않는다. */
  .sh-qty--sticky { flex: none; }
  .sh-qty__value {
    width: 22px;
    display: inline-block;
    text-align: center;
    font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); font-weight: var(--m3-body-small-weight); letter-spacing: var(--m3-body-small-tracking);
    color: var(--color-sand);
  }

  /* 고정 바 옵션 트리거 + 패널 — 라디오(.variant-picker__item)를 복제하지 않고, 현재 선택된
     옵션 라벨만 보여주는 트리거 버튼과, 클릭 시 그 위에 뜨는 목록 패널로 구성한다.
     트리거는 가격·수량·담기·구매와 같은 줄에 끼워 넣지 않는다 — 한 줄에 다 몰리면
     좁은 화면에서만 줄바꿈되어 "가끔은 옵션이 버튼들과 한 줄"이 되는 문제(2026-08-15
     사용자 피드백)가 있었다. .sh-sticky-bar 자체를 세로로 쌓아 옵션 줄을 항상 별도
     행으로 고정한다. */
  .sh-opt {
    position: relative;
    max-width: 720px;
    margin: 0 auto var(--spacing-sm);
    width: 100%;
  }
  /* 테두리·배경 없는 플랫 텍스트 줄(2026-08-15 디자인 재조정) — 패널·리스트가 전부
     각진 표면이라, 알약 모양 트리거만 튀는 문제가 있었다. 박스를 없애고 아래 가격 줄과의
     구분은 border-bottom 헤어라인 하나로만 준다. */
  .sh-opt-strip {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--spacing-xs);
    width: 100%;
    padding: var(--spacing-sm) 0;
    background: transparent;
    border: none;
    border-bottom: var(--border-width) solid var(--color-border);
    color: var(--color-text);
    font-family: var(--font-family);
    font-size: var(--m3-label-large-size); line-height: var(--m3-label-large-line); font-weight: var(--m3-label-large-weight); letter-spacing: var(--m3-label-large-tracking);
    white-space: nowrap;
    cursor: pointer;
  }
  .sh-opt-strip:hover { color: var(--color-primary-text); }
  .sh-opt-strip:disabled { opacity: .5; cursor: not-allowed; }
  .sh-opt-strip[aria-expanded="true"] { color: var(--color-primary-text); border-bottom-color: var(--color-primary); }

  /* 패널 표면은 base .variant-picker/.variant-picker__item과 시각적으로 맞추되(radius-lg·shadow-sm·
     선택 강조 대신 여기는 목록 자체가 임시 팝업이라 hover만), 라디오 사본이 아니므로
     새 클래스로 독립 정의한다. .sh-opt이 이제 고정 바 맨 위 줄이라, 패널은 트리거
     바로 위(=고정 바 전체 위)에 앵커된다. */
  .sh-opt-panel {
    position: absolute;
    left: 0;
    right: 0;
    bottom: calc(100% + var(--spacing-sm));
    max-height: 50vh;
    overflow-y: auto;
    overscroll-behavior: contain;
    background: var(--color-bg);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
    display: flex;
    flex-direction: column;
  }
  .sh-opt-panel[hidden] { display: none; }
  .sh-opt-panel__item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--spacing-md);
    padding: var(--spacing-md) var(--spacing-lg);
    border: none;
    border-bottom: var(--border-width) solid var(--color-border);
    background: none;
    color: var(--color-text);
    font-family: var(--font-family);
    font-size: var(--m3-body-medium-size); line-height: var(--m3-body-medium-line); font-weight: var(--m3-body-medium-weight); letter-spacing: var(--m3-body-medium-tracking);
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
  }
  .sh-opt-panel__item:last-child { border-bottom: none; }
  .sh-opt-panel__item:hover { background: var(--color-bg-subtle); }
  .sh-opt-panel__item:disabled {
    color: var(--color-text-disabled);
    opacity: .6;
    cursor: not-allowed;
    text-decoration: line-through;
  }

  /* 패널이 열리면 고정 바 자체를 모달 층으로 올린다 — 패널이 .sh-sticky-bar의
     자손이라 부모의 z-index(--z-sticky, 100) 쌓임 맥락에 갇혀 있으면 문의 FAB
     (--z-overlay, 200)보다 아래로 그려진다. */
  .sh-sticky-bar.is-panel-open { z-index: var(--z-modal); }

  /* ── 리뷰 — 카드·헤더 ── */
  .sh-reviews { display: flex; flex-direction: column; gap: var(--spacing-md); }
  /* Featured·More·Photo 세 소제목이 모두 같은 구분선 스타일을 쓴다(통일) — 위에
     .sh-head--label로 "Reviews" 섹션 제목이 이미 있어 하위는 작은 라벨로 충분하다. */
  .sh-reviews__more-label { font-size: var(--m3-label-medium-size); line-height: var(--m3-label-medium-line); font-weight: var(--m3-label-medium-weight); letter-spacing: var(--m3-label-medium-tracking); color: var(--color-text-muted); display: flex; align-items: center; gap: var(--spacing-md); margin: var(--spacing-xs) var(--spacing-xs); text-transform: uppercase; }
  .sh-reviews__more-label::before, .sh-reviews__more-label::after { content: ""; flex: 1; border-top: var(--border-width) solid var(--color-border); }
  .sh-review { width: 100%; display: block; text-align: left; font-family: inherit; color: inherit; cursor: pointer; background: linear-gradient(180deg, var(--color-bg), var(--color-bg-subtle)); border: none; border-left: 4px solid var(--color-gold); /* 강조 스트라이프 두께 — border-width 스케일(1/1.5/2px) 밖 시각 악센트, 토큰화 불가 */ border-radius: var(--radius-md); box-shadow: var(--shadow-sm); padding: var(--spacing-lg) var(--spacing-xl); transition: background var(--m3-duration-short3) var(--m3-easing-standard-decelerate), box-shadow var(--m3-duration-short3) var(--m3-easing-standard-decelerate); }
  /* --compact 변이(마퀴 카드) — sh-review 기본을 상속하므로 스트라이프·여백·그림자·줄수 제한을 덮는다. */
  .sh-review--compact { flex: none; width: 220px; /* 마퀴 카드 고정 폭 — spacing 리듬 대상 아님 */ border-left: none; padding: var(--spacing-md) var(--spacing-lg); box-shadow: none; }
  .sh-review--compact:hover { box-shadow: var(--shadow-sm); transform: translateY(-1px); /* 호버 미세 부양 — 토큰화 불가 */ }
  /* 선택된 카드 하이라이트 — 대표 카드는 이미 border-left가 있어 배경·그림자만 강조한다 */
  /* 선택 하이라이트 — 대표·마퀴 카드 공통(배경 강조 + 골드 링) */
  /* :hover 규칙(아래)이 box-shadow를 덮어쓰지 않도록 is-selected:hover까지 함께 지정한다 */
  .sh-review.is-selected { background: linear-gradient(180deg, var(--color-bg-subtle), var(--color-accent-soft, var(--color-bg-subtle))); box-shadow: var(--shadow-sm), inset 0 0 0 var(--border-width) var(--color-gold); }
  /* --compact:hover(box-shadow: shadow-sm)가 소스 순서상 나중이라 동일 특이도에서 is-selected를 덮는다 — 선택+호버 상태를 되살린다. */
  .sh-review--compact.is-selected:hover { box-shadow: var(--shadow-sm), inset 0 0 0 var(--border-width) var(--color-gold); }
  .sh-review__head,
  .sh-review-detail__head { display: flex; align-items: center; gap: var(--spacing-sm); font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); font-weight: var(--m3-body-small-weight); letter-spacing: var(--m3-body-small-tracking); color: var(--color-text-muted); }
  .sh-review__head .stars,
  .sh-review-detail__head .stars { color: var(--color-gold); font-size: var(--m3-label-large-size); line-height: var(--m3-label-large-line); font-weight: var(--m3-label-large-weight); letter-spacing: var(--m3-label-large-tracking); }
  .sh-review__head .date,
  .sh-review-detail__head .date { margin-left: auto; }
  .sh-review__author,
  .sh-review-detail__author { color: var(--color-accent); font-size: var(--m3-label-large-size); line-height: var(--m3-label-large-line); font-weight: var(--m3-label-large-weight); letter-spacing: var(--m3-label-large-tracking); }
  .sh-review__author { margin-top: var(--spacing-sm); /* 카드형은 head 아래 새 줄, 상세 head는 한 줄 정렬이라 불필요 */ }
  .sh-review p { margin: var(--spacing-sm) 0 0; font-size: var(--m3-body-medium-size); line-height: var(--m3-body-medium-line); font-weight: var(--m3-body-medium-weight); letter-spacing: var(--m3-body-medium-tracking); white-space: pre-line; color: var(--color-text-muted); display: -webkit-box; -webkit-line-clamp: 3; /* 표시 줄 수 제한 — 토큰화 불가 */ -webkit-box-orient: vertical; overflow: hidden; }
  .sh-review--compact p { -webkit-line-clamp: 2; /* 표시 줄 수 제한 — 토큰화 불가 */ }

  /* --photo 변이(사진 마퀴 카드) — --compact 위에 얹혀 본문 위에 썸네일 커버를 더한다.
     본문 줄 수를 1로 더 줄이는 이유는 커버 이미지가 이미 카드 높이 대부분을 차지해서다. */
  .sh-review--photo { display: flex; flex-direction: column; }
  .sh-review__photo-cover { aspect-ratio: 1 / 1; border-radius: var(--radius-sm); overflow: hidden; margin-bottom: var(--spacing-sm); }
  .sh-review__photo-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .sh-review--photo p { -webkit-line-clamp: 1; /* 표시 줄 수 제한 — 토큰화 불가 */ }

  /* 상세 패널의 사진 스트립 — 가로 스크롤, 후기 사진 개수(1~5장)가 리뷰마다 달라
     고정 그리드 대신 스크롤로 받는다. */
  /* hero(큰 이미지) + 썸네일 스트립 — 스트립 클릭으로 hero를 교체한다(JS fillPanel). */
  .sh-review-detail__photo-hero:not(:empty) { margin-top: var(--spacing-md); position: relative; z-index: 1; /* 스택 순서 값 — 토큰화 대상 아님 */ }
  .sh-review-detail__photo-hero img { width: 100%; max-height: 360px; /* hero 상한 — spacing 리듬 대상 아님 */ object-fit: contain; border-radius: var(--radius-md); background: var(--color-bg-subtle); }
  .sh-review-detail__photos:not(:empty) { display: flex; gap: var(--spacing-sm); overflow-x: auto; margin-top: var(--spacing-sm); position: relative; z-index: 1; /* 스택 순서 값 — 토큰화 대상 아님 */ }
  .sh-review-detail__photos img { flex: none; width: 64px; /* 스트립 썸네일 고정 폭 — spacing 리듬 대상 아님 */ aspect-ratio: 1 / 1; object-fit: cover; border-radius: var(--radius-sm); cursor: pointer; opacity: 0.6; transition: opacity var(--m3-duration-short3) var(--m3-easing-standard-decelerate); }
  .sh-review-detail__photos img:hover { opacity: 0.85; }
  .sh-review-detail__photos img.is-active { opacity: 1; box-shadow: inset 0 0 0 var(--border-width) var(--color-gold); }

  /* 상단 고정 리뷰 3개 — auto-fit minmax. 640px 접힘 직전(641px)에서 카드 실측
     195px, 그대로 하한으로 썼다. 3→2→1 중간 단계가 새로 생기는 것은 의도된 변화다. */
  .sh-reviews__top { display: grid; grid-template-columns: repeat(auto-fit, minmax(195px, 1fr)); gap: var(--spacing-md); align-items: start; }

  /* ── 리뷰 — 마퀴 ── */
  /* 마퀴 슬라이더 — 항목 집합을 복제해 이어붙이는 표준 무한 루프 기법.
     .sh-marquee__track 안에 기존 목록과 복제 목록이 나란히 있고, 전체 폭의 절반만큼
     왼쪽으로 이동한 뒤 처음(0)으로 되돌아가므로 이음매가 보이지 않는다. */
  /* 마퀴 — 루프 지속(32s)은 규격 눈금(최대 1000ms) 밖이다. UI 전이가 아니라 배경 운동이라
     M3가 다루는 대상이 아니다. 마스크의 #000도 색이 아니라 불투명도 표현이다(모티프). */
  .sh-marquee { margin-top: var(--spacing-lg); overflow: hidden; position: relative; -webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent); /* 마스크 그라디언트 비율 — 토큰화 불가 */ mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent); }
  .sh-marquee__track { display: flex; align-items: flex-start; gap: var(--spacing-md); width: max-content; animation: sh-marquee var(--motif-duration-marquee) var(--m3-easing-linear) infinite; /* 애니메이션 지속 시간 — 토큰화 불가 */ }
  .sh-marquee:hover .sh-marquee__track { animation-play-state: paused; }
  .sh-marquee__track.is-paused { animation-play-state: paused; }
  @keyframes sh-marquee {
    from { transform: translateX(0); }
    to { transform: translateX(-50%); } /* 트랙 이동 폭 — 원본·복제본 절반, 토큰화 불가 */
  }
  .sh-review--compact .sh-review__head .date { display: none; }
  /* 마퀴 카드 선택 — border를 더하면 폭이 흔들리므로 inset shadow로 좌측 강조를 표현한다 */

  /* ── 리뷰 — 상세 패널 ── */
  /* 리뷰 상세 패널 — 카드 클릭 시 제자리 펼침 대신 카드 목록 아래 별도 패널에 전문을 띄운다.
     grid-template-rows 0fr→1fr 기법으로 height:auto도 부드럽게 트랜지션한다. */
  .sh-review-detail { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--m3-duration-medium2) var(--m3-easing-emphasized); margin: var(--spacing-md) 0; }
  .sh-review-detail.is-open { grid-template-rows: 1fr; }
  .sh-review-detail__inner { overflow: hidden; min-height: 0; }
  .sh-review-detail__panel { position: relative; background: linear-gradient(180deg, var(--color-bg), var(--color-bg-subtle)); border-radius: var(--radius-md); box-shadow: var(--shadow-sm); padding: var(--spacing-xl) var(--spacing-2xl) var(--spacing-xl); opacity: 0; transform: translateY(8px); /* 진입 미세 이동 — 토큰화 불가 */ transition: opacity var(--m3-duration-medium1) var(--m3-easing-standard-decelerate) var(--m3-duration-short2), transform var(--m3-duration-medium1) var(--m3-easing-standard-decelerate) var(--m3-duration-short2); }
  .sh-review-detail.is-open .sh-review-detail__panel { opacity: 1; transform: translateY(0); }
  .sh-review-detail:not(.is-open) .sh-review-detail__panel { transition: opacity var(--m3-duration-short4) var(--m3-easing-standard-accelerate), transform var(--m3-duration-short4) var(--m3-easing-standard-accelerate); }
  .sh-review-detail__head { position: relative; z-index: 1; /* 스택 순서 값 — 토큰화 대상 아님 */ }
  .sh-review-detail__close { margin-left: auto; background: none; border: none; font-size: var(--m3-body-large-size); line-height: var(--m3-body-large-line); font-weight: var(--m3-body-large-weight); letter-spacing: var(--m3-body-large-tracking); color: var(--color-text-muted); cursor: pointer; padding: var(--spacing-xs) var(--spacing-xs); font-family: inherit; }
  .sh-review-detail__close:hover { color: var(--color-text); }
  .sh-review-detail__head .date + .sh-review-detail__close { margin-left: var(--spacing-sm); }
  .sh-review-detail__body { margin: var(--spacing-md) 0 0; font-size: var(--m3-body-medium-size); line-height: var(--m3-body-medium-line); font-weight: var(--m3-body-medium-weight); letter-spacing: var(--m3-body-medium-tracking); color: var(--color-text); white-space: pre-line; position: relative; z-index: 1; /* 스택 순서 값 — 토큰화 대상 아님 */ transition: opacity var(--m3-duration-short2) var(--m3-easing-standard-decelerate); }
  .sh-review-detail__body.is-fading { opacity: 0; }

  @media (prefers-reduced-motion: reduce) {
    .sh-marquee__track { animation: none; }
    .sh-marquee { overflow-x: auto; -webkit-mask-image: none; mask-image: none; }
    .sh-review-detail { transition: none; }
    .sh-review-detail__panel { transition: none; }
    .sh-review-detail__body { transition: none; }
    .sh-sticky-bar { transition: none; }
  }

  /* ── 편지 ── */
  .sh-letter { background: var(--color-bg); border-radius: var(--radius-card); box-shadow: var(--shadow-sm); overflow: hidden; }
  /* 옛 560 분기(가로 3xl→xl 32→20px, 아래 2xl→xl 24→20px)를 clamp로 흡수.
     위쪽은 두 값이 같아(2xl) 고정, 가로·아래만 400~560px 선형 보간. */
  .sh-letter__body { padding: var(--spacing-2xl) clamp(var(--spacing-xl), -0.63rem + 7.5vw, var(--spacing-3xl)) clamp(var(--spacing-xl), 0.63rem + 2.5vw, var(--spacing-2xl)); position: relative; }
  .sh-letter__head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--spacing-lg); margin-bottom: var(--spacing-sm); }
  .sh-letter__head .eyebrow { font-family: var(--font-family); font-size: var(--m3-label-medium-size); line-height: var(--m3-label-medium-line); font-weight: var(--m3-label-medium-weight); letter-spacing: var(--m3-label-medium-tracking); text-transform: uppercase; color: var(--color-primary-text); margin: var(--spacing-sm) 0 var(--spacing-xs); }
  .sh-letter__head h2 { font-family: var(--font-family-heading); font-size: var(--m3-headline-small-size); line-height: var(--m3-headline-small-line); font-weight: var(--m3-headline-small-weight); letter-spacing: var(--m3-headline-small-tracking); color: var(--color-accent); margin: 0; }
  /* 옛 560 분기(92→74px)를 clamp로 흡수. 400~560px 선형 보간, 상한은 .sh-postmark
     기본 크기(92px)를 그대로 물려받는다(선언 없으면 컴포넌트 기본값이 적용된다). */
  .sh-letter__postmark {
    flex: none;
    transform: rotate(8deg); /* 소인 기울기 — 크기 스케일 개념 아님, 토큰화 불가 */
    width: clamp(74px, 1.81rem + 11.25vw, 92px);
    height: clamp(74px, 1.81rem + 11.25vw, 92px);
  }
  .sh-letter__postmark .pm-top, .sh-letter__postmark .pm-bottom { font-size: var(--m3-label-small-size); line-height: var(--m3-label-small-line); font-weight: var(--m3-label-small-weight); letter-spacing: var(--m3-label-small-tracking); text-transform: uppercase; }
  .sh-letter__postmark .pm-main { white-space: pre-line; text-align: center; }
  .sh-letter__intro { font-size: var(--m3-body-large-size); line-height: var(--m3-body-large-line); font-weight: var(--m3-body-large-weight); letter-spacing: var(--m3-body-large-tracking); color: var(--color-text); margin: var(--spacing-lg) 0 var(--spacing-xl); max-width: 560px; /* 텍스트 단 너비 상한 — spacing 리듬 대상 아님 */ }
  .sh-letter__promises { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--spacing-lg); }
  .sh-letter__promises li { display: flex; gap: var(--spacing-md); align-items: flex-start; }
  .sh-letter__promises .tick { flex: none; width: 24px; height: 24px; border-radius: 50%; background: var(--color-primary-light); color: var(--color-primary-text); font-size: var(--m3-label-large-size); line-height: var(--m3-label-large-line); font-weight: var(--m3-label-large-weight); letter-spacing: var(--m3-label-large-tracking); display: flex; align-items: center; justify-content: center; margin-top: var(--spacing-xs); }
  .sh-letter__promises h3 { font-family: var(--font-family-heading); font-size: var(--m3-title-medium-size); line-height: var(--m3-title-medium-line); font-weight: var(--m3-title-medium-weight); letter-spacing: var(--m3-title-medium-tracking); color: var(--color-accent); margin: 0 0 var(--spacing-xs); }
  .sh-letter__promises p { font-size: var(--m3-body-large-size); line-height: var(--m3-body-large-line); font-weight: var(--m3-body-large-weight); letter-spacing: var(--m3-body-large-tracking); color: var(--color-text-muted); margin: 0; }
  .sh-letter__ps { margin: var(--spacing-xl) 0 0; padding-top: var(--spacing-lg); font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); font-weight: var(--m3-body-small-weight); letter-spacing: var(--m3-body-small-tracking); color: var(--color-text-muted); }
  .sh-letter__sign { display: flex; align-items: center; justify-content: flex-end; gap: var(--spacing-md); margin-top: var(--spacing-lg); }
  .sh-letter__sign .sign-name { font-family: var(--font-family-heading); font-size: var(--m3-title-medium-size); line-height: var(--m3-title-medium-line); font-weight: var(--m3-title-medium-weight); letter-spacing: var(--m3-title-medium-tracking); color: var(--color-accent); }

  /* ── 카트(국가별 배송비, US-049) ── */
  /* 항목 그리드 자체는 공용 style.css .cart-item(명시적 grid-column/grid-row)을 그대로
     쓴다 — 여기서는 이 사이트 전용 요소(단가 라인·배송지 select·소계행)만 더한다. */
  /* 브랜드는 크기는 옵션과 같게 두고 색만 진하게 — 굵기·크기까지 올리면 상품명과
     경쟁해 위계가 흐려진다(설계 논의 08-13). 상품명 쪽은 반대로 base의 bold(700)를
     내려 브랜드와 같은 굵기로 맞춘다 — 구분은 크기(title-medium 상당)만으로 준다. */
  .cart-item-brand { color: var(--color-text); }
  .cart-item-name { font-weight: normal; }
  /* 단가(each)는 브랜드·옵션과 같은 정보 위계라 원래 body-small(12px)이었는데, 그러다
     보니 바로 아래 store 줄(공용 스타일 미지정 — 기본 본문 크기로 커짐)보다 작아
     보여 위계가 거꾸로였다. 가격을 title-medium으로 올리고 store를 brand·옵션과
     같은 label-medium으로 내려 "이름 > 가격 > 브랜드/옵션/스토어" 3단으로 정리한다. */
  .cart-item-price { color: var(--color-text); font-size: var(--m3-title-medium-size); line-height: var(--m3-title-medium-line); font-weight: var(--m3-title-medium-weight); letter-spacing: var(--m3-title-medium-tracking); margin-top: var(--spacing-xs); }
  .cart-item-seller { color: var(--color-text-muted); font-size: var(--m3-label-medium-size); line-height: var(--m3-label-medium-line); font-weight: var(--m3-label-medium-weight); letter-spacing: var(--m3-label-medium-tracking); margin-top: var(--spacing-xs); }
  /* 소계·합계만(Ship to·안내문구·버튼 제외) 감싸는 박스 — base는 위쪽 선(border-top)
     하나로 카트 목록 전체(Ship to·버튼 포함)와 나누는데, 그 얇은 선을 페이지 다른
     곳(그룹 제목 위 등)에서도 이미 쓰고 있어 구분 신호로 약해지는 문제(2026-08-13)를
     사각 테두리 박스로 바꿔 해소한다 — 배경은 채우지 않는다(카드 안에 카드처럼 무거워
     보이는 것을 피함). Total은 선을 더 추가하지 않고 accent 색으로만 다른 소계 행과
     구분한다. cart.html·order_summary.html이 공유하는 신규 클래스(cart-summary-card는
     그대로 loose wrapper로 남는다 — 여기서 스타일을 얹으면 Ship to·버튼까지 테두리
     안에 들어가 범위가 의도보다 커진다). */
  .cart-totals-box {
    border: var(--border-width) solid var(--color-border);
    border-radius: var(--radius-md);
    padding: var(--spacing-lg);
  }
  .cart-totals-box .cart-summary:not(.cart-summary--sub) .cart-total {
    color: var(--color-primary-text);
  }
  /* sh-ship-to-row — 카트·체크아웃 요약 공용(설계 논의 08-09). 체크아웃의
     sh-order-shipping-country는 다음 라운드에 이 클래스로 합류한다(그때까지 별도 유지). */
  .sh-ship-to-row { display: flex; align-items: center; justify-content: space-between; gap: var(--spacing-md); margin-bottom: var(--spacing-md); }
  .sh-ship-to-row label { font-size: var(--m3-label-large-size); line-height: var(--m3-label-large-line); font-weight: var(--m3-label-large-weight); letter-spacing: var(--m3-label-large-tracking); }
  .sh-ship-to-row select { max-width: 220px; }
  .cart-summary--sub { font-size: var(--m3-title-small-size); line-height: var(--m3-title-small-line); font-weight: var(--m3-title-small-weight); letter-spacing: var(--m3-title-small-tracking); color: var(--color-text-muted); }
  .cart-summary--sub .cart-summary-label { font-size: var(--m3-title-small-size); line-height: var(--m3-title-small-line); font-weight: var(--m3-title-small-weight); letter-spacing: var(--m3-title-small-tracking); }
  .sh-cart-browse-link { background: var(--color-bg); border: var(--border-width) solid var(--color-border); color: var(--color-text); }
  .sh-cart-browse-link:hover { background: var(--color-bg-subtle); }

  /* ── 체크아웃 ── */
  /* 목업(sites/santa-here/mockups/checkout.html) 기준 — 색·경계는 사이트 토큰 재사용.
     구 .sh-checkout-card 표면(배경·테두리·반경·여백)은 base .card 컴포넌트 슬롯으로
     흡수됐다(EN-052) — 이 화면은 base .card를 쓰고, 반경·여백·테두리는 아래 카드
     슬롯 대입(체크아웃 카드 값)으로 받는다. margin-bottom만 카드 본체 밖의 차이라
     변이 .card--flow로 남긴다. */
  /* 단폭은 --page-max로 넘긴다 — 헤더는 공용 컴포넌트라 페이지가 직접 덮지 않는다
     (components.css .sh-header .head-in 주석 참조). body.sh-product와 같은 방식이다. */
  body.sh-checkout {
    --page-max: var(--width-content);   /* 상품 상세(body.sh-product)와 통일 — 4열 표 수용 목적으로
                                            520→680까지 넓혔던 값이라 720에서도 표가 좁아 접히지
                                            않는다(설계 논의 08-14). spacing 리듬 대상 아님 */
    background: var(--color-bg-subtle);
    padding-bottom: var(--spacing-4xl);
  }
  .l-checkout { width: 100%; max-width: var(--page-max, var(--width-wide)); margin-left: auto; margin-right: auto; padding: 0 var(--spacing-lg); }  /* width:100% 근거는 .l-product 참조 */
  .l-checkout h1 { font-size: var(--m3-title-large-size); line-height: var(--m3-title-large-line); font-weight: var(--m3-title-large-weight); letter-spacing: var(--m3-title-large-tracking); margin: var(--spacing-2xl) var(--spacing-xs) var(--spacing-lg); /* 장식적 자간 — 스케일 대상 아님 */ }

  .card--flow { margin-bottom: var(--spacing-lg); }
  .card--flow h2 { font-size: var(--m3-title-small-size); line-height: var(--m3-title-small-line); font-weight: var(--m3-title-small-weight); letter-spacing: var(--m3-title-small-tracking); margin-bottom: var(--spacing-md); }

  /* ── 주문 요약 표 — 상품/수량/단가/금액 4열 ── */
  .sh-order-table { width: 100%; border-collapse: collapse; font-size: var(--m3-body-medium-size); line-height: var(--m3-body-medium-line); font-weight: var(--m3-body-medium-weight); letter-spacing: var(--m3-body-medium-tracking); }
  .sh-order-table th, .sh-order-table td { padding: var(--spacing-sm) 0; text-align: left; }
  .sh-order-table thead th { color: var(--color-text-muted); font-size: var(--m3-label-medium-size); line-height: var(--m3-label-medium-line); font-weight: var(--m3-label-medium-weight); letter-spacing: var(--m3-label-medium-tracking); border-bottom: var(--border-width) solid var(--color-border); padding-bottom: var(--spacing-sm); }
  .sh-order-table tbody tr:last-child td { padding-bottom: var(--spacing-md); }
  /* :not(:last-child) 조건부 셀렉터라 Jinja for 루프 안에서 hairline-bottom 클래스로
     못 옮긴다(EN-071) — 값은 .hairline-bottom과 완전히 같다(border-width solid color-border). */
  .sh-order-table tbody tr:not(:last-child) td { border-bottom: var(--border-width) solid var(--color-border); padding-bottom: var(--spacing-md); }
  /* th/td에 걸린 기본 text-align: left(요소+클래스, 0,1,1)보다 명시도가 낮아 묻히지
     않도록 th·td를 각각 명시해 맞춰 올린다(0,2,1). */
  .sh-order-table th.sh-order-table__num, .sh-order-table td.sh-order-table__num { text-align: right; white-space: nowrap; padding-left: var(--spacing-lg); }
  .sh-order-table th.sh-order-table__qty, .sh-order-table td.sh-order-table__qty { text-align: center; white-space: nowrap; padding-left: var(--spacing-lg); }
  .sh-order-table__option, .sh-order-table__seller { color: var(--color-text-muted); font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); font-weight: var(--m3-body-small-weight); letter-spacing: var(--m3-body-small-tracking); margin-top: var(--spacing-xs); }

  /* 상품 이미지 — 카트(.cart-item-image)와 같은 크기·모양을 표 첫 열 안에서 재현한다. */
  .sh-order-table__cell { display: flex; align-items: center; gap: var(--spacing-md); }
  .sh-order-table__image { width: var(--size-92); height: var(--size-92); border-radius: var(--radius-md); overflow: hidden; background: var(--color-bg-subtle); flex-shrink: 0; }
  .sh-order-table__image img { width: 100%; height: 100%; object-fit: cover; display: block; }

  /* 좁은 폭에서 숫자 열 3개(단가·수량·금액)가 각각 padding-left: var(--spacing-lg)를
     먹어 상품명 열에 남는 폭이 거의 없어진다 — 이름이 6줄로 붕괴하는 원인. 숫자 열의
     여백·글자 크기를 줄여 이름 열에 폭을 돌려준다. */
  @media (max-width: 480px) {
    .sh-order-table th.sh-order-table__num, .sh-order-table td.sh-order-table__num,
    .sh-order-table th.sh-order-table__qty, .sh-order-table td.sh-order-table__qty {
      padding-left: var(--spacing-sm);
      font-size: var(--m3-label-medium-size); line-height: var(--m3-label-medium-line); font-weight: var(--m3-label-medium-weight); letter-spacing: var(--m3-label-medium-tracking);
    }
  }

  /* ── 구매자 주문 확인(buyer_order.html) — 섹션 위계를 구분선이 아니라 head() 제목
     크기(--label 최상위 / --group 하위)로 표현한다(설계 논의 08-14, 상품 상세 페이지
     8e3694e8과 같은 패턴). .card--order는 표면이 아니라 폭·배치 변이다 — 이 화면은
     항상 `card card--order`로 함께 쓴다(buyer_order.html). 카드 표면 없이 이 규칙만
     단독으로 쓰지 않는다. ── */
  .card--order { width: 100%; max-width: var(--width-form); display: flex; flex-direction: column; gap: var(--spacing-md); }
  .sh-order-info-grid { display: grid; grid-template-columns: 120px 1fr; gap: var(--spacing-sm) var(--spacing-lg); margin: 0; }
  .sh-order-info-grid dt { color: var(--color-text-muted); }
  .sh-order-info-grid dd { margin: 0; }
  /* Items 소제목 — ID·Order status(위 dt)와 같은 굵기·크기로 맞춘다(설계 논의 08-14).
     head()의 group/label 변이는 굵게·대문자·큰 제목이라 이 위계엔 과하다 — 표 앞
     라벨은 그냥 dt와 같은 톤이어야 한다. */
  .sh-order-field-label { margin: 0; color: var(--color-text-muted); }

  /* 문의 행 — 아이콘·문구·화살표를 한 버튼에 담아 행 전체를 클릭 영역으로 넓힌다
     (설계 논의 08-14, 좁은 버튼 대신 리스트 행 패턴). 환불 문의가 비활성일 때는
     흐리게 처리하고 사유 힌트를 문구 밑에 작게 붙인다. */
  .sh-inquiry-rows { display: flex; flex-direction: column; border: var(--border-width) solid var(--color-border); border-radius: var(--radius-md); overflow: hidden; }
  .sh-inquiry-row {
    display: flex;
    align-items: center;
    gap: var(--spacing-md);
    width: 100%;
    padding: var(--spacing-md) var(--spacing-lg);
    background: none;
    border: none;
    border-bottom: var(--border-width) solid var(--color-border);
    text-align: left;
    cursor: pointer;
    color: var(--color-text);
    font-family: var(--font-family);
    font-size: var(--m3-body-medium-size); line-height: var(--m3-body-medium-line); font-weight: var(--m3-body-medium-weight); letter-spacing: var(--m3-body-medium-tracking);
  }
  .sh-inquiry-rows .sh-inquiry-row:last-child { border-bottom: none; }
  .sh-inquiry-row:hover:not(:disabled) { background: var(--color-bg-subtle); }
  .sh-inquiry-row:disabled { color: var(--color-text-muted); cursor: not-allowed; }
  .sh-inquiry-row__icon { flex-shrink: 0; font-size: var(--m3-title-medium-size); color: var(--color-primary); }
  .sh-inquiry-row:disabled .sh-inquiry-row__icon { color: var(--color-text-muted); }
  .sh-inquiry-row__text { flex: 1; }
  .sh-inquiry-row__hint { display: block; font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); color: var(--color-text-muted); margin-top: 2px; }
  .sh-inquiry-row__chevron { flex-shrink: 0; font-size: var(--m3-title-medium-size); color: var(--color-text-muted); }

  .card--flow label { display: block; font-size: var(--m3-label-large-size); line-height: var(--m3-label-large-line); font-weight: var(--m3-label-large-weight); letter-spacing: var(--m3-label-large-tracking); margin: var(--spacing-md) 0 var(--spacing-sm); }
  .card--flow label:first-of-type { margin-top: 0; }
  /* 입력 필드 표면(테두리·반경·포커스)은 components.css .input이 담당 — select도 동일 클래스를 쓴다 */
  .card--flow select.input { font-family: inherit; }
  .card--flow #checkout-places-search-slot { margin: var(--spacing-sm) 0; }
  .card--flow #checkout-places-search-slot gmp-place-autocomplete,
  .card--flow #checkout-places-search {
    width: 100%;
    display: block;
  }
  /* PlaceAutocompleteElement는 닫힌 shadow DOM이라 ::part()·CSS 커스텀 프로퍼티가 안 먹는다 —
     대신 호스트 엘리먼트에 준 일반 background-color·border·border-radius·color-scheme만
     Google이 내부로 반영한다(공식 지원 4종, font·color 등은 대상 밖). 나머지는 못 맞춘다. */
  .card--flow #checkout-places-search-slot gmp-place-autocomplete {
    background-color: var(--color-bg);
    border: var(--border-width) solid var(--color-sand);
    border-radius: var(--radius-md);
    color-scheme: light;
  }
  .card--flow #checkout-places-search-slot gmp-place-autocomplete:focus-within {
    border-color: var(--color-border-focus, var(--color-sand));
  }
  .sh-checkout-field-hint { font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); font-weight: var(--m3-body-small-weight); letter-spacing: var(--m3-body-small-tracking); color: var(--color-text-muted); margin-top: var(--spacing-sm); }

  /* 결제 버튼 표면은 components.css .btn .btn-primary가 담당 — 여기는 폭·정렬만 */
  .sh-checkout-pay { display: block; width: 100%; }
  .sh-checkout-pay small { font-size: var(--m3-label-medium-size); line-height: var(--m3-label-medium-line); font-weight: var(--m3-label-medium-weight); letter-spacing: var(--m3-label-medium-tracking); /* 스케일에 없는 중간 굵기 — 예외 */ opacity: 0.85; }

  .card--flow .sh-checkout-address { border: none; padding: 0; margin: 0; }
  .card--flow .sh-checkout-address legend { font-size: var(--m3-title-small-size); line-height: var(--m3-title-small-line); font-weight: var(--m3-title-small-weight); letter-spacing: var(--m3-title-small-tracking); padding: 0; margin-bottom: var(--spacing-xs); }

  /* 배송 국가 — 주문 요약 카드 안, 금액 표 바로 위. sh-ship-to-row(partials/_ship_to_row.html)로
     카트와 통일했다(설계 논의 08-09). */

  /* 배송지 섹션의 국가 표시 — 입력이 아니라 위 select을 비추는 읽기 전용 값이다.
     입력 칸처럼 보이면 고칠 수 있다는 오해를 주므로 테두리 없이 텍스트로 둔다. */
  .sh-checkout-address__country-label { display: block; font-size: var(--m3-label-medium-size); line-height: var(--m3-label-medium-line); font-weight: var(--m3-label-medium-weight); letter-spacing: var(--m3-label-medium-tracking); color: var(--color-text-muted); }
  .sh-checkout-address__country { display: block; font-size: var(--m3-label-large-size); line-height: var(--m3-label-large-line); font-weight: var(--m3-label-large-weight); letter-spacing: var(--m3-label-large-tracking); margin-bottom: var(--spacing-sm); }
  #paypal-error { margin-top: var(--spacing-md); }
  /* mock 결제 버튼과 PayPal 버튼 영역 사이 시각적 간격 — 기존 카드 간격보다 넉넉히 */
  #paypal-checkout-section { margin-top: var(--spacing-3xl); }

  /* ── 약관 공표 페이지(/terms/*) — 마크다운 렌더 결과(h2·p·ul·ol·table)를 감싼다 ── */
  .terms-body { display: flex; flex-direction: column; gap: var(--spacing-md); line-height: 1.7; }
  .terms-body h2 { margin: var(--spacing-lg) 0 0; font-size: 1.05rem; }
  .terms-body h2:first-child { margin-top: 0; }
  .terms-body p { margin: 0; }
  .terms-body ul, .terms-body ol { margin: 0; padding-left: var(--spacing-xl); display: flex; flex-direction: column; gap: var(--spacing-xs); }
  .terms-body table { border-collapse: collapse; width: 100%; font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); font-weight: var(--m3-body-small-weight); letter-spacing: var(--m3-body-small-tracking); }
  .terms-body th, .terms-body td { border: var(--border-width) solid var(--color-border); padding: var(--spacing-sm); text-align: left; }

  /* ── 체크아웃 동의 블록(checkout_consent.html) — mockups/checkout-consent-v3.html 확정안 (EN-045) ── */
  .sh-consent-all {
    border: var(--border-width) solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-bg-subtle);
    padding: var(--spacing-md) var(--spacing-lg);
    margin-bottom: var(--spacing-xl);
  }
  .sh-consent-all .sh-consent-head label { font-size: var(--m3-title-small-size); line-height: var(--m3-title-small-line); font-weight: var(--m3-title-small-weight); letter-spacing: var(--m3-title-small-tracking); }
  .sh-consent-all .sh-consent-summary { margin-top: var(--spacing-xs); }

  .sh-consent-group {
    border-left: 3px solid var(--color-accent);
    padding-left: var(--spacing-lg);
    margin-bottom: var(--spacing-2xl);
  }
  .sh-consent-group:last-of-type { margin-bottom: 0; }
  .sh-consent-group--privacy { border-left-color: var(--color-tan); }

  /* consent 컨텍스트의 --group 오버라이드 — 색은 order 쪽(--color-text)과 달리
     원래부터 --color-accent였다(대문자·자간만 통일 대상, 색은 그대로 보존). 여백도
     order는 flex gap이 담당해 0이지만 consent는 일반 흐름이라 자체 여백이 필요하다. */

  .sh-consent-lead {
    font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); font-weight: var(--m3-body-small-weight); letter-spacing: var(--m3-body-small-tracking); color: var(--color-text-muted);
    margin: 0 0 var(--spacing-md);
  }

  .sh-consent-item { padding: var(--spacing-md) 0; }
  .sh-consent-item:first-of-type { border-top: none; padding-top: var(--spacing-xs); }
  .sh-consent-item:last-of-type { padding-bottom: 0; }
  .sh-consent-item--optional { border-top-width: 2px; border-top-style: solid; margin-top: var(--spacing-sm); }

  .sh-consent-head { display: flex; align-items: flex-start; gap: var(--spacing-sm); }
  .sh-consent-head input[type="checkbox"] { margin-top: 3px; width: 18px; height: 18px; accent-color: var(--color-primary); flex: none; }
  .sh-consent-head label { font-size: var(--m3-label-large-size); line-height: var(--m3-label-large-line); font-weight: var(--m3-label-large-weight); letter-spacing: var(--m3-label-large-tracking); margin: 0; cursor: pointer; flex: 1; }

  .sh-consent-req { font-size: var(--m3-label-small-size); line-height: var(--m3-label-small-line); font-weight: var(--m3-label-small-weight); letter-spacing: var(--m3-label-small-tracking); color: var(--color-primary-text); margin-left: var(--spacing-xs); white-space: nowrap; }
  .sh-consent-opt { font-size: var(--m3-label-small-size); line-height: var(--m3-label-small-line); font-weight: var(--m3-label-small-weight); letter-spacing: var(--m3-label-small-tracking); color: var(--color-text-muted); margin-left: var(--spacing-xs); white-space: nowrap; }

  .sh-consent-summary {
    font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); font-weight: var(--m3-body-small-weight); letter-spacing: var(--m3-body-small-tracking); color: var(--color-text-muted); 
    margin: var(--spacing-xs) 0 0 26px;
  }
  .sh-consent-toggle {
    background: none; border: none; padding: 0; margin: var(--spacing-xs) 0 0 26px;
    font: inherit; font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); font-weight: var(--m3-body-small-weight); letter-spacing: var(--m3-body-small-tracking); 
    color: var(--color-accent); text-decoration: underline; cursor: pointer;
  }

  .sh-consent-detail {
    margin: var(--spacing-md) 0 0 26px; padding: var(--spacing-md);
    background: var(--color-bg-subtle); border-radius: var(--radius-sm);
    font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); font-weight: var(--m3-body-small-weight); letter-spacing: var(--m3-body-small-tracking); 
  }
  .sh-consent-detail dl { display: grid; grid-template-columns: 62px 1fr; gap: var(--spacing-xs) var(--spacing-md); margin: 0; }
  .sh-consent-detail dt { color: var(--color-text-muted); font-size: var(--m3-label-medium-size); line-height: var(--m3-label-medium-line); font-weight: var(--m3-label-medium-weight); letter-spacing: var(--m3-label-medium-tracking); }
  .sh-consent-detail dd { margin: 0; }
  .sh-consent-detail .full-link { display: inline-block; margin-top: var(--spacing-md); font-size: var(--m3-label-medium-size); line-height: var(--m3-label-medium-line); font-weight: var(--m3-label-medium-weight); letter-spacing: var(--m3-label-medium-tracking); color: var(--color-primary-text); text-decoration: underline; }

  .sh-consent-links { margin: var(--spacing-sm) 0 0 26px; font-size: var(--m3-body-small-size); line-height: var(--m3-body-small-line); font-weight: var(--m3-body-small-weight); letter-spacing: var(--m3-body-small-tracking); }
  .sh-consent-links a { color: var(--color-primary-text); text-decoration: underline; }
  .sh-consent-links span { color: var(--color-text-muted); margin: 0 var(--spacing-xs); }

  .sh-consent-footnote {
    margin: var(--spacing-xl) 0 0; padding-top: var(--spacing-md);
    font-size: var(--m3-label-small-size); line-height: var(--m3-label-small-line); font-weight: var(--m3-label-small-weight); letter-spacing: var(--m3-label-small-tracking); color: var(--color-text-muted); 
  }
  /* ── EN-069 상세 3모듈 체계 ── */

  /* 모듈 1: 인상 정보 — 배경 이미지 위 오버레이 카피, 16:9. preview.html .hero 대응. */
  .sh-detail-impression { position: relative; }
  .sh-detail-impression__bg { width: 100%; display: block; aspect-ratio: 16 / 9; object-fit: cover; }
  .sh-detail-impression__copy {
    position: absolute; left: 0; right: 0; bottom: 0;
    padding: var(--spacing-lg) var(--spacing-md);
    background: linear-gradient(to top, var(--color-scrim), transparent);
    color: var(--color-text-inverse);
  }
  .sh-detail-impression__copy--no-bg {
    position: static; background: none; color: var(--color-text);
    padding: var(--spacing-lg) 0;
  }
  .sh-detail-impression__headline {
    font-size: var(--m3-headline-small-size); line-height: var(--m3-headline-small-line);
    font-weight: var(--m3-headline-small-weight); letter-spacing: var(--m3-headline-small-tracking);
    margin: var(--spacing-xs) 0 2px;
  }
  .sh-detail-impression__sub {
    font-size: var(--m3-body-large-size); line-height: var(--m3-body-large-line);
    font-weight: var(--m3-body-large-weight); letter-spacing: var(--m3-body-large-tracking);
    opacity: .85;
  }

  /* 그룹 헤드라인 — 모듈 안 각 그룹의 소제목. */
  .sh-detail-group-head {
    font-size: var(--m3-title-medium-size); line-height: var(--m3-title-medium-line);
    font-weight: var(--m3-title-medium-weight); letter-spacing: var(--m3-title-medium-tracking);
    margin: var(--spacing-lg) 0 var(--spacing-sm);
  }

  /* media-text-card — 구 claim+concept 통합(EN-069 후속, 08-21). block_type 이름을
     구성 방식(아이콘/이미지+텍스트 카드) 기준으로 통일, layout으로 배치를 명시한다.
     design-system/candidates/card-media-text.html 승격. */
  .sh-card-media-text { margin-bottom: var(--spacing-sm); }

  /* stacked — 헤더(아이콘+이름)+본문 세로 나열. 구 claim. */
  .sh-card-media-text--stacked { display: flex; flex-direction: column; gap: var(--spacing-xs); }
  .sh-card-media-text__header { display: flex; align-items: center; gap: var(--spacing-sm); }
  .sh-card-media-text__header-icon { width: 26px; height: 26px; color: var(--color-primary); flex-shrink: 0; }

  .sh-card-media-text__name {
    font-size: var(--m3-title-medium-size); line-height: var(--m3-title-medium-line);
    font-weight: var(--m3-title-medium-weight); letter-spacing: var(--m3-title-medium-tracking);
    color: var(--color-text);
  }
  .sh-card-media-text__desc {
    font-size: var(--m3-body-large-size); line-height: var(--m3-body-large-line);
    font-weight: var(--m3-body-large-weight); letter-spacing: var(--m3-body-large-tracking);
    color: var(--color-text); margin: 0;
  }
  .sh-card-media-text__fact {
    font-size: var(--m3-label-large-size); line-height: var(--m3-label-large-line);
    font-weight: var(--m3-label-large-weight); letter-spacing: var(--m3-label-large-tracking);
    color: var(--color-primary); margin: 0;
  }
  .sh-card-media-text__source {
    display: flex; align-items: flex-start; gap: 4px;
    font-size: var(--m3-label-small-size); line-height: var(--m3-label-small-line);
    font-weight: var(--m3-label-small-weight); letter-spacing: var(--m3-label-small-tracking);
    color: var(--color-text-muted); margin: 2px 0 0;
  }
  .sh-card-media-text__source .sh-icon-info {
    width: 12px; height: 12px; flex-shrink: 0; color: currentColor;
    margin-top: calc((var(--m3-label-small-line) - 12px) / 2);
  }

  .sh-detail-footnote {
    display: flex; align-items: flex-start; gap: 4px;
    font-size: var(--m3-label-small-size); line-height: var(--m3-label-small-line);
    font-weight: var(--m3-label-small-weight); letter-spacing: var(--m3-label-small-tracking);
    color: var(--color-text-muted); margin-top: var(--spacing-xs);
  }
  .sh-detail-footnote .sh-icon-info {
    width: 12px; height: 12px; flex-shrink: 0;
    margin-top: calc((var(--m3-label-small-line) - 12px) / 2);
  }

  /* side-by-side — 이미지/아이콘 블록+텍스트. 구 concept. */
  .sh-card-media-text--side-by-side { padding: 0; overflow: hidden; }
  .sh-card-media-text__media { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; }
  .sh-card-media-text--side-by-side .sh-card-media-text__body { padding: var(--spacing-md); display: flex; flex-direction: column; gap: var(--spacing-xs); }
  .sh-card-media-text__terms {
    font-size: var(--m3-label-small-size); line-height: var(--m3-label-small-line);
    font-weight: var(--m3-label-small-weight); letter-spacing: var(--m3-label-small-tracking);
    color: var(--color-text-muted);
    padding-left: var(--spacing-sm); border-left: var(--border-width) solid var(--color-border);
    margin-top: var(--spacing-xs);
  }
  .sh-card-media-text__terms div { margin-bottom: var(--spacing-xs); }

  /* 넓은 화면: 이미지 좌측(고정폭)+텍스트 우측(가로) — 이 카드 하나의 판단,
     다른 컴포넌트와 공유하지 않는다. side-by-side는 이제 image 케이스만 남았다
     (아이콘형은 stacked로 통합, EN-069 후속). */
  @media (min-width: 600px) {
    .sh-card-media-text--side-by-side { display: flex; align-items: stretch; }
    .sh-card-media-text--side-by-side .sh-card-media-text__media { width: 200px; aspect-ratio: 4 / 3; flex-shrink: 0; }
    .sh-card-media-text--side-by-side .sh-card-media-text__body { flex: 1; justify-content: center; }
  }

  /* 성분 아이콘 — 역할별 SVG가 확정되기 전 공통 placeholder. 효능의 별 아이콘(sh-icon-star)과
     같은 header-icon 박스(26px)에 맞춰 100%로 채운다(EN-069 후속, key ingredient stacked 통합). */
  .sh-ingredient-icon {
    width: 100%;
    height: 100%;
    fill: none;
    stroke: var(--color-primary);
    stroke-width: 3;
    stroke-linecap: round;
    stroke-linejoin: round;
  }

  /* list-card(구 checklist-text→bullet-list-card, 08-23 재개명 — 마커가 사각형 고정이
     아니라 item마다 icon(기본 bullet, warning 등)으로 바뀔 수 있어 구성 방식 기준으로
     재명명) — 항목 전체를 하나의 .card(다른
     섹션의 media-text-card와 같은 컴포넌트, base .card)로 감싼다(EN-069 후속). 카드
     배경은 .card가 이미 --card-bg(기본 --color-bg)로 처리하므로 항목별 배경은 두지
     않고 문장+마커 간격만 둔다.
     공용 .check-item(✓ 텍스트 프리픽스, components.css)은 이 섹션 전용이 아니라 사이트
     전역에서 쓰여(가입약관·note-list) 여기서 모양을 바꾸면 그쪽까지 번진다 — 그래서
     이 섹션은 .check-item을 쓰지 않고 자체 마커(작은 채움 사각형)를 둔다. */
  .sh-detail-list-card {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-sm);
  }
  .sh-detail-list-text {
    display: flex;
    align-items: flex-start;
    gap: var(--spacing-sm);
    font-size: var(--m3-body-large-size); line-height: var(--m3-body-large-line);
    font-weight: var(--m3-body-large-weight); letter-spacing: var(--m3-body-large-tracking);
    margin: 0;
  }
  .sh-detail-list-text__mark {
    flex-shrink: 0;
    width: 8px;
    height: 8px;
    margin-top: calc((var(--m3-body-large-line) - 8px) / 2); /* 본문 첫 줄 중앙에 마커 맞춤 */
    background: var(--color-primary-text);
    border-radius: 2px;
  }
  /* icon 변형(예: warning) — 사각 마커 대신 SVG 아이콘을 그대로 놓는다. */
  .sh-detail-list-text--icon .sh-detail-list-text__mark {
    width: 18px;
    height: 18px;
    background: none;
    border-radius: 0;
    margin-top: calc((var(--m3-body-large-line) - 18px) / 2);
  }
  .sh-detail-list-text__mark--warning { color: var(--color-error); }
  .sh-detail-list-text__mark svg { width: 100%; height: 100%; }

  /* badge-list(구 verification) — 알약형 뱃지 나열, 기존 .badge 재사용. */
  .sh-detail-badge-list { display: flex; flex-wrap: wrap; gap: var(--spacing-xs); }

  /* qa-accordion(구 qa-text) — details/summary 기반 접힘 목록.
     legacy/patterns/accordion.html .sh-accordion 패턴을 QA 전용 이름으로 옮겨 온다.
     질문 사이는 구분선(원래 방식)으로 나누고, 질문(summary)은 흰 배경으로 도드라지게
     한다. 답변(body)은 흰색이 아닌 --color-kraft(페이지 아이보리보다 어두운 톤 —
     --color-bg-subtle는 이 사이트에서 아이보리와 같은 값이라 못 쓴다, 위 site.theme
     참조)로 별도 표시하고, 위쪽 패딩을 넉넉히 줘 질문 바로 아래 바짝 붙어 보이지
     않게 한다(08-22). */
  .sh-detail-qa-accordion {
    border-top: var(--border-width) solid var(--color-border);
  }
  .sh-detail-qa-accordion details {
    border-bottom: var(--border-width) solid var(--color-border);
  }
  .sh-detail-qa-accordion summary {
    list-style: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--spacing-lg) var(--spacing-md);
    background: var(--color-bg);
    font-size: var(--m3-title-medium-size); line-height: var(--m3-title-medium-line);
    font-weight: var(--m3-title-medium-weight); letter-spacing: var(--m3-title-medium-tracking);
    color: var(--color-accent);
    cursor: pointer;
  }
  .sh-detail-qa-accordion summary::-webkit-details-marker { display: none; }
  .sh-detail-qa-accordion summary::after {
    content: "+";
    font-size: var(--m3-title-medium-size); line-height: var(--m3-title-medium-line);
    font-weight: var(--m3-title-medium-weight); letter-spacing: var(--m3-title-medium-tracking);
    color: var(--color-text-muted);
    transition: transform var(--m3-duration-short3);
  }
  .sh-detail-qa-accordion details[open] summary::after {
    transform: rotate(45deg);
    color: var(--color-primary-text);
  }
  .sh-detail-qa-accordion__body {
    padding: var(--spacing-lg) var(--spacing-md);
    background: var(--color-kraft);
    font-size: var(--m3-body-medium-size); line-height: var(--m3-body-medium-line);
    font-weight: var(--m3-body-medium-weight); letter-spacing: var(--m3-body-medium-tracking);
    color: var(--color-text-muted);
  }

  .sh-icon-star { width: 100%; height: 100%; }
  .sh-icon-info { width: 16px; height: 16px; color: currentColor; }
}
