  /* Paperlogy 웹폰트는 main.css 상단의 전역 @import(dynamic-subset)로 이미 로드되어 있어
     여기서 별도로 불러올 필요가 없습니다. */
  :root{
    --bg:#fbfbfd;
    --hint:#9a9aa5;
    --slot-border:#e3e3ea;
  }

  /* 퍼즐이 잠겨있는 동안(완성 전)에는 아래 실제 사이트가 스크롤/터치되지 않도록 막는다 */
  html.puzzle-locked, html.puzzle-locked body{
    overflow:hidden;
    height:100%;
    touch-action:none;
  }

  /* ---------- 퍼즐 오버레이 (완성 전까지 사이트 전체를 덮는 레이어) ---------- */
  #puzzleOverlay{
    position:fixed; inset:0; width:100%; height:100%;
    z-index:9999;
    overflow:hidden;
    -webkit-tap-highlight-color:transparent;
    background:
      radial-gradient(circle, rgba(90,80,140,0.05) 1px, transparent 1.2px) 0 0/28px 28px,
      var(--bg);
    font-family: var(--ff-body); /* 안내문구·완성 메시지 등 본문 텍스트 = Noto Sans KR */
    transition:opacity 1.1s cubic-bezier(.22,.61,.36,1);
  }
  #puzzleOverlay.hide{
    opacity:0;
    pointer-events:none;
  }

  /* 실제 사이트 콘텐츠: 퍼즐이 걷히는 것과 동시에 서서히 페이드인 되도록
     기본값은 투명 상태로 깔아두고(레이아웃/스크롤 리빌 관측에는 영향 없음),
     완성 시 오버레이가 사라지는 타이밍에 맞춰 함께 나타난다 */
  #siteRoot{
    opacity:0;
    transition:opacity 1.3s cubic-bezier(.22,.61,.36,1);
    will-change:opacity;
  }
  #siteRoot.visible{
    opacity:1;
  }
  #stage{
    position:fixed; inset:0; width:100%; height:100%;
    overflow:hidden;
  }

  /* ---------- decorative animated background ---------- */
  /* kept intentionally light: fewer layers, smaller blur radius, translate-only
     animation (no scale) so the compositor can cache each blob as a single
     GPU layer instead of re-rasterizing it every frame. */
  #bgDecor{
    position:fixed; inset:0; z-index:0;
    overflow:hidden; pointer-events:none;
    contain:strict;
  }
  #bgDecor .blob{
    position:absolute; border-radius:50%;
    filter:blur(38px);
    will-change:transform;
    transform:translateZ(0);
    backface-visibility:hidden;
  }
  #bgDecor .blob1{
    width:34vw; height:34vw; min-width:280px; min-height:280px; max-width:420px; max-height:420px;
    left:-10%; top:-12%;
    background:radial-gradient(circle at 35% 35%, rgba(79,195,216,0.5), rgba(79,195,216,0) 70%);
    animation:drift1 30s ease-in-out infinite;
  }
  #bgDecor .blob2{
    width:32vw; height:32vw; min-width:260px; min-height:260px; max-width:380px; max-height:380px;
    right:-8%; top:-6%;
    background:radial-gradient(circle at 40% 40%, rgba(150,150,225,0.42), rgba(150,150,225,0) 70%);
    animation:drift2 34s ease-in-out infinite;
  }
  #bgDecor .blob3{
    width:36vw; height:36vw; min-width:300px; min-height:300px; max-width:440px; max-height:440px;
    left:-6%; bottom:-14%;
    background:radial-gradient(circle at 45% 45%, rgba(243,133,195,0.4), rgba(243,133,195,0) 70%);
    animation:drift3 32s ease-in-out infinite;
  }
  #bgDecor .glow{
    position:absolute; left:50%; top:50%;
    width:min(70vw,900px); height:min(70vw,900px);
    transform:translate(-50%,-50%);
    background:radial-gradient(circle, rgba(255,255,255,0.9) 0%, rgba(255,255,255,0) 62%);
    opacity:.9;
  }
  @keyframes drift1{
    0%,100%{ transform:translateZ(0) translate(0,0); }
    50%{ transform:translateZ(0) translate(3.5vw,4vh); }
  }
  @keyframes drift2{
    0%,100%{ transform:translateZ(0) translate(0,0); }
    50%{ transform:translateZ(0) translate(-4vw,3.5vh); }
  }
  @keyframes drift3{
    0%,100%{ transform:translateZ(0) translate(0,0); }
    50%{ transform:translateZ(0) translate(3vw,-3.5vh); }
  }

  /* ---------- header hint (안내문이므로 조각을 옮겨도 사라지지 않는다) ---------- */
  #hint{
    position:absolute; top:6%; left:50%; transform:translateX(-50%);
    width:min(92vw, 560px);
    text-align:center; color:var(--hint);
    font-size:clamp(13px,2vw,17px);
    font-weight:500;
    letter-spacing:.02em;
    word-break:keep-all;
    pointer-events:none;
    z-index:5;
    transition:opacity .5s ease;
  }
  #hint b{ color:#6b6b78; }
  #hintTip{
    margin-top:6px;
    color:#bcbcc8;
    font-size:clamp(10px,1.5vw,14px);
    font-weight:400;
    letter-spacing:.01em;
    word-break:keep-all;
  }

  /* ---------- target slots ---------- */
  #target{
    position:absolute; top:50%; left:50%;
    transform:translate(-50%,-50%);
    display:flex; align-items:center; justify-content:center;
    gap:clamp(2px,0.6vw,8px);
    width:90vw;
    max-width:1180px;
    z-index:2;
    pointer-events:none;
  }
  #puzzleOverlay .slot{
    width:var(--piece-size); height:var(--piece-size);
    border-bottom:3px dashed var(--slot-border);
    border-radius:6px;
    display:flex; align-items:center; justify-content:center;
    transition:border-color .3s ease;
  }
  #puzzleOverlay .slot.filled{ border-color:transparent; }

  /* ---------- pieces ---------- */
  /* Rotation is fixed per-piece (never animated), so a piece with its
     drop-shadow filter stays a single unchanging bitmap that the compositor
     just translates each frame — no per-frame re-rasterization needed.
     That combo (animated rotation + filter) was the actual stutter source. */
  #puzzleOverlay .piece{
    position:absolute;
    top:0; left:0;
    width:var(--piece-size); height:var(--piece-size);
    display:flex; align-items:center; justify-content:center;
    font-family: var(--ff-display); /* 조각 글자(CONNECTION 타이틀) = Paperlogy */
    font-size:calc(var(--piece-size) * 0.82);
    font-weight:700;
    line-height:1;
    user-select:none;
    cursor:grab;
    will-change:transform;
    z-index:10;
    filter:drop-shadow(0 5px 8px rgba(60,50,90,0.14));
  }
  #puzzleOverlay .piece.dragging{
    cursor:grabbing;
    z-index:100;
    filter:drop-shadow(0 12px 18px rgba(60,50,90,0.22));
  }
  #puzzleOverlay .piece.locked{
    cursor:default;
    /* 완성 트랜지션(#connectWave, z-index:60)이 밑에서 지나갈 때도
       글자·아이콘이 항상 그 위에 보여야 하므로 더 높여둔다. */
    z-index:150;
  }
  #puzzleOverlay .piece svg{ width:100%; height:100%; display:block; overflow:visible; }
  #puzzleOverlay .piece img{
    width:76%; height:76%; object-fit:contain; display:block; pointer-events:none;
    transition:opacity .18s ease;
  }
  #puzzleOverlay .letter-fill{
    background-clip:text;
    -webkit-background-clip:text;
    color:transparent;
    -webkit-text-fill-color:transparent;
  }
  /* 클로버/스파클 원본 이미지 위에 정확히 겹쳐두는 흰색 버전 PNG 레이어.
     (이전에는 CSS mask-image로 원본 이미지의 실루엣만 흰색으로 채우는
     방식을 썼는데, 일부 렌더링 환경에서 도형 자체가 사라져 보이는 문제가
     있었다. 그래서 디자이너가 직접 제공한 흰색 PNG를 그대로 겹쳐두고,
     평소엔 투명하게 숨겼다가 필요할 때 원본 이미지와 크로스페이드하는
     방식으로 대체했다 — 훨씬 더 확실하게 렌더링된다.) */
  #puzzleOverlay .piece-icon-white{
    position:absolute;
    top:50%; left:50%;
    transform:translate(-50%,-50%);
    width:76%; height:76%;
    object-fit:contain;
    opacity:0;
    pointer-events:none;
    transition:opacity .18s ease;
  }
  /* 핑크색 캡슐 길이 나타나는 순간(둘 다 같은 타이밍에 트리거된다) 흰색으로
     크로스페이드되어, 어떤 원래 색이었든 핑크 배경 위에서 절대 묻혀
     보이지 않도록 한다. */
  #puzzleOverlay .piece.wave-white.letter-fill{
    -webkit-text-fill-color:#fff;
    color:#fff;
  }
  #puzzleOverlay .piece.wave-white img{ opacity:0; }
  #puzzleOverlay .piece.wave-white .piece-icon-white{ opacity:1; }

  /* ---------- 완성 트랜지션: 핑크 길 대신 폭죽 ---------- */
  /* 기존 핑크색 길/갈래길은 더 이상 렌더링하지 않는다. */
  #connectWave,
  #connectRoutes{ display:none !important; }

  /* CONNECTION이 모인 중심에서 순간적으로 터지는 폭죽 컨테이너 */
  #connectionFirework{
    position:fixed;
    left:50%;
    top:50%;
    width:0;
    height:0;
    z-index:210;
    pointer-events:none;
    opacity:0;
    transform:translate(-50%,-50%);
  }
  #connectionFirework.is-active{
    opacity:1;
  }
  #connectionFirework::before{
    content:"";
    position:absolute;
    left:50%;
    top:50%;
    width:18px;
    height:18px;
    border-radius:50%;
    background:#fff;
    box-shadow:0 0 18px 7px rgba(244,114,182,.95),
               0 0 50px 18px rgba(249,168,212,.7),
               0 0 90px 30px rgba(126,174,226,.35);
    transform:translate(-50%,-50%) scale(.2);
    opacity:0;
  }
  #connectionFirework.is-active::before{
    animation:firework-flash .42s ease-out forwards;
  }
  .firework-particle{
    position:absolute;
    left:50%;
    top:50%;
    width:var(--size);
    height:calc(var(--size) * 3.2);
    border-radius:999px;
    background:var(--particle-color);
    box-shadow:0 0 12px rgba(255,255,255,.85), 0 0 20px var(--particle-color);
    transform-origin:50% 0;
    opacity:0;
    transform:translate(-50%,0) rotate(var(--rotation)) scale(.15);
  }
  #connectionFirework.is-active .firework-particle{
    animation:firework-particle var(--duration) ease-out var(--delay) forwards;
  }

  @keyframes firework-flash{
    0%{ opacity:0; transform:translate(-50%,-50%) scale(.2); }
    12%{ opacity:1; transform:translate(-50%,-50%) scale(2.4); }
    100%{ opacity:0; transform:translate(-50%,-50%) scale(6.8); }
  }
  @keyframes firework-particle{
    0%{ opacity:0; transform:translate(-50%,0) rotate(var(--rotation)) scale(.2); }
    10%{ opacity:1; transform:translate(calc(-50% + var(--x) * .12), calc(var(--y) * .12)) rotate(var(--rotation)) scale(1); }
    72%{ opacity:.82; transform:translate(calc(-50% + var(--x) * .78), calc(var(--y) * .78)) rotate(var(--rotation)) scale(.8); }
    100%{ opacity:0; transform:translate(calc(-50% + var(--x)), var(--y)) rotate(var(--rotation)) scale(.35); }
  }


  /* ---------- 건너뛰기 버튼 ---------- */
  #skipBtn{
    position:fixed;
    right:max(18px, env(safe-area-inset-right, 18px));
    bottom:max(18px, env(safe-area-inset-bottom, 18px));
    z-index:200;
    padding:11px 20px;
    border:none;
    border-radius:999px;
    background:rgba(255,255,255,0.65);
    backdrop-filter:blur(8px);
    -webkit-backdrop-filter:blur(8px);
    box-shadow:0 6px 18px rgba(60,50,90,0.14);
    color:#7a7a88;
    font-family: var(--ff-body);
    font-size:13px;
    font-weight:600;
    letter-spacing:.01em;
    cursor:pointer;
    transition:background .2s ease, color .2s ease, transform .2s ease, box-shadow .2s ease, opacity .3s ease;
  }
  #skipBtn span{ display:inline-block; transition:transform .2s ease; margin-left:2px; }
  #skipBtn:hover{
    background:rgba(255,255,255,0.92);
    color:#4a4a58;
    transform:translateY(-2px);
    box-shadow:0 10px 24px rgba(60,50,90,0.18);
  }
  #skipBtn:hover span{ transform:translateX(3px); }
  #skipBtn:active{ transform:translateY(0); }
  #skipBtn.fading{ opacity:0; pointer-events:none; }

  @media (max-width:640px){
    #skipBtn{ font-size:12px; padding:9px 16px; }
  }

  /* main.css 는 사이트 전체에 대해 (접근성을 위해) 시스템의 '동작 줄이기'
     설정이 켜져 있으면 모든 transition-duration 을 0.01ms 로 강제한다
     (*{ transition-duration:0.01ms !important }). 그런데 이 조각 맞추기
     인터랙션은 장식이 아니라 그 자체가 핵심 동작이라, 이 규칙에 걸리면
     "부드럽게 이동"이 아니라 완전히 순간이동처럼 보여버린다.
     그래서 퍼즐 오버레이 쪽만 ID 선택자(*보다 우선순위가 높음)로
     실제 지속시간을 다시 살려준다 — 사이트의 나머지 스크롤 리빌 등은
     원래 설계대로 계속 '동작 줄이기'를 존중한다. */
  @media (prefers-reduced-motion: reduce){
    #puzzleOverlay .piece{ transition-duration: 0.4s !important; }
    #puzzleOverlay{ transition-duration: 1.1s !important; }
    #siteRoot{ transition-duration: 1.3s !important; }
    #connectionFirework.is-active .firework-particle{ animation-duration:1.45s !important; animation-delay:0ms !important; }
  }


/* ==========================================================================
   RESPONSIVE VIEWPORT HARDENING
   ========================================================================== */
#puzzleOverlay{
  height: 100svh;
  min-height: 100svh;
}
#stage{
  height: 100svh;
}
@supports (height: 100dvh){
  #puzzleOverlay{
    height: 100dvh;
    min-height: 100svh;
  }
  #stage{
    height: 100dvh;
  }
}

@media (max-width: 640px){
  #hint{
    top: max(5%, calc(64px + env(safe-area-inset-top, 0px)));
    width: min(92vw, 520px);
    padding: 0 8px;
  }
  #skipBtn{
    right: max(14px, env(safe-area-inset-right, 14px));
    bottom: max(14px, env(safe-area-inset-bottom, 14px));
  }
}
