FinGraph / src /utils /ui_templates.py
dev-yuje's picture
feat: expand chatbot width to 96% to improve visual space
15f8db2
# -*- coding: utf-8 -*-
"""FinGraph UI Templates and Styling Assets.
This module houses all custom CSS and HTML templates used by the main Gradio
application to keep the main app.py clean and readable.
"""
from typing import Any, Dict
# ── 남색 νŒ”λ ˆνŠΈ (Navy Blue) ──────────────────────────────────────
# μ£Ό ν…μŠ€νŠΈ: #1e3a5f / 보쑰: #3b5a82 / μ—°ν•œ: #6b8ab0
# κ°•μ‘°(λ°”μ΄μ˜¬λ ›): #7c3aed / 포인트 ν…Œλ‘λ¦¬: rgba(124,58,237,0.15)
# ── 1. μ»€μŠ€ν…€ CSS ────────────────────────────────────────────────
CUSTOM_CSS: str = """
/* ── Google Fonts λ‘œλ“œ ── */
@import url('https://fonts.googleapis.com/css2?family=Sora:wght@400;600;700;800&family=JetBrains+Mono:wght@400;500&family=Inter:wght@400;600&display=swap');
/* ── 전체 λ°°κ²½ / κΈ°λ³Έ 폰트 / ν™”λ©΄ λ„ˆλΉ„ ν™•λŒ€ ── */
body, .gradio-container {
background-color: #F6F5FA !important;
font-family: 'Sora', 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;
color: #1e3a5f !important;
}
.gradio-container {
max-width: 1400px !important;
width: 95% !important;
margin: 0 auto !important;
}
/* ── Ambient glow ── */
.ambient-glow {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background:
radial-gradient(circle at 80% 10%, rgba(124,58,237,0.05) 0%, transparent 40%),
radial-gradient(circle at 20% 90%, rgba(12,217,247,0.05) 0%, transparent 40%);
z-index: -1;
pointer-events: none;
}
/* ── μ‚¬μ΄λ“œλ°” 전체 μ»¨ν…Œμ΄λ„ˆ ── */
.sidebar-container {
border: 1px solid #e2e8f0 !important;
border-radius: 12px !important;
padding: 16px 16px !important;
background: #ffffff !important;
min-height: 700px !important;
height: 100% !important;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.04) !important;
display: flex !important;
flex-direction: column !important;
justify-content: flex-start !important;
gap: 8px !important;
}
/* ── ꡬ뢄선 ── */
.divider {
border: none;
border-top: 1px solid #e2e8f0;
margin: 6px 0 !important;
}
/* ── νŒ¨λ„ 라벨 (μ„Ήμ…˜ 제λͺ©) ── */
.panel-label {
font-size: 12.5px !important;
font-weight: 700;
color: #64748b;
text-transform: uppercase;
letter-spacing: 0.07em;
margin-bottom: 4px !important;
margin-top: 2px !important;
}
/* ── 상단 2 μΉ΄λ“œ (κ°€λ‘œ 배치) ── */
.top-cards {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
margin-bottom: 4px !important;
}
.top-card {
background: #f8fafc !important;
border: 1px solid #e2e8f0 !important;
border-radius: 10px !important;
padding: 14px 14px 12px !important;
}
.top-card-lbl {
font-size: 11px;
font-weight: 600;
color: #64748b;
margin-bottom: 5px;
}
.top-card-val {
font-size: 22px;
font-weight: 800;
color: #1e293b;
line-height: 1.1;
}
.top-card-sub {
font-size: 10px;
color: #94a3b8;
margin-top: 3px;
}
/* ── μΈμ‚¬μ΄νŠΈ ν–‰ ── */
.insight-row {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
margin-bottom: 16px;
}
.insight-card {
background: #f0f4ff !important;
border: 1px solid #c7d2fe !important;
border-radius: 9px !important;
padding: 10px 12px !important;
}
.insight-lbl {
font-size: 10px;
font-weight: 700;
color: #6366f1;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 4px;
}
.insight-val {
font-size: 13px;
font-weight: 700;
color: #1e293b;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* ── μ£Όμš” 기술 ν‚€μ›Œλ“œ λ°°μ§€ (배경색 μ—†μŒ, μ—°ν•œ 보라 ν…μŠ€νŠΈ 개편) ── */
.keyword-container {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 4px !important;
}
.keyword-badge {
display: inline-block;
background: transparent !important;
border: 1px solid #ddd6fe !important;
border-radius: 9999px !important;
padding: 4px 12px !important;
font-size: 12.5px !important;
font-weight: 600 !important;
color: #8b5cf6 !important;
}
.keyword-badge-first {
background: transparent !important;
color: #8b5cf6 !important;
border: 1px solid #ddd6fe !important;
}
/* ── νšŒμ‚¬ ν‚€μ›Œλ“œ λ°°μ§€ (ν‚€μ›Œλ“œ 배지와 λ™μΌν•œ 색, 크기, λ°°κ²½μƒ‰μœΌλ‘œ 톡일) ── */
.company-badge-container {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 4px !important;
}
.company-badge {
display: inline-block;
background: transparent !important;
border: 1px solid #ddd6fe !important;
border-radius: 9999px !important;
padding: 4px 12px !important;
font-size: 12.5px !important;
font-weight: 600 !important;
color: #8b5cf6 !important;
}
.company-badge-first {
background: transparent !important;
color: #8b5cf6 !important;
border: 1px solid #ddd6fe !important;
}
/* ── μ΅œμ‹  λ‰΄μŠ€ ν”Όλ“œ ── */
.news-feed-container {
flex-grow: 1 !important;
max-height: 250px !important;
overflow-y: auto !important;
}
.news-feed-container::-webkit-scrollbar { width: 3px; }
.news-feed-container::-webkit-scrollbar-track { background: transparent; }
.news-feed-container::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 2px;
}
.news-item-link {
text-decoration: none;
display: block;
margin-bottom: 8px;
}
.news-item-link:last-child { margin-bottom: 0; }
.news-item {
border-left: 3px solid #6366f1 !important;
padding: 10px 12px !important;
background: #f8fafc !important;
border-radius: 0 8px 8px 0 !important;
transition: all 0.18s ease !important;
}
.news-item-link:hover .news-item {
background: #ffffff !important;
border-left-color: #06b6d4 !important;
transform: translateX(3px) !important;
box-shadow: 0 2px 8px rgba(0,0,0,0.06) !important;
}
.news-title {
font-size: 13px !important;
font-weight: 600 !important;
color: #1e293b !important;
line-height: 1.45 !important;
white-space: normal;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.news-meta {
font-size: 11px !important;
color: #94a3b8 !important;
margin-top: 4px;
}
/* ── 챗봇 μ»¨ν…Œμ΄λ„ˆ ν…Œλ‘λ¦¬ 제거 ── */
div[data-testid="chatbot"], .chatbot-container, .chatbot {
border: none !important;
}
/* ── 챗봇 μ˜μ—­ λ„ˆλΉ„ ν™•λŒ€ 및 쀑앙 μ •λ ¬ ── */
#chat-column {
display: flex !important;
flex-direction: column !important;
align-items: center !important;
width: 100% !important;
}
#chat-column > div,
#chat-column > .form {
width: 96% !important;
max-width: 100% !important;
margin: 0 auto !important;
display: flex !important;
flex-direction: column !important;
gap: 6px !important; /* μ»΄ν¬λ„ŒνŠΈ κ°„ μœ„μ•„λž˜ 간격 μΆ•μ†Œ */
}
/* ── 챗봇 λ‚΄λΆ€ Placeholder(μ†Œκ°œκΈ€ μ˜μ—­) ── */
.placeholder, [class*="placeholder"] {
padding: 0 !important;
overflow: auto !important;
margin: 0 auto !important;
width: 100% !important;
max-width: 100% !important;
}
/* ── μ†Œκ°œκΈ€(Prose) μ›°μ»΄ λ³΄λ“œ (독립적인 프리미엄 λΌμš΄λ“œ μΉ΄λ“œ ꡬ쑰) ── */
.placeholder .prose {
background: #f8fafc !important;
border: 1px solid #e2e8f0 !important;
border-radius: 12px !important;
padding: 10px 14px !important; /* νŒ¨λ”© μΆ•μ†Œ */
max-width: 100% !important;
width: 100% !important;
margin: 0 auto !important;
display: block !important;
position: relative !important;
z-index: 2 !important;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05) !important;
}
.placeholder h3, [class*="placeholder"] h3 {
color: #334155 !important;
font-weight: 800 !important;
font-size: 15px !important; /* 폰트 살짝 μΆ•μ†Œ */
margin-top: 0 !important;
margin-bottom: 6px !important;
}
.placeholder .prose ul {
list-style-type: none !important;
padding-left: 0 !important;
margin-bottom: 6px !important;
}
.placeholder .prose li {
margin-bottom: 2px !important; /* 간격 μΆ•μ†Œ */
color: #475569 !important;
font-size: 13px !important;
line-height: 1.4 !important;
}
.placeholder .prose p {
font-size: 13px !important;
line-height: 1.4 !important;
margin-bottom: 4px !important;
}
.placeholder .prose p:last-child {
font-size: 12.5px !important;
font-weight: 700 !important;
color: #4c1d95 !important;
background: #f3e8ff !important;
padding: 4px 10px !important; /* νŒ¨λ”© μΆ•μ†Œ */
border-radius: 6px !important;
display: inline-block !important;
margin-bottom: 0 !important;
}
/* ── μ˜ˆμ‹œ 질문 μ»¨ν…Œμ΄λ„ˆ (μœ„μͺ½ λ³΄λ“œμ™€ 동일 λ„ˆλΉ„λ‘œ μ •λ ¬λœ 독립 λΌμš΄λ“œ μΉ΄λ“œ) ── */
[class*="examples"], .gr-samples-wrapper, .examples-container {
display: grid !important;
grid-template-columns: repeat(2, 1fr) !important;
gap: 6px !important; /* κ°­ μΆ•μ†Œ */
width: 100% !important;
max-width: 100% !important;
margin: 4px auto 6px auto !important; /* λ§ˆμ§„ μΆ•μ†Œ */
background: #f8fafc !important;
border: 1px solid #e2e8f0 !important;
border-radius: 12px !important;
padding: 10px 14px !important; /* νŒ¨λ”© μΆ•μ†Œ */
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05) !important;
position: relative !important;
z-index: 1 !important;
}
/* κ°œλ³„ λ²„νŠΌ λ””μžμΈ (연보라색 ν…Œλ§ˆ & κ°•λ ₯ν•œ 쀑앙 μ •λ ¬) */
.examples-container button, div[data-testid="chatbot"] button.example, button.example, .example-btn {
border-radius: 8px !important;
padding: 10px 12px !important;
text-align: center !important;
font-size: 13px !important;
font-weight: 600 !important;
color: #4c1d95 !important;
background: #f5f3ff !important;
background-color: #f5f3ff !important;
border: 1px solid #e9d5ff !important;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) !important;
transition: all 0.2s ease-in-out !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
min-height: 52px !important;
width: 100% !important;
white-space: normal !important;
}
.examples-container button:hover, div[data-testid="chatbot"] button.example:hover, button.example:hover, .example-btn:hover {
border-color: #a855f7 !important;
background: #f3e8ff !important;
background-color: #f3e8ff !important;
transform: translateY(-2px) !important;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.08) !important;
color: #7c3aed !important;
}
/* ── 전솑 λ²„νŠΌ: λ„ˆλΉ„ λ„“κ³  높이 μž…λ ₯창에 맞좀 ── */
button[class*="submit-btn"], #submit-btn, button[id*="submit"], button.submit-button {
background: linear-gradient(135deg, #1e3a5f 0%, #7c3aed 100%) !important;
color: white !important;
font-weight: 700 !important;
font-size: 13px !important;
border: none !important;
border-radius: 8px !important;
box-shadow: 0 2px 10px rgba(30,58,95,0.18) !important;
transition: all 0.16s ease !important;
cursor: pointer !important;
height: 46px !important;
min-width: 68px !important;
max-width: 88px !important;
padding: 0 16px !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
box-sizing: border-box !important;
opacity: 1 !important;
visibility: visible !important;
}
button[class*="submit-btn"]:hover,
[data-testid="submit-button"]:hover {
background: linear-gradient(135deg, #2a4f82 0%, #8b47ff 100%) !important;
box-shadow: 0 4px 16px rgba(124,58,237,0.24) !important;
transform: translateY(-1px) !important;
}
/* ── μž…λ ₯μ°½ ── */
textarea,
[class*="input-container"] textarea,
[data-testid="textbox"] textarea {
height: 46px !important;
min-height: 46px !important;
max-height: 46px !important;
font-size: 13px !important;
padding: 14px 14px !important;
line-height: 1.5 !important;
border-radius: 8px !important;
border: 1px solid rgba(30,58,95,0.15) !important;
background: rgba(255,255,255,0.80) !important;
color: #1e3a5f !important;
resize: none !important;
overflow-y: hidden !important;
box-sizing: border-box !important;
}
textarea:focus {
border-color: #7c3aed !important;
background: rgba(255,255,255,0.97) !important;
box-shadow: 0 0 0 3px rgba(124,58,237,0.09) !important;
outline: none !important;
}
div:has(> button[class*="submit-btn"]),
div:has(> [data-testid="submit-button"]),
.input-container, [class*="input-container"] {
gap: 6px !important; /* κ°­ μΆ•μ†Œ */
align-items: center !important;
max-width: 100% !important;
width: 100% !important;
margin-left: 0 !important;
margin-right: 0 !important;
margin-top: 4px !important; /* μœ„μ•„λž˜ 간격 μ΅œμ ν™” */
}
/* ── 챗봇 νƒ­/라벨 μˆ¨κΉ€ ── */
.chatbot-label, div[class*="chatbot"] .label,
[data-testid="chatbot"] .label, .chatbot-header,
.gr-panel-title, .gr-chatbot-label,
[data-testid="chatbot"] > label,
div[class*="chatbot"] > label,
label.svelte-1ipelgc, span.svelte-1ipelgc {
display: none !important;
}
/* ── λ©”μ‹œμ§€ 버블 κΈ°λ³Έ 크기 μΆ•μ†Œ (μ‚¬μš©μž/봇 곡톡 μ„Έλ‘œ 높이 μ΅œμ ν™” & 100% λΆ€λͺ¨ λ„ˆλΉ„ 채움) ── */
.message-wrap, .message-container, div[data-testid="chatbot"] .wrapper {
max-width: 100% !important;
width: 100% !important;
margin-left: 0 !important;
margin-right: 0 !important;
}
.message,
.message-wrap .message,
[data-testid="user-message"],
[data-testid="bot-message"] {
padding: 10px 14px !important;
min-height: auto !important;
}
/* ── μ‚¬μš©μž 버블 (λ‹€ν¬κ·Έλ ˆμ΄ 프리미엄 ν…Œλ§ˆ) ── */
.message.user,
.message.user-message,
[data-testid="user-message"] {
background-color: #111827 !important;
border-radius: 12px 12px 0 12px !important;
padding: 10px 14px !important;
margin: 2px 0 !important;
border: none !important;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.08) !important;
color: #ffffff !important;
}
.message.user *,
.message.user-message *,
[data-testid="user-message"] * {
color: #ffffff !important;
line-height: 1.4 !important;
margin: 0 !important;
background: transparent !important;
}
/* ── 봇 버블 (ν™”μ΄νŠΈ & 그레이 경계선 ν…Œλ§ˆ) ── */
.message.bot,
.message.bot-message,
[data-testid="bot-message"] {
background-color: #ffffff !important;
color: #1f2937 !important;
border: 1px solid #e5e7eb !important;
border-radius: 12px 12px 12px 0 !important;
padding: 16px 20px !important;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05) !important;
}
.message.bot *,
.message.bot-message *,
[data-testid="bot-message"] * {
color: #1e3a5f !important;
background: transparent !important;
}
/* ── λ©”μ‹œμ§€ λ‚΄λΆ€ μ—¬λ°± μ™„μ „ μ œμ–΄ ── */
.message p, .message li,
[class*="message"] p, [class*="message"] li {
line-height: 1.55 !important;
margin: 0 !important;
margin-bottom: 6px !important;
padding: 0 !important;
border: none !important;
border-left: none !important;
box-shadow: none !important;
background: transparent !important;
color: #1e3a5f !important;
}
.message p:last-child, .message li:last-child {
margin-bottom: 0 !important;
}
.message blockquote, [class*="message"] blockquote {
border: none !important;
border-left: none !important;
padding: 0 !important;
margin: 0 !important;
background: transparent !important;
}
.message h3, [class*="message"] h3 {
margin-top: 14px !important;
margin-bottom: 6px !important;
font-weight: 800 !important;
color: #1e3a5f !important;
}
/* ── μ „μ—­ 링크 / CSS λ³€μˆ˜ ── */
:root {
--color-accent: #7c3aed !important;
--primary-500: #7c3aed !important;
--primary-600: #6d28d9 !important;
}
a { color: #7c3aed !important; }
/* ── secondary λ²„νŠΌ ── */
button.secondary, button.lg.secondary, button.sm.secondary,
button.wrap, button.variant-secondary, .secondary-btn {
background-color: rgba(255,255,255,0.75) !important;
color: #1e3a5f !important;
border: 1px solid rgba(30,58,95,0.13) !important;
font-weight: 600 !important;
transition: all 0.16s ease !important;
}
button.secondary:hover, button.variant-secondary:hover {
background-color: rgba(255,255,255,0.97) !important;
border-color: rgba(124,58,237,0.24) !important;
}
/* ── 메인 λ ˆμ΄μ•„μ›ƒ 컬럼 높이 동기화 ── */
#main-row { align-items: stretch !important; }
#main-row > div[class*="column"] {
display: flex !important;
flex-direction: column !important;
}
/* ── μ™Όμͺ½ νŒ¨λ„ λ„ˆλΉ„ μ˜€λ²„ν”Œλ‘œμš° μ™„μ „ 차단 ── */
.sidebar-container * {
max-width: 100% !important;
box-sizing: border-box !important;
word-break: break-word !important;
overflow-wrap: break-word !important;
}
/* ── μ—„μ§€ ν”Όλ“œλ°± λ²„νŠΌ μˆ¨κΉ€ ── */
.feedback-area,
[data-testid="like-dislike"],
.like-dislike-area,
.message-buttons,
.chatbot-action-buttons,
button[aria-label="Good response"],
button[aria-label="Bad response"],
button[aria-label="thumbs up"],
button[aria-label="thumbs down"],
.bot + div > div > button,
.svelte-1ed2p3z { display: none !important; }
/* ── Gradio 6.x Chatbot λ‚΄λΆ€ μ˜€λ²„ν”Œλ‘œμš° 및 μŠ€ν¬λ‘€λ°” μ΅œμ ν™” ── */
div[data-testid="chatbot"] .message-wrap,
div[data-testid="chatbot"] .message-container,
div[data-testid="chatbot"] > div.wrapper,
div[data-testid="chatbot"] > div.scrollbar {
overflow-y: auto !important;
max-height: 600px !important;
}
/* ── λͺ¨λ°”일 ν™”λ©΄ λŒ€μ‘: 800px μ΄ν•˜μ—μ„œλŠ” 100% λ„ˆλΉ„ ── */
@media (max-width: 800px) {
#chat-column > div,
#chat-column > .form {
width: 100% !important;
max-width: 100% !important;
}
[class*="examples"], .gr-samples-wrapper, .examples-container {
grid-template-columns: 1fr !important;
padding: 10px !important;
}
}
"""
def build_stats_html(stats: Dict[str, Any]) -> str:
"""μ™Όμͺ½ μ‚¬μ΄λ“œλ°” 전체 HTML 생성.
ꡬ성:
- 상단 2 μΉ΄λ“œ (뢄석 λͺ¨λΈ / κΈ°μ–΅μˆ˜)
- ꡬ뢄선
- νšŒμ‚¬ ν‚€μ›Œλ“œ λ°°μ§€ (5개)
- ꡬ뢄선
- λ‰΄μŠ€ ν‚€μ›Œλ“œ λ°°μ§€
- ꡬ뢄선
- μ΅œμ‹  λ‰΄μŠ€ ν”Όλ“œ
"""
# ── νšŒμ‚¬ ν‚€μ›Œλ“œ λ°°μ§€ (5개) ───────────────────────────
company_html = ""
for idx, c in enumerate(stats.get("companies_list", [])):
css = "company-badge company-badge-first" if idx == 0 else "company-badge"
company_html += f'<span class="{css}"># {c["name"]}</span>\n'
if not company_html:
company_html = '<span style="font-size:10px;color:#6b8ab0;">λ“±λ‘λœ νšŒμ‚¬ μ—†μŒ</span>'
# ── λ‰΄μŠ€ ν‚€μ›Œλ“œ λ°°μ§€ ──────────────────────────────
keyword_html = ""
for idx, t in enumerate(stats.get("techs_list", [])):
css = "keyword-badge keyword-badge-first" if idx == 0 else "keyword-badge"
keyword_html += f'<span class="{css}"># {t["name"]}</span>\n'
if not keyword_html:
keyword_html = '<span style="font-size:10px;color:#6b8ab0;">ν‚€μ›Œλ“œ μ—†μŒ</span>'
# ── μ΅œμ‹  λ‰΄μŠ€ ν”Όλ“œ ──────────────────────────────
news_html = ""
for a in stats.get("recent_articles", []):
title = a["title"] or ""
url = a["url"] if a["url"] and str(a["url"]).lower() != "nan" else "#"
target = 'target="_blank"' if url != "#" else ""
date_str = str(a["date"])[:10] if a["date"] else ""
news_html += f"""
<a class="news-item-link" href="{url}" {target}>
<div class="news-item">
<div class="news-title">{title}</div>
<div class="news-meta">{date_str}</div>
</div>
</a>"""
if not news_html:
news_html = '<div style="font-size:10px;color:#6b8ab0;">기사λ₯Ό λΆˆλŸ¬μ˜€λŠ” 쀑...</div>'
return f"""
<div class="sidebar-container">
<!-- 상단 2 μΉ΄λ“œ -->
<div class="top-cards">
<div class="top-card">
<div class="top-card-lbl">πŸ€– 뢄석 λͺ¨λΈ</div>
<div class="top-card-val">GPT-4o</div>
<div class="top-card-sub">μ΄ˆκ±°λŒ€ AI μ—”μ§„</div>
</div>
<div class="top-card">
<div class="top-card-lbl">🏒 λŒ€μƒ κΈ°μ—…</div>
<div class="top-card-val">{stats["companies"]}κ³³</div>
<div class="top-card-sub">κ·Έλž˜ν”„ λ‚΄ κΈ°μ—…</div>
</div>
</div>
<hr class="divider">
<!-- νšŒμ‚¬ ν‚€μ›Œλ“œ -->
<div class="panel-label">🏒 μ£Όμš” λŒ€μƒ κΈ°μ—…</div>
<div class="company-badge-container">
{company_html}
</div>
<hr class="divider">
<!-- λ‰΄μŠ€ ν‚€μ›Œλ“œ -->
<div class="panel-label">🏷️ μ£Όμš” 기술 ν‚€μ›Œλ“œ</div>
<div class="keyword-container">
{keyword_html}
</div>
<hr class="divider">
<!-- μ΅œμ‹  λ‰΄μŠ€ ν”Όλ“œ -->
<div class="panel-label">πŸ“‘ μ΅œμ‹  λ‰΄μŠ€</div>
<div class="news-feed-container">
{news_html}
</div>
</div>
"""