persona_survey / index.html
jonghhhh's picture
Initial commit: NBS Persona Survey System
62b1578
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Persona Issue Survey(Beta version) - NBS Avatar System</title>
<link href="https://fonts.googleapis.com/css2?family=Pretendard:wght@400;500;700&display=swap" rel="stylesheet">
<style>
:root {
--primary: #6366f1;
--primary-dark: #4f46e5;
--secondary: #ec4899;
--bg: #f8fafc;
--sidebar-bg: #0f172a;
--card-bg: #ffffff;
--text-main: #1e293b;
--text-muted: #64748b;
--glass: rgba(255, 255, 255, 0.7);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Pretendard', sans-serif;
}
body {
background-color: var(--bg);
color: var(--text-main);
display: flex;
min-height: 100vh;
}
/* Sidebar Styling */
.sidebar {
width: 320px;
background-color: var(--sidebar-bg);
color: white;
padding: 2.5rem 1.5rem;
display: flex;
flex-direction: column;
position: fixed;
height: 100vh;
left: 0;
top: 0;
box-shadow: 4px 0 20px rgba(0, 0, 0, 0.1);
z-index: 100;
}
.logo {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 3rem;
background: linear-gradient(to right, #818cf8, #f472b6);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
display: flex;
align-items: center;
gap: 10px;
}
.menu-item {
padding: 1rem 1.25rem;
border-radius: 12px;
margin-bottom: 0.5rem;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 12px;
color: #94a3b8;
font-weight: 500;
}
.menu-item:hover {
background: rgba(255, 255, 255, 0.05);
color: white;
}
.menu-item.active {
background: var(--primary);
color: white;
}
.api-key-section {
margin-top: auto;
padding: 1.5rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 16px;
font-size: 0.875rem;
}
.api-key-label {
display: block;
margin-bottom: 0.75rem;
font-weight: 500;
color: #e2e8f0;
}
.api-key-container {
position: relative;
width: 100%;
margin-bottom: 1rem;
}
.api-key-input {
width: 100%;
padding: 0.75rem;
padding-right: 2.5rem;
/* Space for toggle icon */
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.2);
background: rgba(255, 255, 255, 0.05);
color: #f8fafc;
font-size: 0.85rem;
transition: all 0.2s;
}
.api-key-input:focus {
outline: none;
background: rgba(255, 255, 255, 0.1);
border-color: var(--primary);
box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.3);
}
.toggle-password {
position: absolute;
right: 0.75rem;
top: 50%;
transform: translateY(-50%);
cursor: pointer;
color: #94a3b8;
font-size: 0.9rem;
user-select: none;
}
.toggle-password:hover {
color: white;
}
.key-guide {
color: #94a3b8;
font-size: 0.75rem;
line-height: 1.5;
}
.key-guide a {
color: #818cf8;
text-decoration: none;
}
/* Main Content Styling */
.main-content {
flex: 1;
margin-left: 320px;
padding: 3rem;
max-width: 1200px;
}
.header-box {
margin-bottom: 3rem;
}
.title {
font-size: 2.25rem;
font-weight: 700;
margin-bottom: 1rem;
color: #0f172a;
}
.description {
color: var(--text-muted);
line-height: 1.6;
font-size: 1.1rem;
}
.system-info {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
margin-top: 2rem;
}
.info-card {
background: white;
padding: 1.5rem;
border-radius: 20px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.03);
border: 1px solid #f1f5f9;
}
.info-card h4 {
margin-bottom: 0.5rem;
color: var(--primary);
}
.info-card p {
font-size: 0.9rem;
color: var(--text-muted);
line-height: 1.5;
}
/* Form Styling */
.survey-form {
background: white;
padding: 2.5rem;
border-radius: 24px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.04);
margin-bottom: 3rem;
border: 1px solid #f1f5f9;
}
.form-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem;
margin-bottom: 2rem;
}
.input-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.input-group.full {
grid-column: span 2;
}
label {
font-weight: 600;
font-size: 0.9rem;
color: #475569;
}
select,
input[type="text"],
input[type="number"],
textarea {
padding: 0.875rem;
border-radius: 12px;
border: 1px solid #e2e8f0;
background: #f8fafc;
font-size: 1rem;
transition: all 0.2s;
}
select:focus,
input:focus,
textarea:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1);
background: white;
}
textarea {
resize: vertical;
min-height: 100px;
}
.btn-submit {
background: linear-gradient(135deg, var(--primary), var(--primary-dark));
color: white;
padding: 1rem 2rem;
border-radius: 14px;
border: none;
font-weight: 600;
font-size: 1.1rem;
cursor: pointer;
width: 100%;
transition: all 0.3s;
box-shadow: 0 4px 15px rgba(79, 70, 229, 0.3);
}
.btn-submit:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(79, 70, 229, 0.4);
}
.btn-submit:active {
transform: translateY(0);
}
.btn-submit:disabled {
background: #cbd5e1;
box-shadow: none;
cursor: not-allowed;
}
.btn-reset {
background: white;
color: #64748b;
padding: 1rem 1.5rem;
border-radius: 14px;
border: 1px solid #e2e8f0;
font-weight: 600;
font-size: 1.1rem;
cursor: pointer;
transition: all 0.2s;
}
.btn-reset:hover {
background: #f8fafc;
border-color: #cbd5e1;
color: #1e293b;
}
.button-row {
display: flex;
gap: 1rem;
}
/* Results Styling */
.results-container {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.result-card {
background: white;
padding: 2rem;
border-radius: 20px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.03);
border: 1px solid #f1f5f9;
animation: slideIn 0.5s ease forwards;
opacity: 0;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.avatar-info {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1.5rem;
padding-bottom: 1rem;
border-bottom: 1px solid #f1f5f9;
}
.avatar-icon {
width: 48px;
height: 48px;
background: #e0e7ff;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
color: var(--primary);
font-weight: 700;
}
.avatar-meta {
font-size: 0.85rem;
color: var(--text-muted);
}
.response-text {
line-height: 1.8;
font-size: 1.05rem;
color: #334155;
white-space: pre-wrap;
}
.context-chips {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 1.5rem;
}
.chip {
padding: 0.25rem 0.75rem;
background: #f1f5f9;
border-radius: 20px;
font-size: 0.75rem;
color: var(--text-muted);
}
.loader {
display: none;
text-align: center;
padding: 2rem;
}
.spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid var(--primary);
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 1rem;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.notice-label {
font-size: 0.8rem;
color: var(--secondary);
font-weight: 500;
margin-top: 0.5rem;
}
</style>
</head>
<body>
<aside class="sidebar">
<div class="logo">
<span>โœจ</span> Issue Survey (Beta)
</div>
<div class="menu-item active" id="menu-actual" onclick="switchMode('actual')">
<span>๐Ÿ‘ค</span> Actual Respondent
</div>
<div class="menu-item" id="menu-synthetic" onclick="switchMode('synthetic')">
<span>๐Ÿ“Š</span> Statistical Synthetic
</div>
<div class="api-key-section">
<label class="api-key-label">Gemini API Key</label>
<div class="api-key-container">
<input type="password" id="api-key" class="api-key-input" placeholder="์—ฌ๊ธฐ์— API ํ‚ค ์ž…๋ ฅ">
<span class="toggle-password" id="toggle-key" onclick="toggleKeyVisibility()">๐Ÿ‘๏ธ</span>
</div>
<div class="key-guide">
<p>Gemini API ํ‚ค๊ฐ€ ์—†์œผ์‹ ๊ฐ€์š”?</p>
<p><a href="https://aistudio.google.com/app/apikey" target="_blank">Google AI Studio</a>์—์„œ ๋ฌด๋ฃŒ๋กœ ๋ฐœ๊ธ‰๋ฐ›์œผ์‹ค ์ˆ˜
์žˆ์Šต๋‹ˆ๋‹ค.</p>
</div>
</div>
</aside>
<main class="main-content">
<div class="header-box">
<h1 class="title">Persona Issue Survey (Beta version)</h1>
<p class="description">2020๋…„ 7์›”(1์ฐจ)๋ถ€ํ„ฐ 2025๋…„ 9์›”(164์ฐจ)๊นŒ์ง€ ์ˆ˜ํ–‰๋œ <b>์ „๊ตญ์ง€ํ‘œ์กฐ์‚ฌ(NBS)</b> ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ํŠน์ • ๊ณ„์ธต์˜ ๋ชฉ์†Œ๋ฆฌ๋ฅผ
์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•ฉ๋‹ˆ๋‹ค.</p>
<p style="margin-top: 0.5rem; font-size: 0.85rem; color: #f43f5e; font-weight: 500;">โš ๏ธ ๋ณธ ์‹œ์Šคํ…œ์€ ์‚ฌํšŒ์  ์ด์Šˆ ๋ถ„์„์šฉ์ด๋ฉฐ
์ธ๋ฌผ ํ‰๊ฐ€์—๋Š” ๋ถ€์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๊ฒฐ๊ณผ๋Š” ์ฐธ๊ณ ์šฉ์œผ๋กœ๋งŒ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.</p>
<div class="system-info">
<div class="info-card">
<h4>๐Ÿ‘ค Actual vs ๐Ÿ“Š Synthetic</h4>
<p><b>Actual</b>: ์‹ค์ œ ๊ฐœ๋ณ„ ์‘๋‹ต์ž์˜ ์ด๋ ฅ๊ณผ ๊ฐ€์น˜๊ด€์„ ์ถ”์ ํ•˜์—ฌ ์ƒ์ƒํ•œ ๊ฐœ์ธ์˜ ๋ชฉ์†Œ๋ฆฌ๋ฅผ ์‹œ๋‚˜๋ฆฌ์˜คํ™” ํ•ฉ๋‹ˆ๋‹ค. (์‹ฌ์ธต ์ธํ„ฐ๋ทฐ ๋Œ€์šฉ)<br>
<b>Synthetic</b>: ๊ทธ๋ฃน ์ „์ฒด์˜ ํ†ต๊ณ„์  ๋‹ต๋ณ€ ๋ถ„ํฌ๋ฅผ ์š”์•ฝํ•˜์—ฌ ์ง‘๋‹จ์˜ ํ‰๊ท ์ ์ด๊ณ  ์ „ํ˜•์ ์ธ ๊ฒฝํ–ฅ์„ฑ์„ ๋„์ถœํ•ฉ๋‹ˆ๋‹ค. (์—ฌ๋ก  ์ง€ํ˜• ๋ถ„์„์šฉ)
</p>
</div>
<div class="info-card">
<h4>๐Ÿ›  ๊ตฌํ˜„ ๊ณผ์ •</h4>
<p>163ํšŒ์˜ NBS ์„ค๋ฌธ(16๋งŒ๋ช…)์„ ํ†ตํ•ฉ ์ „์ฒ˜๋ฆฌํ•˜๊ณ , SBERT ์˜๋ฏธ๋ก ์  ๊ฒ€์ƒ‰์„ ํ†ตํ•ด ์•„๋ฐ”ํƒ€์˜ '๊ธฐ์–ต'์„ AI์—๊ฒŒ ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค.</p>
</div>
<div class="info-card">
<h4>๐Ÿ’ก ์‚ฌ์šฉ๋ฒ•</h4>
<p>1. <b>API ํ‚ค๋ฅผ ๋จผ์ € ์ž…๋ ฅ</b>ํ•˜์„ธ์š”. 2. ์ธ๊ตฌํ†ต๊ณ„ ์กฐ๊ฑด์„ ์„ค์ •ํ•˜๊ณ  ์ด์Šˆ ์งˆ๋ฌธ์„ ์ž…๋ ฅ(์ตœ์ €์ž„๊ธˆ ์‚ฌ๋ก€ ๋“ฑ ๊ตฌ์ฒด์  ๋ฐฐ๊ฒฝ ๊ถŒ์žฅ) ํ›„ ์‹œ์ž‘ํ•˜์„ธ์š”.</p>
</div>
</div>
</div>
<div class="survey-form">
<div class="form-grid">
<div class="input-group">
<label>์ง€์—ญ</label>
<select id="region">
<option value="">์ „์ฒด (์ „๊ตญ)</option>
<option value="์„œ์šธ">์„œ์šธ</option>
<option value="๊ฒฝ๊ธฐ">๊ฒฝ๊ธฐ</option>
<option value="์ธ์ฒœ">์ธ์ฒœ</option>
<option value="๋ถ€์‚ฐ">๋ถ€์‚ฐ</option>
<option value="๋Œ€๊ตฌ">๋Œ€๊ตฌ</option>
<option value="๊ด‘์ฃผ">๊ด‘์ฃผ</option>
<option value="๋Œ€์ „">๋Œ€์ „</option>
<option value="์šธ์‚ฐ">์šธ์‚ฐ</option>
<option value="์„ธ์ข…">์„ธ์ข…</option>
<option value="์ถฉ๋ถ">์ถฉ๋ถ</option>
<option value="์ถฉ๋‚จ">์ถฉ๋‚จ</option>
<option value="์ „๋ถ">์ „๋ถ</option>
<option value="์ „๋‚จ">์ „๋‚จ</option>
<option value="๊ฒฝ๋ถ">๊ฒฝ๋ถ</option>
<option value="๊ฒฝ๋‚จ">๊ฒฝ๋‚จ</option>
<option value="์ œ์ฃผ">์ œ์ฃผ</option>
<option value="๊ฐ•์›">๊ฐ•์›</option>
</select>
</div>
<div class="input-group">
<label>์„ฑ๋ณ„</label>
<select id="gender">
<option value="">์ „์ฒด (์„ฑ๋ณ„๋ฌด๊ด€)</option>
<option value="๋‚จ์ž">๋‚จ์ž</option>
<option value="์—ฌ์ž">์—ฌ์ž</option>
</select>
</div>
<div class="input-group">
<label>์—ฐ๋ น๋Œ€</label>
<select id="age">
<option value="">์ „์ฒด (์—ฐ๋ น๋ฌด๊ด€)</option>
<option value="18~29">20๋Œ€ (18~29)</option>
<option value="30~39">30๋Œ€ (30~39)</option>
<option value="40~49">40๋Œ€ (40~49)</option>
<option value="50~59">50๋Œ€ (50~59)</option>
<option value="60~69">60๋Œ€ (60~69)</option>
<option value="70~99">70๋Œ€ ์ด์ƒ</option>
</select>
</div>
<div class="input-group">
<label>๊ตฌ์ฒด์  ์—ฐ๋ น (์„ ํƒ)</label>
<input type="number" id="age-specific" placeholder="์˜ˆ: 32">
</div>
<div class="input-group">
<label>์ง์—…</label>
<select id="job">
<option value="">์ „์ฒด (์ง์—…๋ฌด๊ด€)</option>
<option value="ํ•™์ƒ">ํ•™์ƒ</option>
<option value="์‚ฌ๋ฌด/๊ธฐ์ˆ ์ง">์‚ฌ๋ฌด/๊ธฐ์ˆ ์ง</option>
<option value="์ž์˜์—…">์ž์˜์—…</option>
<option value="์ฃผ๋ถ€">์ฃผ๋ถ€</option>
<option value="๊ฒฝ์˜/๊ด€๋ฆฌ/์ „๋ฌธ์ง">๊ฒฝ์˜/๊ด€๋ฆฌ/์ „๋ฌธ์ง</option>
<option value="์ƒ์‚ฐ/๊ธฐ๋Šฅ/๋…ธ๋ฌด์ง">์ƒ์‚ฐ/๊ธฐ๋Šฅ/๋…ธ๋ฌด์ง</option>
<option value="๋†/๋ฆผ/์ˆ˜์‚ฐ์—…">๋†/๋ฆผ/์ˆ˜์‚ฐ์—…</option>
<option value="๋ฌด์ง/ํ‡ด์ง/๊ธฐํƒ€">๋ฌด์ง/ํ‡ด์ง/๊ธฐํƒ€</option>
</select>
</div>
<div class="input-group full">
<label>๋ถ„์„ ์ด์Šˆ (์งˆ๋ฌธ)</label>
<textarea id="question"
placeholder="์˜ˆ: ์ตœ๊ทผ ๊ณ ๋ฌผ๊ฐ€ ์ƒํ™ฉ์—์„œ ์„œ๋ฏผ ๊ฒฝ์ œ ์•ˆ์ •์„ ์œ„ํ•ด ์ตœ์ €์ž„๊ธˆ์„ ์ธ์ƒํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ฃผ์žฅ์— ์–ผ๋งˆ๋‚˜ ๋™์˜ํ•˜์‹ญ๋‹ˆ๊นŒ? (1: ์ „ํ˜€ ๋™์˜ ์•ˆํ•จ ~ 5: ๋งค์šฐ ๋™์˜)"></textarea>
</div>
<div class="input-group" id="sample-group">
<label>์ƒ์„ฑํ•  ์•„๋ฐ”ํƒ€(์‘๋‹ต) ์ˆ˜</label>
<input type="number" id="sample" value="3" min="1" max="10">
<div id="sample-notice" class="notice-label">Actual ๋ชจ๋“œ๋Š” ์ตœ๋Œ€ 10๋ช…์œผ๋กœ ์ œํ•œ๋ฉ๋‹ˆ๋‹ค.</div>
</div>
</div>
<div class="button-row">
<button class="btn-submit" id="btn-submit" onclick="runSimulation()">์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์‹œ์ž‘</button>
<button class="btn-reset" id="btn-reset" onclick="resetForm()">์ดˆ๊ธฐํ™”</button>
</div>
</div>
<div class="loader" id="loader">
<div class="spinner"></div>
<p>๋ฐ์ดํ„ฐ์—์„œ ํŽ˜๋ฅด์†Œ๋‚˜๋ฅผ ์ถ”์ถœํ•˜๊ณ  AI ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค...</p>
</div>
<div class="results-container" id="results">
<!-- Results will be injected here -->
</div>
</main>
<script>
let currentMode = 'actual';
function toggleKeyVisibility() {
const keyInput = document.getElementById('api-key');
const toggleIcon = document.getElementById('toggle-key');
if (keyInput.type === 'password') {
keyInput.type = 'text';
toggleIcon.innerText = '๐Ÿ™ˆ';
} else {
keyInput.type = 'password';
toggleIcon.innerText = '๐Ÿ‘๏ธ';
}
}
function switchMode(mode) {
currentMode = mode;
document.querySelectorAll('.menu-item').forEach(el => el.classList.remove('active'));
document.getElementById(`menu-${mode}`).classList.add('active');
const sampleGroup = document.getElementById('sample-group');
const sampleInput = document.getElementById('sample');
const notice = document.getElementById('sample-notice');
if (mode === 'actual') {
sampleGroup.style.display = "flex";
sampleInput.max = 10;
if (sampleInput.value > 10) sampleInput.value = 10;
notice.innerText = "Actual ๋ชจ๋“œ๋Š” ์ตœ๋Œ€ 10๋ช…์œผ๋กœ ์ œํ•œ๋ฉ๋‹ˆ๋‹ค. (๋Œ€๊ทœ๋ชจ ๋ถ„์„์€ Synthetic ์ถ”์ฒœ)";
notice.style.color = "#ec4899";
} else {
// Synthetic is always 1 summarized avatar per request in current implementation
sampleGroup.style.display = "none";
sampleInput.value = 1;
}
}
function resetForm() {
document.getElementById('question').value = "";
document.getElementById('region').value = "";
document.getElementById('gender').value = "";
document.getElementById('age').value = "";
document.getElementById('age-specific').value = "";
document.getElementById('job').value = "";
document.getElementById('sample').value = (currentMode === 'actual' ? "3" : "1");
document.getElementById('results').innerHTML = "";
}
async function runSimulation() {
const apiKey = document.getElementById('api-key').value.trim();
const question = document.getElementById('question').value.trim();
const region = document.getElementById('region').value;
const gender = document.getElementById('gender').value;
// Priority: specific age > age range
const ageSpecific = document.getElementById('age-specific').value.trim();
const ageRange = document.getElementById('age').value;
const age = ageSpecific || ageRange;
const job = document.getElementById('job').value;
const sample = parseInt(document.getElementById('sample').value);
if (!apiKey) {
alert("์‹œ์ž‘ํ•˜๋ ค๋ฉด Gemini API ํ‚ค๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š” (์‚ฌ์ด๋“œ๋ฐ” ํ•˜๋‹จ).");
return;
}
if (!question) {
alert("์งˆ๋ฌธ์„ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.");
return;
}
const resultsDiv = document.getElementById('results');
const loader = document.getElementById('loader');
const btn = document.getElementById('btn-submit');
resultsDiv.innerHTML = "";
loader.style.display = "block";
btn.disabled = true;
const payload = {
question,
region: region || null,
gender: gender || null,
age: age || null,
job: job || null,
sample
};
const endpoint = `/simulate/${currentMode}`;
try {
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey || ""
},
body: JSON.stringify(payload)
});
if (!response.ok) {
const err = await response.json();
throw new Error(err.detail || "์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.");
}
const data = await response.json();
renderResults(data);
} catch (error) {
alert(error.message);
} finally {
loader.style.display = "none";
btn.disabled = false;
}
}
function renderResults(data) {
const resultsDiv = document.getElementById('results');
if (currentMode === 'synthetic') {
resultsDiv.innerHTML = `<h2 style="margin-bottom: 1.5rem;">์ง‘๋‹จ ๋Œ€ํ‘œ(Representative) ๋ถ„์„ ๊ฒฐ๊ณผ</h2>`;
} else {
resultsDiv.innerHTML = `<h2 style="margin-bottom: 1.5rem;">์ด ${data.length}๋ช…์˜ ๋‹ต๋ณ€ ๊ฒฐ๊ณผ</h2>`;
}
data.forEach((item, index) => {
const card = document.createElement('div');
card.className = "result-card";
card.style.animationDelay = `${index * 0.1}s`;
const isSynthetic = !item.respondent_id;
const idLabel = item.respondent_id ? `ID: ${item.respondent_id}` : `Group Representative (Representative)`;
const roundLabel = item.survey_round ? ` | Survey Round: ${item.survey_round}` : "";
let demographicsStr = "";
if (item.demographics) {
const d = item.demographics;
demographicsStr = `${d.region || '์ „๊ตญ'} / ${d.age || '์—ฐ๋ น๋Œ€'} / ${d.gender || '์„ฑ๋ณ„'} / ${d.job || '์ง์—…'}`;
}
const contextHtml = (item.referenced_context || item.referenced_stat_context || [])
.map(c => `<span class="chip">${c.split('\n')[0].replace('Q: ', '')}</span>`)
.join('');
card.innerHTML = `
<div class="avatar-info">
<div class="avatar-icon">${item.respondent_id ? 'P' : 'S'}</div>
<div>
<div style="font-weight: 700; color: #0f172a;">${idLabel}${roundLabel}</div>
<div class="avatar-meta">${demographicsStr}</div>
</div>
</div>
<div class="response-text">${item.response}</div>
<div class="api-key-label" style="margin-top: 1.5rem; font-size: 0.75rem; color: #94a3b8;">์ฐธ์กฐ๋œ ๊ณผ๊ฑฐ ๋ฐ์ดํ„ฐ ํ•„๋“œ:</div>
<div class="context-chips">
${contextHtml}
</div>
`;
resultsDiv.appendChild(card);
});
}
</script>
</body>
</html>