/* styles/app/11-play-modes.css — experimental play modes (settings → "플레이 방식").
   ISOLATED on purpose: deleting this file + its <link> reverts all mode visuals. Default ("기본") uses none. */

/* ═══ "소거" (owner's clear mode): a correctly-solved region is REMOVED. The tile goes fully blank
      (transparent — shows the lead field behind, exactly like an off-board void), and edges.js re-bounds
      the surviving blocks with crisp lead lines. So the block truly disappears and the board reads as
      "shrunk", not a dim leftover grid. ═══ */
.cell.cleared {
  background: transparent !important;
  box-shadow: none !important;
  /* 2026-07-14 bugfix: #game .cell's own border (styles/app/01-base-tokens.css, added for the border-only
     tile isolation redesign) was never excluded here the way .void already is — a cleared cell kept its
     dark border after everything else vanished, leaving a hollow "afterimage" box where the tile used to be. */
  border: none !important;
}
.cell.cleared::before, .cell.cleared::after { content: none !important; }   /* no glass facet / gem gloss on a removed tile */

/* one-shot release the instant the region clears: the block PUNCHES bright & big, then bursts away.
   2026-07-31 강화 (오너 "정답일 때 타일이 터지는 액션을 더 쥬시하게"): 세 가지를 올렸다 —
   ① **아주 짧은 움츠림(10%, scale .9)**: 터지기 직전 한 프레임 움츠렸다 튀어나온다. 애니메이션의 예비
      동작(anticipation)으로, 최대 크기를 키우지 않고도 타격감을 키우는 가장 싼 방법이다.
   ② 정점 크기 1.46 → **1.66**, 글로우 34/15px → 46/19px (같은 색 유지 — 축은 크기·밝기만 올린다).
   ③ 길이 .52s → **.56s**, 꼬리를 scale 1.12 → 1.26으로 더 밀어내 "사라졌다"가 아니라 "흩어졌다"로 읽히게.
   흰색 전면 플래시는 그대로 없다(연출 헌법 §8) — 밝아지는 건 타일 한 칸뿐이다. */
.cell.clearFlash {
  z-index: 5;
  animation: clearBloom .56s cubic-bezier(.2,.85,.3,1) both !important;
}
@keyframes clearBloom {
  0%   { transform: scale(1);    opacity: 1; background: rgba(255,251,232,1);  box-shadow: 0 0 0 0 rgba(255,230,150,0);   filter: brightness(1.5); }
  10%  { transform: scale(.9);   opacity: 1; background: rgba(255,249,226,1);  box-shadow: 0 0 10px 2px rgba(255,230,150,.6); filter: brightness(1.85); }
  34%  { transform: scale(1.66); opacity: 1; background: rgba(255,247,214,.82); box-shadow: 0 0 46px 19px rgba(255,228,150,.98); filter: brightness(2.25); }
  100% { transform: scale(1.26); opacity: 0; background: transparent;         box-shadow: 0 0 0 0 rgba(255,228,150,0);   filter: brightness(1); }
}

html[data-motion="reduced"] .cell.clearFlash { animation: none !important; }

/* gather clear finale: "light echo" is not a normal solve replay. The solved picture returns once as a
   translucent memory, then dissolves upward into starlight, so the harvest never feels undone. */
#board.lightEcho {
  filter:
    drop-shadow(0 0 10px rgba(255,235,170,.55))
    drop-shadow(0 18px 34px rgba(90,56,20,.28))
    brightness(1.06);
}
#board.lightEcho .leadLayer { opacity: .46; filter: drop-shadow(0 0 4px rgba(255,228,150,.35)); }
#board.lightEcho .cell.unfilled {
  background: transparent !important;
  box-shadow: none !important;
  color: transparent !important;
}
#board.lightEcho .cell.unfilled::before,
#board.lightEcho .cell.unfilled::after { content: none !important; }
#board.lightEcho .cell.s1,
#board.lightEcho .cell.s2,
#board.lightEcho .cell.s3,
#board.lightEcho .cell.s4,
#board.lightEcho .cell.s5,
#board.lightEcho .cell.sX {
  opacity: .74;
  color: transparent !important;
  text-shadow: none !important;
  filter: saturate(.72) brightness(1.55) drop-shadow(0 0 10px rgba(255,232,166,.6)) !important;
  box-shadow:
    inset 0 2px 0 rgba(255,255,255,.5),
    inset 0 -6px 12px rgba(0,0,0,.12),
    0 0 18px rgba(255,228,150,.42) !important;
}
#board.lightEcho .cell .clue { opacity: 0 !important; }
#board.lightEcho .cell.s1::before,
#board.lightEcho .cell.s2::before,
#board.lightEcho .cell.s3::before,
#board.lightEcho .cell.s4::before,
#board.lightEcho .cell.s5::before,
#board.lightEcho .cell.sX::before {
  mix-blend-mode: screen;
  opacity: .44;
}
/* ⚠️ 이 글로우는 `inset: -18%`로 **일부러 칸보다 크다.** 2026-08-06에 게임판 칸의 `overflow:hidden`을
   풀었으므로(타일이 커지는 연출의 상한이었다), 이 모드에서만 **원래대로 묶어 둔다** — 빛 거두기는
   이번 변경의 대상이 아니고, 새어 나간 모습을 검증하지 않았다. 나중에 이 모드를 손볼 때 함께 판단할 것. */
#board.lightEcho .cell { overflow: hidden; }
#board.lightEcho .cell.s1::after,
#board.lightEcho .cell.s2::after,
#board.lightEcho .cell.s3::after,
#board.lightEcho .cell.s4::after,
#board.lightEcho .cell.s5::after,
#board.lightEcho .cell.sX::after {
  content: "";
  position: absolute;
  inset: -18%;
  pointer-events: none;
  background:
    radial-gradient(circle at 35% 25%, rgba(255,255,255,.8), transparent 28%),
    radial-gradient(circle at 50% 54%, rgba(255,238,180,.34), transparent 68%);
  opacity: .72;
}
#board.lightEchoHold .cell.s1,
#board.lightEchoHold .cell.s2,
#board.lightEchoHold .cell.s3,
#board.lightEchoHold .cell.s4,
#board.lightEchoHold .cell.s5,
#board.lightEchoHold .cell.sX {
  animation: gatherEchoBreath 1.05s ease-in-out infinite alternate;
}
@keyframes gatherEchoBreath {
  from { opacity: .58; filter: saturate(.66) brightness(1.42) drop-shadow(0 0 8px rgba(255,232,166,.46)); }
  to   { opacity: .82; filter: saturate(.78) brightness(1.7)  drop-shadow(0 0 15px rgba(255,232,166,.72)); }
}
#board.lightEchoDissolve .cell.s1,
#board.lightEchoDissolve .cell.s2,
#board.lightEchoDissolve .cell.s3,
#board.lightEchoDissolve .cell.s4,
#board.lightEchoDissolve .cell.s5,
#board.lightEchoDissolve .cell.sX {
  animation: gatherEchoDissolve .82s cubic-bezier(.24,.55,.34,1) forwards !important;
}
@keyframes gatherEchoDissolve {
  0%   { opacity: .82; transform: translateY(0) scale(1); }
  35%  { opacity: .92; transform: translateY(-5px) scale(1.04); filter: brightness(1.8) saturate(.75) drop-shadow(0 0 18px rgba(255,232,166,.8)); }
  100% { opacity: 0; transform: translateY(-22px) scale(.72); filter: brightness(1.25) saturate(.4) drop-shadow(0 0 0 rgba(255,232,166,0)); }
}
.gatherEchoMote {
  position: fixed;
  width: 8px;
  height: 8px;
  margin: -4px 0 0 -4px;
  border-radius: 50%;
  pointer-events: none;
  z-index: 62;
  background: radial-gradient(circle, rgba(255,255,255,.98), var(--echo-color, #ffe07a) 56%, rgba(255,255,255,0) 82%);
  box-shadow: 0 0 10px var(--echo-color, #ffe07a);
}
html[data-motion="reduced"] #board.lightEchoHold .cell,
html[data-motion="reduced"] #board.lightEchoDissolve .cell { animation: none !important; }

/* ═══ 「빛 거두기」 gather run — 황혼의 수확 의식 (Model 2). Mood BOOST (오너 승인 2026-07-05, Quiet-Canvas
      제약 해제): a real DUSK — deeper (~−14%), amber-warm, with a soft canvas VIGNETTE so the board + jewels
      hold the light. jewel tokens (--s1..--s5), buttons, HUD stay put (they read relatively BRIGHTER). Both
      the vignette and the twilight are BODY-background layers → behind all content, so tiles/HUD are untouched.
      Paired with the audio mood-shift (audio.js setGatherAmbience) + ambient motes + Sha 입회 below. ═══ */
body.gatherRun[data-screen="game"] {
  background:
    radial-gradient(120% 82% at 50% 42%, rgba(0,0,0,0) 40%, rgba(40,25,6,0.30) 100%),               /* board spotlight / vignette */
    radial-gradient(120% 95% at 50% 6%, #e0d2ac 0%, #ccbb8e 48%, #b09a68 100%) !important;           /* deep amber dusk */
}
/* entry ceremony — "황혼이 내린다": a warm veil DESCENDS from the top over ~1.2s, then clears (one-shot) */
body.gatherRun[data-screen="game"]::after {
  content: ""; position: fixed; inset: 0; pointer-events: none; z-index: 43;
  background: linear-gradient(180deg, rgba(255,214,150,0.44) 0%, rgba(255,224,170,0.18) 34%, transparent 60%);
  animation: gatherDescend 1.2s cubic-bezier(.25,.7,.3,1) 1 both;
}
@keyframes gatherDescend {
  0%   { opacity: 0; transform: translateY(-30%); }
  30%  { opacity: 1; }
  100% { opacity: 0; transform: translateY(6%); }
}
html[data-motion="reduced"] body.gatherRun[data-screen="game"]::after { content: none; }   /* reduced-motion: no descend; the dusk state still applies */

/* ambient light-motes — a slow, sparse dusk-air field for the WHOLE run. Mood as a STATE (remembered), not an
   event. 7 motes, drift up, very dim; body-scoped so they vanish with the class. Suppressed under reduced-motion. */
#gatherAmbient { display: none; }
body.gatherRun[data-screen="game"] #gatherAmbient { display: block; position: fixed; inset: 0; pointer-events: none; z-index: 2; }
#gatherAmbient i {
  position: absolute; width: 7px; height: 7px; border-radius: 50%;
  background: radial-gradient(circle, rgba(255,246,218,0.9), rgba(255,216,152,0.5) 55%, transparent 78%);
  opacity: 0; animation: gatherFloat 11s ease-in-out infinite;
}
#gatherAmbient i:nth-child(1) { left: 12%; bottom: -3%; animation-delay: 0s;    animation-duration: 10.5s; }
#gatherAmbient i:nth-child(2) { left: 27%; bottom: -3%; animation-delay: 3.1s;  animation-duration: 12.5s; }
#gatherAmbient i:nth-child(3) { left: 41%; bottom: -3%; animation-delay: 6.4s;  animation-duration: 9.5s;  }
#gatherAmbient i:nth-child(4) { left: 56%; bottom: -3%; animation-delay: 1.7s;  animation-duration: 13s;   }
#gatherAmbient i:nth-child(5) { left: 68%; bottom: -3%; animation-delay: 8.2s;  animation-duration: 11s;   }
#gatherAmbient i:nth-child(6) { left: 81%; bottom: -3%; animation-delay: 4.6s;  animation-duration: 12s;   }
#gatherAmbient i:nth-child(7) { left: 90%; bottom: -3%; animation-delay: 9.4s;  animation-duration: 10s;   }
@keyframes gatherFloat {
  0%   { opacity: 0;   transform: translateY(0) scale(0.8); }
  14%  { opacity: 0.5; }
  70%  { opacity: 0.36; }
  100% { opacity: 0;   transform: translateY(-74vh) scale(1.1); }
}
html[data-motion="reduced"] #gatherAmbient { display: none !important; }

/* Sha 입회 (threshold witness, §4.7) — Sha steps into the bottom corner holding light, looks up at the board,
   fades. One-shot on entry (class re-added each gather). Existing asset; never covers the board or key numbers. */
#gatherSha { display: none; }
body.gatherRun[data-screen="game"] #gatherSha {
  display: block; position: fixed; left: 8px; bottom: calc(86px + env(safe-area-inset-bottom, 0px));
  width: 92px; height: auto; pointer-events: none; z-index: 41; transform-origin: bottom left;
  animation: gatherShaIn 3.4s ease-in-out 1 both;
}
/* 쫀득 물리(정본 §4.6) — **진입 구간(0~14%)에만** 얹는다. 14~72%의 유지와 퇴장은 물리가 아니라
   내용이므로 길이를 건드리지 않는다(전체 3.4s 유지). `transform-origin: bottom left`이라 발밑을
   딛고 눌리는 것처럼 읽힌다 = 마스코트에 딱 맞는 방향. */
@keyframes gatherShaIn {
  0%   { opacity: 0; transform: translateY(22px) scaleX(.82) scaleY(.99); animation-timing-function: cubic-bezier(.2,.72,.3,1); }
  8%   { opacity: 1; transform: translateY(0) scaleX(1.12) scaleY(.90); animation-timing-function: ease-in-out; }
  14%  { opacity: 1; transform: translateY(0) scaleX(.955) scaleY(1.05); animation-timing-function: ease-in-out; }
  19%  { opacity: 1; transform: translateY(0) scaleX(1.025) scaleY(.982); animation-timing-function: ease-in-out; }
  24%  { opacity: 1; transform: translateY(0) scale(1); }
  72%  { opacity: 1; transform: translateY(0) scale(1); }
  100% { opacity: 0; transform: translateY(-6px) scale(1); }
}
html[data-motion="reduced"] body.gatherRun[data-screen="game"] #gatherSha { display: none; }

/* 거울 변이 보상 타이머: 실패 시계가 아니라 지금 확보 가능한 별빛의 밀도다.
   보상 숫자가 주인공이고 시간은 보조 정보다. 6에 닿으면 시계가 사라지고 보장 보상만 남는다. */
#game .mutationTimer {
  position: absolute; left: 0; right: 0; top: 100%; z-index: 11;
  width: max-content; margin: 4px auto 0; padding: 5px 11px 6px;
  display: flex; align-items: baseline; justify-content: center; gap: 8px;
  border: 2px solid #e1bb60; border-radius: 999px;
  background: linear-gradient(180deg, #594078, #302343);
  color: #fff1bd;
  box-shadow: 0 4px 12px rgba(35,20,55,.42), 0 0 10px rgba(221,174,255,.28), inset 0 1px 0 rgba(255,247,217,.25);
  text-shadow: 0 1px 2px rgba(15,8,24,.7);
}
#game .mutationTimerReward { font-size: 17px; line-height: 1; font-weight: 900; }
#game .mutationTimerClock { min-width: 34px; font-size: 13px; line-height: 1; font-weight: 800; color: #eadcf8; }
#game .mutationTimer.floor { border-color: #c9af74; background: linear-gradient(180deg, #524664, #342d3e); box-shadow: 0 4px 10px rgba(30,20,40,.34), inset 0 1px 0 rgba(255,247,217,.2); }
#game .mutationTimer.floor .mutationTimerClock { min-width: 0; color: #e7d8b4; }
#game .mutationTimer.decay { animation: mutationRewardSettle .46s linear both; }
@keyframes mutationRewardSettle {
  0%   { transform: scale(1); filter: brightness(1); animation-timing-function: ease-in-out; }
  32%  { transform: scaleX(1.118) scaleY(.968); filter: brightness(1.35); animation-timing-function: ease-in-out; }
  66%  { transform: scaleX(.968) scaleY(1.03); animation-timing-function: ease-in-out; }
  100% { transform: scale(1); filter: brightness(1); }
}
html[data-motion="reduced"] #game .mutationTimer.decay { animation: none; }

/* unlit stained glass — empty tiles trade the campaign's warm cream glass for a neutral grayscale pane,
   so the dusk mood reads "waiting for light" and a sealed shard's full jewel colour lands as a real
   payoff against it. Excludes .startHint so the idle "start here" amber glow still reads normally.
   Owner 2026-07-18. */
body.gatherRun[data-screen="game"] #game .cell.unfilled:not(.startHint) {
  background:
    linear-gradient(154deg, rgba(255,255,255,.36) 0%, rgba(255,255,255,.10) 44%, transparent 70%),
    radial-gradient(100% 96% at 50% 32%, #dcdcdc 0%, #c2c2c2 70%, #a4a4a4 100%);
  color: #4a4a4a;
  box-shadow:
    inset 0 2px 0 rgba(255,255,255,.55),
    inset 0 0 0 1px rgba(70,70,70,.26),
    inset 0 -6px 11px rgba(40,40,40,.24),
    inset 0 7px 12px rgba(240,240,240,.18);
}
body.gatherRun[data-screen="game"] #game .cell.unfilled:not(.startHint)::before {
  filter: saturate(0) contrast(.94) brightness(1.1);
}

/* ═══ flawless→bonus crossing moment (game/gatherTransition.js) — the completed stained-glass window
      shatters, the screen whites out (board swap happens invisibly underneath), shards reform into the
      bonus board. NOT scoped to body.gatherRun — this plays BEFORE that class is added, as the crossing
      itself. Owner 2026-07-17. ═══ */
#gatherTransition { position: fixed; inset: 0; z-index: 61; pointer-events: none; overflow: hidden; }
#gatherTransition.active { pointer-events: auto; }
.gShard {
  position: absolute; z-index: 61; pointer-events: none;
  transform: translate(-50%, -50%) scale(.3) rotate(0deg);
  opacity: 0;
  background:
    radial-gradient(circle at 35% 25%, rgba(255,255,255,.9) 0 12%, transparent 26%),
    linear-gradient(135deg, rgba(255,250,225,.95), var(--jewel, #f0b43c) 65%, rgba(60,40,10,.9));
  box-shadow: 0 0 14px rgba(255,220,140,.55);
  animation: gShardBurst .55s cubic-bezier(.2,.85,.25,1) both;
}
.gShard.v1 { clip-path: polygon(50% 0%, 100% 38%, 82% 100%, 15% 92%, 0% 32%); }
.gShard.v2 { clip-path: polygon(20% 0%, 100% 15%, 85% 100%, 0% 78%); }
.gShard.v3 { clip-path: polygon(0% 20%, 60% 0%, 100% 55%, 55% 100%, 5% 75%); }
.gShard.v4 { clip-path: polygon(40% 0%, 100% 30%, 70% 100%, 0% 60%); }
.gShard.reform { animation: gShardReform .82s cubic-bezier(.25,.1,.2,1) both; }
@keyframes gShardBurst {
  0%   { transform: translate(-50%,-50%) scale(.25) rotate(0deg); opacity: 0; }
  22%  { opacity: 1; }
  100% { transform: translate(calc(-50% + var(--tx)), calc(-50% + var(--ty))) scale(1.5) rotate(var(--rot)); opacity: 0; }
}
@keyframes gShardReform {
  0%   { transform: translate(calc(-50% + var(--tx)), calc(-50% + var(--ty))) scale(1.3) rotate(var(--rot)); opacity: 0; }
  30%  { opacity: 1; }
  100% { transform: translate(-50%,-50%) scale(.2) rotate(0deg); opacity: 0; }
}
html[data-motion="reduced"] .gShard { animation: none !important; opacity: 0 !important; }

#gatherWhiteout {
  position: fixed; inset: 0; z-index: 60; pointer-events: none;
  background: radial-gradient(120% 100% at 50% 40%, #fff8e2 0%, #ffe6a8 55%, #f2c563 100%);
  opacity: 0;
}
#gatherWhiteout.on { opacity: 1; }
#gatherWhiteout.building { animation: gwIn .5s cubic-bezier(.2,.8,.3,1) forwards; }
#gatherWhiteout.peak { opacity: 1; }
#gatherWhiteout.clearing { animation: gwOut .85s cubic-bezier(.3,0,.4,1) forwards; }
@keyframes gwIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes gwOut { from { opacity: 1; } to { opacity: 0; } }
html[data-motion="reduced"] #gatherWhiteout.building,
html[data-motion="reduced"] #gatherWhiteout.clearing { animation-duration: .16s; }
