/* 아호비 로그인 경험 — 데모(ahovye-os-access-demo) 전면 이식.
   원본 CSS를 그대로 옮기고, 실서비스용 확장(아이디 폼, 오류 표시, 시큐어 초록)만 덧붙였다.
   토큰: --ink #121831 / --violet #6657ff / --paper #fbfaf8 */
:root{--ink:#121831;--violet:#6657ff;--soft-violet:#a89eff;--paper:#fbfaf8}
.experience{isolation:isolate;background:radial-gradient(circle,#fffffff5 0 18%,#0000 52%),linear-gradient(125deg,#f8f7f5 0%,#fff 46%,#f8f6ff 100%);width:100vw;height:100svh;min-height:620px;position:fixed;inset:0;overflow:clip;transition:opacity var(--motion-dur-view) var(--motion-ease-in);z-index:100}
.experience.is-gone{opacity:0;pointer-events:none}
/* 노이즈 질감. mix-blend-mode:multiply 제거(2026-07-29 끊김 픽스) — 전체 화면
   blend 는 소프트웨어 합성에서 매우 비싸고, .experience 는 isolation:isolate +
   상시 애니메이션(ambient·glow·blink)이라 사실상 매 프레임 재합성됐다.
   normal 합성이라 질감이 밝아지므로 opacity 를 낮춰 균형을 맞춘다. */
.experience:after{content:"";pointer-events:none;opacity:.08;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");position:absolute;inset:0}
.topbar,.lg-footer{z-index:20;justify-content:space-between;align-items:center;display:flex;position:absolute;left:34px;right:34px}
.topbar{top:27px}
.lg-footer{color:#9298a8;font-size:12px;bottom:24px}
.wordmark{letter-spacing:.29em;font-size:13px;font-weight:590}
.replay,.lg-footer button{appearance:none;color:#8c91a1;cursor:pointer;background:0 0;border:0;padding:8px;font-size:12px;transition:color var(--motion-dur-micro) var(--motion-ease-out)}
.replay:hover,.lg-footer button:hover{color:var(--ink)}
/* 은은한 큰 원 2개. blur(90px) 였으나 제거하고 radial-gradient 로 페더를 그린다
   (2026-07-29 끊김 픽스): 52vw 짜리 초대형 블러를 7초 무한 애니메이션으로 돌리면
   GPU 꺼짐 고정 환경에선 매 프레임 CPU 컨볼루션이라 로그인 화면 전체가 상시
   프레임을 잃는다. 그 위에서 어떤 전환을 해도 끊긴다. 룩은 거의 동일. */
.ambient{opacity:.17;pointer-events:none;border-radius:50%;width:52vw;height:52vw;animation:7s ease-in-out infinite alternate breathe;position:absolute}
.ambient-one{background:radial-gradient(circle,#b7c8ff 0 46%,#b7c8ff00 100%);top:-20vw;left:-25vw}
.ambient-two{background:radial-gradient(circle,#b5a5ff 0 46%,#b5a5ff00 100%);animation-delay:-3s;bottom:-27vw;right:-28vw}
.stage{--birth-x:50%;--birth-y:48%;--mouth-anchor-y:calc(var(--birth-y) + 12px);--stage-h:calc(100svh - 108px);position:absolute;inset:62px 0 46px}
.timecode,.core-wrap,.orbit,.motion-line{left:var(--birth-x)}
.timecode,.core-wrap{top:var(--birth-y)}
.orbit,.motion-line{top:var(--mouth-anchor-y)}
.timecode{z-index:9;color:#6f7380;font-variant-numeric:tabular-nums;letter-spacing:-.065em;opacity:1;align-items:center;gap:8px;font-size:clamp(54px,7.2vw,104px);font-weight:560;transition:color var(--motion-dur-view) var(--motion-ease-out),opacity var(--motion-dur-view) var(--motion-ease-out),transform var(--motion-dur-view) var(--motion-ease-out);display:flex;position:absolute;translate:-50% -50%}
.timecode i{background:#f3a937;border-radius:50%;width:8px;height:8px;position:relative;transition:background var(--motion-dur-view) var(--motion-ease-out);animation:1.5s ease-in-out infinite coffee-pulse;box-shadow:0 0 #f3a93759}
.timecode i:after{content:"";position:absolute;inset:0;border-radius:inherit;box-shadow:0 0 18px #6657ff8c;opacity:0;pointer-events:none;transition:opacity var(--motion-dur-view) var(--motion-ease-out)}
.is-nine .timecode{color:#343849;transform:scale(1.035)}
.is-nine .timecode i{background:var(--violet)}
.is-nine .timecode i:after{opacity:1}
.is-revealed .timecode{opacity:0;transform:translateY(-7px)}
.character{z-index:7;pointer-events:none;position:absolute;inset:0}
/* 캐릭터의 열림(is-open)·준비(is-ready) 물러남은 transform 이 아니라 translate
   속성으로 준다. 톤 애니메이션(character-hello/shy/warm)이 both 채움으로 transform
   을 계속 잡아, 물러남을 transform 으로 주면 무효화돼 캐릭터가 48% 에 머물고 입장
   텍스트("곧 만나요")와 겹쳤다(2026-07-31 픽스 · 재발 금지). translate 는 톤
   애니 transform 과 독립 합성이라 물러남이 항상 유지된다. */
.core-wrap{z-index:6;opacity:0;place-items:center;width:118px;height:118px;--core-dy-f:0;transition:translate var(--motion-dur-view) var(--motion-ease-out),scale var(--motion-dur-view) var(--motion-ease-out);display:grid;position:absolute;translate:-50% -50%}
.is-revealed .core-wrap{opacity:1;animation:.9s cubic-bezier(.2,.8,.2,1) .12s both core-arrive}
/* 물러남 계수는 리터럴로 박는다(var 금지). transition:translate 가 걸린 값이
   커스텀 속성에 의존하면, 그 속성이 바뀌어도 transition 이 값을 갱신하지 않아
   물러남이 통째로 무시된다(브라우저 한계). --stage-h 는 상태 전환 때 변하지 않으므로
   그대로 써도 안전하다. */
.is-ready .core-wrap{scale:1.05;translate:-50% calc(-50% + var(--stage-h) * -.07)}
.is-open .core-wrap{scale:.88;translate:-50% calc(-50% + var(--stage-h) * -.13)}
.tone-hello .core-wrap{animation:.65s cubic-bezier(.2,.9,.25,1) both character-hello}
.tone-shy .core-wrap{animation:1.25s cubic-bezier(.2,.8,.2,1) both character-shy}
.tone-warm .core-wrap{animation:1.4s cubic-bezier(.2,.8,.2,1) both character-warm}
/* blur(8px) 제거 — 이미 radial-gradient 라 육안 차이가 거의 없는데,
   2.4초 무한 애니메이션이라 매 프레임 재블러 비용만 냈다. */
.core-glow{background:radial-gradient(circle,#6855ff3d,#aea4ff14 36%,#0000 70%);border-radius:50%;animation:2.4s ease-in-out 2.4s infinite alternate glow;position:absolute;inset:-70%}
/* drop-shadow 제거 — .core-wrap 이 애니메이션되는 내내(등장·표정·떨림) 매 프레임
   블러 그림자를 다시 그렸다. 캐릭터는 .core-glow 가 바닥을 받쳐 준다. */
.a-mark{color:#10162d;letter-spacing:-.1em;justify-content:center;align-items:center;gap:2px;width:164px;height:64px;font-size:49px;font-weight:670;line-height:1;display:flex;position:relative}
.nine-char,.oh-char{font-variant-numeric:tabular-nums;display:inline-block}
.nine-char{transform:rotate(-2deg)}
.oh-char{font-size:.92em;transform:translateY(1px)rotate(2deg)}
.eye{color:var(--violet);transform-origin:50% 70%;text-shadow:0 0 15px #6657ff7a;font-size:.84em;animation:4.8s ease-in-out infinite character-blink;display:inline-block}
.eye-right{animation-delay:40ms}
.a-mouth{color:#202640;text-align:center;transform-origin:50%;min-width:23px;font-size:.67em;font-weight:530;transition:transform var(--motion-dur-micro) var(--motion-ease-out);display:inline-block;position:relative}
/* 열린 입(말할 때). left:50% + translate(-50%,-50%) 로 얼굴 중심에 정렬한다
   (2026-07-31 픽스 — 예전엔 translate 없이 left:50% 라 중심 우측으로 쏠렸다).
   중심 정렬을 유지하려 아래 키프레임도 translate(-50%,-50%) 를 항상 포함한다. */
.a-mouth:after{content:"";opacity:0;border:.085em solid #202640;border-radius:50%;width:.34em;height:.3em;position:absolute;top:100%;left:50%;transform:translate(-50%,-50%) scale(.4)}
/* 말할 때 닫힌 입(_) 은 숨기고, 열린 입은 :after 하나만 움직인다.
   예전엔 부모 .a-mouth 를 scaleX(최대 1.28) 로 흔들어 :after 에 그 변형이
   겹쳐(이중 변형) 입 크기·비율이 뒤틀렸다(2026-07-31 픽스). 톤 개성(빠르게/
   수줍게/따뜻하게)은 :after 키프레임의 가로세로 비율로만 준다. */
.is-speaking .a-mouth{color:#0000}
.is-speaking.tone-hello .a-mouth:after{animation:.32s ease-in-out infinite mouth-round-fast}
.is-speaking.tone-shy .a-mouth:after{animation:.52s ease-in-out infinite mouth-round-shy}
.is-speaking.tone-warm .a-mouth:after{animation:.42s ease-in-out infinite mouth-round-warm}
.voice{color:#252a3c;letter-spacing:-.035em;text-align:center;place-items:center;width:min(500px,100vw - 40px);min-height:42px;font-size:clamp(18px,1.7vw,25px);font-weight:520;transition:opacity var(--motion-dur-view) var(--motion-ease-out);display:grid;position:absolute;top:calc(48% + 76px);left:50%;translate:-50%}
.voice span{animation:.62s cubic-bezier(.2,.8,.2,1) both sentence-in}
.is-ready .voice{opacity:0}
.fragments{z-index:2;position:absolute;inset:0}
.fragment{left:var(--x);top:var(--y);width:calc(32px * var(--s));height:calc(20px * var(--s));opacity:0;transform:translate(-50%, -50%) rotate(var(--r));animation:fragment-fold 2.75s cubic-bezier(.65,0,.18,1) calc(.9s + var(--d)) both;background:linear-gradient(135deg,#ffffffb8,#a89eff33);border:1px solid #6659d738;border-radius:4px;position:absolute;box-shadow:0 12px 26px #36307617,inset 0 1px #ffffffbf}
.fragment-1{width:calc(12px * var(--s));height:calc(12px * var(--s));background:#6a58ff;border-radius:50%}
.fragment-2{width:calc(52px * var(--s));height:calc(4px * var(--s));border-radius:4px}
.fragment-3{width:calc(20px * var(--s));height:calc(34px * var(--s))}
.fragment-4{width:calc(7px * var(--s));height:calc(7px * var(--s));background:#9d91ff;border:0;box-shadow:0 0 18px #8e81ff}
.orbit{opacity:0;border:1px solid #6a58ff4d;border-radius:50%;animation:2.8s cubic-bezier(.2,.8,.2,1) .35s both orbit-in;position:absolute;transform:translate(-50%,-50%)rotate(-12deg)scale(.2)}
.orbit-one{width:28vw;height:12vw}
.orbit-two{width:42vw;height:18vw;animation-delay:.5s;transform:translate(-50%,-50%)rotate(19deg)scale(.2)}
.orbit-three{width:58vw;height:25vw;animation-delay:.65s;transform:translate(-50%,-50%)rotate(-4deg)scale(.2)}
.motion-line{z-index:3;opacity:0;   /* blur(.2px) 제거 — 육안 효과 0, 레이어만 강제 생성 */border-top:2px solid #6c5bff8a;border-radius:50%;width:62vw;height:22vw;animation:2.1s ease-out .8s both streak;position:absolute;translate:-50% -50%}
.line-two{width:46vw;height:16vw;animation-delay:1.05s;transform:rotate(17deg)}
.white-transition{z-index:5;pointer-events:none;opacity:0;background:#fff;position:absolute;inset:0}
.is-revealed .white-transition{animation:.72s cubic-bezier(.3,0,.2,1) both white-bloom}
.entry{z-index:8;opacity:0;pointer-events:none;flex-direction:column;align-items:center;width:min(360px,100vw - 48px);transition:opacity var(--motion-dur-view) var(--motion-ease-out),transform var(--motion-dur-view) var(--motion-ease-out);display:flex;position:absolute;top:58%;left:50%;translate:-50%;transform:translateY(22px)}
.is-ready .entry{opacity:1;pointer-events:auto;transform:none}
.is-open .entry{--entry-open-dy-f:-.10;transform:translate3d(0,calc(var(--stage-h) * var(--entry-open-dy-f)),0)}
.profile{cursor:pointer;background:#fffffff2;   /* backdrop-filter 제거(호버 transform 과 공존 금지) — 배경을 불투명에 가깝게 */border:1px solid #161c371a;border-radius:999px;grid-template-columns:42px 1fr 32px;align-items:center;gap:10px;width:224px;height:62px;padding:9px 10px;position:relative;transition:transform var(--motion-dur-micro) var(--motion-ease-out),border-color var(--motion-dur-micro) var(--motion-ease-out);display:grid;box-shadow:0 18px 50px #21214a1a,inset 0 1px #ffffffe6}
.profile:after{content:"";position:absolute;inset:0;border-radius:inherit;box-shadow:0 22px 60px #3c338c26;opacity:0;pointer-events:none;z-index:-1;transition:opacity var(--motion-dur-micro) var(--motion-ease-out)}
.profile:hover{border-color:#6657ff40;transform:translateY(-3px)}
.profile:hover:after{opacity:1}
.avatar{color:#fff;background:linear-gradient(145deg,#222a49,#0c1127);border-radius:50%;place-items:center;width:42px;height:42px;font-size:13px;display:grid}
.profile-name{text-align:left;letter-spacing:-.02em;font-weight:540}
.arrow{color:#fff;background:linear-gradient(135deg,#7b6cff,#5847e8);border-radius:50%;place-items:center;width:32px;height:32px;display:grid;box-shadow:0 8px 20px #5b48e847}
.hint{color:#a2a5b1;letter-spacing:.02em;margin-top:14px;font-size:11px}
.login{background:#fffffff5;   /* backdrop-filter 제거(login-in 애니메이션과 공존 금지) */border:1px solid #191e391a;border-radius:20px;grid-template-columns:1fr 48px;gap:8px;width:100%;padding:12px;animation:.55s cubic-bezier(.2,.8,.2,1) both login-in;display:grid;box-shadow:0 24px 70px #27225d1f}
.login label{flex-direction:column;gap:3px;padding:2px 12px;display:flex}
.login label span{color:#969aa8;font-size:10px}
.login input{color:var(--ink);letter-spacing:.08em;background:0 0;border:0;outline:0;font-size:15px;min-width:0}
.login button{color:#fff;cursor:pointer;background:linear-gradient(135deg,#7767ff,#5543e1);border:0;border-radius:14px;box-shadow:0 10px 22px #5844e042}
.success{color:#5b6072;align-items:center;gap:10px;animation:.5s both login-in;display:flex}
.success-dot{background:#6a57ff;border-radius:50%;width:8px;height:8px;box-shadow:0 0 16px #6a57ff}
/* ── 실서비스 확장 (데모에 없는 부분) ───────────────────────── */
/* 로그인 실패 표정: 9._.;;o — 땀 두 방울이 맺혔다 흘러내리고 몸이 살짝 떨린다 */
/* sweat(;;)는 오류(is-oops) 때만 보이는 장식 — 평상시 opacity:0.
   inline-block 이라 flex 흐름에서 폭을 먹어 오른쪽 눈~o 사이만 벌어져 얼굴이
   비대칭으로 넓어졌다(2026-07-31 픽스). absolute 로 흐름에서 빼 평상시 9._.o 를
   좌우 대칭으로 되돌린다. 위치는 우측 눈 위. sweat-drop 은 translateY 라 공존. */
.sweat{position:absolute;left:60%;top:2px;color:#8a93c4;font-size:.36em;font-weight:620;letter-spacing:-.08em;opacity:0;pointer-events:none}
.is-oops .sweat{animation:sweat-drop 1.6s ease both}
.is-oops .core-wrap{animation:oops-shake .55s cubic-bezier(.36,.07,.19,.97) both}
.is-oops .a-mouth{transform:scaleY(.55)translateY(1px)}
.is-oops .eye{animation-play-state:paused}
@keyframes sweat-drop{0%{opacity:0;transform:translateY(-6px)}15%{opacity:1;transform:none}65%{opacity:1;transform:translateY(4px)}to{opacity:0;transform:translateY(9px)}}
/* 물러남(y)은 이제 translate 속성이 담당하므로 흔들림은 x 축만 준다(합성 유지). */
@keyframes oops-shake{10%,90%{transform:translate3d(-2px,0,0)}20%,80%{transform:translate3d(3px,0,0)}30%,50%,70%{transform:translate3d(-4px,0,0)}40%,60%{transform:translate3d(4px,0,0)}}
.login .fields{display:flex;flex-direction:column;gap:6px;min-width:0}   /* 아이디+비번 2단 폼 */
.login.is-error{animation:.4s ease login-shake}
.entry-err{color:#d5566a;margin-top:12px;font-size:12px;letter-spacing:.02em;animation:.3s both login-in}
@keyframes login-shake{0%,to{transform:none}25%{transform:translateX(-7px)}55%{transform:translateX(6px)}80%{transform:translateX(-3px)}}
/* ── 데모 원본 키프레임 ─────────────────────────────────────── */
@keyframes fragment-fold{0%{opacity:.72;transform:translate(-50%,-50%) rotate(var(--r)) scale(1)}45%{opacity:.8}to{left:var(--birth-x);top:var(--birth-y);opacity:0;transform:translate(-50%,-50%) rotate(calc(var(--r) + 260deg)) scale(.06)}}
@keyframes core-arrive{0%{opacity:0;scale:.16}70%{opacity:1;scale:1.12}to{opacity:1;scale:1}}
@keyframes white-bloom{0%{opacity:0}32%{opacity:.96}to{opacity:0}}
@keyframes orbit-in{0%{opacity:0}40%{opacity:.9}to{opacity:0;transform:translate(-50%,-50%)rotate(210deg)scale(1.7)}}
@keyframes streak{0%{opacity:0;transform:rotate(-20deg)scale(.3)}45%{opacity:.9}to{opacity:0;transform:rotate(100deg)scale(1.5)}}
@keyframes glow{to{opacity:.72;transform:scale(1.15)}}
@keyframes breathe{to{transform:scale(1.16)translate(2vw,-1vw)}}
@keyframes login-in{0%{opacity:0;transform:translateY(12px)scale(.97)}to{opacity:1;transform:none}}
@keyframes coffee-pulse{50%{transform:scale(.72)}}   /* box-shadow 단계 제거(§4.5) — 1.5초 무한 재래스터였다 */
@keyframes sentence-in{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
@keyframes character-hello{0%,to{rotate:none}45%{transform:translateY(-5px)}}
@keyframes character-shy{0%,to{rotate:none}40%{transform:translateY(2px)scale(.97)}70%{rotate:2deg}}
@keyframes character-warm{0%,to{transform:none}45%{transform:translateY(-4px)scale(1.045)}}
@keyframes character-blink{0%,45%,49%,to{transform:scaleY(1)}47%{transform:scaleY(.12)}}
/* 열린 입 톤별 — 모두 translate(-50%,-50%) 로 중심 고정, scale(가로,세로)로만
   여닫힘/개성. fast=또렷하게 세로로 벌어짐, shy=작고 살짝 기울임, warm=둥글고 살짝 넓게. */
@keyframes mouth-round-fast{0%,to{opacity:.5;transform:translate(-50%,-50%) scale(.6,.42)}50%{opacity:1;transform:translate(-50%,-50%) scale(.72,1.12)}}
@keyframes mouth-round-shy{0%,to{opacity:.5;transform:translate(-50%,-50%) scale(.6,.45)}50%{opacity:1;transform:translate(-50%,-50%) scale(.66,.98)rotate(-6deg)}}
@keyframes mouth-round-warm{0%,to{opacity:.55;transform:translate(-50%,-50%) scale(.78,.55)}50%{opacity:1;transform:translate(-50%,-50%) scale(1.05,.92)}}
@media (width<=700px){.stage{--birth-y:45%}.topbar,.lg-footer{left:20px;right:20px}.orbit-one{width:70vw;height:32vw}.orbit-two{width:100vw;height:48vw}.orbit-three{width:130vw;height:62vw}.fragment{opacity:.55}.is-open .core-wrap{translate:-50% calc(-50% + var(--stage-h) * -.10)}.entry{top:57%}.is-open .entry{--entry-open-dy-f:-.09}.voice{font-size:19px;top:calc(45% + 80px)}}
/* 폰 화면(팀원 베타는 모바일 접속 많음): iOS 포커스 확대 방지 + 여백/크기 보정 */
.experience button{touch-action:manipulation}
@media (width<=520px){
  .login input{font-size:16px}                 /* 16px 미만이면 iOS가 강제 줌 */
  .entry{width:min(340px,100vw - 32px);top:56%}
  .profile{width:232px;height:58px}
  .voice{width:calc(100vw - 32px)}
  .topbar{top:16px}
  .lg-footer{bottom:14px}
  .wordmark{letter-spacing:.22em;font-size:12px}
  .a-mark{font-size:44px}
  .hint{font-size:12px}
}
@media (prefers-reduced-motion:reduce){.fragment,.orbit,.motion-line,.ambient{animation:none}.fragment,.orbit,.motion-line{display:none}.core-wrap,.is-revealed .core-wrap,.tone-hello .core-wrap,.tone-shy .core-wrap,.tone-warm .core-wrap,.is-oops .core-wrap{animation:none;opacity:1}.entry{opacity:1;pointer-events:auto;transform:none}}
