gpt5-mini-v1 / index.html
thucdangvan020999's picture
Upload index.html with huggingface_hub
9328c4f verified
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Vietnam Economic Growth Report 2025 — Interactive Presentation</title>
<!-- Font Awesome for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" integrity="sha512-yH6fK7Y4W5r8Xwq5mZ4mN6kJm3rQ0y7s8gqO2x1Q7s6Rz5yG3qk9qE5a7Y6vN2uB4t8vZ3F3p2j9h6w=="
crossorigin="anonymous" referrerpolicy="no-referrer" />
<style>
:root{
--bg: #f7f9fb;
--card: #ffffff;
--muted: #6b7280;
--accent: linear-gradient(135deg,#0ea5a2 0%,#06b6d4 100%);
--primary: #0b7285;
--success: #16a34a;
--danger: #ef4444;
--glass: rgba(255,255,255,0.6);
--radius: 14px;
--max-width: 1200px;
--shadow: 0 6px 24px rgba(12,18,31,0.08);
--kb: 14px;
--font-sans: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
}
/* Theme toggling */
:root[data-theme='dark']{
--bg: #071022;
--card: #0b1623;
--muted: #9aa6b2;
--primary: #2ad4e0;
--glass: rgba(255,255,255,0.03);
color-scheme: dark;
}
/* Global styles */
*{box-sizing:border-box}
html,body{height:100%;}
body{
margin:0;
background: radial-gradient(1200px 400px at 10% 10%, rgba(6,182,212,0.06), transparent), var(--bg);
font-family:var(--font-sans);
color:var(--muted);
-webkit-font-smoothing:antialiased;
-moz-osx-font-smoothing:grayscale;
line-height:1.45;
font-size:clamp(14px, 1.4vw, 16px);
padding:24px;
}
.container{
max-width:var(--max-width);
margin:0 auto;
display:grid;
grid-template-columns: 1fr;
gap:20px;
}
header.site-header{
display:flex;
gap:12px;
align-items:center;
justify-content:space-between;
}
.brand{
display:flex;
gap:12px;
align-items:center;
}
.logo{
width:56px;
height:56px;
border-radius:12px;
background:var(--accent);
display:flex;
align-items:center;
justify-content:center;
color:white;
font-weight:700;
box-shadow:var(--shadow);
flex:0 0 56px;
}
h1{
margin:0;
color:var(--primary);
font-size:clamp(18px,2.6vw,22px);
}
p.lead{
margin:0;
color:var(--muted);
font-size:clamp(12px,1.4vw,14px);
}
.controls{
display:flex;
gap:10px;
align-items:center;
}
.btn{
background:var(--card);
border-radius:10px;
padding:10px 12px;
box-shadow:var(--shadow);
border:1px solid rgba(0,0,0,0.04);
color:var(--muted);
cursor:pointer;
display:inline-flex;
align-items:center;
gap:8px;
font-size:13px;
}
.btn.icon{padding:8px}
.btn.primary{
background:linear-gradient(180deg, rgba(6,182,212,0.14), rgba(6,182,212,0.04));
color:var(--primary);
border:1px solid rgba(6,182,212,0.18);
}
/* Layout for main content with TOC sidebar */
.layout{
display:grid;
gap:18px;
grid-template-columns: 1fr;
}
/* TOC */
nav.toc{
position:sticky;
top:24px;
align-self:start;
background:var(--card);
border-radius:12px;
padding:12px;
box-shadow:var(--shadow);
display:flex;
flex-direction:column;
gap:10px;
}
.toc .search{
display:flex;
gap:8px;
align-items:center;
}
.toc input[type="search"]{
border:0;
outline:0;
background:var(--glass);
padding:8px 10px;
border-radius:8px;
width:100%;
color:var(--muted);
font-size:13px;
}
.toc .links{
display:flex;
flex-direction:column;
gap:6px;
margin-top:6px;
}
.toc a{
text-decoration:none;
color:var(--muted);
padding:8px;
border-radius:8px;
font-size:13px;
display:flex;
gap:8px;
align-items:center;
}
.toc a.active{
background:linear-gradient(90deg, rgba(6,182,212,0.08), rgba(6,182,212,0.02));
color:var(--primary);
font-weight:600;
}
.toc .progress-vert{
height:6px;
background:linear-gradient(90deg,#e6fdfd,transparent);
border-radius:99px;
overflow:hidden;
margin-top:6px;
}
.progress-vert > i{
display:block;
height:100%;
width:0%;
background:linear-gradient(90deg,#06b6d4,#0ea5a2);
}
/* Main content area */
main.content{
display:grid;
gap:18px;
}
.card{
background:var(--card);
border-radius:var(--radius);
padding:16px;
box-shadow:var(--shadow);
border:1px solid rgba(0,0,0,0.04);
}
/* Executive summary */
.summary{
display:flex;
flex-direction:column;
gap:12px;
}
.summary .kpi-row{
display:flex;
gap:12px;
flex-wrap:wrap;
}
.kpi{
flex:1 1 160px;
background:linear-gradient(180deg, rgba(6,182,212,0.06), rgba(6,182,212,0.02));
border-radius:10px;
padding:12px;
min-width:140px;
}
.kpi h3{margin:0;color:var(--primary);font-size:clamp(16px,2.2vw,18px)}
.kpi p{margin:6px 0 0 0;color:var(--muted);font-weight:600}
/* Indicators grid */
.indicators-grid{
display:grid;
gap:12px;
grid-template-columns: repeat(auto-fit,minmax(220px,1fr));
}
/* Charts area */
.charts{
display:grid;
gap:12px;
grid-template-columns: 1fr;
}
.chart{
height:260px;
aspect-ratio: 16 / 9;
position:relative;
overflow:visible;
}
.chart svg{width:100%;height:100%}
.tooltip{
position:fixed;
pointer-events:none;
background:var(--card);
color:var(--muted);
padding:8px 10px;
border-radius:8px;
box-shadow:var(--shadow);
font-size:13px;
border:1px solid rgba(0,0,0,0.04);
transform:translate(-50%,-120%);
white-space:nowrap;
z-index:9999;
display:none;
}
/* Sortable table */
table{
width:100%;
border-collapse:collapse;
font-size:13px;
}
thead th{
text-align:left;
padding:10px;
background:transparent;
color:var(--muted);
font-weight:700;
cursor:pointer;
}
tbody td{
padding:10px;
border-top:1px solid rgba(0,0,0,0.04);
color:var(--muted);
}
tbody tr:hover td{background:rgba(6,182,212,0.03)}
/* Collapsible lists */
details summary{
list-style:none;
cursor:pointer;
display:flex;
gap:10px;
align-items:center;
font-weight:700;
color:var(--primary);
}
details[open] summary .chev{transform:rotate(180deg)}
/* References */
.references li{margin-bottom:8px}
.citation{
display:flex;
gap:8px;
align-items:center;
font-size:13px;
color:var(--muted);
}
.badge{
background:linear-gradient(90deg,#06b6d4,#0ea5a2);
color:white;
padding:6px 8px;
border-radius:8px;
font-weight:700;
font-size:12px;
}
/* Responsive layout adjustments */
@media (min-width:768px){
.layout{grid-template-columns: 260px 1fr; align-items:start}
.container{grid-template-columns: 1fr}
.charts{grid-template-columns: 1fr 1fr}
}
@media (min-width:1024px){
.container{padding:0}
.charts{grid-template-columns: 2fr 1fr}
.chart.large{aspect-ratio: 21/9; height:340px}
}
/* Print-friendly */
@media print{
.controls, nav.toc, .btn, .tooltip{display:none}
body{background:white;padding:0}
}
/* Container query example for a card's content */
.card:where(:not(:root)){ container-type: inline-size;}
@container (min-width:420px){
.card .meta-row{display:flex;justify-content:space-between;align-items:center}
}
/* small utilities */
.muted{color:var(--muted)}
.small{font-size:12px}
.right{text-align:right}
.center{text-align:center}
.flex{display:flex}
.gap-8{gap:8px}
.pill{padding:6px 10px;border-radius:999px;background:var(--glass);font-weight:700;color:var(--muted)}
.legend{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.legend-item{display:flex;gap:8px;align-items:center;font-size:13px}
.swatch{width:14px;height:14px;border-radius:4px}
footer{opacity:0.9;color:var(--muted);font-size:13px;text-align:center;padding:12px 0}
</style>
</head>
<body>
<div class="container">
<header class="site-header">
<div class="brand">
<div class="logo">VN</div>
<div>
<h1>Vietnam Economic Growth Report — 2025</h1>
<p class="lead">Interactive analysis of recent performance, outlook and risks</p>
</div>
</div>
<div class="controls" role="toolbar" aria-label="Controls">
<button class="btn" id="copySummary" title="Copy Executive Summary"><i class="fa fa-copy"></i> Copy Summary</button>
<button class="btn" id="exportCSV" title="Export indicators as CSV"><i class="fa fa-file-csv"></i> Export CSV</button>
<button class="btn" id="exportJSON" title="Export report as JSON"><i class="fa fa-code"></i> Export JSON</button>
<button class="btn" id="printBtn" title="Print view"><i class="fa fa-print"></i></button>
<button class="btn icon" id="themeToggle" title="Toggle theme"><i class="fa fa-moon"></i></button>
</div>
</header>
<div class="layout">
<!-- TOC Sidebar -->
<nav class="toc card" aria-label="Table of contents">
<div class="search">
<i class="fa fa-search" style="color:var(--muted)"></i>
<input type="search" id="globalSearch" placeholder="Search report..." aria-label="Search report"/>
</div>
<div class="links" id="tocLinks">
<a href="#executive" data-id="executive"><i class="fa fa-star"></i> Executive Summary</a>
<a href="#indicators" data-id="indicators"><i class="fa fa-chart-line"></i> Key Indicators</a>
<a href="#sectors" data-id="sectors"><i class="fa fa-industry"></i> Sectoral Analysis</a>
<a href="#fdi" data-id="fdi"><i class="fa fa-hand-holding-dollar"></i> FDI & Capital</a>
<a href="#history" data-id="history"><i class="fa fa-history"></i> Historical Comparison</a>
<a href="#outlook" data-id="outlook"><i class="fa fa-compass"></i> Economic Outlook</a>
<a href="#risks" data-id="risks"><i class="fa fa-exclamation-triangle"></i> Challenges & Risks</a>
<a href="#references" data-id="references"><i class="fa fa-book"></i> References</a>
</div>
<div class="progress-vert" aria-hidden="true">
<i id="tocProgress"></i>
</div>
<div style="display:flex;gap:8px;margin-top:8px">
<button class="btn" id="saveBookmark" title="Save current section"><i class="fa fa-bookmark"></i> Save</button>
<button class="btn" id="gotoBookmark" title="Open saved section"><i class="fa fa-location-arrow"></i> Go</button>
</div>
</nav>
<!-- Content -->
<main class="content">
<!-- Executive Summary -->
<section id="executive" class="card" tabindex="0" aria-labelledby="executiveTitle">
<div class="meta-row">
<div>
<h2 id="executiveTitle" style="margin:0;color:var(--primary)">Executive Summary</h2>
<p class="small muted">Snapshot of growth, drivers and immediate outlook</p>
</div>
<div class="pill small">Last updated: 2025</div>
</div>
<div class="summary">
<p class="muted">Vietnam's economy sustained strong momentum in 2025, achieving 7.52% growth in H1 and 7.96% YoY in Q2. The expansion is led by services and manufacturing, supported by robust FDI inflows and resilient domestic demand despite elevated global uncertainty.</p>
<div class="kpi-row">
<div class="kpi">
<h3>7.52%</h3>
<p>GDP Growth H1 2025 (highest H1 since 2011)</p>
</div>
<div class="kpi">
<h3>7.96%</h3>
<p>GDP Growth Q2 2025 YoY</p>
</div>
<div class="kpi">
<h3>3.57%</h3>
<p>Inflation (June 2025)</p>
</div>
<div class="kpi">
<h3>US$21.51B</h3>
<p>FDI (First Half 2025)</p>
</div>
</div>
<div style="display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-top:6px">
<button class="btn primary" id="readMoreExec"><i class="fa fa-eye"></i> Expand Summary</button>
<div class="muted small">Use the table of contents to navigate sections. Use search to find keywords.</div>
</div>
</div>
</section>
<!-- Key Indicators -->
<section id="indicators" class="card" tabindex="0" aria-labelledby="indicatorsTitle">
<h2 id="indicatorsTitle" style="margin:0;color:var(--primary)">Key Economic Indicators</h2>
<p class="small muted">Core datasets and comparisons for 2025</p>
<div class="indicators-grid" style="margin-top:12px">
<div class="card" style="padding:12px">
<h3 style="margin:0;color:var(--primary)">GDP Series</h3>
<p class="small muted">Quarterly / mid-year performance</p>
<div style="display:flex;gap:8px;align-items:center;margin-top:10px;flex-wrap:wrap">
<div><strong>Q1 2025</strong><div class="muted small">6.9% YoY</div></div>
<div><strong>Q2 2025</strong><div class="muted small">7.96% YoY</div></div>
<div><strong>H1 2025</strong><div class="muted small">7.52% YoY</div></div>
</div>
</div>
<div class="card" style="padding:12px">
<h3 style="margin:0;color:var(--primary)">Inflation & Labor</h3>
<p class="small muted">Key macro indicators</p>
<div style="display:flex;gap:12px;align-items:center;margin-top:10px">
<div><strong>Inflation</strong><div class="muted small">May: 3.24% • Jun: 3.57%</div></div>
<div><strong>Unemployment</strong><div class="muted small">Q1 2025: 2.20%</div></div>
</div>
</div>
<div class="card" style="padding:12px">
<h3 style="margin:0;color:var(--primary)">FDI</h3>
<p class="small muted">Capital flows and confidence</p>
<div style="margin-top:10px">
<div><strong>Registered (Jan–May)</strong><div class="muted small">US$18.4B (+51% YoY)</div></div>
<div style="margin-top:6px"><strong>Disbursed (Jan–May)</strong><div class="muted small">US$8.9B</div></div>
</div>
</div>
<div class="card" style="padding:12px">
<h3 style="margin:0;color:var(--primary)">Forecasts</h3>
<p class="small muted">Cross-institution projections (2025)</p>
<ul style="margin:8px 0 0 16px" class="muted small">
<li>World Bank: 5.8%</li>
<li>ADB: 6.6%</li>
<li>IMF: 5.2%</li>
<li>Government Target: 8.3–8.5%</li>
</ul>
</div>
</div>
<!-- Charts -->
<div class="charts" style="margin-top:14px">
<div class="card chart large" id="chartHistoryHolder">
<h4 style="margin:0;color:var(--primary)">Historical GDP Growth (2020–2025 Q1 series)</h4>
<p class="small muted">Year-on-year growth in first quarter (2020–2025)</p>
<div id="chartHistory" class="chart" aria-hidden="false"></div>
</div>
<div class="card chart" id="chartForecastHolder">
<h4 style="margin:0;color:var(--primary)">2025 Growth Forecasts</h4>
<p class="small muted">Institutional consensus vs Government target</p>
<div id="chartForecast" class="chart"></div>
<div style="margin-top:8px" class="legend" id="forecastLegend"></div>
</div>
</div>
<!-- Sortable table -->
<div class="card" style="margin-top:12px">
<div style="display:flex;justify-content:space-between;align-items:center">
<h4 style="margin:0;color:var(--primary)">Indicator Table (sortable)</h4>
<div class="small muted">Click headers to sort • Use export buttons above</div>
</div>
<div style="margin-top:10px;overflow:auto">
<table id="indicatorTable" aria-label="Key indicators table">
<thead>
<tr>
<th data-key="indicator">Indicator <i class="fa fa-sort"></i></th>
<th data-key="value">Value</th>
<th data-key="period">Period</th>
<th data-key="change">Change</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</section>
<!-- Sectoral Analysis -->
<section id="sectors" class="card" tabindex="0" aria-labelledby="sectorsTitle">
<h2 id="sectorsTitle" style="margin:0;color:var(--primary)">Sectoral Analysis</h2>
<p class="small muted">Decomposition and performance drivers</p>
<div style="display:grid;grid-template-columns:1fr;gap:12px;margin-top:12px">
<div class="card" style="display:flex;gap:12px;align-items:center;flex-wrap:wrap">
<div style="flex:1;min-width:220px">
<h4 style="margin:0;color:var(--primary)">Primary Growth Drivers</h4>
<ul class="muted small" style="margin-top:8px">
<li>Services: major contributor</li>
<li>Manufacturing: recovery & development</li>
<li>Export industries: export backbone</li>
<li>Banking: earnings projected +17% in 2025</li>
</ul>
</div>
<div style="width:320px">
<div id="donutSectors" class="chart" style="height:220px"></div>
<div class="legend" id="donutLegend" style="margin-top:10px"></div>
</div>
</div>
<details class="card" open>
<summary><i class="chev fa fa-chevron-down"></i> Retail & Consumption</summary>
<div style="margin-top:8px" class="muted small">
Retail sales Q1 2025: 1.708 quadrillion VND (~US$66.83B), +9.9% YoY — strong domestic consumption supports services and retail sectors.
</div>
</details>
</div>
</section>
<!-- FDI & dataset -->
<section id="fdi" class="card" tabindex="0" aria-labelledby="fdiTitle">
<h2 id="fdiTitle" style="margin:0;color:var(--primary)">FDI & Capital Flows</h2>
<p class="small muted">Monthly breakdown and filters</p>
<div style="display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-top:12px">
<label class="muted small">Filter:</label>
<select id="fdifeature" class="btn" style="padding:8px;border-radius:10px">
<option value="all">Show all</option>
<option value="registered">Registered capital</option>
<option value="disbursed">Disbursed capital</option>
</select>
<div style="margin-left:auto;display:flex;gap:8px">
<button class="btn" id="copyFDI"><i class="fa fa-clipboard"></i> Copy FDI Table</button>
<button class="btn" id="downloadFDI"><i class="fa fa-download"></i> Download CSV</button>
</div>
</div>
<div style="margin-top:12px;overflow:auto" class="card">
<table id="fdiTable">
<thead>
<tr>
<th>Month</th>
<th>Registered (US$B)</th>
<th>Disbursed (US$B)</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="card" style="margin-top:12px">
<h4 style="margin:0;color:var(--primary)">FDI Trend</h4>
<p class="small muted">Registered vs Disbursed (Jan–May 2025)</p>
<div id="fdiChart" class="chart"></div>
</div>
</section>
<!-- Historical Comparison -->
<section id="history" class="card" tabindex="0" aria-labelledby="historyTitle">
<h2 id="historyTitle" style="margin:0;color:var(--primary)">Historical Comparison</h2>
<p class="small muted">Contextualizing 2025 against recent years</p>
<div style="display:grid;grid-template-columns:1fr;gap:12px;margin-top:12px">
<div class="card">
<h4 style="margin:0;color:var(--primary)">Trendline</h4>
<p class="small muted">First-quarter YoY growth 2020–2025</p>
<div id="miniHistory" class="chart" style="height:180px"></div>
</div>
<details class="card">
<summary><i class="chev fa fa-chevron-down"></i> Year-by-year notes</summary>
<div class="muted small" style="margin-top:8px">
2024: 7.1% growth overall. 2025 shows a strong start but may moderate with global headwinds. Long-term fundamentals remain resilient.
</div>
</details>
</div>
</section>
<!-- Outlook & projections -->
<section id="outlook" class="card" tabindex="0" aria-labelledby="outlookTitle">
<h2 id="outlookTitle" style="margin:0;color:var(--primary)">Economic Outlook & Projections</h2>
<p class="small muted">Scenarios, risks and recommended mitigations</p>
<div style="display:grid;grid-template-columns:1fr;gap:12px;margin-top:12px">
<div class="card">
<h4 style="margin:0;color:var(--primary)">Near-term Prospects</h4>
<ul class="muted small" style="margin-top:8px">
<li>Strong domestic fundamentals support continued growth.</li>
<li>Government target (8.3–8.5%) ambitious vs international forecasts.</li>
<li>Risks centered on trade tensions and geopolitical uncertainty.</li>
</ul>
</div>
<div class="card">
<h4 style="margin:0;color:var(--primary)">Policy Responses</h4>
<p class="muted small" style="margin-top:8px">Government measures: diversify export markets, strengthen domestic demand, preserve macro stability and use fiscal space for targeted support.</p>
</div>
</div>
</section>
<!-- Risks -->
<section id="risks" class="card" tabindex="0" aria-labelledby="risksTitle">
<h2 id="risksTitle" style="margin:0;color:var(--primary)">Challenges & Risk Factors</h2>
<p class="small muted">Key risks and suggested mitigations</p>
<div style="display:grid;gap:12px;margin-top:12px">
<div class="card">
<h4 style="margin:0;color:var(--primary)">Top Risks</h4>
<ol class="muted small" style="margin-top:8px">
<li>Global trade tensions affecting exports</li>
<li>US tariff policies pressuring export-oriented firms</li>
<li>Geopolitical instability raising uncertainty</li>
<li>Overreliance on FDI and inflationary pressures</li>
<li>Need to protect macroeconomic stability while pursuing growth</li>
</ol>
</div>
<details class="card">
<summary><i class="chev fa fa-chevron-down"></i> Mitigation Strategies</summary>
<div class="muted small" style="margin-top:8px">
Diversify export partners, boost domestic value chains, prudent fiscal policy, and strengthen financial oversight to reduce vulnerabilities.
</div>
</details>
</div>
</section>
<!-- References -->
<section id="references" class="card" tabindex="0" aria-labelledby="referencesTitle">
<h2 id="referencesTitle" style="margin:0;color:var(--primary)">Sources & References</h2>
<p class="small muted">Cited reports, datasets and links</p>
<ul class="references" style="margin-top:12px">
<li>
<div class="citation">
<span class="badge">1</span>
<div style="flex:1">
Trading Economics - Vietnam GDP Annual Growth Rate — <a href="https://tradingeconomics.com/vietnam/gdp-growth-annual" target="_blank">tradingeconomics.com</a>
</div>
<button class="btn" data-copy="Trading Economics - Vietnam GDP Annual Growth Rate — https://tradingeconomics.com/vietnam/gdp-growth-annual"><i class="fa fa-copy"></i></button>
</div>
</li>
<li>
<div class="citation">
<span class="badge">2</span>
<div style="flex:1">
IMF - Vietnam Country Profile — <a href="https://www.imf.org/en/Countries/VNM" target="_blank">imf.org</a>
</div>
<button class="btn" data-copy="IMF - Vietnam Country Profile — https://www.imf.org/en/Countries/VNM"><i class="fa fa-copy"></i></button>
</div>
</li>
<li>
<div class="citation">
<span class="badge">3</span>
<div style="flex:1">
World Economics - Vietnam GDP Estimates — <a href="https://www.worldeconomics.com/GDP/Vietnam.gdp" target="_blank">worldeconomics.com</a>
</div>
<button class="btn" data-copy="World Economics - Vietnam GDP Estimates — https://www.worldeconomics.com/GDP/Vietnam.gdp"><i class="fa fa-copy"></i></button>
</div>
</li>
<li style="margin-top:8px">
<div class="muted small">Full list includes government statistics (GSO), ADB, FocusEconomics, Vietnam Briefing and local news sources. Use citation buttons to copy links quickly.</div>
</li>
</ul>
<div style="display:flex;gap:8px;margin-top:12px">
<button class="btn" id="copyRefs"><i class="fa fa-clipboard"></i> Copy All References</button>
<button class="btn" id="exportRefsJSON"><i class="fa fa-file-export"></i> Export References (JSON)</button>
</div>
</section>
<footer>
<div>Prepared from public data sources • Interactive report © 2025</div>
</footer>
</main>
</div>
</div>
<div class="tooltip" id="tooltip"></div>
<script>
/* ===========================
Data
===========================*/
const data = {
historyQ1: [
{year:2020, value:3.21},
{year:2021, value:4.85},
{year:2022, value:5.42},
{year:2023, value:3.46},
{year:2024, value:5.98},
{year:2025, value:6.93}
],
forecasts: [
{name:"World Bank", value:5.8, color:"#06b6d4"},
{name:"ADB", value:6.6, color:"#0ea5a2"},
{name:"IMF", value:5.2, color:"#06a5d4"},
{name:"Government target", value:8.4, color:"#0b7285"}
],
sectors: [
{name:"Services", value:45, color:"#06b6d4"},
{name:"Manufacturing", value:30, color:"#0ea5a2"},
{name:"Agriculture", value:8, color:"#7dd3fc"},
{name:"Export-related", value:12, color:"#38bdf8"},
{name:"Other", value:5, color:"#a7f3d0"}
],
indicatorsTable:[
{indicator:"GDP Q1 2025", value:"6.9%", period:"Q1 2025", change:"–"},
{indicator:"GDP Q2 2025", value:"7.96%", period:"Q2 2025", change:"–"},
{indicator:"GDP H1 2025", value:"7.52%", period:"H1 2025", change:"Highest H1 since 2011"},
{indicator:"Inflation May 2025", value:"3.24%", period:"May 2025", change:"–"},
{indicator:"Inflation Jun 2025", value:"3.57%", period:"Jun 2025", change:"Highest YTD"},
{indicator:"Unemployment Q1 2025", value:"2.20%", period:"Q1 2025", change:"Down from 2.22%"},
{indicator:"FDI Registered (Jan–May)", value:"$18.4B", period:"Jan–May 2025", change:"+51% YoY"},
{indicator:"FDI Disbursed (Jan–May)", value:"$8.9B", period:"Jan–May 2025", change:"–"},
],
fdiMonthly:[
{month:"Jan 2025", registered:5.2, disbursed:2.3},
{month:"Feb 2025", registered:3.8, disbursed:1.5},
{month:"Mar 2025", registered:4.1, disbursed:1.8},
{month:"Apr 2025", registered:2.9, disbursed:1.7},
{month:"May 2025", registered:2.4, disbursed:1.6} // sums approx to totals
],
references:[
{id:1, text:"Trading Economics - Vietnam GDP Annual Growth Rate — https://tradingeconomics.com/vietnam/gdp-growth-annual"},
{id:2, text:"IMF - Vietnam Country Profile — https://www.imf.org/en/Countries/VNM"},
{id:3, text:"World Economics - Vietnam GDP Estimates — https://www.worldeconomics.com/GDP/Vietnam.gdp"},
{id:4, text:"Government of Vietnam - General Statistics Office — https://www.gso.gov.vn/en/"},
{id:5, text:"Wikipedia - Economy of Vietnam — https://en.wikipedia.org/wiki/Economy_of_Vietnam"},
{id:6, text:"ADB - Vietnam Country Partnership — https://www.adb.org/countries/viet-nam/main"}
]
};
/* ===========================
Utilities
===========================*/
const $ = sel => document.querySelector(sel);
const $$ = sel => Array.from(document.querySelectorAll(sel));
const fmt = n => (typeof n === "number" ? n.toFixed(2) : n);
/* Smooth scrolling for TOC links */
$$('.toc a').forEach(a => {
a.addEventListener('click', e => {
e.preventDefault();
const id = a.getAttribute('href').slice(1);
const target = document.getElementById(id);
if(target){
target.scrollIntoView({behavior:'smooth', block:'start'});
history.replaceState(null, '', '#'+id);
}
});
});
/* ===========================
Theme & LocalStorage
===========================*/
const themeToggle = $('#themeToggle');
const root = document.documentElement;
const savedTheme = localStorage.getItem('vne_theme') || (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
if(savedTheme === 'dark') root.setAttribute('data-theme','dark');
else root.removeAttribute('data-theme');
themeToggle.addEventListener('click', () => {
if(root.getAttribute('data-theme') === 'dark'){
root.removeAttribute('data-theme');
localStorage.setItem('vne_theme','light');
themeToggle.innerHTML = '<i class="fa fa-moon"></i>';
} else {
root.setAttribute('data-theme','dark');
localStorage.setItem('vne_theme','dark');
themeToggle.innerHTML = '<i class="fa fa-sun"></i>';
}
});
/* ===========================
Populate Indicator Table (sortable)
===========================*/
const indicatorTableBody = $('#indicatorTable tbody');
function renderIndicatorTable(rows){
indicatorTableBody.innerHTML = rows.map(r => `
<tr>
<td>${r.indicator}</td>
<td>${r.value}</td>
<td>${r.period}</td>
<td>${r.change}</td>
</tr>
`).join('');
}
renderIndicatorTable(data.indicatorsTable);
let sortState = {key:null,dir:1};
$$('#indicatorTable thead th').forEach(th => {
th.addEventListener('click', () => {
const key = th.dataset.key;
if(sortState.key === key) sortState.dir *= -1;
else { sortState.key = key; sortState.dir = 1; }
const sorted = [...data.indicatorsTable].sort((a,b) => {
let va = a[key] || '', vb = b[key] || '';
// numeric if possible
const na = parseFloat(String(va).replace(/[^0-9.-]+/g,'')), nb = parseFloat(String(vb).replace(/[^0-9.-]+/g,''));
if(!isNaN(na) && !isNaN(nb)) return (na-nb)*sortState.dir;
return String(va).localeCompare(String(vb)) * sortState.dir;
});
renderIndicatorTable(sorted);
});
});
/* ===========================
Chart Utilities (SVG)
===========================*/
function createSVG(w=800,h=300){
const ns = "http://www.w3.org/2000/svg";
const svg = document.createElementNS(ns,'svg');
svg.setAttribute('viewBox',`0 0 ${w} ${h}`);
svg.setAttribute('width','100%');
svg.setAttribute('height','100%');
return svg;
}
function clearEl(el){ while(el.firstChild) el.removeChild(el.firstChild); }
/* Tooltip */
const tooltip = $('#tooltip');
function showTooltip(html,x,y){
tooltip.style.display = 'block';
tooltip.innerHTML = html;
const rect = tooltip.getBoundingClientRect();
tooltip.style.left = (x)+'px';
tooltip.style.top = (y)+'px';
}
function hideTooltip(){ tooltip.style.display = 'none'; }
/* ===========================
Historical Line Chart (2020–2025)
===========================*/
function drawHistoryChart(){
const holder = $('#chartHistory');
clearEl(holder);
const w = 720, h = 320;
const svg = createSVG(w,h);
holder.appendChild(svg);
const padding = {t:24,r:24,b:36,l:56};
const innerW = w - padding.l - padding.r;
const innerH = h - padding.t - padding.b;
const values = data.historyQ1.map(d => d.value);
const minV = Math.min(...values) - 1;
const maxV = Math.max(...values) + 1;
// scales
const xs = idx => padding.l + (idx/(values.length-1))*innerW;
const ys = v => padding.t + innerH - ((v - minV)/(maxV - minV))*innerH;
// grid lines and y-axis ticks
const ns = "http://www.w3.org/2000/svg";
for(let t = Math.ceil(minV); t <= Math.floor(maxV); t+=1){
const y = ys(t);
const line = document.createElementNS(ns,'line');
line.setAttribute('x1',padding.l);
line.setAttribute('x2',w-padding.r);
line.setAttribute('y1',y);
line.setAttribute('y2',y);
line.setAttribute('stroke','rgba(0,0,0,0.04)');
line.setAttribute('stroke-width','1');
svg.appendChild(line);
const text = document.createElementNS(ns,'text');
text.setAttribute('x',padding.l-10);
text.setAttribute('y',y+4);
text.setAttribute('text-anchor','end');
text.setAttribute('fill','var(--muted)');
text.setAttribute('style','font-size:12px');
text.textContent = t + '%';
svg.appendChild(text);
}
// path
let d = '';
data.historyQ1.forEach((pt,i)=>{
const x = xs(i), y = ys(pt.value);
d += (i===0?'M':'L') + x + ' ' + y + ' ';
});
const path = document.createElementNS(ns,'path');
path.setAttribute('d', d);
path.setAttribute('fill','none');
path.setAttribute('stroke','#06b6d4');
path.setAttribute('stroke-width','3');
path.setAttribute('stroke-linecap','round');
svg.appendChild(path);
// area fill
const areaD = d + ` L ${padding.l+innerW} ${padding.t+innerH} L ${padding.l} ${padding.t+innerH} Z`;
const area = document.createElementNS(ns,'path');
area.setAttribute('d', areaD);
area.setAttribute('fill','rgba(6,182,212,0.08)');
svg.insertBefore(area, path);
// points and hover
data.historyQ1.forEach((pt,i)=>{
const x = xs(i), y = ys(pt.value);
const circle = document.createElementNS(ns,'circle');
circle.setAttribute('cx',x);
circle.setAttribute('cy',y);
circle.setAttribute('r',5);
circle.setAttribute('fill','#fff');
circle.setAttribute('stroke','#06b6d4');
circle.setAttribute('stroke-width',2);
circle.style.cursor='pointer';
svg.appendChild(circle);
circle.addEventListener('mouseenter', (e) => {
const html = `<strong>${pt.year}</strong><div class="muted small">${pt.value}% YoY</div>`;
const rect = holder.getBoundingClientRect();
showTooltip(html, rect.left + x, rect.top + y - 8);
});
circle.addEventListener('mouseleave', hideTooltip);
});
// labels (years)
data.historyQ1.forEach((pt,i)=>{
const x = xs(i), y = padding.t + innerH + 18;
const nsTxt = document.createElementNS("http://www.w3.org/2000/svg",'text');
nsTxt.setAttribute('x',x);
nsTxt.setAttribute('y',y);
nsTxt.setAttribute('text-anchor','middle');
nsTxt.setAttribute('fill','var(--muted)');
nsTxt.setAttribute('style','font-size:12px');
nsTxt.textContent = pt.year;
svg.appendChild(nsTxt);
});
}
drawHistoryChart();
/* ===========================
Forecast Bar Chart
===========================*/
function drawForecastChart(){
const holder = $('#chartForecast');
clearEl(holder);
const w=420,h=260;
const svg = createSVG(w,h);
holder.appendChild(svg);
const ns = "http://www.w3.org/2000/svg";
const padding = {t:20,r:20,b:40,l:50};
const innerW = w - padding.l - padding.r;
const innerH = h - padding.t - padding.b;
const maxVal = Math.max(...data.forecasts.map(f=>f.value))+1;
data.forecasts.forEach((f,i)=>{
const barW = innerW / data.forecasts.length * 0.7;
const gap = innerW / data.forecasts.length;
const x = padding.l + i*gap + (gap - barW)/2;
const y = padding.t + innerH - (f.value/maxVal)*innerH;
const rect = document.createElementNS(ns,'rect');
rect.setAttribute('x', x);
rect.setAttribute('y', y);
rect.setAttribute('width', barW);
rect.setAttribute('height', padding.t + innerH - y);
rect.setAttribute('rx',8);
rect.setAttribute('fill', f.color);
svg.appendChild(rect);
rect.addEventListener('mouseenter', (ev)=>{
const rectEl = holder.getBoundingClientRect();
showTooltip(`<strong>${f.name}</strong><div class="muted small">${f.value}%</div>`, rectEl.left + x + barW/2, rectEl.top + y);
});
rect.addEventListener('mouseleave', hideTooltip);
// labels
const text = document.createElementNS(ns,'text');
text.setAttribute('x', x + barW/2);
text.setAttribute('y', padding.t + innerH + 18);
text.setAttribute('text-anchor','middle');
text.setAttribute('fill','var(--muted)');
text.setAttribute('style','font-size:12px');
text.textContent = f.name.replace('Government target','Gov target');
svg.appendChild(text);
});
// y-axis ticks
for(let t=0;t<=Math.ceil(maxVal);t+=1){
const y = padding.t + innerH - (t/maxVal)*innerH;
const tx = document.createElementNS(ns,'text');
tx.setAttribute('x',padding.l-8);
tx.setAttribute('y',y+4);
tx.setAttribute('text-anchor','end');
tx.setAttribute('fill','var(--muted)');
tx.setAttribute('style','font-size:11px');
tx.textContent = t + '%';
svg.appendChild(tx);
}
// legend
const legend = $('#forecastLegend');
legend.innerHTML = '';
data.forecasts.forEach(f => {
const item = document.createElement('div');
item.className='legend-item';
item.innerHTML = `<span class="swatch" style="background:${f.color}"></span><div class="small muted">${f.name}${f.value}%</div>`;
legend.appendChild(item);
});
}
drawForecastChart();
/* ===========================
Donut Chart for Sectors
===========================*/
function drawDonut(){
const holder = $('#donutSectors');
clearEl(holder);
const w=320,h=220;
const svg = createSVG(w,h);
holder.appendChild(svg);
const ns = "http://www.w3.org/2000/svg";
const cx = w/2, cy = h/2 - 10, r = Math.min(w,h)/3;
const total = data.sectors.reduce((s,d)=>s+d.value,0);
let angle = -Math.PI/2;
const donutGroup = document.createElementNS(ns,'g');
svg.appendChild(donutGroup);
data.sectors.forEach((s,idx)=>{
const sliceAngle = (s.value/total)*(Math.PI*2);
const end = angle + sliceAngle;
const x1 = cx + r*Math.cos(angle);
const y1 = cy + r*Math.sin(angle);
const x2 = cx + r*Math.cos(end);
const y2 = cy + r*Math.sin(end);
const large = sliceAngle > Math.PI ? 1 : 0;
const pathD = `M ${cx} ${cy} L ${x1} ${y1} A ${r} ${r} 0 ${large} 1 ${x2} ${y2} Z`;
const path = document.createElementNS(ns,'path');
path.setAttribute('d', pathD);
path.setAttribute('fill', s.color);
path.setAttribute('opacity',0.95);
donutGroup.appendChild(path);
path.addEventListener('mouseenter', () => {
const rect = holder.getBoundingClientRect();
showTooltip(`<strong>${s.name}</strong><div class="muted small">${s.value}%</div>`, rect.left + cx + (r*Math.cos(angle+sliceAngle/2)), rect.top + cy + (r*Math.sin(angle+sliceAngle/2)));
});
path.addEventListener('mouseleave', hideTooltip);
angle = end;
});
// inner circle to make donut
const inner = document.createElementNS(ns,'circle');
inner.setAttribute('cx',cx);
inner.setAttribute('cy',cy);
inner.setAttribute('r',r*0.6);
inner.setAttribute('fill','var(--card)');
inner.setAttribute('stroke','rgba(0,0,0,0.02)');
svg.appendChild(inner);
// legend mapping with toggles
const legend = $('#donutLegend');
legend.innerHTML = '';
data.sectors.forEach(s=>{
const item = document.createElement('div');
item.className = 'legend-item';
item.innerHTML = `<span class="swatch" style="background:${s.color}"></span><div class="small muted">${s.name}${s.value}%</div>`;
legend.appendChild(item);
});
}
drawDonut();
/* ===========================
FDI Table and Chart
===========================*/
function renderFDITable(filter='all'){
const tbody = $('#fdiTable tbody');
const rows = data.fdiMonthly.map(r => {
return `<tr>
<td>${r.month}</td>
<td>${r.registered.toFixed(2)}</td>
<td>${r.disbursed.toFixed(2)}</td>
</tr>`;
}).join('');
tbody.innerHTML = rows;
}
renderFDITable();
function drawFDIChart(){
const holder = $('#fdiChart');
clearEl(holder);
const w=640,h=260;
const svg = createSVG(w,h);
holder.appendChild(svg);
const ns = "http://www.w3.org/2000/svg";
const padding = {t:20,r:20,b:50,l:50};
const innerW = w - padding.l - padding.r;
const innerH = h - padding.t - padding.b;
const maxV = Math.max(...data.fdiMonthly.flatMap(x=>[x.registered,x.disbursed]))+1;
// stacked bars side-by-side
data.fdiMonthly.forEach((m,i)=>{
const gap = innerW / data.fdiMonthly.length;
const barW = gap*0.35;
const x = padding.l + i*gap;
const yReg = padding.t + innerH - (m.registered/maxV)*innerH;
const yDis = padding.t + innerH - (m.disbursed/maxV)*innerH;
const rectR = document.createElementNS(ns,'rect');
rectR.setAttribute('x', x - barW/2);
rectR.setAttribute('y', yReg);
rectR.setAttribute('width', barW);
rectR.setAttribute('height', padding.t + innerH - yReg);
rectR.setAttribute('fill','#06b6d4');
rectR.setAttribute('rx',6);
svg.appendChild(rectR);
const rectD = document.createElementNS(ns,'rect');
rectD.setAttribute('x', x + barW/2);
rectD.setAttribute('y', yDis);
rectD.setAttribute('width', barW);
rectD.setAttribute('height', padding.t + innerH - yDis);
rectD.setAttribute('fill','#0ea5a2');
rectD.setAttribute('rx',6);
svg.appendChild(rectD);
[rectR,rectD].forEach((el,kindIdx)=>{
el.addEventListener('mouseenter',()=>{
const rect = holder.getBoundingClientRect();
const text = kindIdx===0 ? `Registered: ${m.registered}B` : `Disbursed: ${m.disbursed}B`;
showTooltip(`<strong>${m.month}</strong><div class="muted small">${text}</div>`, rect.left + x, rect.top + (kindIdx===0?yReg:yDis));
});
el.addEventListener('mouseleave', hideTooltip);
});
// label
const label = document.createElementNS(ns,'text');
label.setAttribute('x', x);
label.setAttribute('y', padding.t + innerH + 22);
label.setAttribute('text-anchor','middle');
label.setAttribute('fill','var(--muted)');
label.setAttribute('style','font-size:11px');
label.textContent = m.month.split(' ')[0];
svg.appendChild(label);
});
// legend
const legendDiv = document.createElement('div');
legendDiv.style.marginTop = '8px';
}
drawFDIChart();
/* ===========================
Interactions: search, copy summary, export
===========================*/
const globalSearch = $('#globalSearch');
globalSearch.addEventListener('input', (e) => {
const q = e.target.value.trim().toLowerCase();
// highlight and open matching sections, show first match
const sections = Array.from(document.querySelectorAll('main.content section'));
let firstMatch = null;
sections.forEach(sec => {
const txt = sec.textContent.toLowerCase();
if(q && txt.includes(q)){
sec.classList.add('highlight');
sec.style.boxShadow = '0 6px 30px rgba(6,182,212,0.06)';
if(!firstMatch) firstMatch = sec;
} else {
sec.classList.remove('highlight');
sec.style.boxShadow = '';
}
});
if(firstMatch){
firstMatch.scrollIntoView({behavior:'smooth', block:'center'});
}
});
/* Copy executive summary to clipboard */
$('#copySummary').addEventListener('click', async () => {
const summary = document.querySelector('#executive .summary p').textContent.trim();
try{
await navigator.clipboard.writeText(summary);
alert('Executive summary copied to clipboard.');
}catch(e){
console.warn(e);
alert('Copy failed — permission denied.');
}
});
/* Export indicators to CSV */
$('#exportCSV').addEventListener('click', () => {
const rows = data.indicatorsTable;
const csv = ['Indicator,Value,Period,Change', ...rows.map(r=>`"${r.indicator}","${r.value}","${r.period}","${r.change}"`)].join('\\n');
const blob = new Blob([csv], {type:'text/csv'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url; a.download = 'vietnam_indicators_2025.csv';
document.body.appendChild(a); a.click(); a.remove();
URL.revokeObjectURL(url);
});
/* Export JSON */
$('#exportJSON').addEventListener('click', () => {
const payload = {
meta:{title:'Vietnam Economic Growth Report 2025', date:2025},
data
};
const blob = new Blob([JSON.stringify(payload,null,2)],{type:'application/json'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url; a.download = 'vietnam_report_2025.json';
document.body.appendChild(a); a.click(); a.remove();
URL.revokeObjectURL(url);
});
/* Print */
$('#printBtn').addEventListener('click', () => window.print());
/* Copy FDI table */
$('#copyFDI').addEventListener('click', async () => {
const txt = data.fdiMonthly.map(r=>`${r.month}: Registered ${r.registered}B — Disbursed ${r.disbursed}B`).join('\\n');
try{ await navigator.clipboard.writeText(txt); alert('FDI table copied to clipboard.'); }catch(e){ alert('Copy failed.'); }
});
/* Download FDI CSV */
$('#downloadFDI').addEventListener('click', () => {
const csv = ['Month,Registered,Disbursed', ...data.fdiMonthly.map(r=>`${r.month},${r.registered},${r.disbursed}`)].join('\\n');
const blob = new Blob([csv],{type:'text/csv'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url; a.download = 'fdi_jan_may_2025.csv';
document.body.appendChild(a); a.click(); a.remove();
URL.revokeObjectURL(url);
});
/* Copy references buttons */
$$('.references button').forEach(btn=>{
btn.addEventListener('click', async () => {
const txt = btn.dataset.copy;
try{ await navigator.clipboard.writeText(txt); alert('Citation copied.'); }catch(e){ alert('Copy failed.'); }
});
});
$('#copyRefs').addEventListener('click', async () => {
const all = data.references.map(r=>r.text).join('\\n');
try{ await navigator.clipboard.writeText(all); alert('All references copied.'); }catch(e){ alert('Copy failed.'); }
});
$('#exportRefsJSON').addEventListener('click', () => {
const blob = new Blob([JSON.stringify(data.references,null,2)],{type:'application/json'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url; a.download = 'references.json';
document.body.appendChild(a); a.click(); a.remove();
URL.revokeObjectURL(url);
});
/* Export entire visible report to JSON (quick) - reuse exportJSON */
$('#readMoreExec').addEventListener('click', () => {
const more = `Vietnam's economy maintained robust growth in H1 2025 with leads from services and manufacturing. Macroeconomic control (inflation ~3.5%) and strong FDI flows underpin resilience, yet external trade tensions pose key risks.`;
alert(more);
});
/* ===========================
Table filtering (FDI feature)
===========================*/
$('#fdifeature').addEventListener('change', (e)=>{
const v = e.target.value;
const tbody = document.querySelectorAll('#fdiTable tbody tr');
tbody.forEach(tr => tr.style.display = 'table-row'); // single view in table; filtering shows/hides columns
const cells = $('#fdiTable thead').querySelectorAll('th');
if(v === 'registered'){
cells[1].style.display='table-cell';
cells[2].style.display='none';
$('#fdiTable tbody tr').forEach(tr=> tr.children[1].style.display='table-cell', tr.children[2].style.display='none');
} else if(v === 'disbursed'){
cells[1].style.display='none';
cells[2].style.display='table-cell';
$('#fdiTable tbody tr').forEach(tr=> tr.children[1].style.display='none', tr.children[2].style.display='table-cell');
} else {
cells[1].style.display='table-cell';
cells[2].style.display='table-cell';
$('#fdiTable tbody tr').forEach(tr=> tr.children[1].style.display='table-cell', tr.children[2].style.display='table-cell');
}
});
/* ===========================
TOC Active Link and Progress (IntersectionObserver)
===========================*/
const sections = Array.from(document.querySelectorAll('main.content section'));
const tocLinks = Array.from(document.querySelectorAll('.toc a'));
const progressEl = $('#tocProgress');
const obs = new IntersectionObserver(entries=>{
entries.forEach(entry=>{
const id = entry.target.id;
const link = document.querySelector('.toc a[data-id="'+id+'"]');
if(entry.isIntersecting){
tocLinks.forEach(l=>l.classList.remove('active'));
if(link) link.classList.add('active');
localStorage.setItem('vne_last_section', id);
}
// calculate progress: visible proportion of document
const visible = Math.max(0,entry.intersectionRatio);
// approximate progress based on scroll position
const docHeight = document.body.scrollHeight - window.innerHeight;
const scrolled = window.scrollY;
const progressPerc = Math.min(100, Math.round((scrolled/docHeight)*100));
progressEl.style.width = progressPerc + '%';
progressEl.style.display = 'block';
// animate width using transform
progressEl.style.transform = `translateX(${0}%)`;
$('#tocProgress').style.width = progressPerc + '%';
});
}, {threshold:[0.15,0.45,0.75]});
sections.forEach(s => obs.observe(s));
/* Save & go bookmark (stores last open section id) */
$('#saveBookmark').addEventListener('click', () => {
const active = document.querySelector('.toc a.active');
if(active){
const id = active.dataset.id;
localStorage.setItem('vne_bookmark', id);
alert('Saved current section: '+id);
} else alert('No active section selected.');
});
$('#gotoBookmark').addEventListener('click', () => {
const id = localStorage.getItem('vne_bookmark');
if(id){
const target = document.getElementById(id);
target.scrollIntoView({behavior:'smooth', block:'start'});
} else alert('No bookmark saved.');
});
/* Restore last section if any */
window.addEventListener('load', () => {
const last = localStorage.getItem('vne_last_section');
if(last){
const link = document.querySelector('.toc a[data-id="'+last+'"]');
if(link) link.classList.add('active');
}
});
/* ===========================
Small mini history chart
===========================*/
function drawMiniHistory(){
const holder = $('#miniHistory');
clearEl(holder);
const w=560,h=160;
const svg = createSVG(w,h);
holder.appendChild(svg);
const padding={t:16,b:30,l:40,r:16};
const innerW = w-padding.l-padding.r;
const innerH = h-padding.t-padding.b;
const values = data.historyQ1.map(d=>d.value);
const minV = Math.min(...values)-1;
const maxV = Math.max(...values)+1;
const xs = idx => padding.l + (idx/(values.length-1))*innerW;
const ys = v => padding.t + innerH - ((v-minV)/(maxV-minV))*innerH;
const ns = "http://www.w3.org/2000/svg";
let d = '';
data.historyQ1.forEach((pt,i)=>{
d += (i===0?'M':'L') + xs(i) + ' ' + ys(pt.value) + ' ';
});
const path = document.createElementNS(ns,'path');
path.setAttribute('d',d);
path.setAttribute('fill','none');
path.setAttribute('stroke','#0ea5a2');
path.setAttribute('stroke-width','2');
path.setAttribute('stroke-linecap','round');
svg.appendChild(path);
data.historyQ1.forEach((pt,i)=>{
const c = document.createElementNS(ns,'circle');
c.setAttribute('cx',xs(i));
c.setAttribute('cy',ys(pt.value));
c.setAttribute('r',3);
c.setAttribute('fill','#fff');
c.setAttribute('stroke','#0ea5a2');
c.setAttribute('stroke-width',1.5);
svg.appendChild(c);
});
}
drawMiniHistory();
/* ===========================
Make FDI table rows clickable to copy
===========================*/
$('#fdiTable tbody').addEventListener('click', async (e) => {
let tr = e.target.closest('tr');
if(!tr) return;
const cells = Array.from(tr.children).map(td=>td.textContent.trim()).join(' | ');
try{ await navigator.clipboard.writeText(cells); alert('Row copied: '+cells); }catch(e){ alert('Copy failed'); }
});
/* Accessibility: keyboard navigation for TOC */
$$('.toc a').forEach((a,i)=>{
a.setAttribute('tabindex',0);
a.addEventListener('keydown', (e)=>{
if(e.key === 'Enter') a.click();
});
});
/* Resize handlers: redraw charts on resize to keep resolution appropriate */
let resizeTimer;
window.addEventListener('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(()=>{ drawHistoryChart(); drawForecastChart(); drawDonut(); drawFDIChart(); drawMiniHistory(); }, 200);
});
/* Initialize other UI states */
(function init(){
renderFDITable();
// populate toc active
const hash = location.hash.replace('#','');
if(hash){
const target = document.getElementById(hash);
if(target) target.scrollIntoView();
}
})();
</script>
</body>
</html>