chore: Vignette 모노레포 초기 스캐폴드 + 설계 문서 이관

- apps/web(React) · apps/api(FastAPI) · infra(Docker) · docs(설계 SoT)
- 마스터플랜·적대검증·메모리설계·디자인컨셉 docs 이관
- 미성년 사례데이터는 .gitignore로 제외(개인정보 보호)
This commit is contained in:
Yun Chan 2026-06-25 21:16:45 +09:00
commit 709baadd01
12 changed files with 3087 additions and 0 deletions

486
docs/mockups/session.html Normal file
View file

@ -0,0 +1,486 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>한신대 AI 심리상담 시뮬레이션 — 상담 세션 화면 시안</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/variable/pretendardvariable.min.css">
<style>
/* =====================================================================
한신대 AI 심리상담 시뮬레이션 — 상담 세션 화면 시안 (단일 파일)
토큰: DESIGN_CONCEPT.md §3 그대로 적용. 외부 의존 = Pretendard CDN 1개.
철칙 준수: border-left 0 · 이모지 0 · 카드덤프 0 · 에디토리얼 미니멀 ·
순흑/순백 금지 · 강조는 weight/tint/kicker/dot로.
===================================================================== */
:root{
/* 중성 베이스 (따뜻한 종이 + 잉크 네이비-그린) */
--paper:#FBFAF8; --paper-2:#F4F2EE; --surface:#FFFFFF;
--neutral-150:#E5E1DA;
--ink:#1C2A2A; --ink-2:#5A6663; --ink-3:#93A09C; --hair:#E5E1DA;
/* 악센트 세이지-틸 (주) + 테라코타 (보조) */
--accent:#3E7A6E; --accent-deep:#316257; --accent-tint:#EEF4F2; --accent-bright:#5F968B;
--clay:#B0735C; --clay-deep:#94604C; --clay-tint:#F6F0EC;
/* 의미색 */
--pos-text:#2C6E49; --pos-solid:#3B8C5E; --pos-tint:#E8F3EC;
--warn-text:#8A5A00; --warn-solid:#C28A2C; --warn-tint:#FAF1E0;
--crit-text:#A8352C; --crit-solid:#C4453A; --crit-tint:#FAEAE8;
--info-text:#2E5A7A; --info-solid:#3C7299; --info-tint:#EAF1F6;
/* stage */
--bg-stage:#1E2724;
/* 별칭 */
--text-on-accent:#FBFAF8;
--focus-ring:rgba(62,122,110,.45);
/* 폰트 */
--font-sans:"Pretendard Variable",Pretendard,-apple-system,"Apple SD Gothic Neo","Noto Sans KR",system-ui,sans-serif;
--font-num:"Pretendard Variable","JetBrains Mono",ui-monospace,monospace;
/* spacing */
--sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:24px; --sp-6:32px;
--radius:8px; --radius-sm:6px; --radius-lg:12px;
--shadow-sm:0 1px 2px rgba(28,42,42,.05);
--shadow-md:0 4px 14px rgba(28,42,42,.07);
--ease-out:cubic-bezier(.22,.61,.36,1);
}
*{margin:0;padding:0;box-sizing:border-box;}
html,body{height:100%;}
body{
font-family:var(--font-sans); background:var(--paper); color:var(--ink);
-webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
letter-spacing:-0.01em; line-height:1.6; overflow:hidden;
}
.tab-num{font-variant-numeric:tabular-nums;}
/* ============ 앱 셸 ============ */
.app{display:flex; flex-direction:column; height:100vh; min-height:640px;}
/* 상단 바 */
.topbar{
height:56px; flex:none; background:var(--surface);
border-bottom:1px solid var(--hair);
display:flex; align-items:center; gap:var(--sp-5);
padding:0 var(--sp-6);
}
.topbar .scn{display:flex; flex-direction:column; gap:1px;}
.topbar .scn .k{
font-family:var(--font-num); font-size:11px; font-weight:600;
letter-spacing:0.1em; text-transform:uppercase; color:var(--accent);
}
.topbar .scn .t{font-size:14px; font-weight:600; color:var(--ink); letter-spacing:-0.01em;}
.topbar .stagebar{
margin:0 auto; display:flex; align-items:center; gap:var(--sp-4);
}
.topbar .stagebar .seg{display:flex; align-items:center; gap:7px;}
.topbar .stagebar .seg .pt{width:7px; height:7px; border-radius:50%; background:var(--neutral-150);}
.topbar .stagebar .seg.done .pt{background:var(--accent-bright);}
.topbar .stagebar .seg.cur .pt{background:var(--accent); box-shadow:0 0 0 3px var(--accent-tint);}
.topbar .stagebar .seg .lb{font-size:12px; color:var(--ink-3); font-weight:500;}
.topbar .stagebar .seg.cur .lb{color:var(--ink); font-weight:600;}
.topbar .stagebar .ln{width:18px; height:1px; background:var(--hair);}
.topbar .right{display:flex; align-items:center; gap:var(--sp-5);}
.topbar .elapsed{font-family:var(--font-num); font-size:14px; font-weight:600; color:var(--ink-2); letter-spacing:0.02em;}
.topbar .elapsed .sub{font-size:11px; color:var(--ink-3); font-weight:500; margin-left:4px;}
.topbar .obs{display:flex; align-items:center; gap:7px; font-size:12px; color:var(--ink-3);}
.topbar .obs .d{width:6px; height:6px; border-radius:50%; background:var(--info-solid);}
/* 본문 3컬럼 */
.main{flex:1; display:grid; grid-template-columns:320px 1fr 300px; min-height:0;}
/* ---- LEFT ---- */
.left{
background:var(--surface); border-right:1px solid var(--hair);
padding:var(--sp-6) var(--sp-5); display:flex; flex-direction:column; gap:var(--sp-6);
overflow:auto;
}
.kicker{
font-family:var(--font-num); font-size:12px; font-weight:600;
letter-spacing:0.08em; color:var(--accent);
display:flex; align-items:center; gap:8px; margin-bottom:var(--sp-4);
}
.kicker .dot{width:6px; height:6px; border-radius:50%; background:var(--accent-bright);}
/* 회기 단계 세로 트랙커 */
.tracker{display:flex; flex-direction:column;}
.tk{display:grid; grid-template-columns:24px 1fr auto; align-items:center; column-gap:var(--sp-3); min-height:30px;}
.tk .mk{position:relative; display:flex; justify-content:center;}
.tk .mk .o{width:14px; height:14px; border-radius:50%; border:2px solid var(--neutral-150); background:var(--surface); z-index:1;}
.tk.done .mk .o{background:var(--accent-bright); border-color:var(--accent-bright);}
.tk.cur .mk .o{border-color:var(--accent); box-shadow:0 0 0 4px var(--accent-tint);}
.tk.cur .mk .o::after{content:""; position:absolute; top:5px; left:50%; transform:translateX(-50%); width:6px; height:6px; border-radius:50%; background:var(--accent);}
.tk .nm{font-size:15px; color:var(--ink-3); font-weight:500;}
.tk.done .nm{color:var(--ink-2);}
.tk.cur .nm{color:var(--ink); font-weight:700;}
.tk .tm{font-family:var(--font-num); font-size:12px; color:var(--ink-3); letter-spacing:0.02em;}
.tk.cur .tm{color:var(--accent); font-weight:600;}
.tkline{grid-column:1; width:2px; height:14px; margin-left:11px; background:var(--hair);}
.tkline.done{background:var(--accent-bright);}
/* 내담자 컨텍스트 카드 */
.ctx .row{display:flex; flex-direction:column; gap:3px; padding:var(--sp-3) 0; border-bottom:1px solid var(--paper-2);}
.ctx .row:last-child{border-bottom:none;}
.ctx .row .l{font-size:12px; color:var(--ink-3); font-weight:500;}
.ctx .row .v{font-size:14px; color:var(--ink); font-weight:500; line-height:1.5;}
.ctx .row .v.em{font-weight:700;}
/* ---- CENTER STAGE ---- */
.stage{
background:var(--bg-stage); position:relative;
display:flex; flex-direction:column; align-items:center;
padding:var(--sp-6) var(--sp-6) 0;
min-height:0;
}
.stage .label{
position:absolute; top:18px; left:50%; transform:translateX(-50%);
font-family:var(--font-num); font-size:11px; font-weight:600;
letter-spacing:0.12em; text-transform:uppercase; color:rgba(232,238,242,.55);
display:flex; align-items:center; gap:8px;
}
.stage .label .d{width:6px; height:6px; border-radius:50%; background:var(--clay);}
/* 아바타 + 오브 */
.avatarwrap{
margin-top:44px; position:relative; width:240px; height:240px;
display:flex; align-items:center; justify-content:center; flex:none;
}
.orb{
position:absolute; inset:6px; border-radius:50%;
box-shadow:0 0 0 3px var(--ring), 0 0 30px 6px var(--ring-glow);
--ring:rgba(176,115,92,.55); --ring-glow:rgba(176,115,92,.30);
animation:orbpulse 4.2s ease-in-out infinite;
}
@keyframes orbpulse{0%,100%{transform:scale(1); opacity:.9} 50%{transform:scale(1.03); opacity:1}}
.avatar{width:200px; height:200px; border-radius:50%; overflow:visible; display:block;}
/* 호흡: 어깨 미세 상하 */
.av-body{animation:breathe 3.6s ease-in-out infinite; transform-origin:center bottom;}
@keyframes breathe{0%,100%{transform:translateY(0)} 50%{transform:translateY(2px)}}
/* 깜빡임 */
.av-eyes{animation:blink 6s steps(1) infinite; transform-origin:center; transform-box:fill-box;}
@keyframes blink{0%,95%,100%{transform:scaleY(1)} 97.5%{transform:scaleY(0.1)}}
/* 페르소나 메타 */
.persona{margin-top:26px; text-align:center; color:#E8EEF2;}
.persona .nm{font-size:19px; font-weight:600; letter-spacing:-0.01em;}
.persona .meta{font-size:13px; color:rgba(232,238,242,.6); margin-top:4px;}
.persona .div{width:36px; height:1px; background:rgba(232,238,242,.2); margin:12px auto;}
.persona .state{font-size:14px; color:rgba(232,238,242,.78); display:flex; align-items:center; gap:8px; justify-content:center;}
.persona .state .d{width:6px; height:6px; border-radius:50%; background:var(--clay); animation:orbpulse 4.2s ease-in-out infinite;}
/* 자막 (대본 스타일) */
.transcript{
width:100%; max-width:680px; flex:1; min-height:0; margin-top:22px;
overflow:auto; display:flex; flex-direction:column; gap:var(--sp-4);
padding-bottom:var(--sp-6); -webkit-mask-image:linear-gradient(to bottom,transparent 0,#000 28px);
}
.turn{padding:var(--sp-3) var(--sp-4); border-radius:var(--radius);}
.turn.client{background:rgba(176,115,92,.14);}
.turn.learner{background:rgba(95,150,139,.16);}
.turn .head{display:flex; justify-content:space-between; align-items:baseline; margin-bottom:5px;}
.turn .who{font-size:12px; font-weight:600; letter-spacing:0.02em;}
.turn.client .who{color:#E2B79F;}
.turn.learner .who{color:#A9D2C6;}
.turn .ts{font-family:var(--font-num); font-size:11px; color:rgba(232,238,242,.45);}
.turn .tx{font-size:16px; line-height:1.65; color:#DDE5E4;}
.turn .tx.partial{color:rgba(221,229,228,.5);}
.turn .tx .cursor{display:inline-block; width:2px; height:1.05em; background:#A9D2C6; vertical-align:-2px; margin-left:2px; animation:cur 1s steps(1) infinite;}
@keyframes cur{0%,50%{opacity:1}50.01%,100%{opacity:0}}
/* ---- RIGHT (피드백 레일) ---- */
.right{
background:var(--surface); border-left:1px solid var(--hair);
padding:var(--sp-6) var(--sp-5); display:flex; flex-direction:column; gap:var(--sp-6);
overflow:auto;
}
/* 내담자 상태 미터 */
.meter{display:flex; flex-direction:column; gap:var(--sp-4);}
.meter .m .top{display:flex; justify-content:space-between; align-items:baseline; margin-bottom:7px;}
.meter .m .nm{font-size:13px; color:var(--ink-2); font-weight:500;}
.meter .m .val{font-size:13px; color:var(--ink); font-weight:600;}
.meter .m .bar{height:4px; background:var(--paper-2); border-radius:2px; overflow:hidden;}
.meter .m .bar .fill{height:100%; border-radius:2px; transition:width 2s var(--ease-out);}
.meter .m .bar .fill.clay{background:var(--clay);}
.meter .m .bar .fill.sage{background:var(--accent-bright);}
/* 라이브 신호 (앰비언트) */
.signal{display:flex; flex-direction:column; gap:var(--sp-4);}
.signal .live{
display:flex; align-items:center; gap:10px;
padding:var(--sp-3) 0;
animation:fadein .4s var(--ease-out);
}
@keyframes fadein{from{opacity:0; transform:translateY(3px)}to{opacity:1; transform:none}}
.signal .live .d{width:9px; height:9px; border-radius:50%; flex:none;}
.signal .live .d.pos{background:var(--pos-solid);}
.signal .live .d.warn{background:var(--warn-solid);}
.signal .live .nm{font-size:14px; color:var(--ink); font-weight:500;}
.signal .live .nm .sub{font-size:12px; color:var(--ink-3); font-weight:400; display:block; margin-top:1px;}
.signal .seq{display:flex; align-items:center; gap:9px; padding-top:var(--sp-2); border-top:1px solid var(--paper-2);}
.signal .seq .sl{font-size:11px; color:var(--ink-3); font-weight:500; letter-spacing:0.02em;}
.signal .seq .dots{display:flex; gap:7px; align-items:center;}
.signal .seq .dots span{width:7px; height:7px; border-radius:50%;}
.signal .seq .dots .pos{background:var(--pos-solid);}
.signal .seq .dots .warn{background:var(--warn-solid);}
.signal .seq .dots .neu{background:var(--neutral-150);}
/* 안전 점검 콜아웃 (warn, 좌측바 없음 → dot+kicker+tint) */
.callout{border-radius:var(--radius); padding:var(--sp-4); background:var(--warn-tint);}
.callout .clh{font-size:12px; font-weight:600; letter-spacing:0.04em; color:var(--warn-text); display:flex; align-items:center; gap:8px; margin-bottom:6px;}
.callout .clh::before{content:""; width:7px; height:7px; border-radius:50%; background:var(--warn-solid);}
.callout .cl{font-size:13px; color:var(--warn-text); line-height:1.55;}
/* 셀프 노트 */
.note textarea{
width:100%; min-height:78px; resize:none;
background:var(--paper-2); border:1px solid var(--hair); border-radius:var(--radius-sm);
padding:10px 12px; font-family:var(--font-sans); font-size:13px; color:var(--ink); line-height:1.6;
}
.note textarea::placeholder{color:var(--ink-3);}
.note textarea:focus{outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--focus-ring);}
/* ============ 하단 컨트롤 바 ============ */
.controls{
height:84px; flex:none; background:var(--surface); border-top:1px solid var(--hair);
display:grid; grid-template-columns:1fr auto 1fr; align-items:center;
padding:0 var(--sp-6); gap:var(--sp-5);
}
.controls .mic{display:flex; align-items:center; gap:var(--sp-4);}
.controls .mic .btn{
width:52px; height:52px; border-radius:50%; flex:none; border:none; cursor:pointer;
background:var(--accent); display:flex; align-items:center; justify-content:center;
animation:micbreath 1.5s ease-in-out infinite;
}
@keyframes micbreath{0%,100%{box-shadow:0 0 0 0 rgba(62,122,110,.35)}50%{box-shadow:0 0 0 7px rgba(62,122,110,0)}}
.controls .mic .btn svg{width:22px; height:22px; stroke:var(--text-on-accent);}
.controls .mic .lbl{display:flex; flex-direction:column; gap:1px;}
.controls .mic .lbl .s{font-size:14px; font-weight:600; color:var(--ink);}
.controls .mic .lbl .h{font-size:12px; color:var(--ink-3);}
/* segmented control (피드백 모드) */
.modeseg{display:flex; background:var(--paper-2); border-radius:var(--radius); padding:3px; gap:2px; justify-self:center;}
.modeseg button{
font-family:var(--font-sans); font-size:13px; font-weight:500; color:var(--ink-2);
border:none; background:transparent; padding:8px 16px; border-radius:6px; cursor:pointer;
transition:all .16s var(--ease-out);
}
.modeseg button.on{background:var(--surface); color:var(--ink); font-weight:600; box-shadow:var(--shadow-sm);}
.controls .rightc{display:flex; align-items:center; gap:var(--sp-4); justify-self:end;}
.btn-ghost{
font-family:var(--font-sans); font-size:14px; font-weight:500; color:var(--ink-2);
border:1px solid var(--hair); background:var(--surface); padding:9px 16px; border-radius:var(--radius); cursor:pointer;
display:flex; align-items:center; gap:8px;
}
.btn-ghost svg{width:16px; height:16px; stroke:var(--ink-2);}
/* 밀어서 종료 */
.slide-end{
position:relative; width:148px; height:40px; border-radius:var(--radius);
background:var(--crit-tint); overflow:hidden; cursor:pointer; user-select:none;
}
.slide-end .track{position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
font-size:13px; font-weight:600; color:var(--crit-text); letter-spacing:0.01em;}
.slide-end .knob{position:absolute; top:3px; left:3px; width:34px; height:34px; border-radius:6px;
background:var(--crit-solid); display:flex; align-items:center; justify-content:center;}
.slide-end .knob svg{width:16px; height:16px; stroke:#fff;}
@media (prefers-reduced-motion: reduce){
*,*::before,*::after{animation-duration:.01ms!important; transition-duration:.01ms!important;}
}
</style>
</head>
<body>
<div class="app">
<!-- ============ 상단 바 ============ -->
<header class="topbar">
<div class="scn">
<span class="k">LIVE SESSION</span>
<span class="t">우울 호소 청소년 · 회기 1</span>
</div>
<div class="stagebar">
<div class="seg done"><span class="pt"></span><span class="lb">라포</span></div>
<span class="ln"></span>
<div class="seg cur"><span class="pt"></span><span class="lb">탐색</span></div>
<span class="ln"></span>
<div class="seg"><span class="pt"></span><span class="lb">개입</span></div>
<span class="ln"></span>
<div class="seg"><span class="pt"></span><span class="lb">정리</span></div>
</div>
<div class="right">
<div class="obs"><span class="d"></span>교수자 관찰 중</div>
<div class="elapsed tab-num">12:41<span class="sub">경과</span></div>
</div>
</header>
<!-- ============ 본문 3컬럼 ============ -->
<div class="main">
<!-- LEFT: 회기 단계 + 내담자 컨텍스트 -->
<aside class="left">
<section>
<div class="kicker"><span class="dot"></span>회기 진행</div>
<div class="tracker">
<div class="tk done"><div class="mk"><span class="o"></span></div><div class="nm">라포 형성</div><div class="tm tab-num">09:12</div></div>
<div class="tkline done"></div>
<div class="tk cur"><div class="mk"><span class="o"></span></div><div class="nm">탐색</div><div class="tm">진행중</div></div>
<div class="tkline"></div>
<div class="tk"><div class="mk"><span class="o"></span></div><div class="nm">개입</div><div class="tm"></div></div>
<div class="tkline"></div>
<div class="tk"><div class="mk"><span class="o"></span></div><div class="nm">정리</div><div class="tm"></div></div>
</div>
</section>
<section class="ctx">
<div class="kicker"><span class="dot"></span>내담자 정보</div>
<div class="row"><span class="l">호소 문제</span><span class="v em">2주째 무기력 · 등교 거부</span></div>
<div class="row"><span class="l">시작 정서</span><span class="v">위축 · 방어적, 자기개방 낮음</span></div>
<div class="row"><span class="l">배경</span><span class="v">고2 · 최근 성적 하락 · 또래 관계 위축</span></div>
<div class="row"><span class="l">연습 목표</span><span class="v em">개방형 질문으로 자기개방 끌어내기</span></div>
</section>
</aside>
<!-- CENTER: 내담자 아바타 + 음성 오브 + 자막 -->
<main class="stage">
<div class="label"><span class="d"></span>교육용 가상 내담자</div>
<div class="avatarwrap">
<div class="orb"></div>
<!-- 반구상 양식화 흉상 (사실성 ~40%): 코·주름·모공 없음, 눈/눈썹/입/어깨 4채널 -->
<svg class="avatar" viewBox="0 0 200 200" aria-label="가상 내담자 서연">
<defs>
<radialGradient id="aura" cx="50%" cy="42%" r="58%">
<stop offset="0%" stop-color="#2E3A36"/>
<stop offset="100%" stop-color="#1E2724"/>
</radialGradient>
<clipPath id="head"><circle cx="100" cy="100" r="96"/></clipPath>
</defs>
<g clip-path="url(#head)">
<rect width="200" height="200" fill="url(#aura)"/>
<g class="av-body">
<!-- 어깨/상체 실루엣 (후디, 디테일 없음) -->
<path d="M44 200 C44 168 66 150 100 150 C134 150 156 168 156 200 Z" fill="#3C4A45"/>
<!---->
<rect x="88" y="120" width="24" height="26" rx="11" fill="#E8C9A8"/>
<!-- 머리카락 뒤 -->
<path d="M58 96 C58 64 76 50 100 50 C124 50 142 64 142 96 L142 124 C142 122 138 110 132 108 L132 92 C132 76 118 70 100 70 C82 70 68 76 68 92 L68 108 C62 110 58 122 58 124 Z" fill="#2B2723"/>
<!-- 얼굴 -->
<path d="M70 92 C70 74 83 64 100 64 C117 64 130 74 130 92 L130 108 C130 126 117 138 100 138 C83 138 70 126 70 108 Z" fill="#E8C9A8"/>
<!-- 눈썹 (정서: 약간 안쪽 하강 = 위축/불안) -->
<path d="M80 92 q9 -3 18 0" stroke="#3A322C" stroke-width="2.4" fill="none" stroke-linecap="round"/>
<path d="M102 92 q9 -3 18 0" stroke="#3A322C" stroke-width="2.4" fill="none" stroke-linecap="round"/>
<!-- 눈 (큰 눈 = teen, 시선 살짝 아래) -->
<g class="av-eyes">
<ellipse cx="89" cy="102" rx="6.2" ry="5" fill="#FBFAF8"/>
<ellipse cx="111" cy="102" rx="6.2" ry="5" fill="#FBFAF8"/>
<circle cx="88.5" cy="103.5" r="3.1" fill="#3A2E26"/>
<circle cx="110.5" cy="103.5" r="3.1" fill="#3A2E26"/>
</g>
<!-- 입 (닫힘 호 = listening 상태, 미세 긴장) -->
<path d="M91 120 q9 4 18 0" stroke="#9A6A56" stroke-width="2.2" fill="none" stroke-linecap="round"/>
<!-- 앞머리 -->
<path d="M68 92 L68 86 C68 72 82 66 100 66 C118 66 132 72 132 86 L132 92 C128 80 116 78 108 80 C104 81 100 84 96 84 C88 84 76 82 68 92 Z" fill="#2B2723"/>
</g>
</g>
</svg>
</div>
<div class="persona">
<div class="nm">서연</div>
<div class="meta">17세 · 고2</div>
<div class="div"></div>
<div class="state"><span class="d"></span>머뭇거리며 말하는 중</div>
</div>
<!-- 자막 (대본 스타일, 아래→위 흐름) -->
<div class="transcript">
<div class="turn learner">
<div class="head"><span class="who">나 (학습자)</span><span class="ts tab-num">12:33</span></div>
<div class="tx">학교에 가는 게 많이 버겁게 느껴지는 것 같네요. 혹시 어떤 순간이 가장 힘든지 조금 더 들려줄 수 있을까요?</div>
</div>
<div class="turn client">
<div class="head"><span class="who">서연 (내담자)</span><span class="ts tab-num">12:34</span></div>
<div class="tx">아침에… 눈을 뜨면요. 그냥 천장만 보고 있어요. 일어나야 하는 건 아는데, 몸이 안 움직여요. 그게 제일… 무서워요.</div>
</div>
<div class="turn learner">
<div class="head"><span class="who">나 (학습자)</span><span class="ts tab-num">12:35</span></div>
<div class="tx">눈을 떠도 몸이 따라주지 않는 그 무력감이, 무섭게까지 느껴지는군요.</div>
</div>
<div class="turn client">
<div class="head"><span class="who">서연 (내담자)</span><span class="ts tab-num">12:36</span></div>
<div class="tx partial">네… 그리고 요즘은 친구들이랑 있어도 자꾸 혼자인 것 같고<span class="cursor"></span></div>
</div>
</div>
</main>
<!-- RIGHT: 피드백 레일 (은은한 신호 모드) -->
<aside class="right">
<section class="meter">
<div class="kicker"><span class="dot"></span>내담자 상태</div>
<div class="m">
<div class="top"><span class="nm">감정 톤</span><span class="val">불안 · 위축</span></div>
<div class="bar"><div class="fill clay" style="width:68%"></div></div>
</div>
<div class="m">
<div class="top"><span class="nm">마음 열림</span><span class="val">조금씩 열림</span></div>
<div class="bar"><div class="fill sage" style="width:34%"></div></div>
</div>
</section>
<section class="signal">
<div class="kicker"><span class="dot"></span>라이브 신호</div>
<div class="live">
<span class="d pos"></span>
<span class="nm">공감적 반영<span class="sub">방금 반영이 자기개방을 열었어요</span></span>
</div>
<div class="seq">
<span class="sl">최근 흐름</span>
<span class="dots">
<span class="neu"></span><span class="pos"></span><span class="warn"></span><span class="pos"></span><span class="pos"></span>
</span>
</div>
</section>
<section>
<div class="callout">
<span class="clh">안전 점검</span>
<span class="cl">내담자가 무기력·무력감을 반복해 표현하고 있어요. 위험 신호 여부를 자연스럽게 탐색해 보세요.</span>
</div>
</section>
<section class="note">
<div class="kicker"><span class="dot"></span>이번 회기 메모</div>
<textarea placeholder="떠오른 가설·다음에 시도할 질문을 적어두세요. 회기말 리뷰에 함께 보여집니다."></textarea>
</section>
</aside>
</div>
<!-- ============ 하단 컨트롤 바 ============ -->
<footer class="controls">
<div class="mic">
<button class="btn" aria-label="마이크 켜짐">
<svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="3" width="6" height="11" rx="3"/><path d="M5 11a7 7 0 0 0 14 0"/><line x1="12" y1="18" x2="12" y2="21"/>
</svg>
</button>
<div class="lbl"><span class="s">듣고 있어요</span><span class="h">말하면 자동으로 전달돼요</span></div>
</div>
<div class="modeseg" role="tablist" aria-label="피드백 모드">
<button role="tab">몰입</button>
<button role="tab" class="on" aria-selected="true">은은한 신호</button>
<button role="tab">코칭</button>
</div>
<div class="rightc">
<button class="btn-ghost">
<svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="5" width="4" height="14"/><rect x="14" y="5" width="4" height="14"/></svg>
일시정지
</button>
<div class="slide-end" title="밀어서 세션 종료">
<div class="track">밀어서 종료</div>
<div class="knob">
<svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>
</div>
</div>
</div>
</footer>
</div>
</body>
</html>