/* ───────── tokens ───────── */
:root {
  --ink: #0b1622;
  --ink2: #0f2033;
  --glass: rgba(10, 22, 36, .78);
  --line: #cfe8ff;
  --sun: #ffcf7a;
  --tide: #6fd3ff;
  --paper: #f6f1e6;
  --text: #f3f6fa;
  --muted: #9fb3c8;
  --danger: #ff6b6b;
  --ease: cubic-bezier(.2, .7, .2, 1);
  --ease-io: cubic-bezier(.65, 0, .35, 1);
  --serif: 'Gowun Batang', 'Noto Serif KR', 'AppleMyungjo', 'Batang', serif;
  --sans: 'Pretendard Variable', Pretendard, -apple-system, BlinkMacSystemFont, 'Apple SD Gothic Neo', 'Noto Sans KR', 'Malgun Gothic', system-ui, sans-serif;
  --hand: 'Nanum Pen Script', var(--serif);
  --latin: 'Cormorant Garamond', 'Gowun Batang', Georgia, serif;
  --win-op: .78;
  --spk: #d6e2ee;
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  color-scheme: dark;
}
* { box-sizing: border-box; }
html { font-size: 7.5px; background: var(--ink); -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
html, body { margin: 0; height: 100%; overflow: hidden; }
body {
  background: var(--ink);
  color: var(--text);
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior: none;
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
button:focus-visible, [tabindex]:focus-visible, input:focus-visible { outline: 2px solid var(--tide); outline-offset: 3px; }
.hidden { display: none !important; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }

/* ───────── app & stage ───────── */
#app { position: fixed; inset: 0; overflow: hidden; background: radial-gradient(120% 90% at 50% 40%, #10243a 0%, var(--ink) 60%); }
#ambient {
  position: absolute; inset: -60px;
  background-size: cover; background-position: center;
  filter: blur(40px) saturate(1.2) brightness(.32);
  opacity: 0; transition: opacity 1.2s var(--ease), background-image .8s;
  pointer-events: none;
}
body.portrait #ambient, body.letterbox #ambient { opacity: 1; }

#stage {
  position: absolute; left: 0; top: 0;
  width: 1920px; height: 1080px;
  transform-origin: 0 0;
  overflow: hidden;
  background: #000;
  contain: layout paint;
}
body.portrait #stage { box-shadow: 0 24px 40px -18px rgba(0,0,0,.8); }
#shaker, #scene, #bg-wrap, #cg-wrap, #sprites, #grade, #bg-grade, #fxo, #grade .g, #bg-grade .g, #fxo > div { position: absolute; inset: 0; }
/* background depth-of-field while characters are on stage (extends past the stage so blurred edges stay opaque) */
#bg-wrap { inset: -24px; }

.bg-layer, .cg-layer {
  position: absolute; inset: 0;
  background: #000 center / cover no-repeat;
  opacity: 0;
  will-change: opacity, transform;
}
.bg-layer.on, .cg-layer.on { opacity: 1; }
.bg-layer.solid-white { background-color: #fff; }
.bg-layer .kb, .cg-layer .kb { position: absolute; inset: 0; background: inherit; background-color: transparent; }
#cg-wrap { pointer-events: none; }
#particles, #glitch-canvas { position: absolute; left: 0; top: 0; width: 1920px; height: 1080px; pointer-events: none; }
#glitch-canvas { display: none; z-index: 6; }
#flash, #fade, #trans { position: absolute; inset: 0; pointer-events: none; opacity: 0; }
#flash { background: #fff; z-index: 7; }
#fade { background: #000; z-index: 8; }
#trans { z-index: 9; background: #000; }
.ripple-canvas { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 3; }

/* sprites */
.sprite {
  /* sprite webp = 1054×1566 (1024×1536 art + sticker padding); shown large, cut by the stage bottom */
  --spr-h: 1250px;
  position: absolute; top: 14px;
  height: var(--spr-h); width: calc(var(--spr-h) * 1054 / 1566);
  margin-left: calc(var(--spr-h) * -527 / 1566);
  opacity: 0;
  transform: translateY(12px);
  transition: opacity .4s var(--ease), transform .4s var(--ease), left .5s var(--ease-io), filter .25s var(--ease);
  filter: brightness(var(--b, 1));
  will-change: opacity, transform;
}
.sprite.on { opacity: 1; transform: translateY(0); }
.sprite.dim { --b: .82; }
.sprite img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; object-position: center top; transition: opacity .22s ease; -webkit-user-drag: none; }
.sprite img.out { opacity: 0; }
.sprite.ghost { --gh: .78; }
.sprite.ghost.on { opacity: .78; animation: ghost-drift 6s ease-in-out infinite; }
.sprite.ghost .imgs { filter: drop-shadow(0 0 18px rgba(160, 230, 255, .6)) drop-shadow(0 0 4px rgba(200, 245, 255, .5)); animation: ghost-flicker 7.3s steps(1) infinite; }
@keyframes ghost-drift { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-9px); } }
@keyframes ghost-flicker {
  0%, 61%, 63.5%, 87%, 88.2%, 100% { opacity: 1; }
  62% { opacity: .55; } 62.8% { opacity: .92; } 87.6% { opacity: .7; }
}
#sprites.ca .sprite .imgs { filter: drop-shadow(3px 0 0 rgba(255, 70, 120, .3)) drop-shadow(-3px 0 0 rgba(70, 230, 255, .34)); }
#sprites.ca .sprite.ghost .imgs { filter: drop-shadow(0 0 18px rgba(160, 230, 255, .6)) drop-shadow(3px 0 0 rgba(255, 70, 120, .3)) drop-shadow(-3px 0 0 rgba(70, 230, 255, .34)); }
.sprite .imgs { position: absolute; inset: 0; }
/* living sprite (web/js/spritelife.js): a WebGL canvas redraws the expression images with wind once it has drawn;
   the <img>s stay (hidden) and keep driving opacity / crossfades. The blink patch sits over the eyes, toggled ~120 ms */
.sprite .imgs > canvas.lv { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1; pointer-events: none; visibility: hidden; }
.sprite.live .imgs > canvas.lv { visibility: visible; }
.sprite.live .imgs > img:not(.blink) { visibility: hidden; }
.sprite .imgs > img.blink { inset: auto; z-index: 2; opacity: 0; transition: opacity 40ms linear; object-fit: fill; pointer-events: none; }
.sprite .imgs > img.blink.on { opacity: 1; }
/* idle breathing (scale from about the stage's bottom edge), staggered per character via --bt/--bd */
.sprite .breath { position: absolute; inset: 0; transform-origin: 50% 85%; animation: spr-breathe var(--bt, 4.6s) ease-in-out var(--bd, 0s) infinite; }
@keyframes spr-breathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.0035, 1.0045); } }
body.reduced .sprite .breath { animation: none; }

/* per-image ambient life (web/js/ambient.js): lives inside the bg/cg layer so it follows Ken Burns, fades and grades */
.lfx { position: absolute; inset: 0; pointer-events: none; }
.lfx-water { position: absolute; display: block; }
.lfx-light { position: absolute; inset: 0; width: 100%; height: 100%; mix-blend-mode: screen; }
.lfx-clouds { position: absolute; inset: 0; overflow: hidden; mix-blend-mode: multiply; opacity: var(--ca, .14); }
.lfx-clouds i { position: absolute; left: 0; top: 0; bottom: 0; width: 300%; will-change: transform;
  background-image:
    radial-gradient(ellipse 9% 34% at 12% 38%, rgba(28, 44, 70, .9), transparent 72%),
    radial-gradient(ellipse 7% 26% at 30% 72%, rgba(28, 44, 70, .75), transparent 72%),
    radial-gradient(ellipse 11% 30% at 52% 30%, rgba(28, 44, 70, .85), transparent 72%),
    radial-gradient(ellipse 6% 22% at 71% 64%, rgba(28, 44, 70, .7), transparent 72%),
    radial-gradient(ellipse 9% 28% at 88% 46%, rgba(28, 44, 70, .8), transparent 72%);
  background-size: 33.3333% 100%; background-repeat: repeat-x; }

/* ───────── colour grades (overlay layers; the CSS filter lives on #scene) ───────── */
#grade .g, #bg-grade .g { opacity: 0; transition: opacity 1s var(--ease); pointer-events: none; }
#grade .g.on { opacity: 1; }
/* tint grades: full on the background (#bg-grade 0.7 + #grade 0.35), only a light share on characters and CGs so their
   colouring stays the same from scene to scene (dark/red keep more: lights-out and the darkroom must read on them too) */
#bg-grade { pointer-events: none; }
#bg-grade .g.on { opacity: .7; }
#grade .g-night.on, #grade .g-dusk.on, #grade .g-afterimage.on, #grade .g-cold.on { opacity: .35; }
#grade .g-dark.on, #grade .g-red.on { opacity: .6; }
.g-night { background: linear-gradient(180deg, rgba(20, 44, 120, .5), rgba(6, 16, 50, .55)); mix-blend-mode: multiply; }
.g-night::after { content: ''; position: absolute; inset: 0; background: radial-gradient(90% 70% at 60% 0%, rgba(120, 170, 255, .16), transparent 70%); mix-blend-mode: screen; }
.g-dusk { background: linear-gradient(180deg, rgba(255, 150, 70, .55) 0%, rgba(240, 90, 120, .45) 55%, rgba(90, 40, 120, .5) 100%); mix-blend-mode: soft-light; }
.g-dusk::after { content: ''; position: absolute; inset: 0; background: radial-gradient(70% 60% at 70% 20%, rgba(255, 190, 120, .28), transparent 70%); mix-blend-mode: screen; }
.g-sepia2014 { background: radial-gradient(130% 100% at 50% 45%, transparent 55%, rgba(60, 30, 10, .42) 100%); }
.g-sepia2014 .film { position: absolute; inset: 0; width: 100%; height: 100%; }
.g-afterimage { background: linear-gradient(180deg, rgba(60, 200, 210, .32), rgba(20, 90, 140, .28)); mix-blend-mode: soft-light; }
.g-afterimage::before { content: ''; position: absolute; inset: 0; background: radial-gradient(120% 95% at 50% 50%, transparent 52%, rgba(150, 240, 255, .38) 100%); mix-blend-mode: screen; animation: breathe 5s ease-in-out infinite; }
.g-afterimage::after { content: ''; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(120, 230, 255, .12), transparent 30%, transparent 70%, rgba(120, 230, 255, .1)); mix-blend-mode: screen; }
@keyframes breathe { 0%, 100% { opacity: .75; } 50% { opacity: 1; } }
.g-memory { background: radial-gradient(115% 90% at 50% 50%, transparent 45%, rgba(255, 252, 240, .62) 100%); mix-blend-mode: screen; }
.g-memory::after { content: ''; position: absolute; inset: 0; background: rgba(240, 230, 210, .12); mix-blend-mode: soft-light; }
.g-dark { background: radial-gradient(85% 75% at 50% 45%, rgba(10, 14, 28, .2), rgba(0, 0, 0, .78) 100%); }
.g-red { background: #b3120c; mix-blend-mode: multiply; }
.g-red::after { content: ''; position: absolute; inset: 0; background: radial-gradient(60% 55% at 50% 12%, rgba(255, 60, 30, .3), transparent 70%), radial-gradient(130% 100% at 50% 50%, transparent 50%, rgba(0, 0, 0, .55)); }
.g-cold { background: linear-gradient(180deg, rgba(90, 130, 190, .4), rgba(40, 60, 100, .35)); mix-blend-mode: color; }
.g-cold::after { content: ''; position: absolute; inset: 0; background: radial-gradient(130% 100% at 50% 50%, transparent 55%, rgba(5, 12, 25, .5)); }
.g-rain { background: linear-gradient(180deg, rgba(20, 34, 54, .28), rgba(10, 20, 30, .18)); }
.g-timerain { background: radial-gradient(100% 70% at 50% 0%, rgba(111, 211, 255, .16), transparent 70%); mix-blend-mode: screen; }

/* fx overlays */
#fxo > div { opacity: 0; transition: opacity 1s var(--ease); pointer-events: none; }
.o-vignette { background: radial-gradient(125% 100% at 50% 50%, transparent 48%, rgba(2, 6, 12, .78) 100%); }
.o-heartbeat { background: radial-gradient(110% 95% at 50% 50%, transparent 40%, rgba(90, 0, 12, .78) 100%); }
#fxo .o-vignette.on { opacity: 1; }
#fxo .o-heartbeat.on { opacity: 1; animation: heartbeat .857s infinite; }
@keyframes heartbeat { 0% { opacity: .35; } 12% { opacity: 1; } 24% { opacity: .55; } 36% { opacity: .9; } 70%, 100% { opacity: .35; } }

body.reduced .sprite.ghost.on { animation: none; }
@media (prefers-reduced-motion: reduce) {
  .sprite.ghost.on { animation: none; }
  .sprite .breath { animation: none; }
}
