/*
 * items.css — 아이템 시스템 시각 스타일 (아이템 블록 셀 + 발동 시각 피드백)
 *
 * 모든 디자인 값은 tokens.css 의 토큰(var(--token))만 참조한다. 원시 색/서체/
 * 간격/모서리/그림자/테두리/모션 값을 직접 쓰지 않는다(하드코딩 금지). 글리프
 * 크기·aspect·gradient 기하·clip·transform 거리 등은 시각 스타일이 아니라 반응형/
 * 구현 설정값이므로 토큰으로 만들지 않고 여기서 직접 쓴다.
 *
 * 이 파일은 기존 보드/HUD 스타일(game.css)을 덮어쓰지 않는다. 아이템 블록 셀은
 * 기존 조각 셀(.cell--filled) 위에 마커를 덧입히는 가산 클래스(.cell--item +
 * .cell--item--{id})로, 아이템이 실려 온 칸(치환 블록)을 일반 셀과 구별하고 그 칸이
 * 품은 아이템의 아이콘을 드러낸다. 아이콘 글리프는 카탈로그 단일 출처에서 CSS 변수
 * (--item-glyph-{id})로 주입되어 content 로 소비된다(이모지 하드코딩 없음).
 *
 * 아이템 상호작용 모델 전면 개정(치환 블록·라인 제거 즉시 발동·자동 대상)으로
 * 획득 인벤토리 HUD·사용 가능 슬롯·아이템 확정 카드·대상 지목 컨트롤이 폐기됐다
 * (item-inventory(+usable)·item-card·target-selector — item-effect-model §0). 이
 * 파일에서도 해당 스타일(.item-inventory* · .item-slot* · .item-card* ·
 * .target-selector*)을 제거했다. 발동 시각 피드백(effect-overlay)과 아이템 블록 셀
 * 마커(.cell--item)는 즉시 발동 모델에서도 유지된다. 상단 통지 배너(effect-banner)는
 * 보드 위 캡션과 단일 통합 메시지(.effect-overlay__message)로 합쳐지며 폐기됐다 —
 * 이 파일의 관련 스타일(.effect-banner*)도 제거했다.
 *
 * 카툰 렌더링 원칙(팝 컬러 · 잉크 아웃라인 · 하드 그림자 · 둥근 형태)을 소비한다.
 * 디밍/스크림은 raw opacity 가 아니라 color-ink 파생 국소 이펙트(모달·game-overlay·
 * opponent-board 스크림과 동일 계층)로 표현한다 — 신규 opacity 토큰을 만들지 않는다.
 *
 * Design Spec:
 *   - components/effect-overlay/base.md — 발동 시 영향받은 보드 위에서 잠깐 재생되는 카툰 이펙트 버스트 + 단어별 '박힘' 통합 메시지.
 *   - components/tetris-board/item-cell.md — 아이템 블록 셀 Extension.
 *   - components/item-inventory/base.md §아이템 카탈로그 — 아이템 정체성(id·이름·아이콘·대표색) 단일 출처(존치).
 *   - components/item-card/base.md §아이템 아이콘 스타일 규칙 — 아이콘 렌더 규칙 단일 출처(존치).
 *   - principles/cartoon-rendering.md §아이템 블록 셀 — 셀 마커의 카툰 렌더링 규칙.
 *   - principles/item-effect-model.md — 상호작용 모델(§0)·대상 표현(§1)·효과 서술(§3, effect.summary) 단일 출처.
 *   - token-groups/motion/base.md — 이펙트 오버레이·배너의 등장/재생/퇴장 지속시간·이징.
 */

/* ── 아이템 블록 셀 (tetris-board / item-cell Extension) ───────────
   기존 조각 셀(.cell--filled + .cell--{type}) 위에 얹는 가산 마커. 조각색은
   그대로 유지하되(떨어지는 조각의 일부임), 밝은 광택 + 그 칸이 품은 아이템의 아이콘
   (잉크 아웃라인)으로 '아이템을 품은 칸'을 일반 셀과 구별하고 어떤 아이템인지 드러낸다.
   광택은 color-text-inverse 파생 국소 이펙트(조각 bevel·락 flash 와 동일 계층)이며
   별도 토큰을 만들지 않는다. 아이콘 글리프는 카탈로그 단일 출처에서 주입되고(정체성
   재정의 없음), 글리프 렌더링의 미세 크기는 렌더링 grain 소관 구현 설정값이다. */
.cell--item {
  position: relative;
  /* 밝기 펄스(shimmer): 아이템을 품은 칸을 잉크(검정)↔흰빛 사이로 반복해 밝기 왕복
     시켜(자연스러운 반짝임) 일반 셀 사이에서 눈에 띄게 만든다. 왕복하는 두 밝기는
     color-ink↔color-text-inverse 토큰, 왕복 주기·곡선은 duration-flicker·easing-gentle
     토큰만 소비한다(원시 색/시간 하드코딩 없음).
       성능: 밝기 왕복을 배경색(background-color, 합성 불가 페인트 속성)으로 매 프레임
     다시 칠하는 대신, 잉크(어두운 끝)를 셀의 정적 배경으로 두고 흰빛(밝은 끝)을
     ::before 흰빛 시트의 opacity 왕복으로 표현한다. opacity 는 합성(compositor) 스레드
     에서 처리되므로, 자기 메인 + 최대 3 상대 미니 보드의 아이템 셀에서 상시 무한
     반복돼도 매 프레임 셀 리페인트를 일으키지 않는다(같은 룩, 낮은 렌더 비용). 이
     잉크 배경 위로 .cell--filled 의 조각색 bevel(background-image)이 남아 조각 정체성도
     은은히 유지되고, 아이콘(::after) 마커는 그 위로 겹쳐 그대로 읽힌다. */
  background-color: var(--color-ink);
}

/* 밝기 펄스 시트 겸 광택(gloss): 좌상단이 더 밝은 흰빛 시트. 이 시트의 opacity 를
   0(잉크)↔1(흰빛)로 매끄럽게 왕복(easing-gentle)시켜 스냅 없는 자연스러운 반짝임을
   만든다. 페인트 유발 속성(background-color)이 아니라 opacity 만 애니메이트하므로
   무한 반복 반짝임이 매 프레임 리페인트를 일으키지 않는다(합성 스레드 처리). 흰빛은
   color-text-inverse 토큰 파생 국소 이펙트다(어떤 조각색 위에서도 '반짝임'으로 읽힌다). */
.cell--item::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(
    circle at 32% 28%,
    var(--color-text-inverse),
    color-mix(in srgb, var(--color-text-inverse) 86%, transparent) 92%
  );
  opacity: 0;
  animation: item-cell-shimmer var(--duration-flicker) var(--easing-gentle)
    infinite;
  pointer-events: none;
}

/* 부드러운 왕복 밝기 펄스: 흰빛 시트를 잉크(0)에서 흰빛(1)까지 홀드/스냅 없이 매끄럽게
   오간다. 양 끝(0%/100% 잉크=시트 투명, 50% 흰빛=시트 불투명)에서 easing-gentle 로
   완만히 머물며 '자연스럽게 반짝이는' 숨쉬는 인상을 준다. background-color 가 아니라
   opacity 만 왕복한다(합성 친화). */
@keyframes item-cell-shimmer {
  0% { opacity: 0; }
  50% { opacity: 1; }
  100% { opacity: 0; }
}

/* 아이콘 마커: 셀 중앙에 **그 칸이 품은 아이템의 아이콘**(💣⚡🛡️🧪)을 얹어 어떤
   아이템인지 드러낸다. 7색 어떤 조각색 채움 위에서도 또렷하도록 잉크 하드 그림자
   (color-ink shadow-pop-sm)로 코믹 아웃라인을 준다(item-card §아이콘 스타일 규칙의
   "컬러 채움 위 코믹 아웃라인"). 크기는 셀에 비례하는 구현 설정값(em).
   글리프 값은 카탈로그 단일 출처에서 --item-glyph-{id} 로 런타임 주입되며(CSS 에
   이모지를 중복 하드코딩하지 않는다), 이 base 규칙의 ★ 는 주입 전/미로딩 시 폴백이다. */
.cell--item::after {
  content: "★";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.72em;
  line-height: 1;
  color: var(--color-text-inverse);
  text-shadow: var(--shadow-pop-sm);
  pointer-events: none;
}

/* 아이템 id 별 아이콘 글리프. content 는 카탈로그 단일 출처에서 주입된
   --item-glyph-{id}(예: "🧪") 를 소비하고, 미주입 시 base 의 ★ 로 폴백한다.
   시각 처리(코믹 아웃라인·광택·크기)는 base 규칙에서 상속한다. base 와 동일
   특정도이므로 소스 순서상 뒤에 와서 content 를 덮는다. */
.cell--item--bomb::after { content: var(--item-glyph-bomb, "★"); }
.cell--item--lightning::after { content: var(--item-glyph-lightning, "★"); }
.cell--item--shield::after { content: var(--item-glyph-shield, "★"); }
.cell--item--potion::after { content: var(--item-glyph-potion, "★"); }
.cell--item--magnet::after { content: var(--item-glyph-magnet, "★"); }
.cell--item--star::after { content: var(--item-glyph-star, "★"); }
.cell--item--meteor::after { content: var(--item-glyph-meteor, "★"); }
.cell--item--broom::after { content: var(--item-glyph-broom, "★"); }
.cell--item--fog::after { content: var(--item-glyph-fog, "★"); }

/* ── Effect Overlay (effect-overlay) ──────────────────────────────
   아이템 사용이 서버에서 확정될 때(item:effect), 효과가 적용된 보드(자신 메인
   tetris-board · 상대 미니 opponent-board) 위에서 잠깐 재생되는 카툰 이펙트
   피드백. 스크림·패널·버튼 없이 보드 위를 잠깐 덮었다 사라지므로 조작을 막지
   않는다(pointer-events:none). game-overlay(일시정지·게임오버 상주 표시)와 구분된다.
   색·글로우·글리프·캡션은 전부 토큰/토큰 파생 국소 이펙트로 표현하고 아이템별 전용
   토큰을 만들지 않는다. 모션(등장·전체 재생·퇴장)은 motion 토큰을 소비한다.
   position/z-index/pointer-events·키프레임 형태·글리프 미세 크기는 구현 설정값. */
.effect-overlay {
  position: absolute;
  inset: 0;
  z-index: 6; /* 셀·게임오버 스크림 위에 잠깐 얹힌다(구현 설정값) */
  display: flex;
  flex-direction: column;
  align-items: center;
  /* 통합 메시지·글리프를 보드 상단에 배치한다(요청: '상단에 위치'). 상단 정렬 +
     상단 패딩을 키워 확대된 메시지가 보드 위쪽에서 또렷이 읽히게 한다. */
  justify-content: flex-start;
  gap: var(--space-xs);
  padding: var(--space-lg) var(--space-2xs) var(--space-2xs);
  border-radius: var(--radius-md);
  overflow: hidden;
  pointer-events: none; /* 조작을 막지 않는다(구현 설정값) */
  /* 전체 수명 envelope: 등장→유지→퇴장 3위상을 --duration-effect-life 안에서 끝낸다.
     위상 경계는 effect-overlay-life 키프레임(등장 끝 41%·유지 끝 88%)이 소유하고, 퇴장
     구간 페이드는 easing-exit(빠르게 가속)로 마무리한다. 총 수명은 motion 토큰 단일 출처. */
  animation: effect-overlay-life var(--duration-effect-life) var(--easing-exit) both;
}

/* Effect Burst: 아이템 대표색으로 물드는 글로우 펄스. 배경 면(radial-gradient 채움)
   은 두지 않는다 — 전체화면 버스트가 화면을 색으로 덮어 시야를 가리는 것을 피하고,
   대표색은 **테두리(inset 링)와 글로우에만** 얹어 '무언가 강하게 터졌다'를 테두리·
   광채로만 읽히게 한다(톤다운). 대표색은 --item-color(=color-item-{id}) 주입, 링·글로우
   는 그 파생 국소 이펙트(전역 이펙트 색 토큰 없음). 강조 광채는 강화된 shadow-glow-
   strong 재사용, 대표색 글로우/inset 링은 반경·알파·두께를 키운다(inset 링은 border-
   width-chunky 토큰). glow 펄스는 통합 이펙트 전체 수명(--duration-effect-life, easing-
   standard)에 걸쳐 크게 부풀었다 안착해 유지 위상 동안 광채를 남긴다. 글로우 반경(px)은
   시각 스타일이 아니라 구현 설정값. */
.effect-overlay__burst {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow:
    inset 0 0 0 var(--border-width-chunky)
      color-mix(in srgb, var(--item-color) 68%, transparent),
    0 0 34px color-mix(in srgb, var(--item-color) 82%, transparent),
    var(--shadow-glow-strong);
  animation: effect-overlay-burst-buff var(--duration-effect-life) var(--easing-standard) both;
}

/* 방해 효과(bomb/lightning): 충격 성격의 스케일 펄스. 이로운 효과(potion/shield)는
   밝게 차오르는 등장(위 기본 = buff). 색·글리프·모션은 전부 토큰/파생 이펙트.
   이 base 규칙은 미니(상대) 보드처럼 보드 일부만 덮는 오버레이용으로 강한 스케일
   펄스(effect-overlay-burst-attack)를 쓴다 — 회전·좌우 흔들림은 시야 흔들림을 피해
   두지 않고 스케일/페이드만 유지한다. */
.effect-overlay--attack .effect-overlay__burst {
  animation-name: effect-overlay-burst-attack;
}

/* 전체 화면(메인/self) 방해 버스트: 미니 보드가 없는 경로에서 attack 오버레이는 내
   메인 보드 = 실질적으로 화면 전체를 덮으므로, 화면 전체를 회전·좌우로 흔들면 시야가
   과하게 흔들린다. 전체 화면 오버레이(비-미니)에서는 회전·좌우 흔들림 없이 스케일
   펄스만 준다(부풀었다 잦아듦). 미니 오버레이가 남는 경로가 있어도 base 의 흔들림은
   그대로 유지되고, buff(등장 펄스)도 영향받지 않도록 attack + 비-미니로만 한정한다.
   시간·이징은 base 버스트가 소비하는 motion 토큰(duration-effect-life·easing-standard)을
   그대로 상속하고 키프레임 형태(스케일 량)만 바꾼다(신규 모션 토큰 없음). */
.effect-overlay--attack:not(.effect-overlay--mini) .effect-overlay__burst {
  animation-name: effect-overlay-burst-pulse;
}

/* Item Glyph: 어떤 아이템이 작용했는지 알리는 이모지. 대표색·이펙트 위에 얹히므로
   color-ink 하드 그림자(shadow-pop-sm) 코믹 아웃라인으로 또렷하게(item-card 아이콘
   규칙). 버스트 스케일에 맞춰 크게. 등장은 duration-base + easing-pop(오버슈트 팝). */
.effect-overlay__glyph {
  position: relative;
  /* 이미지(글리프)를 더 크게: 메인 오버레이는 2xl→3xl 로 한 단계 키운다(요청). */
  font-size: var(--text-size-3xl);
  line-height: 1;
  text-shadow: var(--shadow-pop-sm);
  animation: effect-overlay-pop var(--duration-base) var(--easing-pop) both;
}

/* Effect Caption: 무엇이 일어났는지 한 줄(item-effect-model §3 effect.summary 소비).
   어둡거나 컬러풀한 이펙트 위 판독을 위해 color-text-inverse + color-ink 하드 그림자.
   미니 보드에서는 렌더러가 생략한다(좁음). */
.effect-overlay__caption {
  position: relative;
  max-width: 94%;
  text-align: center;
  font-size: var(--text-size-sm);
  font-weight: var(--font-weight-bold);
  line-height: var(--line-height-snug);
  color: var(--color-text-inverse);
  text-shadow: var(--shadow-pop-sm);
  animation: effect-overlay-pop var(--duration-base) var(--easing-pop) both;
}

/* Effect Message: 보드 캡션과 상단 통지 배너를 합친 **단일 통합 메시지**(주어·아이템·
   서술). 문장을 **글자(음절) 단위**로 분해해 각 글자를 .effect-overlay__char 로 감싸
   하나씩 '박히듯' 순차 등장시킨다. 어절(공백)은 시각적 묶음(줄바꿈 경계)으로만
   유지되고 스탬프 대상이 아니다 — 어절은 비-애니메이션 래퍼 .effect-overlay__word 로
   감싸 어절 중간에서 줄이 끊기지 않게 한다. 컨테이너는 전체 문장을 한 번에 통지하는
   상태 영역(role="status" + aria-label 전체 문장)이고, 분할 조각은 개별 낭독을 막도록
   aria-hidden 처리한다(조립·aria 배선은 소비 grain 소관). 보드 상단에 배치·확대하며
   짧은 구조화 문장이라 크게 키워도 무리가 없다 — 이전 축소 크기(--text-size-xs)에서
   여러 단계 키운 --text-size-lg 를 소비해 상단에서 또렷이 읽히게 한다(요청: 텍스트 확대).
   어둡거나 컬러풀한 버스트 위 판독을 위해 color-text-inverse + color-ink 하드 그림자.
   어절 간 가로 간격·줄바꿈 허용·baseline 정렬은 구현 설정값(em/flex).
   격투 게임식 강조를 위해 이전 --text-size-lg 에서 한 단계 더 키운 --text-size-xl 를
   소비해 어절이 크게 박히는 타격감을 키운다(짧은 구조화 문장이라 확대해도 무리 없음).
   톤 글로우의 단일 진입점 --message-glow 를 여기서 기본값(골드 계열)으로 선언하고,
   방향 modifier(발동=outgoing·피격=incoming)가 이를 갈래친다 — 어절이 이 변수를 발광
   색으로 소비한다. */
.effect-overlay__message {
  position: relative;
  max-width: 94%;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 0 0.3em;
  text-align: center;
  font-family: var(--font-family-display);
  font-weight: var(--font-weight-black);
  font-size: var(--text-size-xl);
  line-height: var(--line-height-snug);
  color: var(--color-text-inverse);
  /* 톤 글로우 기본값(방향 modifier 미부여 방어 폴백): 하이라이트 골드. */
  --message-glow: var(--color-secondary);
}

/* 어절 래퍼: 글자 조각을 어절 단위로 묶는 **비-애니메이션 레이아웃 래퍼**다(스탬프
   대상 아님). 어절 중간에서 줄이 끊기지 않도록 글자를 한 줄로 묶고(inline-flex), 어절
   자체는 메시지 flex 의 wrap 단위로만 흐른다(어절 간 gap = 시각적 공백 유지). 스타일·
   등장 모션은 전부 안쪽 글자 조각(.effect-overlay__char)이 소유한다. */
.effect-overlay__word {
  display: inline-flex;
  align-items: baseline;
}

/* 글자 조각: 문장을 글자(음절) 단위로 쪼갠 스탬프 단위. 하나씩 '박히듯' 순차 등장한다.
   등장 애니메이션(effect-overlay-char-stamp)은 scale-down 오버슈트(easing-pop)로 크게
   나타났다 제자리에 꽂힌다. 타이포 강조: 코믹 외곽선(-webkit-text-stroke = color-ink
   잉크, paint-order 로 채움이 위)으로 글자 윤곽을 또렷이 하고, 하드 그림자
   (shadow-pop-sm)에 톤 색 발광(--message-glow 파생)을 덧대 격투 게임식 강조·가독성을
   함께 얹는다. 등장 지속은 스탬프 전용 모션 토큰 --duration-word-stamp(motion 단일
   출처)를 소비한다(글리프·캡션 공용 --duration-base 와 분리 — 스탬프는 개념상 별개
   모션). 계단식 등장은 각 글자의 animation-delay 를 **글자 순번(--char-index, 조립 grain
   이 주입)** 에서 파생한다 — 순번 × 유효 계단 스텝. 유효 스텝은 --char-stagger(글자 수 N
   에 맞춰 조립 grain 이 (enter−stamp)/(N−1) 로 캡해 컨테이너에 주입)를 소비하되, 미주입
   방어 경로에서는 nominal --duration-stagger-step(motion 토큰 단일 출처)로 폴백한다.
   both fill 로 자기 delay 이전엔 숨고 이후 최종 상태를 유지한다. 외곽선 두께·발광 반경
   (px)은 시각 스타일이 아니라 구현 설정값(값을 바꿔도 '외곽선·발광' 역할 불변). */
.effect-overlay__char {
  display: inline-block;
  color: var(--color-text-inverse);
  -webkit-text-stroke: 2px var(--color-ink);
  paint-order: stroke fill;
  text-shadow:
    var(--shadow-pop-sm),
    0 0 12px color-mix(in srgb, var(--message-glow, var(--color-secondary)) 80%, transparent);
  animation: effect-overlay-char-stamp var(--duration-word-stamp) var(--easing-pop) both;
  animation-delay: calc(var(--char-index, 0) * var(--char-stagger, var(--duration-stagger-step)));
}

/* 방향 톤 구분(발동/피격): 발동자 화면(내가 발동)은 힘있는 골드 톤, 피격(당함)은 경보
   레드 톤으로 어절 발광 색만 갈래친다(채움=color-text-inverse·외곽선=color-ink 은 양쪽
   공통이라 가독성 유지 — 톤은 발광 색으로만 읽힌다). game-client.js 가 오버레이에
   effect-overlay--outgoing / --incoming modifier 를 주입하고, 어절이 --message-glow 를
   발광 색으로 소비한다. 색은 기존 색 토큰(color-secondary/​color-danger)을 인용하며
   전용 이펙트 색 토큰을 신설하지 않는다. */
.effect-overlay--outgoing .effect-overlay__message { --message-glow: var(--color-secondary); }
.effect-overlay--incoming .effect-overlay__message { --message-glow: var(--color-danger); }

/* 미니 보드(opponent-board) 위: 글리프를 한 단계 작게(footprint 에 비례 — 구현 설정값).
   메인 확대(3xl)에 맞춰 미니도 lg→xl 로 한 단계 키운다. */
.effect-overlay--mini .effect-overlay__glyph {
  font-size: var(--text-size-xl);
}

/* 아이템 id → 대표색 매핑(color-item-{id}). 슬롯 매핑과 동일 패턴. */
.effect-overlay--bomb { --item-color: var(--color-item-bomb); }
.effect-overlay--lightning { --item-color: var(--color-item-lightning); }
.effect-overlay--shield { --item-color: var(--color-item-shield); }
.effect-overlay--potion { --item-color: var(--color-item-potion); }
.effect-overlay--magnet { --item-color: var(--color-item-magnet); }
.effect-overlay--star { --item-color: var(--color-item-star); }
.effect-overlay--meteor { --item-color: var(--color-item-meteor); }
.effect-overlay--broom { --item-color: var(--color-item-broom); }
.effect-overlay--fog { --item-color: var(--color-item-fog); }

/* 통합 이펙트 수명 페이드 envelope. 위상 경계 %는 각 위상/전체 수명 비율의 투영이다
   (motion 토큰 단일 출처 — 키프레임 offset 은 토큰화 불가):
     0% → 41%   등장(--duration-effect-enter 700ms / 1700ms ≈ 41%): 0 → 불투명
     41% → 88%  유지(--duration-effect-hold 800ms): 불투명 유지(완성 메시지 판독)
     88% → 100% 퇴장(--duration-effect-exit 200ms / 1700ms ≈ 12%): 불투명 → 0(easing-exit) */
@keyframes effect-overlay-life {
  0% { opacity: 0; }
  41% { opacity: 1; }
  88% { opacity: 1; }
  100% { opacity: 0; }
}

/* 등장 팝: 더 작게 웅크렸다 튀어나오도록 시작 스케일을 낮춰 오버슈트(easing-pop)
   임팩트를 키운다. 글리프·캡션·통지 칩이 공유한다. */
@keyframes effect-overlay-pop {
  0% { transform: scale(0.25); opacity: 0; }
  100% { transform: scale(1); opacity: 1; }
}

/* 글자 '박힘' 등장: 더 크게 나타나(scale 2.5) 제자리 크기(1)로 내려꽂히며, easing-pop
   오버슈트가 살짝 지나쳤다 되돌아와 '쾅 박히는' 느낌을 준다(scale-down 오버슈트). 시작
   스케일 오버슈트 2.5 로 글자가 크게 나타났다 안착해 글자별 등장이 또렷이 구별되고
   타격감이 커진다. 텍스트 그림자는 0 오프셋·무발광에서 하드 그림자(shadow-pop-sm)+톤
   발광(--message-glow 파생)으로 튀어나와 도장처럼 찍힌다 — 최종 상태(100%)는 base 규칙과
   같은 외곽선·발광을 유지해(both fill) 유지 위상 동안 강조가 남는다. 각 조각의 시작
   시점만 animation-delay 로 어긋내면 글자가 하나씩 순차로 박힌다. */
@keyframes effect-overlay-char-stamp {
  0% {
    transform: scale(2.5);
    opacity: 0;
    text-shadow: 0 0 0 var(--color-ink);
  }
  100% {
    transform: scale(1);
    opacity: 1;
    text-shadow:
      var(--shadow-pop-sm),
      0 0 12px color-mix(in srgb, var(--message-glow, var(--color-secondary)) 80%, transparent);
  }
}

/* 이로운 효과: 밝게 차오르는 등장. 더 작게 웅크렸다 크게 부풀어 힘있게 안착한다. */
@keyframes effect-overlay-burst-buff {
  0% { transform: translateY(14%) scale(0.55); }
  42% { transform: translateY(-2%) scale(1.2); }
  100% { transform: translateY(-4%) scale(1); }
}

/* 방해 효과: 강한 충격 스케일 펄스. 스케일 폭을 키워 세게 읽히게 하되, 회전·좌우
   흔들림은 두지 않는다(시야 흔들림 회피 — 스케일/페이드만). 페이드는 컨테이너
   effect-overlay-life 가 담당한다. 보드 일부만 덮는 미니(상대) 오버레이 전용 —
   전체 화면 오버레이는 아래 펄스를 쓴다. */
@keyframes effect-overlay-burst-attack {
  0% { transform: scale(0.45); }
  16% { transform: scale(1.28); }
  32% { transform: scale(1.06); }
  100% { transform: scale(1); }
}

/* 전체 화면 방해 버스트 펄스: 회전·좌우 흔들림 없이 스케일만 크게 부풀렸다(오버슈트)
   안착시켜 '무언가 강하게 터졌다'를 화면 전체를 흔들지 않고도 세게 읽히게 한다. attack
   과 같은 스케일 폭(≈1.28 오버슈트)을 유지해 임팩트 강도는 보존하되, 화면 전체를 도는
   회전·좌우 이동만 제거한 펄스(부풀었다 잦아듦)다. */
@keyframes effect-overlay-burst-pulse {
  0% { transform: scale(0.45); }
  40% { transform: scale(1.28); }
  100% { transform: scale(1); }
}

/* 상단 통지 배너(effect-banner)는 보드 위 캡션과 단일 통합 메시지
   (.effect-overlay__message)로 합쳐지며 폐기됐다 — 관련 스타일(.effect-banner-layer·
   .effect-banner·__actor·__object·__predicate·__item·__item-icon·id 매핑·
   effect-banner-life 키프레임)을 제거했다. 통합 메시지의 톤·구조는
   token-groups/item-effect-message-tone, 등장 모션은 effect-overlay-char-stamp 참조. */

/* ── Effect Combo (effect-combo) ──────────────────────────────────
   2줄 이상 동시 제거 또는 짧은 간격의 연속 제거(콤보≥2) 시, 기존 라인 제거 연출(락
   flash·디졸브·낙하 settle)에 더해 자기 메인 보드 프레임 위에 잠깐 얹히는 강화 피드백.
   콤보 카운트 팝업 + 보드 전체를 덮는 더 강한 플래시로 '다중/연속 제거'의 한 방을
   강조한다(화면 흔들림은 보드 프레임 .board--shake modifier 가 담당 — game.css). 1줄
   단발 제거에는 마운트되지 않는다(기존 연출만). 스크림·조작 차단 없음(pointer-events
   none — effect-overlay 와 같은 결). 색·타이포·그림자는 토큰/파생 국소 이펙트, 모션
   (팝업 수명·플래시)은 motion 토큰을 소비한다(신규 타이밍 토큰은 흔들림 --duration-shake
   뿐, 팝업 수명은 통합 이펙트 수명 envelope 재사용). position/z-index/pointer-events·
   플래시 알파·팝업 스케일 오버슈트는 구현 설정값. */
.effect-combo {
  position: absolute;
  inset: 0;
  z-index: 7; /* 아이템 버스트(effect-overlay z6) 위에 콤보 카운트를 얹는다(구현 설정값) */
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none; /* 조작을 막지 않는다(구현 설정값) */
}

/* 더 강한 플래시: 다중/연속 제거 순간 보드 프레임 전체를 흰빛으로 짧게 번쩍인다 —
   1줄 단발의 셀 단위 락 flash(cell-flash)보다 강하게, 보드 면 전체로. 흰빛은 색 토큰
   (color-text-inverse)만 쓰고, 등장 위상(--duration-effect-enter) 동안 빠르게 잦아든다
   (easing-exit). 플래시 시작 알파는 구현 설정값(값을 바꿔도 '강한 플래시' 역할 불변). */
.effect-combo__flash {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background-color: var(--color-text-inverse);
  opacity: 0;
  animation: effect-combo-flash var(--duration-effect-enter) var(--easing-exit) both;
}

/* 콤보 카운트 팝업: 몇 콤보/몇 줄인지 크게 '팡' 튀어나오는 카운트 텍스트. display 서체·
   black 무게·text-size-3xl 로 크게, 코믹 잉크 외곽선(-webkit-text-stroke=color-ink,
   paint-order 로 채움이 위) + 하이라이트 골드 발광(color-secondary 파생)으로 타격감을
   준다(effect-overlay 글자 강조와 동일 결). 등장은 easing-pop 오버슈트, 수명은 통합
   이펙트 수명 envelope(--duration-effect-life)를 재사용해 등장→유지→퇴장한다(신규 타이밍
   토큰 없음). 외곽선 두께·발광 반경(px)·시작 스케일 오버슈트는 구현 설정값. */
.effect-combo__count {
  position: relative;
  font-family: var(--font-family-display);
  font-weight: var(--font-weight-black);
  font-size: var(--text-size-3xl);
  line-height: 1;
  text-align: center;
  color: var(--color-text-inverse);
  -webkit-text-stroke: 2px var(--color-ink);
  paint-order: stroke fill;
  text-shadow:
    var(--shadow-pop-sm),
    0 0 14px color-mix(in srgb, var(--color-secondary) 82%, transparent);
  animation: effect-combo-pop var(--duration-effect-life) var(--easing-pop) both;
}

/* 강한 플래시: 흰빛 시작 알파(0.66)에서 0 으로 빠르게 잦아든다(색은 토큰만, 알파 기하는
   구현 설정값). */
@keyframes effect-combo-flash {
  0% { opacity: 0.66; }
  100% { opacity: 0; }
}

/* 콤보 카운트 수명 envelope: 등장(팝) → 유지 → 퇴장을 통합 이펙트 수명(--duration-effect-
   life 1700ms) 비율에 맞춘다(effect-overlay-life 와 같은 위상 경계 41%/88% 근방). 시작
   스케일에서 오버슈트(easing-pop)로 크게 튀어나왔다 제자리에 안착하고, 유지 위상 동안
   머문 뒤 살짝 커지며 페이드 아웃한다. 스케일 기하는 구현 설정값이다. */
@keyframes effect-combo-pop {
  0% { transform: scale(0.4); opacity: 0; }
  16% { transform: scale(1.16); opacity: 1; }
  24% { transform: scale(1); opacity: 1; }
  88% { transform: scale(1); opacity: 1; }
  100% { transform: scale(1.06); opacity: 0; }
}

/* ── Effect Projectile (effect-projectile) ────────────────────────
   상대 대상 아이템(single_opponent·all_opponents) 발동 시, 아이템 정체성 아이콘 하나가
   화면을 가로질러 한 보드에서 다른 보드로 날아가는 투사체. 방향은 뷰어 관점(발동자: 내
   보드→상대 미니 / 피격자: 상대 미니→내 보드)이고, 시작·도착 좌표는 보드 DOM 실측
   (getBoundingClientRect)이라 토큰이 아니라 런타임 주입(--travel-from-*/--travel-to-*)이다.
   self 대상은 발사하지 않는다(게이트·좌표 산출·발사는 발사 계층 소관 — 이 파일은 표현만).
   화면 전체를 가로지르므로 position:fixed 로 뷰포트에 고정하고, 어떤 오버레이·셀도 막지
   않도록 pointer-events:none, 전체화면 이펙트 오버레이 위로 뜨도록 높은 z-index 를 준다
   (position/z-index/pointer-events 는 구현 설정값). 대표색·아이콘은 신설하지 않고
   --item-color(=color-item-{id})·--item-glyph-{id} 를 그대로 실어 나른다. 이동 시간·곡선은
   투사체 이동 모션 토큰(--duration-travel·--easing-travel) 단일 출처를 소비한다(하드코딩 없음). */
.effect-projectile {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 60; /* 전체화면 이펙트 오버레이·셀 위로 화면을 가로질러 난다(구현 설정값) */
  pointer-events: none; /* 조작·오버레이를 막지 않는다(구현 설정값) */
  display: flex;
  align-items: center;
  justify-content: center;
  /* 글리프 확대: 화면을 가로지르는 투사체가 또렷이 읽히도록 xl→2xl 로 한 단계 키운다
     (effect-overlay 글리프 확대와 같은 결). 크기 값은 typography 토큰 단일 출처. */
  font-size: var(--text-size-2xl);
  line-height: 1;
  /* 대표색 글로우 + 코믹 잉크 아웃라인으로 어떤 아이템이 날아가는지 또렷이 읽히게 한다.
     발광 강화: 근접(선명)·원거리(확산) 2겹 --item-color 글로우를 얹어 잔상 트레일 아우라와
     함께 '빛나며 날아간다'를 세게 읽히게 한다. 글로우는 --item-color 파생 국소 이펙트
     (effect-overlay 버스트와 동일 파생), 아웃라인은 shadow-pop-sm(하드 오프셋). 글로우
     반경(px)·블러 세기는 구현 설정값(값을 바꿔도 '발광'이라는 역할은 불변). */
  text-shadow:
    var(--shadow-pop-sm),
    0 0 14px color-mix(in srgb, var(--item-color) 88%, transparent),
    0 0 30px color-mix(in srgb, var(--item-color) 52%, transparent);
  /* 시작 보드→도착 보드 직선 이동. 시작·도착 translate 값(--travel-from-*/--travel-to-*)은
     보드 실측에서 발사 계층이 주입한다(미주입 시 0 폴백). 발사 보드에서 작게 튀어나가
     도착 보드에서 제 크기로 내려꽂히도록 easing-travel 비대칭 이즈에 스케일을 얹는다.
     both fill 로 도착 지점(버스트 맞물림)에 머문 뒤 발사 계층이 노드를 정리한다. */
  animation: effect-projectile-travel var(--duration-travel) var(--easing-travel) both;
}

/* 잔상 트레일 아우라: 글리프 뒤(z-index -1)에 --item-color 로 물든 부드러운 방사형 광채
   블롭을 깔아, 빠르게 나는 글리프가 끌고 가는 발광 꼬리(코멧 헤드)로 읽히게 한다. 방향
   벡터는 발사 계층 런타임 주입이라 CSS 는 방향 불문 아우라로 두고(어느 방향 발사든 동작),
   이동과 같은 수명·곡선(--duration-travel·--easing-travel)으로 부풀었다 도착 시 잦아드는
   펄스를 준다 — 이동 시간·곡선은 motion 토큰 단일 출처만 소비(하드코딩 없음). 아우라 크기
   (em)·블러(px)·색 알파는 구현 설정값 성격의 기하/광채 파생이다. reduced-motion 에서는
   부모 .effect-projectile 가 display:none 이라 이 아우라도 함께 사라진다(별도 처리 불필요). */
.effect-projectile::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 1.7em;
  height: 1.7em;
  border-radius: 50%;
  background: radial-gradient(
    circle,
    color-mix(in srgb, var(--item-color) 72%, transparent),
    color-mix(in srgb, var(--item-color) 34%, transparent) 46%,
    transparent 72%
  );
  filter: blur(3px);
  z-index: -1; /* 글리프(::after) 뒤로 깔린다(구현 설정값) */
  animation: effect-projectile-trail var(--duration-travel) var(--easing-travel) both;
}

/* 착탄 임팩트(플래시 + 충격파 링): 투사체가 도착 보드에 '착탄'하는 순간의 임팩트 연출.
   발사 계층이 글리프와 함께 실어 보내는 자식 노드(.effect-projectile__impact)로, 부모
   투사체의 도착 좌표(both fill 로 --travel-to-* 지점에 머문 상태) 중앙에 정렬돼 착탄점에서
   터진다. 흰빛 코어 플래시(color-text-inverse 파생) + 대표색 충격파 링(--item-color 파생
   box-shadow 링·글로우 + 강화 광채 shadow-glow-strong)이 작게 시작해 바깥으로 퍼지며 사라진다.
   **타이밍 맞물림**: animation-delay = --duration-travel(도착 시점) 후 --duration-impact 동안
   재생 → 도착(480ms) 직후 시작해 등장 위상(--duration-effect-enter 700ms) 안(480+180=660<700)
   에서 마무리되어 대상 보드 전체화면 버스트의 등장 위상과 시간적으로 겹쳐 '착탄=버스트'로
   읽힌다. 시간·지연·곡선은 전부 motion 토큰(--duration-travel·--duration-impact·easing-
   standard) 단일 출처 소비(하드코딩 없음). both fill 로 지연 이전엔 숨고(플래시 전 무표시)
   재생 후 최종(투명) 상태를 유지한 뒤 발사 계층이 부모째 타이머로 정리한다. 링/코어 기하
   (em)·블러(px)는 구현 설정값 성격의 광채 파생이다. reduced-motion 에서는 부모
   .effect-projectile 가 display:none 이라 이 임팩트도 함께 사라진다(별도 처리 불필요). */
.effect-projectile__impact {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 1.1em;
  height: 1.1em;
  border-radius: 50%;
  /* 흰빛 코어 플래시(가운데) → 대표색으로 번지는 방사형(착탄 섬광). */
  background: radial-gradient(
    circle,
    color-mix(in srgb, var(--color-text-inverse) 92%, transparent),
    color-mix(in srgb, var(--item-color) 66%, transparent) 52%,
    transparent 74%
  );
  /* 대표색 충격파 링(box-shadow ring) + 대표색 글로우 + 강화 광채(버스트와 같은 파생). */
  box-shadow:
    0 0 0 var(--border-width-thick)
      color-mix(in srgb, var(--item-color) 82%, transparent),
    0 0 24px color-mix(in srgb, var(--item-color) 60%, transparent),
    var(--shadow-glow-strong);
  opacity: 0;
  animation: effect-projectile-impact var(--duration-impact) var(--easing-standard)
    var(--duration-travel) both;
}

/* 투사체 아이콘 글리프: 셀·오버레이와 동일하게 카탈로그 단일 출처 --item-glyph-{id} 를
   content 로 소비하고, 미주입 시 ★ 로 폴백한다(이모지 하드코딩 없음). 시각 처리는 base 의
   글로우·아웃라인을 상속한다. */
.effect-projectile--bomb::after { content: var(--item-glyph-bomb, "★"); }
.effect-projectile--lightning::after { content: var(--item-glyph-lightning, "★"); }
.effect-projectile--shield::after { content: var(--item-glyph-shield, "★"); }
.effect-projectile--potion::after { content: var(--item-glyph-potion, "★"); }
.effect-projectile--magnet::after { content: var(--item-glyph-magnet, "★"); }
.effect-projectile--star::after { content: var(--item-glyph-star, "★"); }
.effect-projectile--meteor::after { content: var(--item-glyph-meteor, "★"); }
.effect-projectile--broom::after { content: var(--item-glyph-broom, "★"); }
.effect-projectile--fog::after { content: var(--item-glyph-fog, "★"); }

/* 아이템 id → 대표색 매핑(color-item-{id}). effect-overlay 슬롯 매핑과 동일 패턴 — 투사체가
   실어 나르는 대표색을 --item-color 로 주입해 글로우가 아이템별로 물든다. */
.effect-projectile--bomb { --item-color: var(--color-item-bomb); }
.effect-projectile--lightning { --item-color: var(--color-item-lightning); }
.effect-projectile--shield { --item-color: var(--color-item-shield); }
.effect-projectile--potion { --item-color: var(--color-item-potion); }
.effect-projectile--magnet { --item-color: var(--color-item-magnet); }
.effect-projectile--star { --item-color: var(--color-item-star); }
.effect-projectile--meteor { --item-color: var(--color-item-meteor); }
.effect-projectile--broom { --item-color: var(--color-item-broom); }
.effect-projectile--fog { --item-color: var(--color-item-fog); }

/* 투사체 이동: 시작점(--travel-from-*)에서 작게 튀어나가 도착점(--travel-to-*)까지 날며
   점점 커져 제 크기로 내려꽂힌다. 글리프 확대 강화: 발사 스케일을 0.72→0.5 로 낮춰 '작게
   튀어나가 크게 도착'하는 확대 폭을 키우고(비행 중 글리프가 자라며 다가온다), 살짝 기운
   각(-10deg)에서 0 으로 펴지며 날아 활공감을 준다. 도착 스케일은 1 로 유지해 착탄 임팩트
   (grain-3, --duration-impact)가 이어받을 여지를 남긴다. translate 값은 보드 실측 기반
   런타임 주입(좌표는 토큰 아님), 이동 시간·곡선은 motion 토큰(--duration-travel·
   --easing-travel) 단일 출처가 소유한다(스케일·회전량은 컴포넌트 소유 키프레임 값). */
@keyframes effect-projectile-travel {
  from {
    transform: translate(var(--travel-from-x, 0), var(--travel-from-y, 0))
      scale(0.5) rotate(-10deg);
  }
  to {
    transform: translate(var(--travel-to-x, 0), var(--travel-to-y, 0))
      scale(1) rotate(0deg);
  }
}

/* 잔상 트레일 펄스: 발광 아우라를 작게(0.4) 시작해 비행 중간 크게 부풀렸다(1.15) 도착
   시점에 잦아들며(0.7) 페이드아웃한다 — 빠른 글리프가 끌고 가는 코멧 발광 꼬리로 읽히고,
   도착 시 아우라가 사그라들며 전체화면 버스트(등장 위상)로 광채가 자연스레 넘어간다.
   translate(-50%,-50%)로 항상 글리프 중앙에 정렬(방향 불문). 부모(effect-projectile-travel)
   와 동일한 --duration-travel·--easing-travel 로 재생돼 이동과 위상이 맞물린다. */
@keyframes effect-projectile-trail {
  0% {
    transform: translate(-50%, -50%) scale(0.4);
    opacity: 0.2;
  }
  50% {
    transform: translate(-50%, -50%) scale(1.15);
    opacity: 0.6;
  }
  100% {
    transform: translate(-50%, -50%) scale(0.7);
    opacity: 0;
  }
}

/* 착탄 임팩트 재생: 흰빛 코어 플래시 + 대표색 충격파 링이 작게(0.2) 착탄점에서 터져
   바깥으로 크게 퍼지며(2.6) 페이드아웃한다. 초반(0→28%)에 빠르게 밝아졌다 이후 흩어지며
   사라져 '쾅 착탄'의 섬광→충격파 순서를 준다. translate(-50%,-50%)로 항상 부모(도착 지점)
   중앙에 정렬(방향 불문 — 폴백 boardCenter 경로 포함). --easing-standard 로 퍼짐이 감속하며
   잦아들어 대상 보드 버스트 등장 위상으로 광채가 자연스레 넘어간다. */
@keyframes effect-projectile-impact {
  0% {
    transform: translate(-50%, -50%) scale(0.2);
    opacity: 0;
  }
  28% {
    transform: translate(-50%, -50%) scale(1);
    opacity: 1;
  }
  100% {
    transform: translate(-50%, -50%) scale(2.6);
    opacity: 0;
  }
}

/* 모션 최소화 선호 사용자: 통합 이펙트의 등장(팝)·유지·퇴장·글자 stagger 애니메이션을
   모두 끈다. 이펙트 표시 자체와 정리는 유지된다 — 오버레이 노드는 animationend 가 아니라
   JS 타이머(전체 수명 --duration-effect-life = EFFECT_LIFE_MS)로 정리되므로, 애니메이션이
   꺼져도 새 수명 안에서 반드시 사라진다(누수 없음, 큐 잠금 없음). 이펙트 재생은 낙하 진행에
   관여하지 않으므로 게임 타이밍은 모션 유무와 무관하게 불변. */
@media (prefers-reduced-motion: reduce) {
  .effect-overlay,
  .effect-overlay__burst,
  .effect-overlay__glyph,
  .effect-overlay__caption,
  .effect-overlay__message,
  .effect-overlay__char {
    animation: none;
  }
  /* 즉시 최종 상태 보장: 등장 애니메이션(팝·박힘)이 꺼져도 글자/글리프가 stagger·스케일
     팝 없이 곧바로 제자리·불투명으로 나타나 통합 메시지 내용 전달을 유지한다. 애니메이션
     fill 최종 상태에 기대지 않고 명시적으로 고정한다(하드 그림자 텍스트섀도는 각 base
     규칙이 정적으로 유지 → '도장처럼 박힌' 최종 룩 보존). 버스트는 배경 면이 없어(box-
     shadow 링+글로우만) 애니메이션이 꺼져도 배경 없는 테두리(inset 링) 룩 그대로다. */
  .effect-overlay__glyph,
  .effect-overlay__char {
    transform: none;
    opacity: 1;
  }
  /* 다중/연속 제거 강화 연출: 콤보 팝업의 팝·수명 애니메이션과 강한 플래시를 끈다.
     팝업 텍스트는 즉시 제자리·불투명으로 고정해 정보(콤보 수/줄 수)를 그대로 전달하고
     (컨테이너 role=status 낭독도 유지), 과한 모션·광과민을 유발하는 강한 플래시는 정적
     투명(생략)으로 둔다. 노드는 JS 타이머(EFFECT_LIFE_MS)로 정리되므로 누수 없음. */
  .effect-combo,
  .effect-combo__count,
  .effect-combo__flash {
    animation: none;
  }
  .effect-combo__count {
    transform: none;
    opacity: 1;
  }
  .effect-combo__flash {
    opacity: 0;
  }
  /* 아이템 블록 셀 밝기 펄스(셔머)도 끈다. 흰빛 시트(::before) 애니메이션이 꺼지면
     opacity 0(잉크)으로 정지하고, 아이콘 마커(::after)는 그대로 남아 아이템 셀은
     계속 구별된다. */
  .cell--item::before {
    animation: none;
  }
  /* 화면 간 투사체는 모션 최소화 선호 시 표시하지 않는다(내용은 통합 메시지·버스트로
     전달). 발사 계층이 생성 자체를 생략하지만, 생성 경로가 남더라도 표시되지 않도록
     여기서도 감춘다 — 투사체는 순수 이동 연출이라 정지 표시가 무의미하므로 노출을 끈다. */
  .effect-projectile {
    display: none;
  }
}
