/* ==========================================================================
   Pharmforum Landing v3 — main.css
   Concept: "Connected Intelligence"

   디자인(컬러 · 타이포 · 컴포넌트)은 v2 를 그대로 계승합니다.
   v3 에서 바뀐 것은 "움직임의 배선" 뿐입니다.

     · Lenis → ScrollSmoother
       #smooth-content 에 transform 이 걸리므로
         ① position:fixed 요소는 래퍼 밖으로 (index.php)
         ② position:sticky 는 동작하지 않음 → 전부 ScrollTrigger pin 으로 전환
           (11. Strengths 섹션이 여기에 해당합니다)
     · data-speed / data-lag  ScrollSmoother 이펙트용 패럴랙스 레이어
     · --vel                  스크롤 속도(px/s)를 각도로 환산한 전역 변수.
                              가로 트랙 카드가 이 값으로 살짝 기웁니다.
     · [data-split]           SplitText 가 만드는 .sline 구조 (v2 와 동일 계약)

   Palette derived from the printed brochure (navy / blue / light-blue / green)
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. 애니메이션되는 커스텀 속성의 타입 선언

   JS 가 값을 흘려보내는 변수는 전부 여기서 <number> 로 등록합니다.
   등록하지 않으면 브라우저가 이 값을 "정체 불명의 토큰" 으로 취급해서
     · GSAP 이 시작값을 읽지 못하고 0 으로 가정합니다
       (--s 가 0 이 되면 transform: scale(0) → 카드가 통째로 사라집니다)
     · 값 사이의 보간이 불가능해 계단처럼 튑니다
   initial-value 를 함께 주면 JS 가 아직 손대지 않은 요소도 안전한 기본값을 갖습니다.
   -------------------------------------------------------------------------- */
/* 핀을 풀면서 사라진 등록 — 값을 만들어 내던 스크럽이 없어졌습니다.
     v4.1  --s   강점 스택 카드 축소   (스택 해제)
           --dim 강점 스택 카드 딤     (스택 해제)
           --p   Hero 비트 레일 채움   (레일 제거 — 부분 진행률이라는 값이 없습니다)
     v4.2  --c   기능 카드 중앙 근접도 (가로 트랙 → CardSwap 덱)
           --ry  기능 카드 Y축 각도    (위와 같음) */
@property --w   { syntax: "<number>";  inherits: false; initial-value: 1; }  /* 인용 배너 와이프 */
@property --bpop { syntax: "<number>"; inherits: false; initial-value: 1; }  /* 히어로 뱃지 팝 (scale — transform 을 피합니다) */
/* ★ inherits: true — 값은 em 에 쓰고 읽는 쪽은 em::after 입니다.
   가상 요소는 상속 관점에서 자식이라 false 면 초기값만 봅니다 (v4.3 에서 겪은 함정). */
@property --emw  { syntax: "<number>"; inherits: true;  initial-value: 1; }  /* h1 악센트 밑줄 그어지기 */
/* 아래 둘은 inherits: true 여야 합니다.
   JS 는 값을 .btn 에 쓰는데 실제로 읽는 쪽은 .btn::before 입니다.
   가상 요소는 상속 관점에서 자식이라, inherits:false 로 등록하면 pseudo 가
   부모의 값을 못 보고 initial-value(0)만 봅니다 → 링이 영원히 opacity 0.
   (실제로 그렇게 등록했다가 잡은 버그입니다) */
@property --sb-a  { syntax: "<number>"; inherits: true; initial-value: 0; }  /* 버튼 스펙큘러 광원 각도(deg) */
@property --sb-on { syntax: "<number>"; inherits: true; initial-value: 0; }  /* 버튼 스펙큘러 밝기 0..1 */
@property --mx  { syntax: "<number>";  inherits: false; initial-value: 0; }  /* 마그넷 버튼 X */
@property --my  { syntax: "<number>";  inherits: false; initial-value: 0; }  /* 마그넷 버튼 Y */
@property --vel { syntax: "<number>";  inherits: true;  initial-value: 0; }  /* 전역 스크롤 속도 */
@property --i   { syntax: "<integer>"; inherits: false; initial-value: 0; }  /* 스택 순번 */

/* --------------------------------------------------------------------------
   1. Design tokens  (v1 과 동일 — 브랜드 규칙: 블루는 면적, 그린은 점)
   -------------------------------------------------------------------------- */
:root {
  /* brand */
  --navy-900: #0F2A5C;
  --navy-800: #163A79;
  --navy-700: #1D4A9E;
  --blue-600: #1E6ACB;
  --blue-500: #2B7FE0;
  --blue-400: #58A0EC;
  --blue-300: #7FB2E8;
  --blue-200: #B9D7F4;
  --blue-100: #D6E8F7;
  --blue-50:  #EAF3FB;
  --blue-25:  #F5FAFF;

  --green-500: #7AB531;
  --green-400: #8CC63F;
  --green-300: #A9D96A;

  --amber-400: #F2A93B;
  --violet-400: #8C5BD8;

  --ink-900: #0B1B36;
  --ink-700: #2B3A55;
  --ink-600: #4A5568;
  --ink-500: #6B7A90;
  --ink-400: #93A1B5;
  --line:     #DCE8F5;
  --line-soft:#EDF3FA;
  --white:    #FFFFFF;

  /* semantic */
  --bg:        var(--blue-25);
  --surface:   var(--white);
  --text:      var(--ink-700);
  --heading:   var(--navy-900);
  --accent:    var(--blue-500);
  --action:    var(--green-400);

  /* shape */
  --r-sm: 10px;
  --r-md: 16px;
  --r-lg: 24px;
  --r-xl: 32px;
  --r-full: 999px;

  /* elevation — light-blue ground needs cool-tinted shadows */
  --sh-1: 0 2px 8px rgba(15,42,92,.05), 0 1px 2px rgba(15,42,92,.04);
  --sh-2: 0 10px 30px rgba(15,42,92,.08), 0 2px 6px rgba(15,42,92,.04);
  --sh-3: 0 24px 60px rgba(15,42,92,.12), 0 6px 16px rgba(15,42,92,.06);
  --sh-4: 0 40px 90px rgba(15,42,92,.16), 0 10px 24px rgba(15,42,92,.08);
  --sh-in: inset 0 1px 0 rgba(255,255,255,.9);

  /* motion */
  --e-out: cubic-bezier(.22,.85,.3,1);
  --e-io:  cubic-bezier(.65,.02,.28,1);
  --e-mask: cubic-bezier(.16,1,.3,1);   /* v2 — 마스크 리빌 전용, 끝이 아주 부드러움 */

  /* layout */
  --shell: 1240px;
  --gutter: clamp(20px, 5vw, 48px);
  --nav-h: 74px;

  /* motion state — main.js 가 매 프레임 갱신합니다 */
  --vel: 0;      /* 스크롤 속도를 -6deg ~ 6deg 로 환산한 값 */

  /* ---- 로드 인트로 시간 배율 ----------------------------------------------
     인트로 전체(CSS 전환 · JS 타임라인 · 도트 버스트 · 헤더)의
     **유일한 속도 노브**입니다.
     1 = HERO-INTRO-PLAN.md §3 의 원래 1.5초 시퀀스. 2.0 = 히어로 인트로 4.4초.
     CSS 는 calc() 로, JS 는 이 변수를 읽어서 같은 배율을 씁니다 —
     한쪽만 바꾸면 카피와 목업이 서로 어긋납니다.

     ※ 계획서 §3 은 "전체 1.5초. 이보다 길면 로드가 느리다는 인상" 이라고 적어 뒀지만,
       실제로 보니 너무 빨라서 늘렸습니다. 되돌리려면 이 값만 1 로 두면 됩니다.
       2.0 이 상한 — 더 늘리면 바로 스크롤을 내리는 사용자가 연출을 지나칩니다. */
  --intro-s: 2.0;

  /* type */
  --ff: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont,
        "Apple SD Gothic Neo", "Malgun Gothic", system-ui, sans-serif;
  --ff-num: "Outfit", "Pretendard Variable", system-ui, sans-serif;
}

/* --------------------------------------------------------------------------
   2. Reset & base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: auto; /* Lenis takes over */
}

body {
  margin: 0;
  font-family: var(--ff);
  font-size: 16px;
  line-height: 1.7;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  word-break: keep-all;
  /* clip (not hidden) — hidden 은 스크롤 컨테이너를 만들어 position:sticky 를 깨뜨립니다 */
  overflow-x: clip;
}
@supports not (overflow: clip) {
  body { overflow-x: hidden; }
}

body.is-loading { cursor: progress; }
body.nav-open   { overflow: hidden; }

/* ---- ScrollSmoother 래퍼 ----
   실제 배치(position:fixed · overflow:hidden 등)는 ScrollSmoother 가 인라인으로
   심습니다. 여기서는 "스무더가 없을 때도 문서가 정상적으로 흐르도록" 만 보장합니다. */
#smooth-wrapper { position: relative; }
#smooth-content { will-change: transform; }
/* 스무더가 뜨지 않은 경우(reduced-motion · CDN 실패)에는 평범한 블록으로 둡니다 */
.no-smoother #smooth-content { will-change: auto; }

h1, h2, h3, h4 { margin: 0; color: var(--heading); font-weight: 800; letter-spacing: -.02em; line-height: 1.28; }
p  { margin: 0; }
ul, ol, dl, dd { margin: 0; padding: 0; list-style: none; }
a  { color: inherit; text-decoration: none; }
img, svg, canvas { display: block; max-width: 100%; }
button, input, textarea, select { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; padding: 0; }
em { font-style: normal; }
b, strong { font-weight: 700; }

:focus-visible {
  outline: 2px solid var(--blue-500);
  outline-offset: 3px;
  border-radius: 4px;
}

::selection { background: var(--blue-200); color: var(--navy-900); }

.skip {
  position: fixed; top: -100px; left: 16px; z-index: 999;
  padding: 12px 20px; background: var(--navy-900); color: #fff; border-radius: var(--r-sm);
  transition: top .2s;
}
.skip:focus { top: 16px; }

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.br-sm { display: none; }

/* --------------------------------------------------------------------------
   3. Logo
   -------------------------------------------------------------------------- */
/* 로고는 원본 PNG(assets/img/pharmaforumlogo.png 에서 여백을 걷어낸 것) 입니다.
   워드마크 색이 이미지에 구워져 있으므로 밝은/다크 배경용 두 장을 겹쳐 두고
   opacity 로 교차시킵니다. display:none 으로 갈아 끼우면 전환이 툭 끊기고,
   숨긴 쪽이 다시 나타날 때 디코딩이 한 번 더 들어갑니다. */
.logo { display: inline-flex; align-items: center; gap: 10px; }
.logo--stack { flex-direction: column; align-items: flex-start; gap: 9px; }

.logo__img {
  --logo-h: 36px;
  position: relative; display: block; flex: none;
  height: var(--logo-h); aspect-ratio: 926 / 200;
}
.logo__png {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  transition: opacity .35s var(--e-out);
}
.logo__png--on-dark { opacity: 0; }
.logo--light .logo__png--on-light { opacity: 0; }
.logo--light .logo__png--on-dark  { opacity: 1; }

/* 섹션 제목 안의 심볼 — 예전 인라인 SVG 자리 */
.logo__mark { width: 26px; height: auto; flex: none; }

.logo__tagline { font-size: 11.5px; font-weight: 500; color: var(--ink-500); letter-spacing: -.01em; }
.logo--light .logo__tagline { color: var(--blue-200); }

/* --------------------------------------------------------------------------
   4. Buttons
   -------------------------------------------------------------------------- */
.btn {
  --btn-bg: var(--action);
  --btn-fg: #fff;
  position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 13px 24px;
  border-radius: var(--r-full);
  background: var(--btn-bg); color: var(--btn-fg);
  font-weight: 700; font-size: 15px; letter-spacing: -.01em;
  white-space: nowrap;
  transition: transform .35s var(--e-out), box-shadow .35s var(--e-out), background .25s;
  overflow: hidden;
  isolation: isolate;
}
.btn::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(120deg, transparent 30%, rgba(255,255,255,.35), transparent 70%);
  transform: translateX(-120%);
  transition: transform .7s var(--e-out);
}
.btn:hover::after { transform: translateX(120%); }

/* ---- 테두리 스펙큘러 (reactbits.dev/components/specular-button 이식) --------
   원본은 버튼마다 WebGL 캔버스를 띄워 둥근 사각형 SDF 위에 스트릭을 그립니다.
   여기서는 conic-gradient 를 링으로 마스킹해 같은 그림을 냅니다 —
   WebGL 컨텍스트 0개, rAF 루프 하나. (자세한 이유는 assets/js/specular.js 헤더)

     --sb-a    광원 각도(deg). specular.js 가 포인터 방향으로 감쇠시켜 씁니다
     --sb-on   0..1 밝기. 커서가 가까울수록 1 (proximity 260px)
     --sb-line 스트릭 색. 밝은 면 위에서는 흰색이 사라지므로 변형마다 다릅니다

   0° 와 180° 두 곳에 스톱을 두는 것은 원본의 abs(dot(nEll, L)) 을 옮긴 것입니다 —
   광원을 마주 보는 쪽과 등진 쪽이 둘 다 반짝입니다.
   ---------------------------------------------------------------------------- */
.btn::before {
  content: "";
  position: absolute; inset: 0; z-index: 0;
  border-radius: inherit;
  padding: 1.5px;                     /* = 링 두께 */
  pointer-events: none;
  opacity: var(--sb-on, 0);
  /* 스톱을 촘촘히 둬 각도 감쇠를 부드럽게 합니다.
     1.5px 링이라 계단이 지면 스트릭이 아니라 얼룩으로 읽힙니다. */
  background: conic-gradient(from calc(var(--sb-a, 0) * 1deg),
    var(--sb-line) 0deg,
    color-mix(in srgb, var(--sb-line) 58%, transparent) 9deg,
    color-mix(in srgb, var(--sb-line) 20%, transparent) 22deg,
    transparent 40deg,
    transparent 140deg,
    color-mix(in srgb, var(--sb-line) 20%, transparent) 158deg,
    color-mix(in srgb, var(--sb-line) 58%, transparent) 171deg,
    var(--sb-line) 180deg,
    color-mix(in srgb, var(--sb-line) 58%, transparent) 189deg,
    color-mix(in srgb, var(--sb-line) 20%, transparent) 202deg,
    transparent 220deg,
    transparent 320deg,
    color-mix(in srgb, var(--sb-line) 20%, transparent) 338deg,
    color-mix(in srgb, var(--sb-line) 58%, transparent) 351deg,
    var(--sb-line) 360deg);
  /* 안쪽을 도려내 링만 남깁니다 (content-box XOR border-box) */
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask-composite: exclude;
}

/* 색은 버튼이 놓인 바탕에 따라 갈립니다.

   색면 위(그린 프라이머리 등)  흰 스트릭 — 원본 그대로. 잘 읽힙니다.

   흰 고스트 버튼 위            흰 스트릭은 사라집니다. 그렇다고 블루를 쓰면
                                이 버튼의 테두리(--blue-200, hover 시 --blue-400)와
                                같은 색 계열이라 "스트릭" 이 아니라 "테두리가 파래진 것"
                                으로 읽힙니다. 그래서 한 단계 더 어두운 네이비를 씁니다 —
                                밝은 지면에서는 발광이 아니라 잉크가 형태를 드러낸다는
                                v4 의 원칙과 같습니다 (DESIGN-ANALYSIS §3). */
.btn { --sb-line: rgba(255,255,255,.92); }
.btn--ghost { --sb-line: rgba(43,127,224,.8); }

/* ★ inset 은 0 이어야 합니다.
   고스트에는 1.5px 테두리가 있어서, 링을 실제 바깥 가장자리에 맞추려고
   inset: -1.5px 로 밀어냈더니 **링이 통째로 사라졌습니다.**
   .btn 의 overflow: hidden 은 padding box 기준으로 잘라내므로 테두리 영역에
   놓인 것은 전부 클립됩니다. 링은 테두리 안쪽(padding box 가장자리)에 둡니다. */
.btn__icon { width: 18px; height: 18px; transition: transform .35s var(--e-out); }
.btn:hover .btn__icon { transform: translateX(4px); }

.btn--primary { box-shadow: 0 8px 22px rgba(122,181,49,.32); }
.btn--primary:hover { box-shadow: 0 14px 30px rgba(122,181,49,.4); }

.btn--ghost {
  --btn-bg: rgba(255,255,255,.7);
  --btn-fg: var(--navy-900);
  border: 1.5px solid var(--blue-200);
  backdrop-filter: blur(8px);
  box-shadow: var(--sh-1);
}
.btn--ghost:hover { --btn-bg: #fff; border-color: var(--blue-400); box-shadow: var(--sh-2); }

.btn--sm  { padding: 10px 18px; font-size: 14px; }
.btn--lg  { padding: 16px 30px; font-size: 16px; }
.btn--block { display: flex; width: 100%; }

/* 포인터가 다가오면 살짝 끌려오는 버튼.
   ui-motion.js 의 gsap.quickTo 가 --mx/--my 를 "단위 없는 수"로 흘려 보내므로
   여기서 px 로 환산합니다 (quickTo 는 숫자만 다룹니다). */
[data-magnet] {
  transform: translate3d(calc(var(--mx, 0) * 1px), calc(var(--my, 0) * 1px), 0);
}

.btn__spin { display: none; width: 16px; height: 16px; border: 2px solid rgba(255,255,255,.4); border-top-color: #fff; border-radius: 50%; animation: spin .7s linear infinite; }
.btn.is-busy { pointer-events: none; opacity: .8; }
.btn.is-busy .btn__spin { display: block; }
.btn.is-busy .btn__icon { display: none; }
@keyframes spin { to { transform: rotate(360deg); } }

/* --------------------------------------------------------------------------
   5. Section head
   -------------------------------------------------------------------------- */
.sect-head { margin-bottom: clamp(38px, 5vw, 62px); }
.sect-head--center { text-align: center; }

.sect-head__eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 12.5px; font-weight: 700; letter-spacing: .16em;
  color: var(--blue-600); text-transform: uppercase;
  margin-bottom: 14px;
}
.sect-head__eyebrow--light { color: var(--blue-300); }
.dot {
  width: 6px; height: 6px; border-radius: 50%; background: var(--green-400);
  box-shadow: 0 0 0 4px rgba(140,198,63,.2);
}

.sect-head__title {
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap;
  /* 12px 는 로고 마크와 텍스트 사이의 "가로" 간격입니다.
     gap 으로 주면 제목이 두 줄로 넘어갈 때 줄 사이에도 12px 이 끼어들어
     line-height 위에 덧붙습니다. 축을 갈라 둡니다. */
  column-gap: 12px; row-gap: 0;
  font-size: clamp(27px, 4.2vw, 42px);
}
.sect-head--center .sect-head__title { justify-content: center; }
.sect-head__title em { color: var(--blue-500); }
.sect-head__mark { width: 26px; height: auto; }

.sect-head__lead {
  margin-top: 16px;
  font-size: clamp(15px, 1.6vw, 17.5px);
  color: var(--ink-600);
}

/* --------------------------------------------------------------------------
   6. Navigation + 섹션 진행 레일
   -------------------------------------------------------------------------- */
.nav {
  position: fixed; inset: 0 0 auto 0; z-index: 100;
  transition: transform .45s var(--e-out), background .4s, box-shadow .4s;
}
.nav__inner {
  display: flex; align-items: center; gap: 24px;
  height: var(--nav-h);
}
.nav.is-stuck {
  background: rgba(255,255,255,.82);
  backdrop-filter: saturate(180%) blur(16px);
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  box-shadow: 0 1px 0 rgba(15,42,92,.07), 0 8px 24px rgba(15,42,92,.05);
}
.nav.is-hidden { transform: translateY(-100%); }

/* 다크 섹션 위에서는 반전 */
.nav.is-dark:not(.is-stuck) .nav__link { color: #fff; }
.nav.is-dark:not(.is-stuck) .logo__tagline { color: var(--blue-200); }
/* 워드마크가 네이비라 다크 구간에서는 흰 워드마크 판으로 교차시킵니다 */
.nav.is-dark:not(.is-stuck) .logo__png--on-light { opacity: 0; }
.nav.is-dark:not(.is-stuck) .logo__png--on-dark  { opacity: 1; }
.nav.is-dark:not(.is-stuck) .nav__toggle { color: #fff; }

.nav__logo { margin-right: auto; }
.nav__menu { display: flex; align-items: center; gap: 4px; }
.nav__link {
  position: relative; padding: 9px 15px; border-radius: var(--r-full);
  font-size: 15px; font-weight: 600; color: var(--ink-700);
  transition: color .25s, background .25s;
}
.nav__link::after {
  content: ""; position: absolute; left: 50%; bottom: 3px; translate: -50% 0;
  width: 0; height: 2px; border-radius: 2px; background: var(--blue-500);
  transition: width .3s var(--e-out);
}
.nav__link:hover { color: var(--navy-900); background: rgba(43,127,224,.07); }
.nav__link.is-active { color: var(--blue-600); }
.nav__link.is-active::after { width: 18px; }

.nav__actions { display: flex; align-items: center; gap: 10px; }
.nav__toggle { display: none; width: 42px; height: 42px; border-radius: 12px; color: var(--navy-900); }
.nav__toggle svg { width: 24px; height: 24px; margin-inline: auto; }
.nav__toggle-close { display: none; }
.nav__toggle[aria-expanded="true"] .nav__toggle-open { display: none; }
.nav__toggle[aria-expanded="true"] .nav__toggle-close { display: block; }

.nav__progress { position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: transparent; overflow: hidden; }
.nav__progress i {
  display: block; height: 100%; width: 100%;
  background: linear-gradient(90deg, var(--blue-500), var(--green-400));
  transform: scaleX(0); transform-origin: 0 50%;
}

.nav__drawer {
  display: none;
  padding: 14px var(--gutter) 30px;
  background: rgba(255,255,255,.97);
  backdrop-filter: blur(16px);
  box-shadow: var(--sh-3);
  border-top: 1px solid var(--line-soft);
}
.nav__drawer-link {
  display: flex; align-items: center; justify-content: space-between;
  padding: 15px 4px; font-size: 17px; font-weight: 700; color: var(--navy-900);
  border-bottom: 1px solid var(--line-soft);
}
.nav__drawer-link svg { width: 18px; height: 18px; color: var(--blue-300); }
.nav__drawer .btn { margin-top: 20px; }

/* ---- 우측 진행 레일 ---- */
.rail {
  position: fixed; right: clamp(10px, 1.5vw, 24px); top: 50%; translate: 0 -50%;
  z-index: 80;
  display: flex; flex-direction: column; align-items: flex-end; gap: 13px;
  opacity: 0; visibility: hidden;
  transition: opacity .5s var(--e-out), visibility .5s;
}
.rail.is-on { opacity: 1; visibility: visible; }
.rail__dot {
  display: flex; align-items: center; gap: 9px;
  color: var(--ink-500);
}
.rail__dot i {
  display: block; width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: rgba(15,42,92,.2);
  transition: all .4s var(--e-out);
}
.rail__dot span {
  font-size: 11.5px; font-weight: 700; letter-spacing: -.01em;
  opacity: 0; transform: translateX(6px);
  transition: opacity .35s var(--e-out), transform .35s var(--e-out);
  white-space: nowrap;
}
.rail__dot:hover span { opacity: .8; transform: none; }
.rail__dot.is-active i {
  width: 7px; height: 20px; border-radius: var(--r-full);
  background: linear-gradient(180deg, var(--blue-500), var(--green-400));
}
.rail__dot.is-active span { opacity: 1; transform: none; color: var(--navy-900); }
.rail.is-dark .rail__dot i { background: rgba(255,255,255,.28); }
.rail.is-dark .rail__dot span { color: rgba(255,255,255,.9); }
.rail.is-dark .rail__dot.is-active i { background: linear-gradient(180deg, var(--blue-300), var(--green-300)); }

/* --------------------------------------------------------------------------
   7. Hero — 패럴랙스 (핀 없음)

   v4.0 은 이 섹션을 3.1 뷰포트만큼 핀 고정하고 그 위에서 카피 비트를 갈아
   끼웠습니다. v4.1 에서 패럴랙스로 방향을 바꾸며 핀을 풀었고, 그래서
     · 겹쳐 두고 교체하던 .hero__beats 컨테이너가 사라졌습니다
     · 비트는 세로로 펴져 각자 제 자리를 갖습니다 (.hero__fold / .hero__flow)
     · 배경 그라디언트가 .hero__pin 에서 .hero (섹션 전체) 로 올라갔습니다
   -------------------------------------------------------------------------- */
.hero {
  position: relative;
  isolation: isolate;
  /* 느리게 흐르는 배경층이 섹션 밖으로 새지 않게 잘라냅니다 */
  overflow: hidden;
  background:
    radial-gradient(120% 46% at 78% 4%, rgba(255,255,255,.95) 0%, rgba(255,255,255,0) 55%),
    radial-gradient(90% 40% at 8% 52%, rgba(140,198,63,.10) 0%, rgba(140,198,63,0) 60%),
    linear-gradient(168deg, var(--blue-25) 0%, var(--blue-50) 30%, var(--blue-100) 68%, var(--blue-25) 100%);
}

/* ---- 배경 컨테이너 ---------------------------------------------------------
   v4~v7 은 이 안에 .hero__lines 한 장(Floating Lines / scene-lines.js)이
   data-speed="clamp(0.80)" 으로 **스크롤에 묶여** 있었습니다. 층이 하나뿐이면
   속도차가 생길 상대가 없어 깊이가 나오지 않고, 스크롤을 멈추면 배경도 멈춥니다.
   v8 에서 4층(.hero__layers — 아래 § 참조)으로 바꾸고 전부 시간 구동으로
   돌렸습니다. .hero__lines 규칙은 HERO-LAYERED-BG.md §8-4 대로 걷어냈습니다.
   ---------------------------------------------------------------------------- */
.hero__bg {
  position: absolute; inset: 0; z-index: 0;
  pointer-events: none;
  overflow: hidden;
}
/* 인트로 도트 그리드 — 4층 배경보다 아래층.
   비네팅은 마스크가 아니라 점마다 다른 최종 불투명도로 냅니다
   (ANIMEJS-INTRO-ANALYSIS.md §5-2 — stagger 를 값에도 쓴다). */
.hero__dots {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  pointer-events: none;
}

/* 다음 섹션으로 녹아드는 하단 페이드 — 그라디언트와 배경층을 함께 지웁니다 */
.hero::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 200px; z-index: 1;
  background: linear-gradient(180deg, rgba(245,250,255,0) 0%, var(--blue-25) 92%);
  pointer-events: none;
}

/* ══════════════════════════════════════════════════════════════════════════
   7-1. 히어로 4층 배경 — Animated Layered Background

   마크업 includes/hero-bg.php · 스크립트 assets/js/hero-layers.js
   설계   HERO-LAYERED-BG.md

   확정 전까지 assets/css/hero-bg.css 에 따로 두었다가 여기로 병합했습니다
   (§8-3). A/B 비교 단계의 [data-variant] 선택자도 함께 걷어냈습니다 (§8-1).

     L1  가장 멀리  색면  Soft Aurora   CSS 그라디언트    ~100s  ↘  blur 큼
     L2  중경       구조  Dot Grid      CSS 반복 패턴       46s  ↘  blur 0
     L3  주역       결    Silk          WebGL              시간 구동
     L4  전경       입자  근경 입자     2D 캔버스           40s  ↖  blur 7px

   ── 이 블록이 지키는 규칙 ────────────────────────────────────────────────

   ① 애니메이션은 transform / opacity 만 씁니다.
      background-position 을 움직이면 매 프레임 큰 면적을 다시 칠합니다.
      층을 컨테이너보다 크게 잡고(오버스캔) transform 으로 미는 이유입니다.

   ② 오버스캔은 **가상요소**가 맡습니다.
      .hlayer 자체는 inset:0 · overflow:hidden 으로 두고 마스크를 겁니다.
      배경을 .hlayer 에 직접 그리고 inset 을 음수로 주면 마스크의 기준 박스가
      같이 커져 마스크가 밀립니다. 그래서 그리는 면은 ::before/::after 입니다.

   ③ 반복 패턴의 이동량은 **타일 크기의 정수배**입니다.
      L2 도트(34px)가 그렇습니다.
   ══════════════════════════════════════════════════════════════════════════ */

.hero__layers {
  position: absolute; inset: 0; z-index: 0;
  pointer-events: none;
  overflow: hidden;
}

/* 로드 인트로 T=0.00 — 배경이 가장 먼저 살아납니다 (HERO-INTRO-PLAN.md §1-4).
   ★ 특이도를 맞춰야 합니다. 숨김 쪽이 :not() 둘로 (0,3,1) 이므로 노출 쪽도
   같은 수의 :not() 을 답니다. 옛 .hero__lines 규칙에서 한 번 겪고 주석까지
   남긴 함정입니다 — 반복하지 않습니다. */
html:not(.no-js):not(.no-gsap) .hero__layers { opacity: 0; }
html.is-intro:not(.no-js):not(.no-gsap) .hero__layers {
  opacity: 1;
  transition: opacity calc(.8s * var(--intro-s, 1)) var(--e-out);
}

/* ── 층 공통 ───────────────────────────────────────────────────────────── */
.hlayer {
  position: absolute; inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.hlayer::before,
.hlayer::after { content: ""; position: absolute; will-change: transform; }

/* 마스크
     L1  세로 한 장만. 구조가 없는 색이라 카피와 경쟁하지 않습니다.
     L2~L4  옛 .hero__lines 가 쓰던 교집합 마스크를 그대로 물려받습니다.
       ① 102deg  좌측 카피 컬럼을 비켜 가게
       ② 180deg  첫 화면에서 가장 진하고 아래 챕터로 갈수록 잦아들게 */
.hlayer--l1 {
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 46%, rgba(0,0,0,.5) 78%, rgba(0,0,0,0) 100%);
          mask-image: linear-gradient(180deg, #000 0%, #000 46%, rgba(0,0,0,.5) 78%, rgba(0,0,0,0) 100%);
}
.hlayer--l2,
.hlayer--l3,
.hlayer--l4 {
  -webkit-mask-image:
    linear-gradient(102deg, transparent 0%, rgba(0,0,0,.18) 22%, rgba(0,0,0,.62) 44%, #000 68%),
    linear-gradient(180deg, #000 0%, #000 40%, rgba(0,0,0,.52) 72%, rgba(0,0,0,.18) 100%);
  -webkit-mask-composite: source-in;
          mask-image:
    linear-gradient(102deg, transparent 0%, rgba(0,0,0,.18) 22%, rgba(0,0,0,.62) 44%, #000 68%),
    linear-gradient(180deg, #000 0%, #000 40%, rgba(0,0,0,.52) 72%, rgba(0,0,0,.18) 100%);
          mask-composite: intersect;
}

/* ── L1 색면 : Soft Aurora ──────────────────────────────────────────────
   블롭이 blur 로 뭉개져 있어 개별 형태는 보이지 않고 색의 밀도만 아주 천천히
   변합니다. 두 겹(::before/::after)을 서로 다른 주기·반대 방향으로 돌리면
   주기가 맞아떨어지는 순간이 없어 "반복된다"는 느낌이 사라집니다.

   Aurora(다크 전제)가 아니라 Soft Aurora 인 이유는 지면이 라이트 블루라서입니다
   — 가산 합성 계열은 #F5FAFF 위에서 사라집니다.
   그린은 ::after 의 작은 점 하나뿐입니다 — "블루는 면적, 그린은 점". */
.hlayer--l1 {
  background:
    radial-gradient(130% 100% at 70% 0%, rgba(185,215,244,.75) 0%, rgba(185,215,244,0) 64%),
    radial-gradient(100% 80% at 8% 96%,  rgba(234,243,251,.95) 0%, rgba(234,243,251,0) 68%);
}
.hlayer--l1::before {
  inset: -34%;
  background:
    radial-gradient(circle at 26% 30%, var(--blue-200) 0%, rgba(185,215,244,0) 56%),
    radial-gradient(circle at 80% 58%, var(--blue-300) 0%, rgba(127,178,232,0) 58%);
  filter: blur(92px);
  animation: hlBlobA 96s var(--e-io) infinite alternate;
}
.hlayer--l1::after {
  inset: -30%;
  background:
    radial-gradient(circle at 58% 78%, var(--blue-50) 0%, rgba(234,243,251,0) 60%),
    radial-gradient(circle at 88% 22%, rgba(169,217,106,.5) 0%, rgba(169,217,106,0) 26%);
  filter: blur(76px);
  animation: hlBlobB 124s var(--e-io) infinite alternate;
}

@keyframes hlBlobA {
  from { transform: translate3d(-5%, -3%, 0) scale(1);    }
  to   { transform: translate3d( 7%,  5%, 0) scale(1.14); }
}
@keyframes hlBlobB {
  from { transform: translate3d( 6%,  4%, 0) scale(1.08); }
  to   { transform: translate3d(-6%, -5%, 0) scale(0.94); }
}

/* ── L2 구조 : Dot Grid ─────────────────────────────────────────────────
   이동량이 타일(34px)의 정수배라 linear 루프에서 이음매가 보이지 않습니다.
   인트로 도트 버스트(.hero__dots)와 뭉쳐 보이지 않도록 간격을 벌렸습니다. */
.hlayer--l2::before {
  inset: -20%;
  background-image: radial-gradient(circle at center, rgba(43,127,224,.17) 1.3px, rgba(43,127,224,0) 1.4px);
  background-size: 34px 34px;
  animation: hlDriftTile 46s linear infinite;
}
/* 34px = 타일 한 칸. 끝나는 순간의 그림이 시작과 완전히 같습니다 */
@keyframes hlDriftTile {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(34px, 34px, 0); }
}

/* ── L3 주역 : Silk (유일한 WebGL). hero-layers.js 가 그립니다 ─────────── */
.hlayer--l3 canvas {
  position: absolute; inset: 0;
  display: block; width: 100%; height: 100%;
}

/* ── L4 근경 입자 ───────────────────────────────────────────────────────
   L2 와 **반대 방향**(↖)으로 흐릅니다. 전부 같은 방향이면 층이 몇이든 한
   덩어리로 보입니다. 어긋나야 사이가 벌어집니다.

   블러는 CSS 로 겁니다. 캔버스 백버퍼를 작게 잡아(hero-layers.js) 확대하면서
   이미 흐려지므로 필터 반경이 작아도 충분합니다 — 전면 blur 는 비싼 편이라
   반경을 줄일 수 있으면 줄입니다. */
.hlayer--l4 canvas {
  position: absolute; inset: 0;
  display: block; width: 100%; height: 100%;
  filter: blur(7px);
  opacity: .9;
}

/* ── 축소 / 접근성 ──────────────────────────────────────────────────────
   여기 미디어쿼리는 파일 끝의 §16 반응형이 아니라 이 블록 안에 둡니다.
   네 층은 한 덩어리라 흩어 놓으면 어느 폭에서 무엇이 남는지 읽히지 않습니다. */

/* 태블릿 — 근경 입자는 큰 화면에서만 의미가 있습니다 */
@media (max-width: 1024px) {
  .hlayer--l4 { display: none; }
}

/* 모바일 — WebGL 을 내리고 CSS 두 층만 남깁니다.
   옛 .hero__lines 를 같은 폭에서 통째로 껐던 것과 같은 판단입니다. */
@media (max-width: 767px) {
  .hlayer--l3 { display: none; }
  .hlayer--l2 { opacity: .7; }
}

/* 모션 축소 — "끄기" 가 아니라 "멈추기" 입니다.
   배경을 없애면 히어로가 허전해집니다. 색면과 구조는 정지 화면으로 남깁니다. */
@media (prefers-reduced-motion: reduce) {
  .hlayer::before,
  .hlayer::after { animation: none !important; }
  .hlayer--l3,
  .hlayer--l4 { display: none; }
}

/* JS 가 죽어도 배경은 보여야 합니다 (.no-js 폴백과 같은 방침) */
.no-js .hero__layers,
.no-gsap .hero__layers { opacity: 1; }
.no-js .hlayer--l3,
.no-gsap .hlayer--l3 { display: none; }

/* ---- 첫 화면 ---- */
.hero__fold {
  position: relative; z-index: 2;
  min-height: 100svh;
  padding: calc(var(--nav-h) + clamp(24px, 5vw, 60px)) 0 clamp(70px, 9vw, 120px);
  display: flex; flex-direction: column; justify-content: center;
}

.hero__inner {
  display: grid; grid-template-columns: minmax(0, 1.14fr) minmax(0, .86fr);
  gap: clamp(24px, 4vw, 56px); align-items: center;
  perspective: 1700px;
  perspective-origin: 62% 40%;
}

/* 두 컬럼은 서로 다른 속도로 흐르는 별개의 층입니다 (1.06 / 1.18).
   will-change 로 각자 레이어를 잡아 둬야 매 프레임 재합성이 싸집니다. */
.hero__copy   { position: relative; will-change: transform; }
.hero__visual { position: relative; will-change: transform; }

/* ---- 이어지는 카피 챕터 (비트 1·2) ---- */
.hero__flow {
  position: relative; z-index: 2;
  padding-bottom: clamp(90px, 12vw, 170px);
  display: grid;
  gap: clamp(56px, 9vw, 130px);
}

/* ---- 카피 비트 ----
   v4.0 은 절대 배치로 겹쳐 두고 교체했지만, 이제 각자 제 자리를 갖습니다.
   등장 연출(마스크 리빌)은 그대로라 .is-active 계약은 유지됩니다. */
.hbeat {
  position: relative;
  opacity: 0; visibility: hidden;
  transition:
    opacity calc(.5s * var(--intro-s, 1)) var(--e-out),
    visibility calc(.5s * var(--intro-s, 1));
}
.hbeat.is-active { opacity: 1; visibility: visible; }

.hbeat--flow {
  max-width: 46em;
  will-change: transform;
}
/* 우정렬 챕터 — 좌우로 번갈아 놓아야 세로로 흐르는 동안 리듬이 생깁니다 */
.hbeat--end {
  margin-left: auto;
  text-align: right;
}
.hbeat--end .hbeat__lead { margin-left: auto; }

.hbeat__tag {
  position: relative;
  display: inline-flex; align-items: center; gap: 9px;
  padding: 8px 16px 8px 12px;
  border-radius: var(--r-full);
  background: rgba(255,255,255,.75);
  border: 1px solid rgba(43,127,224,.16);
  backdrop-filter: blur(10px);
  box-shadow: var(--sh-1);
  font-size: 12.5px; font-weight: 700; letter-spacing: .12em;
  color: var(--blue-600);
  margin-bottom: 20px;
  overflow: hidden;   /* shiny 스윕을 칩 안에 가둡니다 */
  opacity: 0; transform: translateY(10px);
  transition:
    opacity calc(.6s * var(--intro-s, 1)) var(--e-out),
    transform calc(.6s * var(--intro-s, 1)) var(--e-out);
}
.hbeat.is-active .hbeat__tag { opacity: 1; transform: none; }

/* 로드 인트로 T=0.55 — Shiny Text (HERO-INTRO-PLAN.md §3).
   CSS 만으로 냅니다. 칩 위를 빛이 한 번 훑고 지나갑니다.
   첫 화면의 비트에만 걸고(.hero__fold), 아래 챕터에는 걸지 않습니다. */
.hbeat__tag::after {
  content: ""; position: absolute; inset: 0;
  pointer-events: none;
  background: linear-gradient(100deg,
    transparent 34%, rgba(255,255,255,.9) 50%, transparent 66%);
  transform: translateX(-130%);
}
html.is-intro .hero__fold .hbeat.is-active .hbeat__tag::after {
  animation: tagShine calc(1.15s * var(--intro-s, 1)) var(--e-out)
             calc(.35s * var(--intro-s, 1)) 1;
}
@keyframes tagShine { to { transform: translateX(130%); } }

.hbeat__title {
  font-size: clamp(30px, 4.1vw, 47px); line-height: 1.2; letter-spacing: -.035em;
  color: var(--heading); font-weight: 800;
}
/* 이어지는 챕터는 h1 이 아니라 보조 카피이므로 한 단계 작게 —
   같은 크기로 두면 헤드라인이 세 개인 페이지처럼 읽힙니다 */
.hbeat--flow .hbeat__title { font-size: clamp(26px, 3.4vw, 40px); }
.hbeat__title em {
  position: relative; color: var(--blue-500);
}
.hbeat__title em::after {
  content: ""; position: absolute; left: -2px; right: -2px; bottom: .1em; height: .26em;
  background: linear-gradient(90deg, rgba(140,198,63,.42), rgba(140,198,63,0));
  border-radius: 3px; z-index: -1;
  /* 인트로에서 왼쪽→오른쪽으로 뒤늦게 그어집니다 (ui-motion.js accentSettle).
     ::after 는 GSAP 이 직접 잡을 수 없으므로 부모의 --emw 를 읽습니다. */
  transform: scaleX(var(--emw, 1));
  transform-origin: 0 50%;
}

/* 라인 마스크 리빌 — 각 줄이 아래에서 밀려 올라옵니다 */
.line { display: block; overflow: hidden; padding-bottom: .08em; margin-bottom: -.08em; }
.line__in {
  display: block;
  transform: translateY(115%);
  opacity: 0;
  transition:
    transform calc(.95s * var(--intro-s, 1)) var(--e-mask)
      calc((var(--li, 0) * .09s + .05s) * var(--intro-s, 1)),
    opacity calc(.8s * var(--intro-s, 1)) var(--e-out)
      calc((var(--li, 0) * .09s + .05s) * var(--intro-s, 1));
  will-change: transform;
}
.hbeat.is-active .line__in { transform: none; opacity: 1; }

.hbeat__lead {
  margin-top: 20px;
  font-size: clamp(15.5px, 1.8vw, 18.5px);
  line-height: 1.75; color: var(--ink-600);
  max-width: 34em;
  opacity: 0; transform: translateY(14px);
  transition:
    opacity   calc(.7s * var(--intro-s, 1)) var(--e-out) calc(.3s * var(--intro-s, 1)),
    transform calc(.7s * var(--intro-s, 1)) var(--e-out) calc(.3s * var(--intro-s, 1));
}
.hbeat.is-active .hbeat__lead { opacity: 1; transform: none; }
.hbeat__lead b { color: var(--navy-800); font-weight: 700; }

/* 인트로가 리드를 단어 단위로 맡으면(ui-motion.js heroIntro) 위 CSS 전환을 끕니다.
   두 연출이 겹치면 리드가 두 번 움직입니다. 뒤에 오므로 같은 특이도에서 이깁니다. */
.hbeat__lead.is-js { opacity: 1; transform: none; transition: none; }
.hbeat__lead .hword { display: inline-block; will-change: filter, transform; }

/* 비트 진행 레일(.hero__rail)은 v4.1 에서 마크업과 함께 제거했습니다.
   핀 스크럽이 있어야 "현재 비트의 부분 진행률" 이라는 값이 생기는데,
   패럴랙스에는 그 값이 없습니다. 남겨 두면 항상 0% 인 빈 막대가 됩니다. */

.hero__cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 26px; }

.hero__stats {
  display: flex; gap: clamp(18px, 3vw, 38px); margin-top: 32px;
  padding-top: 24px; border-top: 1px solid rgba(43,127,224,.16);
}
.hero__stat dt { font-size: 12.5px; font-weight: 600; color: var(--ink-500); letter-spacing: -.01em; }
.hero__stat dd { display: flex; align-items: baseline; gap: 3px; margin-top: 3px; }
.hero__stat b {
  font-family: var(--ff-num);
  font-size: clamp(24px, 3vw, 30px); font-weight: 800; color: var(--navy-900); letter-spacing: -.03em;
}
.hero__stat span { font-size: 14px; font-weight: 700; color: var(--blue-500); }

/* ---- 로드 인트로 초기 상태 (HERO-INTRO-PLAN.md §1-1 / §7) ------------------

   진단의 최대 원인은 "왼쪽 카피만 등장하고 오른쪽 절반은 1프레임부터 완성" 이었습니다.
   CTA · 목업 · 뱃지 · 통계 · 스크롤 큐를 숨겨 두고 인트로 타임라인이 데려옵니다.

   ★ 반드시 지킬 것 — 숨기는 조건을 `html:not(.no-js):not(.no-gsap)` 로 좁힙니다.
     JS 나 GSAP 이 죽으면 인트로가 영영 오지 않는데, 그때도 숨어 있으면
     히어로 오른쪽이 **영구히 빈 화면**이 됩니다. reduced motion 도 같은 이유로
     아래 §17 에서 해제합니다.
   ---------------------------------------------------------------------------- */
html:not(.no-js):not(.no-gsap) .hero__fold .hero__cta > *,
html:not(.no-js):not(.no-gsap) .hero__fold .hero__stat {
  opacity: 0; transform: translateY(14px);
}
html:not(.no-js):not(.no-gsap) .hero__fold .hero__device {
  opacity: 0; transform: scale(.96); filter: blur(6px);
}
html:not(.no-js):not(.no-gsap) .hero__scroll { opacity: 0; }

/* 뱃지는 floatA / floatB 가 이미 transform 을 점유하고 있습니다.
   그래서 팝은 transform 을 쓰지 않는 독립 속성으로 냅니다 —
   CSS 의 scale 프로퍼티는 transform 과 별개로 합성됩니다. */
html:not(.no-js):not(.no-gsap) .hero__fold .hero__badge { opacity: 0; }
.hero__fold .hero__badge { scale: var(--bpop, 1); }

/* ---- 헤더도 같은 시퀀스에 태웁니다 (T 0.20) ----
   배경(0.00) → **프레임(헤더)** → 콘텐츠(0.55~) 순서입니다.
   헤더가 처음부터 완성돼 있으면 "페이지는 이미 있는데 내용만 늦게 온다" 로 읽힙니다.

   ★ `.nav` 자체는 절대 건드리지 않습니다.
     `.nav.is-hidden { transform: translateY(-100%) }` 가 스크롤 시 헤더를 숨기는데,
     같은 요소에 인트로 transform 을 얹으면 둘이 서로를 덮어씁니다.
     그래서 **안쪽 항목만** 움직입니다. 진행 바(.nav__progress)도 제외 —
     스크롤 값이지 등장 대상이 아닙니다. */
html:not(.no-js):not(.no-gsap) .nav__logo,
html:not(.no-js):not(.no-gsap) .nav__link,
html:not(.no-js):not(.no-gsap) .nav__actions > * {
  opacity: 0; transform: translateY(-10px);
}

/* ---- device mockups ---- */
.hero__visual { position: relative; z-index: 2; will-change: transform; }
.hero__device {
  position: relative;
  aspect-ratio: 1 / .86;
  transform-style: preserve-3d;
  perspective: 1400px;
}

.device { position: absolute; border-radius: 18px; }
.device__screen { overflow: hidden; background: var(--white); }

.device--laptop {
  left: 0; top: 6%; width: 92%;
  filter: drop-shadow(0 30px 50px rgba(15,42,92,.18));
  animation: floatA 9s ease-in-out infinite;
}
.device--laptop .device__screen {
  border-radius: 12px; border: 9px solid #E5EAF2; border-bottom-width: 12px;
  background: linear-gradient(180deg, #F7FAFE, #EEF4FB);
  aspect-ratio: 16 / 10.4;
  box-shadow: var(--sh-in);
}
.device--laptop .device__base {
  height: 12px; margin: -1px auto 0; width: 108%; margin-left: -4%;
  border-radius: 0 0 14px 14px;
  background: linear-gradient(180deg, #DFE6EF, #C6D2E0);
}

.device--phone {
  right: -2%; bottom: 0; width: 34%;
  border-radius: 26px; padding: 7px;
  background: linear-gradient(160deg, #F0F4F9, #D9E2EC);
  box-shadow: 0 26px 44px rgba(15,42,92,.22), var(--sh-in);
  animation: floatB 7.5s ease-in-out infinite;
}
.device--phone .device__screen { border-radius: 20px; aspect-ratio: 9 / 18.4; background: #F4F8FD; }
.device__notch {
  position: absolute; top: 11px; left: 50%; translate: -50% 0;
  width: 34%; height: 8px; border-radius: var(--r-full); background: #C9D5E3; z-index: 2;
}

@keyframes floatA { 0%,100% { transform: translateY(0) } 50% { transform: translateY(-10px) } }
@keyframes floatB { 0%,100% { transform: translateY(0) } 50% { transform: translateY(-16px) } }

.hero__badge {
  position: absolute; z-index: 3;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 15px; border-radius: var(--r-full);
  background: rgba(255,255,255,.92);
  border: 1px solid rgba(43,127,224,.18);
  backdrop-filter: blur(10px);
  box-shadow: var(--sh-2);
  font-size: 13px; font-weight: 700; color: var(--navy-900);
}
.hero__badge svg { width: 17px; height: 17px; color: var(--blue-500); }
.hero__badge--a { left: -6%; top: 22%; animation: floatB 8.5s ease-in-out infinite .4s; }
.hero__badge--b { right: 2%; top: 4%; animation: floatA 7s ease-in-out infinite .9s; }
.hero__badge--b svg { color: var(--green-500); }

/* dashboard skeleton inside laptop */
.ui-dash { display: flex; height: 100%; font-size: 0; }
.ui-dash__side { width: 16%; padding: 8px 6px; background: var(--navy-900); display: flex; flex-direction: column; gap: 6px; }
.ui-dash__side i { display: block; height: 5px; border-radius: 3px; background: rgba(255,255,255,.22); }
.ui-dash__side i:first-child { background: var(--blue-400); width: 70%; height: 7px; }
.ui-dash__main { flex: 1; padding: 8px; display: flex; flex-direction: column; gap: 6px; }
.ui-dash__bar { height: 8px; border-radius: 4px; background: linear-gradient(90deg, var(--navy-700), var(--blue-400)); }
.ui-dash__kpis { display: grid; grid-template-columns: repeat(4, 1fr); gap: 5px; }
.ui-dash__kpis span { height: 22px; border-radius: 5px; background: #EAF1FA; box-shadow: inset 0 0 0 1px #DCE8F5; }
.ui-dash__kpis span:nth-child(2) { background: #E9F5DC; box-shadow: inset 0 0 0 1px #D6EBBE; }
.ui-dash__chart { flex: 1; min-height: 0; border-radius: 6px; background: #F7FAFE; box-shadow: inset 0 0 0 1px #E7EFF8; padding: 6px; }
.ui-dash__chart svg { width: 100%; height: 100%; }
.spark { fill: none; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.spark--a { stroke: var(--blue-500); }
.spark--b { stroke: var(--green-400); }
.ui-dash__rows { display: flex; flex-direction: column; gap: 4px; }
.ui-dash__rows i { height: 6px; border-radius: 3px; background: #E4EDF7; }
.ui-dash__rows i:nth-child(2) { width: 78%; }
.ui-dash__rows i:nth-child(3) { width: 56%; }

/* app skeleton inside phone */
.ui-app { height: 100%; padding: 16px 8px 8px; display: flex; flex-direction: column; gap: 6px; }
.ui-app__top { height: 7px; width: 55%; border-radius: 4px; background: var(--navy-800); }
.ui-app__hero { height: 26%; border-radius: 8px; background: linear-gradient(140deg, var(--navy-800), var(--blue-600)); }
.ui-app__tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 5px; }
.ui-app__tiles span {
  height: 34px; border-radius: 7px; display: flex; align-items: center; justify-content: center;
  font-size: 8px; font-weight: 800; color: #fff; letter-spacing: -.02em;
  background: linear-gradient(150deg, #2F4858, #24333F);
}
.ui-app__tiles span:last-child { background: linear-gradient(150deg, var(--blue-600), var(--navy-700)); }
.ui-app__menu { flex: 1; display: grid; grid-template-columns: repeat(4, 1fr); gap: 5px; align-content: start; }
.ui-app__menu i { display: block; aspect-ratio: 1; border-radius: 6px; background: #E8F0F9; }
.ui-app__bar { height: 8px; border-radius: 4px; background: #DDE7F3; }

/* scroll cue */
.hero__scroll {
  position: absolute; left: 50%; bottom: 26px; translate: -50% 0; z-index: 3;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  color: var(--ink-500);
}
.hero__scroll-line { position: relative; width: 1px; height: 40px; background: rgba(43,127,224,.25); overflow: hidden; }
.hero__scroll-line i {
  position: absolute; inset: 0; display: block; background: var(--blue-500);
  animation: scrollCue 2s var(--e-io) infinite;
}
@keyframes scrollCue { 0% { transform: translateY(-100%) } 60%,100% { transform: translateY(100%) } }
.hero__scroll-text { font-size: 10px; font-weight: 700; letter-spacing: .22em; }

/* --------------------------------------------------------------------------
   8. Services bar
   -------------------------------------------------------------------------- */
.services { position: relative; z-index: 3; margin-top: clamp(-70px, -6vw, -50px); padding-bottom: clamp(50px, 7vw, 90px); }
.services__bar {
  display: grid; grid-template-columns: repeat(5, 1fr);
  background: rgba(255,255,255,.9);
  backdrop-filter: blur(14px);
  border: 1px solid rgba(255,255,255,.9);
  border-radius: var(--r-xl);
  box-shadow: var(--sh-3);
  overflow: hidden;
}
.svc {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  padding: clamp(26px, 3vw, 38px) 12px;
  text-align: center;
  isolation: isolate;
}
.svc + .svc::before {
  content: ""; position: absolute; left: 0; top: 22%; bottom: 22%; width: 1px; background: var(--line); z-index: 1;
}
.svc__glow {
  position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(180deg, rgba(234,243,251,.9), rgba(255,255,255,0));
  opacity: 0; transition: opacity .35s var(--e-out);
}
.svc:hover .svc__glow { opacity: 1; }
.svc__icon {
  display: grid; place-items: center; width: 48px; height: 48px;
  color: var(--navy-800);
  transition: transform .4s var(--e-out), color .3s;
}
.svc__icon svg { width: 34px; height: 34px; stroke-width: 1.4; }
.svc:hover .svc__icon { transform: translateY(-4px) scale(1.06); color: var(--blue-500); }
.svc__title { font-size: clamp(14px, 1.5vw, 16.5px); font-weight: 800; line-height: 1.45; }

.services__note {
  margin-top: clamp(22px, 3vw, 34px); text-align: center;
  display: flex; align-items: center; justify-content: center; gap: 9px; flex-wrap: wrap;
  font-size: 14px; color: var(--ink-500);
}
.services__note b { color: var(--navy-800); font-weight: 700; }

/* --------------------------------------------------------------------------
   9. Ecosystem — 3D orbit (핀 스크럽)
   -------------------------------------------------------------------------- */
.eco {
  position: relative;
  background:
    radial-gradient(80% 60% at 50% 0%, rgba(255,255,255,.9), rgba(255,255,255,0) 60%),
    linear-gradient(180deg, var(--blue-50), var(--blue-100) 60%, var(--blue-50));
}
.eco::before, .eco::after {
  content: ""; position: absolute; left: 0; right: 0; height: 90px; pointer-events: none; z-index: 1;
}
.eco::before { top: 0;    background: linear-gradient(180deg, var(--blue-25), transparent); }
.eco::after  { bottom: 0; background: linear-gradient(0deg,  var(--blue-25), transparent); }

.eco__pin {
  min-height: 100svh;
  display: flex; flex-direction: column; justify-content: center;
  padding: calc(var(--nav-h) + 26px) 0 36px;
}
.eco__pin > .shell { width: 100%; }
.eco .sect-head { margin-bottom: clamp(16px, 2.2vw, 28px); }

.eco__stage {
  position: relative;
  display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(320px, .85fr);
  gap: clamp(16px, 3vw, 40px); align-items: center;
}

/* height is viewport-bound so the whole pinned stage always fits on screen */
.eco__scene { position: relative; height: clamp(300px, 46svh, 470px); }
.eco__canvas { position: absolute; inset: 0; width: 100%; height: 100%; }

/* 스크럽 진행 링 */
.eco__arc {
  position: absolute; left: 50%; top: 50%; translate: -50% -50%;
  width: min(46%, 300px); aspect-ratio: 1; z-index: 1; pointer-events: none;
  rotate: -90deg;
}
.eco__arc circle { fill: none; stroke-width: 1.6; }
.eco__arc-track { stroke: rgba(43,127,224,.14); }
.eco__arc-fill {
  stroke: var(--blue-500); stroke-linecap: round;
  stroke-dasharray: 578; stroke-dashoffset: 578;   /* 2πr, r=92 */
  opacity: .55;
}

/* center core (HTML overlay for crisp typography) */
.eco__core {
  position: absolute; left: 50%; top: 50%; translate: -50% -50%;
  width: 26%; aspect-ratio: 1; z-index: 2; pointer-events: none;
}
.eco__core-inner {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 26%, #2A5599 0%, var(--navy-900) 55%, #071B3D 100%);
  box-shadow:
    0 24px 50px rgba(15,42,92,.34),
    inset 0 2px 0 rgba(255,255,255,.18),
    0 0 0 10px rgba(255,255,255,.55),
    0 0 0 11px rgba(43,127,224,.14);
  color: #fff; text-align: center;
}
/* .eco__core-mark 은 v8 마크업에 남아 있지 않습니다(3D 오빗 코어와 함께 빠졌습니다).
   로고가 PNG 로 바뀌면서 색을 갈아 끼우던 .lm-a / .lm-b 도 사라졌으므로
   크기 규칙만 남기고 색 규칙은 걷어 냅니다. */
.eco__core-mark { width: 22%; height: auto; margin-bottom: 6px; }
.eco__core-inner strong { font-size: clamp(15px, 2vw, 24px); font-weight: 800; letter-spacing: -.03em; }
.eco__core-inner span { font-size: clamp(10px, 1.1vw, 13px); color: var(--blue-200); font-weight: 500; }
.eco__core-ring {
  position: absolute; inset: -14%;
  border-radius: 50%;
  border: 1px dashed rgba(43,127,224,.35);
  animation: spinSlow 40s linear infinite;
}
@keyframes spinSlow { to { transform: rotate(360deg); } }
/* 노드가 바뀔 때마다 코어에서 퍼지는 파동 (JS가 클래스를 토글) */
.eco__core-pulse {
  position: absolute; inset: 0; border-radius: 50%;
  border: 2px solid var(--node, var(--blue-400));
  opacity: 0; pointer-events: none;
}
.eco__core.is-pulse .eco__core-pulse { animation: corePulse .9s var(--e-out); }
@keyframes corePulse {
  0%   { opacity: .6; transform: scale(1); }
  100% { opacity: 0;  transform: scale(2.1); }
}

.eco__counter {
  position: absolute; left: 0; bottom: 0; z-index: 3;
  display: flex; align-items: baseline; gap: 5px;
  font-family: var(--ff-num); letter-spacing: -.02em;
}
.eco__counter b { font-size: clamp(28px, 3.6vw, 44px); font-weight: 800; color: rgba(15,42,92,.16); line-height: 1; }
.eco__counter span { font-size: 13px; font-weight: 700; color: rgba(15,42,92,.22); }

/* projected node labels — positioned each frame by scene-orbit.js */
.eco__labels { position: absolute; inset: 0; z-index: 3; pointer-events: none; }
.eco__label {
  position: absolute; left: 0; top: 0;
  pointer-events: auto;
  opacity: 0;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 14px 8px 9px;
  border-radius: var(--r-full);
  background: rgba(255,255,255,.94);
  border: 1px solid rgba(15,42,92,.08);
  box-shadow: var(--sh-2);
  white-space: nowrap;
  will-change: transform, opacity;
  transition: background .3s, box-shadow .3s, border-color .3s;
}
.eco__label-icon {
  display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%;
  background: color-mix(in srgb, var(--node) 16%, #fff);
  color: var(--node);
}
.eco__label-icon svg { width: 15px; height: 15px; }
.eco__label-text { font-size: 13.5px; font-weight: 700; color: var(--navy-900); letter-spacing: -.02em; }
.eco__label.is-active {
  background: var(--navy-900);
  border-color: var(--navy-900);
  box-shadow: 0 14px 30px rgba(15,42,92,.28);
}
.eco__label.is-active .eco__label-text { color: #fff; }
.eco__label.is-active .eco__label-icon { background: color-mix(in srgb, var(--node) 82%, #fff); color: #fff; }

/* detail panel */
.eco__panels { position: relative; min-height: 320px; }
.eco-panel {
  position: absolute; inset: 0;
  padding: clamp(24px, 2.6vw, 34px);
  background: rgba(255,255,255,.92);
  backdrop-filter: blur(12px);
  border: 1px solid rgba(255,255,255,.95);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-3);
  opacity: 0; visibility: hidden;
  transform: translateY(18px) scale(.985);
  transition: opacity .45s var(--e-out), transform .45s var(--e-out), visibility .45s;
  overflow: hidden;
}
.eco-panel::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
  background: linear-gradient(180deg, var(--node), color-mix(in srgb, var(--node) 20%, #fff));
}
.eco-panel.is-active { opacity: 1; visibility: visible; transform: none; }

.eco-panel__no {
  font-family: var(--ff-num); font-size: 13px; font-weight: 800; letter-spacing: .06em;
  color: var(--node);
}
.eco-panel__no span { color: var(--ink-400); font-weight: 600; }
.eco-panel__title {
  display: flex; align-items: center; gap: 11px;
  margin-top: 8px; font-size: clamp(21px, 2.4vw, 27px);
}
.eco-panel__icon {
  display: grid; place-items: center; width: 40px; height: 40px; border-radius: 12px;
  background: color-mix(in srgb, var(--node) 13%, #fff);
  color: var(--node);
}
.eco-panel__icon svg { width: 22px; height: 22px; }
.eco-panel__desc { margin-top: 12px; font-size: 15px; color: var(--ink-600); line-height: 1.7; }
.eco-panel__list { margin-top: 18px; display: grid; gap: 9px; }
.eco-panel__list li { display: flex; align-items: center; gap: 9px; font-size: 14.5px; font-weight: 600; color: var(--ink-700); }
.eco-panel__list svg { width: 17px; height: 17px; flex: none; color: var(--node); stroke-width: 2.4; }

/* step indicator */
.eco__steps {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 8px;
  margin-top: clamp(16px, 2.4vw, 28px);
}
.eco__steps button {
  padding: 8px 15px; border-radius: var(--r-full);
  background: rgba(255,255,255,.7);
  border: 1px solid var(--line);
  font-size: 13px; font-weight: 700; color: var(--ink-500);
  transition: all .3s var(--e-out);
}
.eco__steps button:hover { border-color: var(--node); color: var(--navy-900); }
.eco__steps button.is-active {
  background: var(--node); border-color: var(--node); color: #fff;
  box-shadow: 0 8px 18px color-mix(in srgb, var(--node) 35%, transparent);
}

/* mobile fallback grid */
.eco__fallback { display: none; padding: clamp(70px, 9vw, 110px) var(--gutter); position: relative; z-index: 2; }
.eco-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px; }
.eco-card {
  padding: 26px; border-radius: var(--r-lg);
  background: #fff; box-shadow: var(--sh-2);
  border-top: 3px solid var(--node);
}
.eco-card__icon {
  display: grid; place-items: center; width: 44px; height: 44px; border-radius: 13px;
  background: color-mix(in srgb, var(--node) 13%, #fff); color: var(--node); margin-bottom: 14px;
}
.eco-card__icon svg { width: 24px; height: 24px; }
.eco-card h3 { font-size: 19px; margin-bottom: 10px; }
.eco-card ul { display: grid; gap: 6px; }
.eco-card li { position: relative; padding-left: 13px; font-size: 14px; color: var(--ink-600); }
.eco-card li::before { content: ""; position: absolute; left: 0; top: .72em; width: 4px; height: 4px; border-radius: 50%; background: var(--node); }

/* --------------------------------------------------------------------------
   10. Features — CardSwap 덱 (핀 없음)

   v4.1 까지는 핀 고정 가로 스크럽 트랙이었습니다. v4.2 에서 reactbits 의
   CardSwap 으로 바꾸면서 구도가 통째로 달라졌습니다.

     before  전체 폭 가로 트랙 · 카드 7장이 옆으로 흐름 · 핀 필요
     after   2단 (카피·목차·CTA / 덱) · 카드 6장이 제자리에서 순환 · 핀 없음

   덱은 시간으로 돌기 때문에 스크롤 훅이 하나도 없습니다.
   섹션의 패럴랙스는 마크업의 data-speed 가 따로 냅니다.
   -------------------------------------------------------------------------- */
.feat {
  position: relative;
  padding: clamp(84px, 11vw, 150px) 0 clamp(90px, 12vw, 160px);
  background: linear-gradient(180deg, var(--blue-25) 0%, #fff 22%, #fff 76%, var(--blue-25) 100%);
  overflow: hidden;   /* 덱이 뒤로 물러나며 섹션 밖으로 새는 것을 막습니다 */
}

.feat__inner {
  display: grid;
  grid-template-columns: minmax(0, .84fr) minmax(0, 1.16fr);
  gap: clamp(30px, 4vw, 64px);
  align-items: center;
}

/* ---- 왼쪽 : 카피 + 목차 + CTA ---- */
.feat__copy { position: relative; z-index: 2; will-change: transform; }
.feat__copy .sect-head__title { justify-content: flex-start; text-align: left; }
.feat__lead {
  margin-top: 14px;
  font-size: clamp(15px, 1.7vw, 17.5px);
  line-height: 1.75; color: var(--ink-600);
  max-width: 26em;
}

/* 목차 — 자동으로 도는 덱에서 원하는 항목을 기다리지 않게 해 주는 장치 */
.feat__nav {
  margin-top: clamp(22px, 3vw, 34px);
  display: grid; gap: 2px;
  border-top: 1px solid var(--line-soft);
}
.feat__navbtn {
  position: relative;
  width: 100%;
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center;
  gap: 14px;
  padding: 13px 14px 13px 4px;
  border-bottom: 1px solid var(--line-soft);
  text-align: left;
  transition: background .35s var(--e-out), padding-left .35s var(--e-out);
}
.feat__navbtn b {
  font-family: var(--ff-num); font-size: 12.5px; font-weight: 800; letter-spacing: .04em;
  color: var(--ink-400);
  transition: color .35s;
}
.feat__navbtn span {
  font-size: clamp(14.5px, 1.6vw, 16.5px); font-weight: 700; color: var(--ink-700);
  letter-spacing: -.02em;
  transition: color .35s;
}
/* 활성 표시 — 오른쪽 끝의 작은 막대가 자라납니다 */
.feat__navbtn i {
  width: 22px; height: 2px; border-radius: 2px;
  background: linear-gradient(90deg, var(--blue-500), var(--green-400));
  transform: scaleX(0); transform-origin: 100% 50%;
  transition: transform .45s var(--e-out);
}

/* 자동 순환 타이머 — 다음 카드로 넘어갈 때까지 남은 시간이 채워집니다.

   덱이 시간으로 도는 이상 "언제 넘어가는지" 를 알려 주는 단서가 필요합니다.
   없으면 카드가 예고 없이 툭 바뀌고, 읽던 중에 넘어가면 통제권을 뺏긴 느낌이 납니다.
   (가로 트랙 시절의 .feat__bar 가 하던 일 — 그때는 스크롤 진행률이 그 값이었습니다)

   .is-ticking 은 card-swap.js 가 매 주기마다 떼었다 붙여 애니메이션을 재시작합니다.
   --tick 도 JS 가 DELAY 와 같은 값으로 심습니다 — 두 값이 어긋나면 타이머가 거짓말을 합니다. */
.feat__navbtn::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--blue-500), var(--green-400));
  transform: scaleX(0); transform-origin: 0 50%;
}
.feat__navbtn.is-ticking::after {
  animation: featTick var(--tick, 4200ms) linear forwards;
}
/* 덱이 멈추면(호버 · 화면 밖) 타이머도 함께 멈춥니다 */
.feat.is-paused .feat__navbtn.is-ticking::after { animation-play-state: paused; }
@keyframes featTick { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.feat__navbtn:hover { background: var(--blue-25); padding-left: 10px; }
.feat__navbtn:hover span { color: var(--navy-800); }
.feat__navbtn.is-active { background: var(--blue-25); padding-left: 12px; }
.feat__navbtn.is-active b { color: var(--blue-500); }
.feat__navbtn.is-active span { color: var(--navy-900); }
.feat__navbtn.is-active i { transform: scaleX(1); }

.feat__cta { margin-top: clamp(24px, 3vw, 36px); display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.feat__note { font-size: 13px; font-weight: 600; color: var(--ink-500); }

/* ---- 오른쪽 : 덱 ----
   .feat__deck  자리를 잡는 상자. 카드가 위·오른쪽으로 물러나므로 그만큼 높이가 필요합니다.
   .deck        원근 컨테이너. 박스 크기 = 카드 한 장 크기이고,
                card-swap.js 가 offsetWidth/Height 를 읽어 간격을 계산합니다.
                음수 마진으로 가운데 정렬하는 이유 — transform 을 비워 둬야
                GSAP 이 스택 중앙 정렬용 x/y 를 자유롭게 쓸 수 있습니다. */
.feat__deck {
  position: relative;
  min-height: clamp(430px, 52svh, 580px);
  will-change: transform;
}
.deck {
  --cw: clamp(320px, 30vw, 430px);
  --ch: clamp(258px, 23vw, 330px);
  position: absolute;
  left: 50%; top: 50%;
  width: var(--cw); height: var(--ch);
  margin-left: calc(var(--cw) / -2);
  margin-top: calc(var(--ch) / -2);
  perspective: 1250px;
  transform-style: preserve-3d;
}

/* ---- 카드 ----
   가로 트랙 시절에는 세로로 긴 카드(목업 위 · 텍스트 아래)였습니다.
   덱은 가로형이라 목업을 왼쪽 열로 세우고 텍스트를 오른쪽에 붙였습니다. */
.fcard {
  position: absolute; top: 50%; left: 50%;
  width: 100%; height: 100%;
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  gap: clamp(14px, 1.4vw, 22px);
  padding: clamp(18px, 1.7vw, 26px);
  border-radius: var(--r-lg);
  background: #fff;
  border: 1px solid var(--line-soft);
  box-shadow: var(--sh-3);
  overflow: hidden;
  isolation: isolate;
  /* GSAP 이 x/y/z/zIndex/skewY 를 직접 씁니다 → 여기서 transform 을 쓰지 않습니다 */
  transform-style: preserve-3d;
  will-change: transform;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  transition: box-shadow .4s var(--e-out), border-color .4s;
}
.fcard::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: radial-gradient(70% 55% at 82% 6%, color-mix(in srgb, var(--glow) 13%, transparent), transparent 70%);
}
/* 맨 앞 카드만 또렷하게 — 뒤 카드는 살짝 물러난 느낌을 색으로도 줍니다 */
.fcard.is-front { box-shadow: var(--sh-4); border-color: var(--blue-200); }

.fcard__visual {
  position: relative;
  width: clamp(96px, 9vw, 124px);
  display: grid; justify-items: center; align-items: start;
  overflow: hidden;
}
.fcard .device--sm {
  position: relative; right: auto; bottom: auto;
  width: 100%; height: auto;
  padding: 5px;
  animation: floatB 8s ease-in-out infinite;
}
.fcard .device--sm .device__screen { border-radius: 14px; }
.fcard .device--sm .device__notch { top: 8px; height: 5px; }
.fcard__shot { width: 100%; border-radius: 12px; box-shadow: var(--sh-2); }
.fcard__glow {
  position: absolute; left: 50%; top: 46%; translate: -50% -50%; z-index: -1;
  width: 190%; aspect-ratio: 1;
  background: radial-gradient(50% 50% at 50% 50%, color-mix(in srgb, var(--glow) 26%, transparent), transparent 70%);
  filter: blur(20px);
  opacity: .55;
}

.fcard__body { position: relative; z-index: 2; min-width: 0; align-self: center; }
.fcard__no {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 36px; height: 24px; padding: 0 9px; border-radius: 7px;
  background: var(--navy-900); color: #fff;
  font-family: var(--ff-num); font-size: 12.5px; font-weight: 800; letter-spacing: .02em;
  margin-bottom: 10px;
}
.fcard__title { font-size: clamp(17px, 1.75vw, 21px); }
.fcard__lead { margin-top: 5px; font-size: 13px; font-weight: 700; color: var(--blue-500); }
.fcard__list { margin-top: 12px; display: grid; gap: 6px; }
.fcard__list li { display: flex; align-items: flex-start; gap: 8px; font-size: 12.5px; color: var(--ink-600); line-height: 1.5; }
.bullet { flex: none; width: 5px; height: 5px; margin-top: .55em; border-radius: 50%; background: var(--blue-400); }

.fcard__watermark {
  position: absolute; right: -18px; bottom: -22px; z-index: -1;
  color: var(--blue-50); pointer-events: none;
}
.fcard__watermark svg { width: 130px; height: 130px; stroke-width: .9; }

/* ---- 폴백 : 덱을 풀어 세로 목록으로 ----
   좁은 화면 · reduced motion · GSAP 없음. 원본 CSS 는 scale(0.55) 로 줄이지만
   우리 카드는 본문이 들어 있어 절반으로 줄이면 읽히지 않습니다. */
.feat.is-static .feat__inner { grid-template-columns: 1fr; }
.feat.is-static .feat__deck { min-height: 0; }
.feat.is-static .deck {
  position: relative; left: auto; top: auto;
  width: 100%; height: auto; margin: 0;
  perspective: none;
  display: grid; gap: clamp(14px, 2.4vw, 20px);
  transform: none !important;
}
.feat.is-static .fcard {
  position: relative; top: auto; left: auto;
  width: 100%; height: auto; min-height: clamp(190px, 26svh, 240px);
  transform: none !important;
}
.feat.is-static .feat__nav { display: none; }   /* 목록이 곧 카드이므로 중복입니다 */
/* 카드가 짧아지면 워터마크가 상대적으로 커져 본문을 침범합니다 */
.feat.is-static .fcard__watermark svg { width: 96px; height: 96px; }

/* ---- 폰 UI 스켈레톤 (실제 스크린샷이 없을 때 카드 안에 그려지는 목업) ---- */
.sk { height: 100%; padding: 18px 9px 9px; display: flex; flex-direction: column; gap: 6px; font-size: 0; }
.sk__head { display: flex; align-items: center; justify-content: space-between; padding-bottom: 6px; border-bottom: 1px solid #E8EFF7; }
.sk__title { font-size: 8.5px; font-weight: 800; color: var(--navy-900); letter-spacing: -.02em; }
.sk__head em { font-size: 7px; font-weight: 700; color: #fff; background: var(--navy-800); padding: 2px 6px; border-radius: 4px; }

.sk__orb { width: 26px; height: 26px; margin: 8px auto 2px; border-radius: 50%; background: conic-gradient(from 210deg, #1B1B1B, #4A4A4A, #101010); box-shadow: 0 4px 10px rgba(0,0,0,.2); }
.sk__q { font-size: 9px; font-weight: 800; color: var(--navy-900); text-align: center; margin-bottom: 4px; }
.sk__row { display: flex; align-items: center; justify-content: space-between; padding: 6px 7px; border-radius: 7px; background: #F1F6FC; }
.sk__row b { font-size: 7.5px; font-weight: 700; color: var(--ink-700); }
.sk__row i { width: 14px; height: 3px; border-radius: 2px; background: #C9D9EC; }
.sk__input { margin-top: auto; display: flex; align-items: center; gap: 5px; padding: 6px 7px; border-radius: var(--r-full); background: #fff; box-shadow: inset 0 0 0 1px #DCE8F5; }
.sk__input i { width: 8px; height: 8px; border-radius: 50%; background: #C9D9EC; flex: none; }
.sk__input span { flex: 1; height: 3px; border-radius: 2px; background: #E4EDF7; }

.sk--table .sk__scan { position: relative; flex: 1; border-radius: 7px; background: #fff; box-shadow: inset 0 0 0 1px #E4EDF7; overflow: hidden; padding: 6px; }
.sk__grid { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: 3px; }
.sk__grid i { height: 4px; border-radius: 2px; background: #E9F0F8; }
.sk__grid i:nth-child(4n+1) { background: #DDE8F4; }
.sk__scanline { position: absolute; left: 0; right: 0; height: 24%; background: linear-gradient(180deg, rgba(43,127,224,0), rgba(43,127,224,.22), rgba(43,127,224,0)); animation: scanY 2.6s var(--e-io) infinite; }
@keyframes scanY { 0% { top: -25% } 100% { top: 100% } }
.sk__tags { display: flex; gap: 4px; }
.sk__tags span { flex: 1; text-align: center; font-size: 7px; font-weight: 700; padding: 4px 0; border-radius: 5px; background: #E9F5DC; color: #557E1E; }
.sk__tags span:first-child { background: #E4EFFC; color: var(--blue-600); }

.sk__quick { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; }
.sk__quick span { aspect-ratio: 1; border-radius: 6px; background: #EDF3FA; }
.sk__chips { display: flex; flex-wrap: wrap; gap: 3px; }
.sk__chips b { font-size: 6.5px; font-weight: 700; padding: 3px 6px; border-radius: var(--r-full); background: #EAF3FB; color: var(--blue-600); }
.sk__result { display: flex; flex-direction: column; gap: 3px; padding: 6px; border-radius: 6px; background: #F7FAFE; box-shadow: inset 0 0 0 1px #EAF1F9; }
.sk__result i { height: 3.5px; border-radius: 2px; background: #DEE9F5; }
.sk__result i:last-child { width: 60%; background: #E9F0F8; }

.sk__tabs { display: flex; gap: 3px; }
.sk__tabs b { flex: 1; text-align: center; font-size: 6.5px; font-weight: 700; padding: 4px 0; border-radius: 5px; background: #EEF4FB; color: var(--ink-500); }
.sk__tabs b.on { background: var(--navy-800); color: #fff; }
.sk__notice { height: 22px; border-radius: 6px; background: repeating-linear-gradient(135deg, #FDF3E3, #FDF3E3 6px, #FBEBD3 6px, #FBEBD3 12px); }
.sk__field { display: flex; align-items: center; gap: 4px; padding: 6px; border-radius: 6px; background: #fff; box-shadow: inset 0 0 0 1px #DCE8F5; }
.sk__field i { flex: 1; height: 3.5px; border-radius: 2px; background: #E4EDF7; }
.sk__field em { width: 16px; height: 12px; border-radius: 4px; background: #FCE9C4; }
.sk__field--sm { width: 62%; }
.sk__submit { margin-top: auto; height: 18px; border-radius: var(--r-full); background: linear-gradient(90deg, var(--navy-700), var(--blue-500)); }

.sk__quad { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
.sk__quad span { padding: 7px 0; text-align: center; font-size: 7px; font-weight: 700; border-radius: 6px; background: #F1F6FC; color: var(--ink-600); }
.sk__quad span.on { background: #fff; color: var(--blue-600); box-shadow: 0 0 0 1.4px var(--blue-400); }
.sk__list { display: flex; flex-direction: column; gap: 4px; }
.sk__item { display: flex; align-items: center; gap: 5px; padding: 6px; border-radius: 6px; background: #F7FAFE; }
.sk__item i { width: 10px; height: 12px; border-radius: 2px; background: #DCE8F5; flex: none; }
.sk__item em { flex: 1; height: 3.5px; border-radius: 2px; background: #E4EDF7; }

.sk__kpi { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; }
.sk__kpi b { text-align: center; padding: 6px 0; border-radius: 6px; background: #F1F6FC; font-family: var(--ff-num); font-size: 11px; font-weight: 800; color: var(--navy-900); }
.sk__map { position: relative; flex: 1; min-height: 0; display: grid; place-items: center; }
.sk__map svg { width: 62%; height: auto; }
.sk__map path { fill: #E8F1FA; stroke: #C4DAF0; stroke-width: 1.4; }
.sk__map .pin { position: absolute; left: var(--x); top: var(--y); width: 6px; height: 6px; border-radius: 50%; background: var(--blue-500); box-shadow: 0 0 0 3px rgba(43,127,224,.18); }
.sk__map .pin:nth-child(3), .sk__map .pin:nth-child(5) { background: var(--green-400); box-shadow: 0 0 0 3px rgba(140,198,63,.2); }

/* --------------------------------------------------------------------------
   11. Strengths — Magic Bento 비대칭 그리드 + 인용 배너

   원본  https://reactbits.dev/components/magic-bento  (react-bits, MIT)

   ── 라이트 테마 적응 ────────────────────────────────────────────────────
   원본은 다크 전용입니다.

     card       background #120F17 · border #2F293A
     glow       rgba(132, 0, 255, ...)  보라
     spotlight  mix-blend-mode: screen

   v4 의 흰 카드 · 라이트 지면에서 screen 은 "더 밝게" 만드는 연산이라
   통째로 사라집니다. Floating Lines 때와 같은 제약입니다 (DESIGN-ANALYSIS §3).
   그래서 글로벌 스포트라이트를 **multiply** 로 바꿨습니다 —
   흰색은 곱해도 그대로(무변화), 옅은 블루만 지면을 은은하게 물들입니다.
   보라는 브랜드 밖이므로 blue-500 으로 교체했습니다.

   ── 끈 것 ───────────────────────────────────────────────────────────────
   틸트 · 마그네티즘 · 파티클. 원본 663줄의 대부분이 이 셋인데 B2B 랜딩에서는
   산만하고 모바일 비용이 큽니다. spotlight + borderGlow 만 남겼습니다.
   -------------------------------------------------------------------------- */
.str {
  position: relative;
  padding: clamp(70px, 10vw, 120px) 0 clamp(60px, 8vw, 100px);
  background:
    radial-gradient(70% 50% at 50% 0%, rgba(255,255,255,.9), transparent 65%),
    linear-gradient(180deg, var(--blue-25), var(--blue-50));
  isolation: isolate;   /* 글로벌 스포트라이트의 multiply 를 이 섹션 안에 가둡니다 */
}

.str__head-wrap { position: relative; }
.str__head-wrap .sect-head { will-change: transform; }

/* ---- 벤토 그리드 ----
      ┌───────────┬─────┬─────┐
      │           │  02       │   01  2x2
      │    01     ├─────┼─────┤   02  2x1
      │           │ 03  │ 04  │   03  1x1   04  1x1
      ├───────────┴─────┴─────┤
      │          05           │   05  4x1
      └───────────────────────┘
   카드 크기가 서로 다른 것이 이 레이아웃의 전부입니다.
   전부 같은 칸으로 만들면 다시 스프레드시트가 됩니다. */
.bento {
  position: relative;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-auto-rows: minmax(190px, auto);
  gap: clamp(12px, 1.4vw, 18px);
  margin-top: clamp(28px, 4vw, 52px);
}
.bento__card[data-span="2x2"] { grid-column: span 2; grid-row: span 2; }
.bento__card[data-span="2x1"] { grid-column: span 2; }
.bento__card[data-span="4x1"] { grid-column: span 4; }

.bento__card {
  position: relative;
  /* JS 가 흘려보내는 값 — bento.js */
  --glow-x: 50%;
  --glow-y: 50%;
  --glow-intensity: 0;
  --glow-radius: 320px;
  transition: transform .35s var(--e-out);
}
.bento__card:hover { transform: translateY(-3px); }

.bento__in {
  position: relative;
  height: 100%;
  display: flex; flex-direction: column; justify-content: space-between;
  gap: clamp(16px, 2vw, 28px);
  padding: clamp(20px, 2.2vw, 30px);
  border-radius: 20px;
  background: rgba(255,255,255,.94);
  backdrop-filter: blur(14px);
  border: 1px solid var(--line-soft);
  box-shadow: var(--sh-2);
  overflow: hidden;
  isolation: isolate;
  transition: box-shadow .4s var(--e-out), border-color .4s var(--e-out);
}
.bento__card:hover .bento__in { box-shadow: var(--sh-4); border-color: var(--blue-200); }

/* Border glow — 포인터에 가까운 쪽 테두리만 밝아집니다.
   원본과 같은 링 마스크(content-box XOR border-box). 색만 브랜드로 바꿨습니다. */
.bento__in::after {
  content: ""; position: absolute; inset: 0; z-index: 3;
  border-radius: inherit;
  padding: 1.4px;
  pointer-events: none;
  background: radial-gradient(var(--glow-radius) circle at var(--glow-x) var(--glow-y),
    rgba(43,127,224, calc(var(--glow-intensity) * .85)) 0%,
    rgba(43,127,224, calc(var(--glow-intensity) * .35)) 32%,
    transparent 62%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask-composite: exclude;
}
/* 카드 안쪽 광원 — 테두리만으로는 밋밋해서 면에도 아주 옅게 깔아 줍니다.
   z-index: -1 로 두면 내용에 z-index 를 일일이 줄 필요가 없습니다
   (.bento__in 의 isolation: isolate 가 카드 밖으로 새는 것을 막습니다). */
.bento__in::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  pointer-events: none;
  opacity: var(--glow-intensity);
  background: radial-gradient(var(--glow-radius) circle at var(--glow-x) var(--glow-y),
    rgba(43,127,224,.10) 0%, transparent 60%);
}

/* ---- 카드 안 ---- */
.bento__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }

.bento__icon {
  display: grid; place-items: center;
  width: clamp(42px, 4vw, 54px); height: clamp(42px, 4vw, 54px);
  border-radius: 14px;
  background: linear-gradient(150deg, var(--blue-50), #fff);
  box-shadow: inset 0 0 0 1px var(--line-soft);
  color: var(--navy-800);
  flex: none;
}
.bento__icon svg { width: 56%; height: 56%; stroke-width: 1.3; }

.bento__label {
  font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
  font-size: 10.5px; font-weight: 700; letter-spacing: .16em;
  color: var(--ink-400);
}

.bento__body { display: flex; flex-direction: column; }
.bento__title {
  font-size: clamp(18px, 2vw, 23px); line-height: 1.3; letter-spacing: -.02em;
  color: var(--heading);
}
.bento__desc {
  margin-top: 8px;
  font-size: 13.5px; line-height: 1.7; color: var(--ink-600);
  max-width: 34ch;
}

.bento__points { margin-top: 14px; display: grid; gap: 7px; }
.bento__points li {
  display: flex; align-items: flex-start; gap: 9px;
  font-size: 13px; line-height: 1.5; color: var(--ink-700);
}
.bento__points i {
  flex: none; width: 5px; height: 5px; margin-top: .55em;
  border-radius: 50%; background: var(--blue-400);
}

.bento__no {
  position: absolute; right: clamp(16px, 1.8vw, 24px); bottom: clamp(14px, 1.6vw, 20px);
  z-index: 1;
  font-family: var(--ff-num); font-size: 12px; font-weight: 800; letter-spacing: .06em;
  color: var(--blue-300);
}
.bento__ghost {
  position: absolute; right: -30px; bottom: -34px; z-index: 0;
  color: rgba(43,127,224,.055); pointer-events: none;
}
.bento__ghost svg { width: 180px; height: 180px; stroke-width: .8; }

/* 큰 카드는 담을 것이 더 있으므로 타이포도 한 단계 키웁니다 */
.bento__card[data-span="2x2"] .bento__title { font-size: clamp(22px, 2.6vw, 30px); }
.bento__card[data-span="2x2"] .bento__desc  { font-size: 14.5px; }

/* 2x2 는 세로가 560px 이라 space-between 이 헤더와 본문을 양끝으로 크게 벌립니다.
   그 빈 자리를 워터마크로 채웁니다 — 오른쪽 아래에 두면 공백만 남습니다. */
.bento__card[data-span="2x2"] .bento__ghost {
  right: auto; bottom: auto;
  left: 50%; top: 44%;
  transform: translate(-50%, -50%);
}
.bento__card[data-span="2x2"] .bento__ghost svg { width: 260px; height: 260px; stroke-width: .7; }

/* 가로로 긴 마지막 카드는 2단으로 — 한 줄로 늘어놓으면 오른쪽 절반이 빕니다.
   .bento__points 는 .bento__body 안에 중첩돼 있어서 그대로는 그리드 배치가
   먹지 않습니다. body 를 display: contents 로 펴서 자식들을 직접 배치합니다. */
.bento__card[data-span="4x1"] .bento__in {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  grid-template-rows: auto auto 1fr;
  align-items: start;
  column-gap: clamp(28px, 4.5vw, 72px);
  row-gap: 0;
}
.bento__card[data-span="4x1"] .bento__head {
  grid-column: 1; grid-row: 1;
  justify-content: flex-start; gap: 14px;
  margin-bottom: clamp(16px, 2vw, 26px);
}
.bento__card[data-span="4x1"] .bento__body { display: contents; }
.bento__card[data-span="4x1"] .bento__title { grid-column: 1; grid-row: 2; }
.bento__card[data-span="4x1"] .bento__desc  { grid-column: 1; grid-row: 3; }
.bento__card[data-span="4x1"] .bento__points {
  grid-column: 2; grid-row: 1 / span 3;
  align-self: center; margin-top: 0;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px 22px;
}

/* ---- 글로벌 스포트라이트 ----
   섹션 전체를 훑는 큰 광원 하나. bento.js 가 위치를 씁니다.
   다크 원본의 screen 대신 multiply — 흰색은 곱해도 무변화라 가장자리가 자연스럽게
   사라지고, 가운데의 옅은 블루만 지면을 물들입니다. */
.bento__spot {
  position: absolute; z-index: 0;
  width: 640px; height: 640px; margin: -320px 0 0 -320px;
  left: 0; top: 0;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  mix-blend-mode: multiply;
  background: radial-gradient(circle,
    #D7E7FA 0%, #E9F2FC 34%, #F7FAFE 58%, #fff 72%);
  transform: translate3d(var(--spot-x, 0), var(--spot-y, 0), 0);
  transition: opacity .35s var(--e-out);
  will-change: transform, opacity;
}

/* SVG line-draw — CSS 폴백.
   DrawSVGPlugin 이 붙으면 ui-motion.js 가 .is-scrubbed 를 달고, 이 규칙은 통째로
   비활성화됩니다. CSS 선언은 SVG presentation attribute 보다 우선하기 때문에
   :not() 으로 빼 두지 않으면 플러그인이 쓰는 dasharray 를 CSS 가 덮어씁니다. */
[data-draw]:not(.is-scrubbed) svg path,
[data-draw]:not(.is-scrubbed) svg circle,
[data-draw]:not(.is-scrubbed) svg ellipse,
[data-draw]:not(.is-scrubbed) svg rect,
[data-draw]:not(.is-scrubbed) svg line,
[data-draw]:not(.is-scrubbed) svg polyline {
  stroke-dasharray: var(--len, 240);
  stroke-dashoffset: var(--len, 240);
}
[data-draw]:not(.is-scrubbed).is-drawn svg path,
[data-draw]:not(.is-scrubbed).is-drawn svg circle,
[data-draw]:not(.is-scrubbed).is-drawn svg ellipse,
[data-draw]:not(.is-scrubbed).is-drawn svg rect,
[data-draw]:not(.is-scrubbed).is-drawn svg line,
[data-draw]:not(.is-scrubbed).is-drawn svg polyline {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 1.1s var(--e-out) var(--d, 0s);
}

/* 인용 배너 — 스크롤에 따라 컬러 블록이 아래에서 위로 덮입니다 */
.quote {
  position: relative;
  margin: clamp(50px, 7vw, 90px) 0 0;
  padding: clamp(34px, 4.4vw, 56px) clamp(28px, 5vw, 70px);
  border-radius: var(--r-xl);
  background: linear-gradient(120deg, var(--navy-900) 0%, var(--navy-800) 55%, var(--navy-700) 100%);
  color: #fff;
  box-shadow: var(--sh-4);
  overflow: hidden;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 28px;
  /* --w: 0..1 (main.js 스크럽) — 컬러 블록 와이프 */
  clip-path: inset(calc((1 - var(--w, 1)) * 100%) 0 0 0 round var(--r-xl));
}
.quote::before {
  content: ""; position: absolute; inset: 0;
  background:
    radial-gradient(50% 90% at 88% 10%, rgba(43,127,224,.45), transparent 70%),
    radial-gradient(40% 80% at 8% 90%, rgba(140,198,63,.22), transparent 70%);
  pointer-events: none;
}
.quote__mark { position: absolute; left: clamp(14px, 2vw, 28px); top: clamp(14px, 2vw, 24px); color: rgba(255,255,255,.16); }
.quote__mark svg { width: 46px; height: 46px; stroke-width: 1.1; }
.quote p { position: relative; font-size: clamp(18px, 2.5vw, 27px); font-weight: 700; line-height: 1.55; letter-spacing: -.025em; color: #fff; }
.quote p b { color: var(--green-300); font-weight: 800; }
.quote__keys { position: relative; display: flex; gap: clamp(14px, 2.4vw, 30px); }
.quote__keys li { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.quote__keys svg { width: 26px; height: 26px; color: var(--blue-200); stroke-width: 1.3; }
.quote__keys span { font-size: 13px; font-weight: 700; letter-spacing: -.01em; }
.quote__keys li + li { position: relative; padding-left: clamp(14px, 2.4vw, 30px); }
.quote__keys li + li::before { content: ""; position: absolute; left: 0; top: 10%; bottom: 10%; width: 1px; background: rgba(255,255,255,.18); }

/* --------------------------------------------------------------------------
   12. Contact / CTA (dark)
   -------------------------------------------------------------------------- */
.cta {
  position: relative;
  padding: clamp(80px, 11vw, 130px) 0;
  background: linear-gradient(165deg, #0B2148 0%, var(--navy-900) 45%, #123566 100%);
  color: #fff;
  overflow: hidden;
  isolation: isolate;
}
.cta__bg { position: absolute; inset: 0; z-index: -1; }
.cta__bg span {
  position: absolute; border-radius: 50%; filter: blur(70px); opacity: .5;
  will-change: transform;
}
.cta__bg span:nth-child(1) { width: 46vw; height: 46vw; left: -12vw; top: -14vw; background: rgba(43,127,224,.42); }
.cta__bg span:nth-child(2) { width: 34vw; height: 34vw; right: -8vw; bottom: -12vw; background: rgba(140,198,63,.22); }
.cta__bg span:nth-child(3) { width: 26vw; height: 26vw; right: 22vw; top: 8vw; background: rgba(29,74,158,.5); }

.cta__inner {
  display: grid; grid-template-columns: minmax(0, .92fr) minmax(0, 1.08fr);
  gap: clamp(30px, 5vw, 66px); align-items: start;
}
.cta__title { color: #fff; font-size: clamp(28px, 4vw, 44px); line-height: 1.28; }
.cta__title em { color: var(--green-300); }
.cta__lead { margin-top: 18px; font-size: clamp(15px, 1.7vw, 17px); color: rgba(255,255,255,.7); line-height: 1.75; }

.cta__info { margin-top: clamp(28px, 4vw, 44px); display: grid; gap: 14px; }
.cta__info li {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 18px;
  border-radius: var(--r-md);
  background: rgba(255,255,255,.06);
  border: 1px solid rgba(255,255,255,.1);
  transition: background .3s, border-color .3s, transform .3s var(--e-out);
}
.cta__info li:hover { background: rgba(255,255,255,.1); border-color: rgba(140,198,63,.4); transform: translateX(4px); }
.cta__info-icon {
  display: grid; place-items: center; width: 40px; height: 40px; flex: none;
  border-radius: 12px; background: rgba(140,198,63,.16); color: var(--green-300);
}
.cta__info-icon svg { width: 20px; height: 20px; }
.cta__info-body { display: flex; flex-direction: column; min-width: 0; }
.cta__info-body em { font-size: 12px; color: rgba(255,255,255,.5); font-weight: 600; }
.cta__info-body a, .cta__info-body > span { font-size: 15.5px; font-weight: 700; color: #fff; letter-spacing: -.01em; }
.cta__info-body a:hover { color: var(--green-300); }

/* form */
.cform {
  padding: clamp(26px, 3.2vw, 40px);
  border-radius: var(--r-lg);
  background: rgba(255,255,255,.06);
  border: 1px solid rgba(255,255,255,.12);
  backdrop-filter: blur(18px);
  box-shadow: 0 30px 70px rgba(0,0,0,.28);
}
.cform__hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.cform__title { color: #fff; font-size: clamp(20px, 2.4vw, 25px); margin-bottom: 22px; }
.cform__row { display: grid; gap: 14px; margin-bottom: 14px; }
.cform__row--2 { grid-template-columns: 1fr 1fr; }

.field { display: block; margin-bottom: 14px; }
.field__label { display: block; margin-bottom: 7px; font-size: 13px; font-weight: 700; color: rgba(255,255,255,.78); }
.field__label i { color: var(--green-300); font-style: normal; }
.field__input {
  width: 100%; padding: 13px 16px;
  border-radius: 12px;
  background: rgba(255,255,255,.07);
  border: 1px solid rgba(255,255,255,.16);
  color: #fff; font-size: 15px;
  transition: border-color .25s, background .25s, box-shadow .25s;
}
.field__input::placeholder { color: rgba(255,255,255,.36); }
.field__input:hover { border-color: rgba(255,255,255,.3); }
.field__input:focus {
  outline: none;
  border-color: var(--green-400);
  background: rgba(255,255,255,.1);
  box-shadow: 0 0 0 4px rgba(140,198,63,.16);
}
.field__input--area { resize: vertical; min-height: 116px; line-height: 1.65; }
.field__input.is-error { border-color: #FF7A7A; box-shadow: 0 0 0 4px rgba(255,122,122,.14); }

.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip { position: relative; cursor: pointer; }
.chip input { position: absolute; opacity: 0; width: 0; height: 0; }
.chip span {
  display: inline-block; padding: 9px 15px; border-radius: var(--r-full);
  background: rgba(255,255,255,.06);
  border: 1px solid rgba(255,255,255,.16);
  font-size: 13.5px; font-weight: 600; color: rgba(255,255,255,.76);
  transition: all .25s var(--e-out);
}
.chip:hover span { border-color: rgba(255,255,255,.34); color: #fff; }
.chip input:checked + span {
  background: var(--green-400); border-color: var(--green-400); color: #fff; font-weight: 700;
  box-shadow: 0 6px 16px rgba(122,181,49,.3);
}
.chip input:focus-visible + span { outline: 2px solid var(--green-300); outline-offset: 2px; }

.agree { display: flex; align-items: flex-start; gap: 10px; margin: 6px 0 20px; cursor: pointer; }
.agree input { position: absolute; opacity: 0; width: 0; height: 0; }
.agree__box {
  flex: none; display: grid; place-items: center; width: 21px; height: 21px; margin-top: 1px;
  border-radius: 6px; border: 1.5px solid rgba(255,255,255,.3); background: rgba(255,255,255,.06);
  transition: all .22s;
}
.agree__box svg { width: 13px; height: 13px; color: #fff; opacity: 0; transform: scale(.6); transition: all .22s var(--e-out); stroke-width: 3; }
.agree input:checked + .agree__box { background: var(--green-400); border-color: var(--green-400); }
.agree input:checked + .agree__box svg { opacity: 1; transform: none; }
.agree__text { font-size: 13.5px; color: rgba(255,255,255,.7); line-height: 1.6; }
.agree__text em { color: rgba(255,255,255,.42); font-size: 12.5px; }
.agree.is-error .agree__box { border-color: #FF7A7A; }

.cform__msg { margin-top: 14px; font-size: 14px; font-weight: 600; text-align: center; min-height: 22px; }
.cform__msg.is-ok   { color: var(--green-300); }
.cform__msg.is-fail { color: #FF9A9A; }

/* --------------------------------------------------------------------------
   13. Footer
   -------------------------------------------------------------------------- */
.foot { background: #071A38; color: rgba(255,255,255,.68); padding: clamp(48px, 6vw, 70px) 0 26px; }
.foot__inner {
  display: grid; grid-template-columns: minmax(0, 1.4fr) auto minmax(0, 1fr);
  gap: clamp(24px, 4vw, 50px); align-items: start;
  padding-bottom: 34px; border-bottom: 1px solid rgba(255,255,255,.1);
}
.foot__brand .logo__img { --logo-h: 42px; }
.foot__desc { margin-top: 16px; font-size: 14px; line-height: 1.7; max-width: 34ch; }
.foot__nav { display: grid; gap: 11px; }
.foot__nav a { font-size: 14.5px; font-weight: 600; transition: color .25s; }
.foot__nav a:hover { color: #fff; }
.foot__contact { display: grid; gap: 11px; }
.foot__contact li { display: flex; align-items: center; gap: 10px; font-size: 14px; }
.foot__contact svg { width: 17px; height: 17px; flex: none; color: var(--blue-300); }
.foot__contact a:hover { color: #fff; }

.foot__bottom {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 10px;
  padding-top: 22px; font-size: 13px; color: rgba(255,255,255,.42);
}
.foot__slogan { color: rgba(140,198,63,.75); font-weight: 600; }

.totop {
  position: fixed; right: 22px; bottom: 22px; z-index: 90;
  display: grid; place-items: center; width: 48px; height: 48px;
  border-radius: 50%; background: var(--navy-900); color: #fff;
  box-shadow: var(--sh-3);
  opacity: 0; visibility: hidden; transform: translateY(14px);
  transition: all .4s var(--e-out);
}
.totop svg { width: 20px; height: 20px; rotate: -90deg; }
.totop.is-on { opacity: 1; visibility: visible; transform: none; }
.totop:hover { background: var(--blue-600); transform: translateY(-3px); }

/* --------------------------------------------------------------------------
   14. Reveal / split-text 유틸리티
   -------------------------------------------------------------------------- */
[data-reveal] {
  opacity: 0;
  transform: translateY(26px);
  transition:
    opacity .85s var(--e-out) var(--rd, 0s),
    transform .85s var(--e-out) var(--rd, 0s);
  will-change: opacity, transform;
}
[data-reveal].is-in { opacity: 1; transform: none; }
.no-js [data-reveal] { opacity: 1; transform: none; }

/* [data-split] 의 줄 마스크 리빌 — 두 경로가 같은 CSS 계약을 씁니다.
     ① SplitText (type:'lines', mask:'lines', linesClass:'sline__in')
        → 마스크 래퍼 클래스는 플러그인이 "sline__in-mask" 로 만들어 줍니다.
     ② flex 제목(로고 마크 + 텍스트)은 SplitText 가 줄을 flex 아이템으로
        만들어 버리므로 ui-motion.js 가 <br> 기준으로 직접 .sline 을 감쌉니다. */
.sline,
.sline__in-mask { display: block; overflow: clip; padding-bottom: .06em; margin-bottom: -.06em; }
.sline__in { display: block; will-change: transform; }

/* 제목은 flex 컨테이너(로고 마크 + 텍스트)라 줄 래퍼도 flex 를 이어받아야 합니다 */
.sect-head__title .sline { width: 100%; }
.sect-head__title .sline__in {
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap;
  /* 여기도 축을 갈라야 합니다 — 이 flex 안에서 텍스트가 두 줄로 넘어가면
     row-gap 이 line-height 위에 얹혀 줄 사이가 벌어집니다.
     (제목이 flex 라 SplitText 대신 수동 래핑을 쓰므로, 실제 줄바꿈은 여기서 일어납니다) */
  column-gap: 12px; row-gap: 0;
}
.feat__copy .sect-head__title .sline__in { justify-content: flex-start; }

/* DrawSVG 가 스크럽으로 그리는 동안에는 CSS 전환이 개입하면 안 됩니다 */
[data-draw].is-scrubbed svg * { transition: none !important; }

/* [data-stagger] 자식은 JS 가 순차 등장시킵니다 (JS 없으면 그대로 보임) */
html:not(.no-js) [data-stagger] > * { opacity: 0; }
html:not(.no-js) [data-stagger].is-in > * { opacity: 1; }

/* --------------------------------------------------------------------------
   15. v3 — 스크롤 엔진 레이어
   -------------------------------------------------------------------------- */

/* ScrollSmoother 이펙트(data-speed / data-lag)를 받는 요소.
   ※ 핀 고정된 컨테이너 안에서는 쓰지 않습니다 — 핀이 거는 transform 과 겹칩니다.
      핀 내부의 패럴랙스는 scroll.js 의 스크럽 타임라인이 직접 처리합니다. */
[data-speed], [data-lag] { will-change: transform; }

/* 가로 트랙 양끝 페이드(.feat__viewport::before/after)는 v4.2 에서 제거했습니다 —
   트랙 자체가 CardSwap 덱으로 바뀌어 "화면 밖으로 빠져나가는 끝" 이 없습니다. */

/* 카드 그림자 — 목록에서 아래로 갈수록 깊어집니다 (층이 읽히도록) */
/* 벤토 카드 그림자 — --i 가 클수록 살짝 깊게. 격자에 미세한 층을 만듭니다 */
.bento__in { box-shadow: 0 calc(10px + var(--i, 0) * 2px) calc(28px + var(--i, 0) * 5px) rgba(15,42,92,.10), 0 4px 12px rgba(15,42,92,.05); }

/* ---- GSAP / JS 가 없을 때의 정적 폴백 ----
   v4.1 부터 비트는 겹치지 않고 세로로 흐르므로 "첫 비트만 노출" 이 아니라
   전부 노출하는 게 맞습니다. 이제 전부 제 자리에 있는 문서니까요. */
.no-gsap .hbeat,
.no-js   .hbeat { opacity: 1; visibility: visible; }
.no-gsap .line__in,
.no-js   .line__in { transform: none; opacity: 1; }
.no-gsap .hbeat__tag,
.no-js   .hbeat__tag,
.no-gsap .hbeat__lead,
.no-js   .hbeat__lead { opacity: 1; transform: none; }

.no-gsap .quote { clip-path: none; }
.no-gsap .eco__pin { display: none; }
.no-gsap .eco__fallback { display: block; }

/* --------------------------------------------------------------------------
   16. Responsive
   -------------------------------------------------------------------------- */
@media (max-width: 1280px) {
  .rail { display: none; }
}

@media (max-width: 1120px) {
  /* 1단으로 접히면 아이콘이 위로 올라가므로 카드 최소 높이도 함께 늘립니다 */
  /* 4열을 유지하기엔 카드가 좁아집니다 — 2열로 접고 span 을 재배치합니다 */
  .bento { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .bento__card[data-span="2x2"] { grid-column: span 2; grid-row: span 1; }
  .bento__card[data-span="4x1"] { grid-column: span 2; }
  .bento__card[data-span="4x1"] .bento__in { grid-template-columns: 1fr; }
  .bento__card[data-span="4x1"] .bento__head,
  .bento__card[data-span="4x1"] .bento__body,
  .bento__card[data-span="4x1"] .bento__points { grid-column: 1; grid-row: auto; }
  .bento__card[data-span="4x1"] .bento__points { margin-top: 14px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* 생태계 스테이지는 2단을 최대한 유지합니다 — 1단으로 접으면 씬+패널+스텝이
   핀 고정된 뷰포트 높이를 넘어서 활성 노드가 화면 밖으로 밀립니다 */
@media (max-width: 900px) {
  .eco__stage { grid-template-columns: 1fr; }
  .eco__scene { max-width: 560px; margin-inline: auto; width: 100%; }
  .eco__panels { min-height: 300px; }
}

@media (max-width: 1024px) {
  :root { --nav-h: 66px; }
  .nav__logo .logo__img { --logo-h: 31px; }
  .nav__menu { display: none; }
  .nav__toggle { display: grid; place-items: center; }
  .nav.nav--open .nav__drawer { display: block; }

  .hero__inner { grid-template-columns: 1fr; text-align: left; }
  .hero__visual { max-width: 620px; width: 100%; margin-top: 12px; }
  .hero__scroll { display: none; }

  /* 1단으로 접히면 좌우 교차가 의미를 잃습니다 — 전부 좌정렬로 되돌립니다 */
  .hbeat--end { margin-left: 0; text-align: left; }
  .hbeat--end .hbeat__lead { margin-left: 0; }

  .services__bar { grid-template-columns: repeat(3, 1fr); }
  .svc + .svc::before { display: none; }
  .svc { border-top: 1px solid var(--line-soft); border-left: 1px solid var(--line-soft); }
  .svc:nth-child(-n+3) { border-top: 0; }
  .svc:nth-child(3n+1) { border-left: 0; }

  /* 2단을 유지하기엔 덱이 좁아집니다 — 카피 위 / 덱 아래로 접습니다 */
  .feat__inner { grid-template-columns: 1fr; gap: clamp(28px, 5vw, 48px); }
  .feat__deck { min-height: clamp(400px, 46svh, 500px); }
  .feat__nav { max-width: 520px; }

  .cta__inner { grid-template-columns: 1fr; }
  .foot__inner { grid-template-columns: 1fr 1fr; }
  .foot__brand { grid-column: 1 / -1; }
}

@media (max-width: 860px) {
  /* 3D 오빗은 데스크톱 전용 — 아래는 카드 그리드가 대신합니다 */
  .eco__pin { display: none; }
  .eco__fallback { display: block; }

  /* CardSwap 덱은 데스크톱 전용입니다 — card-swap.js 가 .is-static 을 달아
     세로 목록으로 풉니다. 원본 CSS 는 scale(0.55) 로 줄이지만, 우리 카드는
     본문 텍스트가 들어 있어 절반으로 줄이면 읽히지 않습니다. */
  .feat { padding: clamp(64px, 9vw, 100px) 0 clamp(56px, 8vw, 90px); }
}

@media (max-width: 720px) {
  .br-sm { display: inline; }
  .hero__fold { min-height: auto; padding-bottom: 84px; }
  .hero__flow { gap: clamp(44px, 12vw, 76px); padding-bottom: 90px; }
  .hero__stats { flex-wrap: wrap; gap: 18px 26px; }
  .hero__device { aspect-ratio: 1 / .92; }
  .hero__badge { font-size: 11.5px; padding: 8px 12px; }
  .hero__badge--a { left: -2%; }

  .services { margin-top: -48px; }
  .services__bar { grid-template-columns: repeat(2, 1fr); border-radius: var(--r-lg); }
  .svc:nth-child(-n+3) { border-top: 1px solid var(--line-soft); }
  .svc:nth-child(-n+2) { border-top: 0; }
  .svc:nth-child(3n+1) { border-left: 1px solid var(--line-soft); }
  .svc:nth-child(2n+1) { border-left: 0; }
  .svc:last-child { grid-column: 1 / -1; }

  /* 좁은 화면에서는 카드가 이미 화면 폭을 꽉 채우므로 속도차가 어지럽습니다.
     ScrollSmoother 이펙트를 무력화하는 대신 최소 높이만 풀어 둡니다. */
  /* 좁은 화면에서는 전부 1열 — span 을 모두 해제합니다 */
  .bento { grid-template-columns: 1fr; grid-auto-rows: auto; }
  .bento__card[data-span="2x2"],
  .bento__card[data-span="2x1"],
  .bento__card[data-span="4x1"] { grid-column: span 1; grid-row: span 1; }
  .bento__card[data-span="4x1"] .bento__points { grid-template-columns: 1fr; }
  .bento__ghost svg { width: 130px; height: 130px; }

  .quote { flex-direction: column; align-items: flex-start; clip-path: none !important; }
  .quote__keys { width: 100%; justify-content: space-between; gap: 0; }
  .quote__keys li + li { padding-left: 0; }
  .quote__keys li + li::before { display: none; }

  .cform__row--2 { grid-template-columns: 1fr; }
  .foot__inner { grid-template-columns: 1fr; }
  .totop { right: 14px; bottom: 14px; width: 44px; height: 44px; }
}

@media (max-width: 560px) {
  /* the drawer already carries the CTA — keep the bar uncluttered */
  .nav__actions .btn--sm { display: none; }
  .nav__logo .logo__img { --logo-h: 27px; }
}

/* no WebGL / no GSAP — 정적 폴백 */
.eco.is-no3d .eco__pin { display: none; }
.eco.is-no3d .eco__fallback { display: block; }

/* JS 가 통째로 실패하면 card-swap.js 의 .is-static 도 붙지 않습니다.
   덱은 절대 배치라 그대로 두면 카드 6장이 한 자리에 겹칩니다 → CSS 로도 풀어 둡니다.
   (구체적인 정적 레이아웃은 섹션 10 의 .feat.is-static 규칙과 함께 정의돼 있습니다) */
.no-js .feat__inner { grid-template-columns: 1fr; }
.no-js .feat__deck { min-height: 0; }
.no-js .deck {
  position: relative; left: auto; top: auto;
  width: 100%; height: auto; margin: 0;
  perspective: none;
  display: grid; gap: 18px;
}
.no-js .fcard {
  position: relative; top: auto; left: auto;
  width: 100%; height: auto; min-height: 200px;
}
.no-js .feat__nav { display: none; }

/* --------------------------------------------------------------------------
   17. Reduced motion
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  [data-reveal] { opacity: 1 !important; transform: none !important; }
  .line__in, .sline__in { transform: none !important; opacity: 1 !important; }
  /* v4.1 — 비트가 세로로 펴졌으므로 전부 노출합니다.
     v4.0 은 겹쳐 있어서 "첫 비트만" 남겨야 했지만, 이제는 각자 제 자리입니다. */
  .hbeat { opacity: 1; visibility: visible; }
  .hbeat__tag, .hbeat__lead { opacity: 1 !important; transform: none !important; }
  .hbeat__tag::after { display: none; }   /* shiny 스윕 */
  .hero__dots { display: none; }          /* 도트 그리드 버스트 */

  /* 로드 인트로 해제 (HERO-INTRO-PLAN.md §7 주의).
     ui-motion.js 는 reduced motion 이면 인트로를 등록하기 전에 빠져나갑니다.
     초기 상태를 풀어 주지 않으면 히어로 오른쪽이 영구히 빈 화면이 됩니다. */
  .hero__layers,
  .hero__fold .hero__cta > *,
  .hero__fold .hero__stat,
  .hero__fold .hero__device,
  .hero__fold .hero__badge,
  .hero__scroll,
  /* 헤더를 빠뜨리면 reduced motion 에서 로고도 메뉴도 없는 빈 바가 됩니다 */
  .nav__logo,
  .nav__link,
  .nav__actions > * {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
  }
  .eco__pin { display: none; }
  .eco__fallback { display: block; }
  /* 4층 배경은 "끄기" 가 아니라 "멈추기" 입니다 — 위 §7-1 의 reduced 블록이
     애니메이션만 세우고 L1·L2 는 정지 화면으로 남깁니다 */
  .bento__card { transform: none !important; }
  .bento__spot { display: none; }
  .quote { clip-path: none !important; }
  /* 덱은 card-swap.js 가 .is-static 으로 풉니다 — 여기서는 잔여 transform 만 막습니다 */
  .fcard { transform: none !important; }
}
