/**
 * Kafka Roki 효과 모음 — 뼈대 스타일.
 *
 * 색과 크기는 넣지 않습니다. 이 파일은 "움직이려면 반드시 있어야 하는 것"만
 * 담고, 생김새는 각 페이지의 스타일이 정합니다. 데모 페이지마다 색이 다르기
 * 때문입니다.
 *
 * ⛔ 여기에 font-size 를 쓰지 마십시오. 카프카로키 화면 글자 하한이 22px 인데,
 *    효과 파일이 크기를 정하면 페이지마다 그걸 다시 이겨야 합니다.
 */

/* words — 단어가 아래에서 올라오며 또렷해진다 */
[data-fx='words'] .krfx-w {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
}
[data-fx='words'] .krfx-w > span {
  display: inline-block;
  transform: translateY(105%);
  opacity: 0;
  transition: transform .72s cubic-bezier(.22, 1, .36, 1),
              opacity .72s cubic-bezier(.22, 1, .36, 1);
  will-change: transform, opacity;
}
[data-fx='words'].krfx-in .krfx-w > span {
  transform: none;
  opacity: 1;
}

/* elastic — 자식이 변형되므로 잘리지 않게만 잡아 둔다.
   ⚠️ 열끼리 반대로 움직이므로 넘치는 것을 자르면 안 됩니다 — 잘리면
   지그재그가 안 보입니다 (2026-07-30 사장님 요청 2번). */
[data-fx='elastic'] {
  overflow: visible;
}
[data-fx='elastic'] > * {
  backface-visibility: hidden;
  will-change: transform;
}

/* magnet — 따라오는 동안 글자가 선택되면 끌기처럼 보인다 */
[data-fx='magnet'] {
  display: inline-block;
  user-select: none;
}

/* 화면에는 없고 읽어주는 쪽에만 남는 글자.
   count 가 세는 동안의 숫자를 감추고 진짜 값을 여기에 둔다. */
.krfx-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  /* ⚠ 이 글자는 눈에는 안 보이지만 **끌어서 복사하면 같이 딸려온다**.
     그래서 「420」을 복사하면 「420420」이 됐다(2026-09-03). 복사에서도 뺀다. */
  user-select: none;
  -webkit-user-select: none;
}

/* reveal — 오려낸 자리가 열린다. 안에 사진이 있으면 사진도 제자리로 돌아온다 */
[data-fx='reveal'] {
  transition: clip-path .95s cubic-bezier(.22, 1, .36, 1);
  will-change: clip-path;
}
[data-fx='reveal'] img {
  transform: scale(1.14);
  transition: transform 1.3s cubic-bezier(.22, 1, .36, 1);
}
[data-fx='reveal'].krfx-in img {
  transform: none;
}

/* parallax — 액자는 넘치는 것을 잘라야 한다 */
[data-fx='parallax'] {
  display: block;
  backface-visibility: hidden;
}

/* marquee — 한 벌이 자기 뒤 여백까지 들고 있어야 이음매가 고르다 */
.krfx-mq-host {
  overflow: hidden;
}
.krfx-mq {
  display: flex;
  width: max-content;
  will-change: transform;
}
.krfx-mq__row {
  display: flex;
  align-items: center;
  gap: var(--krfx-mq-gap, 2.5rem);
  padding-right: var(--krfx-mq-gap, 2.5rem);
  flex: 0 0 auto;
}

/* swap — 글자 한 벌이 올라가고 다음 한 벌이 따라 올라온다 */
.krfx-swap {
  position: relative;
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
}
.krfx-swap__a,
.krfx-swap__b {
  display: block;
  transition: transform .5s cubic-bezier(.22, 1, .36, 1);
}
.krfx-swap__b {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  transform: translateY(105%);
}
[data-fx='swap']:hover .krfx-swap__a,
[data-fx='swap']:focus-visible .krfx-swap__a {
  transform: translateY(-105%);
}
[data-fx='swap']:hover .krfx-swap__b,
[data-fx='swap']:focus-visible .krfx-swap__b {
  transform: none;
}

/* trail — 지나간 자리에 떴다 사라진다 */
.krfx-trail {
  position: relative;
  overflow: hidden;
}
.krfx-trail__shot {
  position: absolute;
  left: 0;
  top: 0;
  width: 26%;
  max-width: 260px;
  height: auto;
  object-fit: cover;
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, -50%) scale(.86);
}
.krfx-trail__shot.on {
  animation: krfx-trail var(--krfx-life, 700ms) cubic-bezier(.22, 1, .36, 1) forwards;
}
@keyframes krfx-trail {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(.86); }
  18%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1.04); }
}

/* curtain — 위아래 두 겹이 가운데에서 갈라진다.
   색은 페이지가 정한다: --krfx-curtain */
.krfx-curtain-host {
  position: relative;
  overflow: hidden;
}
.krfx-curtain {
  position: absolute;
  left: 0;
  right: 0;
  height: 50.5%;   /* 0.5% 는 두 겹 사이에 실금이 보이지 않게 하는 여유입니다 */
  z-index: 5;
  background: var(--krfx-curtain, #0a0a09);
  transition: transform 1.05s cubic-bezier(.76, 0, .24, 1);
}
.krfx-curtain--a { top: 0; transform-origin: top; }
.krfx-curtain--b { bottom: 0; transform-origin: bottom; transition-delay: .06s; }
.krfx-curtain-host.krfx-open .krfx-curtain { transform: scaleY(0); }

/* stamp — 글자를 원둘레에 세운다. 자리는 스크립트가 잡는다 */
.krfx-stamp {
  position: relative;
  display: block;
  aspect-ratio: 1;
  border-radius: 50%;
  animation: krfx-spin var(--krfx-spin, 16s) linear infinite;
}
.krfx-stamp > span {
  position: absolute;
  left: 50%;
  top: 50%;
  white-space: pre;
}
@keyframes krfx-spin { to { transform: rotate(360deg); } }

/* peek — 보고 있는 줄의 사진만 커서 옆에 뜬다 */
.krfx-peek-host { position: relative; }
.krfx-peek {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 4;
  width: 34%;
  max-width: 300px;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 14px;
  opacity: 0;
  pointer-events: none;
  transition: opacity .28s cubic-bezier(.22, 1, .36, 1);
}
.krfx-peek.on { opacity: 1; }

/* rail — 끌어서 미는 가로 목록.
   ⚠️ 이 클래스를 마크업에도 적어 두십시오. 스크립트가 못 도는 자리(움직임
   줄이기 설정)에서 클래스가 없으면 칸이 옆으로 넘쳐 화면이 깨집니다. */
.krfx-rail {
  display: flex;
  overflow-x: auto;
  scrollbar-width: none;
  cursor: grab;
  overscroll-behavior-x: contain;
}
.krfx-rail::-webkit-scrollbar { display: none; }
.krfx-rail > * { flex: 0 0 auto; }
.krfx-rail.krfx-grab { cursor: grabbing; }
.krfx-rail img { pointer-events: none; }   /* 끌 때 사진이 끌려 나오지 않게 */

/* tint — 배경색이 바뀌는 쪽 */
.krfx-tinted { transition: background-color .7s cubic-bezier(.22, 1, .36, 1); }

/* cascade — 칸이 차례로 올라온다 */
/**
 * ⛔ **`transform` 이 아니라 `translate`·`scale` 로 씁니다** (2026-08-08).
 *
 * 예전에는 `transform: translateY(64px) scale(.88)` 이었습니다. 그런데 얹는 자리가
 * 대개 **손을 올리면 뜨는 카드**입니다 — 페이지는 `.doc:hover{transform:...}` 로
 * 그것을 겁니다. 우리 「다 올라온 상태」 선택자는
 * `[data-fx='cascade'].krfx-in .krfx-rise` 라 **클래스가 셋**이고, kr-fx.css 는
 * 페이지 `<style>` **뒤에** 실립니다. 그래서 우리 `transform:none` 이 페이지의
 * hover 를 **조용히 이겨 없앴습니다** — 검사는 전부 통과하는데 손을 올리면 아무
 * 일도 안 일어납니다(dental 의 `.doc` 에서 실제로 겪었습니다).
 *
 * `translate`·`scale` 은 `transform` 과 **다른 성질**이라 서로 덮지 않고 **겹쳐
 * 그려집니다.** 보이는 움직임은 그대로이고, 페이지의 hover 가 살아납니다.
 * ⛔ 되돌리지 마십시오 — 되돌리면 얹은 페이지마다 hover 가 다시 죽습니다.
 */
.krfx-rise {
  opacity: 0;
  /* ⚠️ 26px 이던 것을 키웠습니다 — 「효과가 미미해 보인다」(2026-07-30). */
  translate: 0 64px;
  scale: .88;
  transition: opacity .7s cubic-bezier(.22, 1, .36, 1),
              translate .7s cubic-bezier(.22, 1, .36, 1),
              scale .7s cubic-bezier(.22, 1, .36, 1);
}
[data-fx='cascade'].krfx-in .krfx-rise {
  opacity: 1;
  translate: none;
  scale: none;
}

/* 17 — 3D 격자. 커서가 지나가면 그 자리가 솟습니다.
   ⚠️ 원근은 상자가 만듭니다 — 없으면 평평하게 보입니다. */
.krfx-mesh3d { position: relative; overflow: hidden; perspective: 620px; }
.krfx-mesh3d__s {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  pointer-events: none;
}
.krfx-mesh3d__s i {
  position: absolute;
  width: var(--krfx-dot, 7px);
  height: var(--krfx-dot, 7px);
  border-radius: 50%;
  background: currentColor;
  will-change: transform, opacity;
}

/* wave(옛 선) — 지금은 안 씁니다. 남겨 둔 것은 되살릴 때를 위해서입니다 */
.krfx-wave-host { position: relative; }
.krfx-wave {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.krfx-wave path {
  fill: none;
  stroke: currentColor;
  stroke-width: .4;
  animation: krfx-flow 12s linear infinite;
}
@keyframes krfx-flow { to { transform: translateX(-100px); } }

/* zoom — 액자는 넘치는 것을 잘라야 한다 */
[data-fx='zoom'] {
  display: block;
  transform-origin: center;
  backface-visibility: hidden;
}

/* cutout — 글자 안쪽이 사진이 된다. 사진은 --krfx-cut 으로 받는다.
   ⚠️ 평소엔 **흰 글자**, 마우스를 올리면 사진이 듭니다
   (2026-07-30 사장님 요청 21번). 그래야 「무엇이 달라지는지」가 보입니다. */
.krfx-cutout {
  background-image: var(--krfx-cut);
  background-size: cover;
  -webkit-background-clip: text;
  background-clip: text;
  color: var(--krfx-cut-ink, #f2efe6);
  -webkit-text-fill-color: var(--krfx-cut-ink, #f2efe6);
  transition: -webkit-text-fill-color .45s cubic-bezier(.22, 1, .36, 1),
              color .45s cubic-bezier(.22, 1, .36, 1);
}
[data-fx='cutout']:hover,
[data-fx='cutout']:focus-visible,
.krfx-cutout.krfx-on {
  color: transparent;
  /* ⚠️ 사진이 안 올 때를 대비해 두 가지를 다 씁니다. */
  -webkit-text-fill-color: transparent;
}

/* scratch — 두 장을 겹치고 위 장에 구멍을 낸다 */
.krfx-scratch {
  position: relative;
  overflow: hidden;
}
.krfx-scratch img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.krfx-scratch img + img {
  position: absolute;
  inset: 0;
}

/* deck — 쌓인 카드 */
.krfx-deck {
  position: relative;
  cursor: pointer;
  outline-offset: 6px;
}
.krfx-deck > * {
  position: absolute;
  inset: 0;
  /* ⚠️ 쌓여 내려간 만큼 키를 깎습니다 — 값은 deck() 이 넣습니다.
     ⛔ 이걸 빼면 맨 뒷장이 상자 밖으로 나가 창에 스크롤바가 섭니다. */
  bottom: auto;
  height: calc(100% - var(--krfx-deck-drop, 0px));
  transform-origin: center top;
  transition: transform .55s cubic-bezier(.22, 1, .36, 1), opacity .55s;
}
/**
 * 맨 앞 장 말고는 **뒷면처럼** 가라앉습니다 (2026-07-31 사장님 요청 20번).
 * 다섯 장이 다 또렷하면 어느 것이 앞인지 안 보입니다.
 */
.krfx-deck > .krfx-deck__back {
  filter: brightness(.62) saturate(.7);
  box-shadow: 0 -6px 18px rgba(0, 0, 0, .45);
}

/* slide — 줄 하나가 밀려나며 선이 그어진다 */
.krfx-slide {
  position: relative;
  display: block;
  width: max-content;
  transition: transform .38s cubic-bezier(.22, 1, .36, 1);
}
.krfx-slide::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: -.08em;
  height: 2px;
  width: 100%;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .38s cubic-bezier(.22, 1, .36, 1);
}
.krfx-slide:hover,
.krfx-slide:focus-visible { transform: translateX(26px); }
.krfx-slide:hover::after,
.krfx-slide:focus-visible::after { transform: scaleX(1); }

/* type — 다 칠 때까지 커서가 깜박인다 */
.krfx-type::after {
  content: '';
  display: inline-block;
  width: .06em;
  height: .9em;
  margin-left: .08em;
  background: currentColor;
  vertical-align: text-bottom;
  animation: krfx-blink 1s steps(1) infinite;
}
.krfx-type.krfx-done::after { display: none; }
@keyframes krfx-blink { 50% { opacity: 0; } }

/* glitch — 같은 사진 두 벌을 띠로 잘라 어긋낸다 */
.krfx-glitch { position: relative; overflow: hidden; }
.krfx-glitch > img { display: block; width: 100%; height: 100%; object-fit: cover; }
.krfx-glitch__x {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
}
.krfx-glitch:hover .krfx-glitch__x { opacity: .85; }
.krfx-glitch:hover .krfx-glitch__x1 {
  filter: hue-rotate(80deg);
  animation: krfx-glitch1 .9s steps(2) infinite;
}
.krfx-glitch:hover .krfx-glitch__x2 {
  filter: hue-rotate(-70deg);
  animation: krfx-glitch2 .7s steps(2) infinite;
}
@keyframes krfx-glitch1 {
  0%   { clip-path: inset(8% 0 76% 0);  transform: translateX(-2.4%); }
  50%  { clip-path: inset(58% 0 26% 0); transform: translateX(2.8%); }
  100% { clip-path: inset(32% 0 54% 0); transform: translateX(-1.6%); }
}
@keyframes krfx-glitch2 {
  0%   { clip-path: inset(70% 0 14% 0); transform: translateX(2.2%); }
  50%  { clip-path: inset(20% 0 66% 0); transform: translateX(-3%); }
  100% { clip-path: inset(46% 0 40% 0); transform: translateX(1.4%); }
}

/* dust — 점이 떠오른다 */
.krfx-dust-host { position: relative; }
.krfx-dust {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.krfx-dust i {
  position: absolute;
  border-radius: 50%;
  background: currentColor;
  animation-name: krfx-rise;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
@keyframes krfx-rise {
  from { transform: translateY(40px); }
  to   { transform: translateY(-40px); }
}

/* pin — 첫 자식이 붙어 선다. ⚠️ 조상에 overflow:hidden 이 있으면 죽는다 */
.krfx-pin { align-self: start; }

/* snap — 멈추는 일은 브라우저가 한다 */
.krfx-snap {
  overflow-y: auto;
  scroll-snap-type: y mandatory;
  scrollbar-width: none;
}
/**
 * ⛔ 여기에 overscroll-behavior 를 기본으로 넣지 마십시오.
 *
 * 가두면 장을 다 넘긴 뒤에도 페이지로 넘겨주지 않아서, 커서가 이 상자 위에
 * 있는 동안 화면이 통째로 멈춥니다(2026-07-29 오보 교차에서 잡음 —
 * 5600px 을 굴렸는데 페이지가 1px 도 안 내려갔습니다).
 * 한 화면을 다 쓰는 자리에서만 data-fx-trap="1" 로 켭니다.
 */
.krfx-snap[data-fx-trap='1'] { overscroll-behavior-y: contain; }
.krfx-snap::-webkit-scrollbar { display: none; }
.krfx-snap__page {
  scroll-snap-align: start;
  scroll-snap-stop: always;
  transition: opacity .45s cubic-bezier(.22, 1, .36, 1);
  opacity: .35;
}
.krfx-snap__page.krfx-here { opacity: 1; }

/* arc — 글자가 길을 탄다. 크기는 페이지가 정한다 */
.krfx-arc { display: block; }
.krfx-arc svg { display: block; width: 100%; height: auto; overflow: visible; }
.krfx-arc text { fill: currentColor; font-weight: 800; font-size: 34px; letter-spacing: -.01em; }

/* shuffle — 뒤집히며 갈아끼워진다 */
.krfx-flip { animation: krfx-flip .56s cubic-bezier(.22, 1, .36, 1); }
@keyframes krfx-flip {
  0%   { transform: rotateY(0); }
  50%  { transform: rotateY(90deg); }
  100% { transform: rotateY(0); }
}

/* spark — 점이 줄줄이 따라온다 */
.krfx-spark-host { position: relative; }
.krfx-spark {
  position: absolute;
  left: 0;
  top: 0;
  border-radius: 50%;
  background: currentColor;
  pointer-events: none;
  will-change: transform;
}
/* 스타일 3종 — data-fx-look="dot|ring|star" (2026-07-30 사장님 요청) */
/* ⚠️ 테두리 동그라미 대신 **커서 모양**으로 바꿨습니다
   (2026-07-30 사장님 요청 29번). */
.krfx-spark--arrow .krfx-spark {
  border-radius: 0;
  clip-path: polygon(0 0, 0 78%, 26% 60%, 44% 100%, 62% 88%, 44% 50%, 74% 44%);
}
.krfx-spark--star .krfx-spark {
  border-radius: 0;
  clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
}

/* ring — 원통에 둘러 붙어 돈다 */
.krfx-ring {
  position: relative;
  transform-style: preserve-3d;
  cursor: grab;
  touch-action: none;
  animation: krfx-turn var(--krfx-turn, 26s) linear infinite;
}
/* ⚠️ 끄는 동안에는 저절로 도는 것을 멈춥니다 — 안 그러면 손과 싸웁니다
   (2026-07-30 사장님 요청 30번). */
.krfx-ring.krfx-held { animation-play-state: paused; cursor: grabbing; }
.krfx-ring img { pointer-events: none; }
.krfx-ring > * {
  position: absolute;
  left: 50%;
  top: 0;
  margin-left: -50%;   /* 칸 스스로 가운데를 잡게 합니다 */
  backface-visibility: hidden;
}
@keyframes krfx-turn { to { transform: rotateY(-360deg); } }

/* loader — 숫자가 차오르고 위로 걷힌다 */
.krfx-loader-host { position: relative; overflow: hidden; }
.krfx-loader {
  position: absolute;
  inset: 0;
  z-index: 8;
  display: grid;
  place-items: center;
  background: var(--krfx-curtain, #0a0a09);
  transition: transform .95s cubic-bezier(.76, 0, .24, 1);
}
.krfx-loader b { font-weight: 800; font-variant-numeric: tabular-nums; }
.krfx-loader-host.krfx-open .krfx-loader { transform: translateY(-100%); }

/* roll — 세로로 끝없이 도는 목록 */
.krfx-roll-host { overflow: hidden; }
.krfx-roll { will-change: transform; }
.krfx-roll__col > * { display: block; }

/* ghost — 잔상이 뒤따라온다 */
.krfx-ghost { position: relative; overflow: hidden; }
.krfx-ghost > img { display: block; width: 100%; height: 100%; object-fit: cover; }
.krfx-ghost__x {
  position: absolute;
  inset: 0;
  pointer-events: none;
  will-change: transform;
}

/* drop — 위에서 떨어져 한 번 튕긴다 */
.krfx-drop { opacity: 0; }
[data-fx='drop'].krfx-in .krfx-drop {
  animation: krfx-drop .78s cubic-bezier(.3, 1.5, .5, 1) both;
}
@keyframes krfx-drop {
  0%   { opacity: 0; transform: translateY(-46px); }
  60%  { opacity: 1; transform: translateY(6px); }
  100% { opacity: 1; transform: none; }
}

/* burger — 세 줄이 X 로 접힌다 */
/**
 * ⚠️ **세 줄 길이가 같고 · 두께 20% 더 · 가로는 짧게** (2026-07-31 사장님 요청 35번).
 *
 * 2 → 2.4px · 54 → 44px · 가운데 줄 62% → 100%.
 * ⛔ 두께와 간격을 고치면 **X 로 접는 거리도 같이 고쳐야 합니다** — 줄 사이
 *    중심 거리(두께 + 간격)만큼 움직여야 가운데에서 만납니다. 안 고치면 X 가
 *    어긋나 십자가 아니라 우산처럼 보입니다.
 */
.krfx-burger {
  position: relative;
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 7px;
  width: 44px;
  height: 54px;
  padding: 0;
}
.krfx-burger__bar {
  display: block;
  height: 2.4px;
  width: 100%;
  background: currentColor;
  transition: transform .38s cubic-bezier(.22, 1, .36, 1), opacity .2s;
}
/* 열리면 위아래가 가운데로 모여 X 가 되고 가운데 줄은 옆으로 빠집니다 */
.krfx-burger.krfx-on .krfx-burger__bar1 { transform: translateY(9.4px) rotate(45deg); }
.krfx-burger.krfx-on .krfx-burger__bar2 { opacity: 0; transform: translateX(60%); }
.krfx-burger.krfx-on .krfx-burger__bar3 { transform: translateY(-9.4px) rotate(-45deg); }

/* 펼침판 — 자리는 페이지가 정한다. 여기서는 여닫는 것만 */
.krfx-sheet {
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  transition: max-height .5s cubic-bezier(.22, 1, .36, 1), opacity .35s;
}
.krfx-sheet.krfx-open {
  max-height: var(--krfx-sheet-max, 60vh);
  opacity: 1;
}

/* fill — 아래에서 색이 차오른다. 색은 페이지가 정한다 */
.krfx-fill {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.krfx-fill::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  background: var(--krfx-fill, currentColor);
  transform: scaleY(0);
  transform-origin: bottom;
  transition: transform .42s cubic-bezier(.22, 1, .36, 1);
}
.krfx-fill__t {
  position: relative;
  z-index: 1;
  transition: color .32s cubic-bezier(.22, 1, .36, 1);
}
[data-fx='fill']:hover::before,
[data-fx='fill']:focus-visible::before { transform: scaleY(1); }
[data-fx='fill']:hover .krfx-fill__t,
[data-fx='fill']:focus-visible .krfx-fill__t { color: var(--krfx-fill-ink, inherit); }

/* ripple — 누른 자리에서 퍼진다 */
.krfx-ripple { position: relative; overflow: hidden; }
.krfx-ripple__w {
  position: absolute;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--krfx-ripple, currentColor);
  pointer-events: none;
  transform: translate(-50%, -50%) scale(0);
  animation: krfx-ripple .68s cubic-bezier(.22, 1, .36, 1) forwards;
}
@keyframes krfx-ripple {
  from { transform: translate(-50%, -50%) scale(0); opacity: .5; }
  to   { transform: translate(-50%, -50%) scale(26); opacity: 0; }
}

/* dock — 커서 가까운 것이 부푼다 */
[data-fx='dock'] > * { transform-origin: center bottom; }
/* 43 — 커지면 옆 것과 겹칩니다. 자리를 넉넉히 잡아 둡니다. */
[data-fx='dock'] { padding-inline: 2.2em; }
/* ⚠️ 부푸는 만큼 자리를 비워 둡니다 — 안 그러면 옆 것과 겹칩니다
   (2026-07-30 사장님 요청 39번). */
[data-fx='dock'] > * { margin-inline: .34em; }

/* grain — 알갱이. 결은 CSS 안에 그려 둔 것이라 외부 파일이 없다 */
.krfx-grain-host { position: relative; }
.krfx-grain {
  position: absolute;
  inset: -50%;
  pointer-events: none;
  mix-blend-mode: soft-light;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3'/></filter><rect width='120' height='120' filter='url(%23n)'/></svg>");
  animation: krfx-grain 1s steps(4) infinite;
}
@keyframes krfx-grain {
  0%   { transform: translate(0, 0); }
  25%  { transform: translate(-4%, 3%); }
  50%  { transform: translate(3%, -4%); }
  75%  { transform: translate(-3%, -3%); }
  100% { transform: translate(0, 0); }
}

/* blob — 색덩어리. 색은 페이지가 정한다 */
.krfx-blob-host { position: relative; overflow: hidden; }
.krfx-blob {
  position: absolute;
  width: 62%;
  aspect-ratio: 1;
  border-radius: 50%;
  filter: blur(60px);
  pointer-events: none;
  animation-name: krfx-blob;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}
/* ⚠️ 색을 한 번 더 바꿨습니다 (2026-07-30 사장님 요청 41번). */
.krfx-blob--1 { left: -8%;  top: -14%; background: var(--krfx-blob-1, rgba(255, 170, 40, .32)); }
.krfx-blob--2 { right: -10%; top: 18%;  background: var(--krfx-blob-2, rgba(150, 80, 255, .3)); }
.krfx-blob--3 { left: 24%;  bottom: -20%; background: var(--krfx-blob-3, rgba(40, 230, 180, .26)); }
@keyframes krfx-blob {
  from { transform: translate(0, 0) scale(1); }
  to   { transform: translate(12%, -10%) scale(1.22); }
}

/* mesh — 격자선이 흐른다 */
.krfx-mesh-host { position: relative; overflow: hidden; }
/* ⛔ 격자선은 뺐습니다 — 사장님 지적으로 **배경 자체가 흐르는** 쪽으로
   바꿨습니다(2026-07-30). 선이 아니라 색이 천천히 지나갑니다. */
.krfx-mesh {
  position: absolute;
  inset: -30%;
  pointer-events: none;
  opacity: var(--krfx-mesh-fade, 1);
  background-image: conic-gradient(
    from 210deg at 50% 50%,
    var(--krfx-mesh-a, rgba(200, 255, 0, .16)) 0deg,
    transparent 90deg,
    var(--krfx-mesh-b, rgba(120, 190, 255, .16)) 180deg,
    transparent 270deg,
    var(--krfx-mesh-a, rgba(200, 255, 0, .16)) 360deg
  );
  filter: blur(40px);
  animation-name: krfx-mesh;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
@keyframes krfx-mesh {
  to { transform: rotate(360deg); }
}

/* orbit — 점이 원을 그리며 돈다 */
.krfx-orbit {
  position: relative;
  aspect-ratio: 1;
  animation-name: krfx-spin;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
.krfx-orbit > i {
  position: absolute;
  left: 50%;
  top: 50%;
  border-radius: 50%;
  background: currentColor;
}

/* bar — 읽은 만큼 차오른다 */
.krfx-bar {
  transform-origin: left center;
  transform: scaleX(0);
  will-change: transform;
}

/* blur — 초점이 맞아 들어온다 */
.krfx-blur {
  filter: blur(14px);
  opacity: 0;
  transition: filter 1.1s cubic-bezier(.22, 1, .36, 1), opacity 1.1s cubic-bezier(.22, 1, .36, 1);
}
.krfx-blur.krfx-in { filter: none; opacity: 1; }

/* outline — 속이 빈 글자에 색이 든다. 두께·색은 페이지가 정한다 */
.krfx-outline {
  -webkit-text-stroke: var(--krfx-line, 3px) currentColor;
  color: transparent;
  transition: color .9s cubic-bezier(.22, 1, .36, 1);
}
.krfx-outline.krfx-in { color: currentColor; }

/* wavy — 단어가 물결친다 */
.krfx-wavy__w {
  display: inline-block;
  animation-name: krfx-wavy;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
}
@keyframes krfx-wavy {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-.16em); }
}

/* stretch — 자간은 스크립트가 정한다. 잘리지 않게만 잡아 둔다 */
[data-fx='stretch'] { will-change: letter-spacing; }

/* flip — 낱말이 세로로 넘어간다 */
.krfx-flip { position: relative; display: inline-block; overflow: hidden; vertical-align: bottom; }
.krfx-flip__w { display: inline-block; }
.krfx-flip__w.krfx-turn { animation: krfx-turnover .42s cubic-bezier(.22, 1, .36, 1); }
/* 가장 긴 낱말만큼 자리를 잡아 두는 자. 보이지는 않는다 */
.krfx-flip__ruler { display: block; height: 0; overflow: hidden; visibility: hidden; }
@keyframes krfx-turnover {
  0%   { transform: translateY(0)    rotateX(0);     opacity: 1; }
  50%  { transform: translateY(-60%) rotateX(90deg); opacity: 0; }
  100% { transform: translateY(0)    rotateX(0);     opacity: 1; }
}

/* blinds — 세로 칸이 한 장씩 걷힌다 */
.krfx-blinds-host { position: relative; overflow: hidden; }
.krfx-blind {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 5;
  background: var(--krfx-curtain, #0a0a09);
  transform-origin: top;
  transition: transform .8s cubic-bezier(.76, 0, .24, 1);
}
.krfx-blinds-host.krfx-open .krfx-blind { transform: scaleY(0); }

/* iris — 원이 커지며 열린다 */
.krfx-iris { transition: clip-path 1.15s cubic-bezier(.76, 0, .24, 1); }

/* slam — 크게 흐릿하게 들어와 딱 멈춘다 */
.krfx-slam {
  opacity: 0;
  transform: scale(1.5);
  filter: blur(10px);
  transition: transform .62s cubic-bezier(.2, 1.2, .3, 1),
              opacity .4s, filter .5s;
}
.krfx-slam.krfx-in { opacity: 1; transform: none; filter: none; }

/* duotone — 흑백이었다가 색이 돈다 */
.krfx-duotone img { filter: grayscale(1) contrast(1.06); transition: filter .55s cubic-bezier(.22, 1, .36, 1); }
.krfx-duotone:hover img { filter: none; }

/* slice — 조각이 어긋나 있다가 제자리를 찾는다 */
.krfx-slice { position: relative; overflow: hidden; }
/* ⛔ 여기에 transition 을 주지 마십시오 — 자리 잡는 것은 루프가 합니다.
   전환에 맡기면 시작값을 브라우저가 못 그려 어긋난 모습이 아예 안 보입니다. */
.krfx-slice__b {
  position: absolute;
  top: 0;
  height: 100%;
  overflow: hidden;
  will-change: transform;
}
/* ⛔ 조각을 배경 그림으로 만들지 마십시오 — 사진이 액자 비율에 맞춰 늘어집니다.
   진짜 사진을 한 장씩 넣고 옆으로 밀어 두면 cover 가 그대로 지켜집니다. */
.krfx-slice__b img {
  display: block;
  height: 100%;
  object-fit: cover;
  max-width: none;
  pointer-events: none;
}

/* compare — 손잡이가 전후를 가른다 */
.krfx-compare {
  position: relative;
  overflow: hidden;
  cursor: ew-resize;
  user-select: none;
  touch-action: none;   /* 손가락으로 끌 때 페이지가 대신 움직이지 않게 */
}
.krfx-compare img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  -webkit-user-drag: none;   /* ⛔ 끄지 않으면 끌기가 「이미지 끌어놓기」가 됩니다 */
}
.krfx-compare img + img { position: absolute; inset: 0; }
.krfx-compare__grip {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  margin-left: -1px;
  background: var(--krfx-grip, #f2efe6);
  pointer-events: none;
  z-index: 3;
}
.krfx-compare__grip::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 42px;
  height: 42px;
  margin: -21px 0 0 -21px;
  border-radius: 50%;
  border: 2px solid var(--krfx-grip, #f2efe6);
  background: rgba(10, 10, 9, .35);
}

/* frame — 둥근 모서리를 따라 테두리가 그려진다.
   ⛔ 곧은 막대 네 개로 만들면 액자의 overflow:hidden 이 끝을 잘라 모서리가
   뚝 끊겨 보입니다. svg 를 액자 위에 얹어 잘리지 않게 둡니다. */
.krfx-frame { position: relative; }
.krfx-frame__svg {
  position: absolute;
  inset: 0;
  z-index: 3;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}
.krfx-frame__svg rect {
  fill: none;
  stroke: var(--krfx-grip, currentColor);
}
.krfx-frame img { opacity: 0; transition: opacity .7s cubic-bezier(.22, 1, .36, 1) .1s; }
.krfx-frame.krfx-in img { opacity: 1; }

/* accordion — 한 칸만 펴진다 */
.krfx-acc__head { cursor: pointer; }
.krfx-acc__body {
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  transition: max-height .5s cubic-bezier(.22, 1, .36, 1), opacity .35s;
}
.krfx-acc__row.krfx-on .krfx-acc__body {
  max-height: var(--krfx-acc-max, 240px);
  opacity: 1;
}

/* zebra — 좌우 번갈아 들어온다 */
/* ⛔ transform 이 아니라 translate 로 씁니다 — 까닭은 위 `.krfx-rise` 주석 참고. */
.krfx-zeb {
  opacity: 0;
  transition: opacity .7s cubic-bezier(.22, 1, .36, 1), translate .7s cubic-bezier(.22, 1, .36, 1);
}
.krfx-zeb--l { translate: -46px 0; }
.krfx-zeb--r { translate: 46px 0; }
[data-fx='zebra'].krfx-in .krfx-zeb { opacity: 1; translate: none; }

/* ⛔ **좌우로 들어오는 것은 제 상자 밖으로 나갑니다.**
 *
 * 넓은 화면에서는 양옆 여백이 그만큼을 먹어 줍니다. 그런데 **폰에는 그 여백이
 * 없습니다** — 나간 만큼 페이지가 통째로 가로로 밀립니다. 390px 에서 실측 :
 * dental 18px · interior 13px · cafe-mz 28px · pilates-mz 29px (2026-08-01).
 *
 * ⚠️ 자리를 옮기는 쪽(krfx-zeb·krfx-rise--side)이 아니라 **담는 상자**에서 잘라야
 *    합니다. 옮기는 쪽을 줄이면 효과가 안 보이게 됩니다.
 * ⚠️ `hidden` 이 아니라 `clip` 입니다 — `hidden` 은 스크롤 상자를 만들어 그 안의
 *    position:sticky 를 죽입니다(데모 여러 장이 sticky 로 사진을 붙여 둡니다).
 */
[data-fx='zebra'],
[data-fx='cascade'][data-fx-from='side'] { overflow-x: clip; }

/* pulse — 테두리만 조용히 번진다 */
.krfx-pulse { position: relative; }
.krfx-pulse::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  border: 3px solid var(--krfx-pulse, currentColor);
  pointer-events: none;
  animation: krfx-pulse var(--krfx-pulse-beat, 2200ms) cubic-bezier(.22, 1, .36, 1) infinite;
}
@keyframes krfx-pulse {
  0%   { transform: scale(1);    opacity: .9; }
  70%  { transform: scale(1.34); opacity: 0; }
  100% { transform: scale(1.34); opacity: 0; }
}

/* scan — 훑는 선이 지나간다 */
.krfx-scan-host { position: relative; overflow: hidden; }
/* ⚠️ 「너무 단순함」(2026-07-30) — 훑는 띠에 **밝은 선 하나**를 얹고, 지나가는
   동안 살짝 넓어졌다 좁아집니다. 뒤에는 옅은 가로줄이 깔립니다. */
.krfx-scan {
  position: absolute;
  left: 0;
  right: 0;
  height: 22%;
  pointer-events: none;
  background:
    linear-gradient(180deg, transparent, var(--krfx-scan, rgba(200, 255, 0, .18)), transparent),
    repeating-linear-gradient(180deg,
      rgba(255, 255, 255, .05) 0 1px, transparent 1px 4px);
  box-shadow: 0 0 24px var(--krfx-scan, rgba(200, 255, 0, .18));
  border-bottom: 1px solid var(--krfx-scan-line, rgba(200, 255, 0, .55));
  animation-name: krfx-scan;
  animation-timing-function: cubic-bezier(.6, 0, .4, 1);
  animation-iteration-count: infinite;
}
@keyframes krfx-scan {
  0%   { transform: translateY(-130%) scaleY(.6); opacity: 0; }
  12%  { opacity: 1; }
  50%  { transform: translateY(240%) scaleY(1.25); opacity: 1; }
  88%  { opacity: 1; }
  100% { transform: translateY(620%) scaleY(.6); opacity: 0; }
}

/* badge — 모서리에 리본이 돌아 들어온다 */
.krfx-badge-host { position: relative; overflow: hidden; }
.krfx-badge {
  position: absolute;
  right: -30%;
  top: 9%;
  z-index: 4;
  width: 80%;
  text-align: center;
  padding: .35em 0;
  background: var(--krfx-badge, currentColor);
  color: var(--krfx-badge-ink, #0a0a09);
  font-weight: 800;
  transform: rotate(38deg) translateX(120%);
  transition: transform .7s cubic-bezier(.3, 1.4, .5, 1);
}
.krfx-badge--l { right: auto; left: -30%; transform: rotate(-38deg) translateX(-120%); }
.krfx-badge-host.krfx-in .krfx-badge { transform: rotate(38deg); }
.krfx-badge-host.krfx-in .krfx-badge--l { transform: rotate(-38deg); }

/* divider — 가운데에서 좌우로 뻗는다 */
.krfx-divider {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1em;
}
.krfx-divider__l {
  flex: 1;
  height: 1px;
  background: var(--krfx-line-color, currentColor);
  opacity: .35;
  transform: scaleX(0);
  transition: transform .8s cubic-bezier(.22, 1, .36, 1);
}
.krfx-divider__l--a { transform-origin: right; order: -1; }
.krfx-divider__l--b { transform-origin: left; }
.krfx-divider.krfx-in .krfx-divider__l { transform: none; }

/* corner — 네 귀퉁이의 ㄱ 자 */
.krfx-corner { position: relative; }
.krfx-corner__c {
  position: absolute;
  width: var(--krfx-corner, 26px);
  height: var(--krfx-corner, 26px);
  z-index: 3;
  border: 2px solid var(--krfx-grip, currentColor);
  opacity: 0;
  transition: opacity .4s cubic-bezier(.22, 1, .36, 1),
              transform .5s cubic-bezier(.22, 1, .36, 1);
}
.krfx-corner__c1 { left: 10px; top: 10px; border-right: 0; border-bottom: 0; transform: translate(-8px, -8px); }
.krfx-corner__c2 { right: 10px; top: 10px; border-left: 0; border-bottom: 0; transform: translate(8px, -8px); }
.krfx-corner__c3 { right: 10px; bottom: 10px; border-left: 0; border-top: 0; transform: translate(8px, 8px); }
.krfx-corner__c4 { left: 10px; bottom: 10px; border-right: 0; border-top: 0; transform: translate(-8px, 8px); }
.krfx-corner.krfx-in .krfx-corner__c { opacity: 1; transform: none; }

/* sticker — 살짝 돌아간 채 튕기며 앉는다 */
.krfx-sticker {
  display: inline-block;
  opacity: 0;
  transform: rotate(var(--krfx-tilt, -7deg)) scale(.4);
  transition: opacity .3s, transform .62s cubic-bezier(.3, 1.6, .5, 1);
}
.krfx-sticker.krfx-in { opacity: 1; transform: rotate(var(--krfx-tilt, -7deg)) scale(1); }

/* quote — 큰 따옴표가 떠오른다 */
/**
 * ⚠️ **따옴표가 들어갈 높이를 글 상자가 스스로 갖습니다**
 * (2026-07-31 사장님 요청 63번 「높이 키워서 잘리지 않게」).
 *
 * 따옴표는 글자보다 두 배 넘게 큰데 상자 **밖에** 걸려 있어서, 상자가 잘리는
 * 자리(카드 미리보기 · 창)에서 위아래가 뭉텅 잘렸습니다. 위아래로 자리를
 * 내어 주고 따옴표를 그 안으로 들였습니다 — 보이는 모양은 그대로입니다.
 */
.krfx-quote {
  position: relative;
  padding-top: .62em;
  padding-bottom: .78em;
}
.krfx-quote__m {
  position: absolute;
  line-height: 1;
  /* ⚠️ 명조로 (2026-07-30 사장님 요청 71번) — 고딕 따옴표는 네모나 보입니다 */
  font-family: 'Nanum Myeongjo', 'Batang', 'Apple SD Gothic Neo', Georgia, serif;
  color: var(--krfx-quote, currentColor);
  opacity: 0;
  transition: opacity .6s cubic-bezier(.22, 1, .36, 1),
              transform .7s cubic-bezier(.22, 1, .36, 1);
  pointer-events: none;
}
/* ⚠️ 상자 안에 들어오는 자리입니다 — 밖으로 내면 다시 잘립니다 */
.krfx-quote__m--a { left: -.1em; top: 0; transform: translateY(14px); }
.krfx-quote__m--b { right: -.1em; bottom: 0; transform: translateY(14px); }
.krfx-quote.krfx-in .krfx-quote__m { opacity: .28; transform: none; }

/* tick — 체크가 그려진다. 자리 잡는 것은 루프가 한다 */
.krfx-tick-host > * { display: flex; align-items: center; gap: .6em; }
.krfx-tick {
  flex: 0 0 auto;
  width: 1.1em;
  height: 1.1em;
  overflow: visible;
}
.krfx-tick path {
  fill: none;
  stroke: var(--krfx-tick, currentColor);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* beam — 빛줄기가 비스듬히 흐른다 */
.krfx-beam-host { position: relative; overflow: hidden; }
.krfx-beam {
  position: absolute;
  top: -40%;
  height: 180%;
  pointer-events: none;
  background: linear-gradient(180deg, transparent, var(--krfx-beam, rgba(200, 255, 0, .13)), transparent);
  transform: rotate(18deg);
  animation-name: krfx-beam;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}
@keyframes krfx-beam {
  from { transform: rotate(18deg) translateX(-40%); opacity: .5; }
  to   { transform: rotate(18deg) translateX(40%);  opacity: 1; }
}

/* stripe — 대각선 줄무늬가 흐른다 */
.krfx-stripe-host { position: relative; overflow: hidden; }
.krfx-stripe {
  position: absolute;
  inset: -20%;
  pointer-events: none;
  opacity: var(--krfx-stripe-fade, .5);
  background-image: repeating-linear-gradient(
    45deg,
    var(--krfx-stripe-color, rgba(242, 239, 230, .07)) 0,
    var(--krfx-stripe-color, rgba(242, 239, 230, .07)) calc(var(--krfx-stripe-size, 26px) / 2),
    transparent calc(var(--krfx-stripe-size, 26px) / 2),
    transparent var(--krfx-stripe-size, 26px)
  );
  animation-name: krfx-stripe;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
@keyframes krfx-stripe {
  to { background-position: var(--krfx-stripe-size, 26px) 0; }
}

/* bokeh — 크고 흐릿한 빛망울 */
.krfx-bokeh-host { position: relative; overflow: hidden; }
.krfx-bokeh { position: absolute; inset: 0; pointer-events: none; }
.krfx-bokeh i {
  position: absolute;
  border-radius: 50%;
  background: var(--krfx-bokeh, currentColor);
  filter: blur(10px);
  animation-name: krfx-float;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}
@keyframes krfx-float {
  from { transform: translate(0, 14px) scale(.9); }
  to   { transform: translate(18px, -14px) scale(1.15); }
}

/* halo — 가운데 빛무리가 숨쉰다 */
.krfx-halo-host { position: relative; overflow: hidden; }
.krfx-halo {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 130%;
  aspect-ratio: 1;
  margin: -65% 0 0 -65%;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(circle, var(--krfx-halo, rgba(200, 255, 0, .18)) 0%, transparent 66%);
  animation-name: krfx-halo;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}
@keyframes krfx-halo {
  from { transform: scale(.62); opacity: .35; }
  to   { transform: scale(1.35); opacity: 1; }
}

/* arrow — 올리면 화살표만 앞으로 간다 */
.krfx-arrow { display: inline-flex; align-items: center; gap: .5em; }
.krfx-arrow__t {
  display: inline-block;
  transition: transform .38s cubic-bezier(.22, 1, .36, 1);
}
[data-fx='arrow']:hover .krfx-arrow__t,
[data-fx='arrow']:focus-visible .krfx-arrow__t { transform: translateX(.42em); }

/* shine — 비스듬한 빛이 한 번 훑는다 */
.krfx-shine { position: relative; overflow: hidden; }
.krfx-shine::after {
  content: '';
  position: absolute;
  top: -60%;
  bottom: -60%;
  width: 45%;
  left: -60%;
  pointer-events: none;
  background: linear-gradient(90deg, transparent, var(--krfx-shine, rgba(255, 255, 255, .34)), transparent);
  transform: rotate(18deg);
}
[data-fx='shine']:hover::after,
[data-fx='shine']:focus-visible::after { animation: krfx-shine .75s ease-out; }
.krfx-shine--auto::after {
  animation: krfx-shine var(--krfx-shine-beat, 2600ms) ease-out infinite;
}
@keyframes krfx-shine {
  from { left: -60%; }
  to   { left: 130%; }
}

/* border — 밝은 조각이 둘레를 돈다.
   ⚠️ frame 과 같은 이유로 둥근 모서리를 따라야 해서 svg 입니다. */
.krfx-border { position: relative; }
.krfx-border__svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}
.krfx-border__svg rect {
  fill: none;
  stroke: var(--krfx-border, currentColor);
  animation: krfx-border var(--krfx-border-turn, 3s) linear infinite;
}
@keyframes krfx-border {
  to { stroke-dashoffset: calc(var(--krfx-border-len, 400) * -1px); }
}

/* press — 눌리면 쑥 들어간다 */
.krfx-press { transition: transform .12s ease-out, filter .12s ease-out; }
.krfx-press:active { transform: scale(.955); filter: brightness(.92); }
.krfx-press--auto { animation: krfx-press var(--krfx-press-beat, 1900ms) ease-in-out infinite; }
@keyframes krfx-press {
  0%, 76%, 100% { transform: none;         filter: none; }
  84%           { transform: scale(.955);  filter: brightness(.92); }
}

/* split — 반씩 좌우로 밀린다. ⛔ 배경 그림이 아니라 진짜 사진을 민다
   (slice 와 같은 이유 — 배경으로 자르면 늘어집니다) */
.krfx-split { position: relative; overflow: hidden; }
.krfx-split__h {
  position: absolute;
  top: 0;
  width: 50%;
  height: 100%;
  overflow: hidden;
  transition: transform 1s cubic-bezier(.76, 0, .24, 1);
}
.krfx-split__h img { display: block; height: 100%; object-fit: cover; max-width: none; }
.krfx-split__h--a { left: 0; }
.krfx-split__h--b { right: 0; }
.krfx-split.krfx-in .krfx-split__h--a { transform: translateX(-101%); }
.krfx-split.krfx-in .krfx-split__h--b { transform: translateX(101%); }
/* 87 — 사선으로 갈라지게 (2026-07-30 사장님 요청).
   두 겹을 비스듬히 잘라 두면 갈라지는 자리가 대각선이 됩니다. */
.krfx-split--slant .krfx-split__h--a { clip-path: polygon(0 0, 100% 0, 78% 100%, 0 100%); }
.krfx-split--slant .krfx-split__h--b { clip-path: polygon(22% 0, 100% 0, 100% 100%, 0 100%); }
.krfx-split--slant.krfx-in .krfx-split__h--a { transform: translateX(-108%); }
.krfx-split--slant.krfx-in .krfx-split__h--b { transform: translateX(108%); }

/* card3d — 앞뒤가 뒤집힌다 */
.krfx-flipcard { position: relative; transform-style: preserve-3d; perspective: 1000px; }
.krfx-flipcard__f,
.krfx-flipcard__b {
  backface-visibility: hidden;
  transition: transform .72s cubic-bezier(.22, 1, .36, 1);
}
.krfx-flipcard__b { position: absolute; inset: 0; transform: rotateY(180deg); }
.krfx-flipcard:hover .krfx-flipcard__f,
.krfx-flipcard.krfx-over .krfx-flipcard__f { transform: rotateY(-180deg); }
.krfx-flipcard:hover .krfx-flipcard__b,
.krfx-flipcard.krfx-over .krfx-flipcard__b { transform: rotateY(0); }

/* grow — 액자 자체가 넓어진다. 폭은 스크립트가 정한다 */
[data-fx='grow'] { display: block; margin-inline: auto; overflow: hidden; }

/* mask — 덮개에 글자 구멍. cutout 과 붙는 자리가 다르다 */
.krfx-mask {
  background-image: var(--krfx-cut);
  background-size: cover;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}

/* swing — 위쪽을 축으로 흔들린다 */
.krfx-swing {
  transform-origin: top center;
  animation-name: krfx-swing;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}
@keyframes krfx-swing {
  from { transform: rotate(calc(var(--krfx-swing-arc, 2.6deg) * -1)); }
  to   { transform: rotate(var(--krfx-swing-arc, 2.6deg)); }
}

/* stackup — 아래에서 한 장씩 올라와 겹친다 */
.krfx-stackup {
  opacity: 0;
  transform: translateY(40px) rotate(0);
  transition: opacity .6s cubic-bezier(.22, 1, .36, 1),
              transform .7s cubic-bezier(.3, 1.3, .5, 1);
}
[data-fx='stackup'].krfx-in .krfx-stackup {
  opacity: 1;
  transform: rotate(var(--krfx-lean, 0deg));
}

/* numbers — 번호가 붙어 나온다 */
/* ⛔ transform 이 아니라 translate 로 씁니다 — 까닭은 `.krfx-rise` 주석 참고.
   (여기에 심은 업종 페이지는 아직 없지만, 함정 모양이 똑같아서 같이 고쳐 둡니다.) */
.krfx-numrow {
  display: flex;
  align-items: baseline;
  gap: .7em;
  opacity: 0;
  translate: -22px 0;
  transition: opacity .6s cubic-bezier(.22, 1, .36, 1),
              translate .6s cubic-bezier(.22, 1, .36, 1);
}
[data-fx='numbers'].krfx-in .krfx-numrow { opacity: 1; translate: none; }
.krfx-no {
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  color: var(--krfx-no, currentColor);
  opacity: .55;
}

/* floaty — 가만히 떠 있다 */
.krfx-floaty {
  animation-name: krfx-floaty;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}
@keyframes krfx-floaty {
  from { transform: translateY(calc(var(--krfx-lift, 10px) * -1)); }
  to   { transform: translateY(var(--krfx-lift, 10px)); }
}

/* crumbs — 지나온 자리가 점으로 남는다 */
.krfx-crumbs { display: flex; gap: .6em; align-items: center; }
/* ⚠️ 카드뉴스 캐러셀 모양 — 지금 장만 길쭉해집니다
   (2026-07-30 사장님 요청 86번). */
.krfx-crumbs i {
  width: 9px;
  height: 9px;
  border-radius: 99px;
  background: currentColor;
  opacity: .28;
  transition: width .34s cubic-bezier(.22, 1, .36, 1), opacity .3s;
}
.krfx-crumbs i.on { width: 34px; opacity: 1; }

/* weave — 두 줄이 엇갈려 흐른다 */
.krfx-weave { overflow: hidden; display: grid; gap: .5em; }
.krfx-weave__r { display: flex; width: max-content; will-change: transform; }
.krfx-weave__t { display: flex; gap: var(--krfx-weave-gap, 2rem); padding-right: var(--krfx-weave-gap, 2rem); flex: 0 0 auto; }

/* glowtext — 글자 둘레가 밝아진다 */
.krfx-glow {
  animation-name: krfx-glow;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}
@keyframes krfx-glow {
  from { text-shadow: 0 0 0 transparent; }
  to   { text-shadow: 0 0 26px var(--krfx-glow, currentColor); }
}

/* knock — 가끔 톡 튄다 */
.krfx-knock { animation: krfx-knock var(--krfx-knock-beat, 3400ms) ease-in-out infinite; }
@keyframes krfx-knock {
  0%, 88%, 100% { transform: none; }
  91%           { transform: translateY(-9px); }
  95%           { transform: translateY(0) scaleY(.97); }
}

/* veil — 얇은 막이 지나간다 */
.krfx-veil-host { position: relative; overflow: hidden; }
.krfx-veil {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(115deg, transparent 20%, var(--krfx-veil, rgba(10, 10, 9, .4)) 50%, transparent 80%);
  background-size: 260% 100%;
  animation-name: krfx-veil;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}
@keyframes krfx-veil {
  from { background-position: 0% 50%; }
  to   { background-position: 100% 50%; }
}

/* ── 같은 엔진의 다른 벌 ──────────────────────────────────────────── */

/* curtain — 좌우로 갈라지는 벌 */
.krfx-curtain--side .krfx-curtain { top: 0; bottom: 0; height: auto; width: 50.5%; }
.krfx-curtain--side .krfx-curtain--a { left: 0; right: auto; transform-origin: left; }
.krfx-curtain--side .krfx-curtain--b { right: 0; left: auto; transform-origin: right; }
.krfx-curtain--side.krfx-open .krfx-curtain { transform: scaleX(0); }

/* cascade — 옆에서 오는 벌 */
/* ⛔ transform 이 아니라 translate·scale 로 씁니다 — 까닭은 `.krfx-rise` 주석 참고. */
.krfx-rise--side {
  opacity: 0;
  translate: 90px 0;
  scale: .9;
  transition: opacity .7s cubic-bezier(.22, 1, .36, 1),
              translate .7s cubic-bezier(.22, 1, .36, 1),
              scale .7s cubic-bezier(.22, 1, .36, 1);
}
[data-fx='cascade'].krfx-in .krfx-rise--side { opacity: 1; translate: none; scale: none; }

/* swap — 아래로 굴러가는 벌 */
.krfx-swap--down .krfx-swap__b { transform: translateY(-105%); }
[data-fx='swap']:hover .krfx-swap--down .krfx-swap__a,
[data-fx='swap']:focus-visible .krfx-swap--down .krfx-swap__a { transform: translateY(105%); }

/* glitch — 저 혼자 지지직거리는 벌 */
.krfx-glitch--auto .krfx-glitch__x { opacity: .85; }
.krfx-glitch--auto .krfx-glitch__x1 { filter: hue-rotate(80deg); animation: krfx-glitch1 .9s steps(2) infinite; }
.krfx-glitch--auto .krfx-glitch__x2 { filter: hue-rotate(-70deg); animation: krfx-glitch2 .7s steps(2) infinite; }

/* dust — 떨어지는 벌 */
.krfx-dust--down i { animation-name: krfx-fall; }
@keyframes krfx-fall {
  from { transform: translateY(-40px); }
  to   { transform: translateY(40px); }
}

/* 움직임을 줄이겠다고 설정한 사람에게는 전부 정지 상태로 보인다.
   ⚠️ 이 블록을 지우지 마십시오 — 스크립트도 같은 판단을 하지만, 스크립트가
   실패해도 화면이 비어 보이면 안 됩니다. */
@media (prefers-reduced-motion: reduce) {
  [data-fx='words'] .krfx-w > span {
    transform: none;
    opacity: 1;
    transition: none;
  }
  [data-fx='elastic'] > *,
  [data-fx='magnet'],
  [data-fx='parallax'],
  [data-fx='tilt'],
  .krfx-mq {
    transform: none !important;
  }
  [data-fx='reveal'] {
    clip-path: none !important;
    transition: none;
  }
  [data-fx='reveal'] img {
    transform: none;
    transition: none;
  }
  .krfx-swap__a,
  .krfx-swap__b {
    transition: none;
  }
  .krfx-trail__shot.on {
    animation: none;
  }
  .krfx-curtain {
    display: none;   /* 덮개가 안 걷힐 바에는 처음부터 없는 편이 낫습니다 */
  }
  .krfx-stamp,
  .krfx-wave path {
    animation: none;
  }
  .krfx-rise {
    opacity: 1;
    translate: none;
    scale: none;
    transition: none;
  }
  .krfx-tinted {
    transition: none;
  }
  [data-fx='zoom'],
  .krfx-slide,
  .krfx-deck > * {
    transform: none !important;
    transition: none;
  }
  .krfx-glitch:hover .krfx-glitch__x {
    opacity: 0;
    animation: none;
  }
  .krfx-dust i {
    animation: none;
  }
  .krfx-type::after {
    animation: none;
  }
  .krfx-ring,
  .krfx-flip {
    animation: none;
  }
  .krfx-snap {
    scroll-snap-type: none;
  }
  .krfx-snap__page {
    opacity: 1;
    transition: none;
  }
  .krfx-loader { display: none; }
  .krfx-roll,
  .krfx-ghost__x {
    transform: none !important;
  }
  .krfx-drop,
  [data-fx='drop'].krfx-in .krfx-drop {
    opacity: 1;
    animation: none;
    transform: none;
  }
  .krfx-grain,
  .krfx-blob,
  .krfx-mesh,
  .krfx-orbit,
  .krfx-ripple__w {
    animation: none;
  }
  .krfx-ripple__w { display: none; }
  [data-fx='dock'] > * { transform: none !important; }
  .krfx-burger__bar,
  .krfx-fill::before,
  .krfx-fill__t,
  .krfx-sheet {
    transition: none;
  }
  .krfx-blur { filter: none; opacity: 1; transition: none; }
  .krfx-outline { color: currentColor; -webkit-text-stroke: 0; }
  .krfx-slam { opacity: 1; transform: none; filter: none; transition: none; }
  .krfx-zeb,
  .krfx-rise--side { opacity: 1; translate: none; scale: none; transition: none; }
  .krfx-blind { display: none; }
  .krfx-iris { clip-path: none !important; transition: none; }
  .krfx-slice__b { transform: none !important; transition: none; }
  .krfx-frame img { opacity: 1; transition: none; }
  .krfx-frame__svg { display: none; }
  .krfx-wavy__w,
  .krfx-flip__w.krfx-turn,
  .krfx-pulse::after,
  .krfx-scan,
  .krfx-glitch--auto .krfx-glitch__x1,
  .krfx-glitch--auto .krfx-glitch__x2 {
    animation: none;
  }
  .krfx-pulse::after,
  .krfx-scan { display: none; }
  .krfx-glitch--auto .krfx-glitch__x { opacity: 0; }
  .krfx-duotone img { transition: none; }
  .krfx-acc__body { transition: none; }
  .krfx-beam,
  .krfx-stripe,
  .krfx-bokeh i,
  .krfx-halo,
  .krfx-border__svg rect,
  .krfx-press--auto,
  .krfx-shine--auto::after {
    animation: none;
  }
  .krfx-beam,
  .krfx-halo,
  .krfx-shine::after { display: none; }
  .krfx-badge,
  .krfx-badge--l { transform: rotate(38deg); transition: none; }
  .krfx-badge--l { transform: rotate(-38deg); }
  .krfx-divider__l { transform: none; transition: none; }
  .krfx-corner__c { opacity: 1; transform: none; transition: none; }
  .krfx-sticker { opacity: 1; transform: rotate(var(--krfx-tilt, -7deg)); transition: none; }
  .krfx-quote__m { opacity: .28; transform: none; transition: none; }
  .krfx-tick path { stroke-dashoffset: 0 !important; }
  .krfx-arrow__t,
  .krfx-press { transition: none; }
  .krfx-swing,
  .krfx-floaty,
  .krfx-glow,
  .krfx-knock,
  .krfx-veil { animation: none; }
  .krfx-veil { display: none; }
  .krfx-split__h,
  .krfx-flipcard__f,
  .krfx-flipcard__b { transition: none; }
  .krfx-stackup {
    opacity: 1;
    transform: rotate(var(--krfx-lean, 0deg));
    transition: none;
  }
  .krfx-numrow { opacity: 1; translate: none; transition: none; }
  .krfx-crumbs i { transition: none; }
}
