thucdangvan020999 commited on
Commit
a592b05
·
verified ·
1 Parent(s): 167f885

Upload index.html with huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +1192 -18
index.html CHANGED
@@ -1,19 +1,1193 @@
1
  <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
6
+ <title>Vietnam Economic Growth Report 2025 — Interactive Dashboard</title>
7
+
8
+ <!-- Google Fonts -->
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700;800&display=swap" rel="stylesheet">
10
+
11
+ <!-- Feather Icons -->
12
+ <script src="https://unpkg.com/feather-icons"></script>
13
+
14
+ <style>
15
+ /* CSS Variables */
16
+ :root{
17
+ --bg: #f6f8fb;
18
+ --card: #ffffff;
19
+ --muted: #6b7280;
20
+ --accent: #0f766e;
21
+ --accent-2: #06b6d4;
22
+ --danger: #ef4444;
23
+ --glass: rgba(255,255,255,0.6);
24
+ --radius: 14px;
25
+ --shadow: 0 8px 30px rgba(16,24,40,0.06);
26
+ --max-width: 1200px;
27
+ --ff: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial;
28
+ }
29
+
30
+ /* Reset & base */
31
+ *{box-sizing:border-box}
32
+ html,body{height:100%}
33
+ body{
34
+ margin:0;
35
+ font-family:var(--ff);
36
+ background:linear-gradient(180deg,#f7fbfc 0%, var(--bg) 100%);
37
+ color:#0f172a;
38
+ -webkit-font-smoothing:antialiased;
39
+ -moz-osx-font-smoothing:grayscale;
40
+ line-height:1.45;
41
+ padding:20px;
42
+ }
43
+ a{color:var(--accent); text-decoration:none}
44
+ small{color:var(--muted)}
45
+
46
+ /* Layout */
47
+ .app{
48
+ max-width:var(--max-width);
49
+ margin:0 auto;
50
+ display:grid;
51
+ gap:18px;
52
+ grid-template-columns: 1fr;
53
+ }
54
+
55
+ /* Header */
56
+ header{
57
+ display:flex;
58
+ gap:16px;
59
+ align-items:center;
60
+ justify-content:space-between;
61
+ background:linear-gradient(90deg, rgba(6,182,212,0.08), rgba(15,118,110,0.06));
62
+ border-radius:var(--radius);
63
+ padding:16px;
64
+ box-shadow:var(--shadow);
65
+ position:sticky;
66
+ top:20px;
67
+ z-index:50;
68
+ backdrop-filter: blur(6px);
69
+ }
70
+ .brand{display:flex; gap:12px; align-items:center}
71
+ .logo{
72
+ width:56px;
73
+ height:56px;
74
+ background:linear-gradient(135deg,var(--accent),var(--accent-2));
75
+ color:white;
76
+ display:grid;
77
+ place-items:center;
78
+ font-weight:800;
79
+ border-radius:12px;
80
+ box-shadow:0 6px 18px rgba(6,182,212,0.14);
81
+ }
82
+ .title h1{margin:0;font-size:clamp(1.05rem,1.6vw,1.3rem)}
83
+ .title p{margin:0;color:var(--muted);font-size:0.86rem}
84
+
85
+ /* Toolbar */
86
+ .tools{display:flex;gap:10px;align-items:center}
87
+ .btn{
88
+ display:inline-flex;
89
+ gap:8px;
90
+ align-items:center;
91
+ border:0;
92
+ padding:8px 12px;
93
+ background:var(--card);
94
+ border-radius:10px;
95
+ box-shadow:var(--shadow);
96
+ cursor:pointer;
97
+ font-weight:600;
98
+ }
99
+ .btn.ghost{background:transparent;box-shadow:none}
100
+ .searchbar{
101
+ display:flex;
102
+ align-items:center;
103
+ gap:8px;
104
+ border-radius:12px;
105
+ padding:8px;
106
+ background:var(--card);
107
+ box-shadow:var(--shadow);
108
+ min-width:180px;
109
+ }
110
+ .searchbar input{
111
+ border:0;background:transparent;outline:none;font-size:0.95rem;
112
+ width:160px;
113
+ }
114
+
115
+ /* Core layout: TOC + content */
116
+ .content{
117
+ display:grid;
118
+ grid-template-columns: 280px 1fr;
119
+ gap:18px;
120
+ align-items:start;
121
+ }
122
+
123
+ /* Mobile adjustments */
124
+ @media (max-width: 1024px){
125
+ .content{grid-template-columns: 220px 1fr}
126
+ }
127
+ @media (max-width: 768px){
128
+ .content{grid-template-columns: 1fr}
129
+ header{position:static}
130
+ }
131
+
132
+ /* TOC */
133
+ nav.toc{
134
+ position:sticky;
135
+ top:110px;
136
+ height:calc(100vh - 150px);
137
+ overflow:auto;
138
+ background:linear-gradient(180deg, rgba(255,255,255,0.9), rgba(255,255,255,0.7));
139
+ border-radius:12px;
140
+ padding:12px;
141
+ box-shadow:var(--shadow);
142
+ scrollbar-width:thin;
143
+ }
144
+ nav.toc h3{margin:0 0 8px 0;font-size:0.95rem}
145
+ nav.toc ul{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:6px}
146
+ nav.toc a{
147
+ padding:8px 10px;
148
+ border-radius:8px;
149
+ color:var(--muted);
150
+ display:flex;gap:8px;align-items:center;
151
+ font-weight:600;
152
+ }
153
+ nav.toc a.active{background:linear-gradient(90deg, rgba(6,182,212,0.08), rgba(15,118,110,0.04)); color:var(--accent)}
154
+ .toc-footer{margin-top:12px;font-size:0.85rem;color:var(--muted)}
155
+
156
+ /* Main article */
157
+ main.report{
158
+ display:flex;
159
+ flex-direction:column;
160
+ gap:18px;
161
+ }
162
+
163
+ /* Section card */
164
+ section.card{
165
+ background:var(--card);
166
+ border-radius:var(--radius);
167
+ padding:16px;
168
+ box-shadow:var(--shadow);
169
+ display:grid;
170
+ gap:14px;
171
+ }
172
+ .card-header{display:flex;align-items:center;justify-content:space-between;gap:12px}
173
+ .card-header h2{margin:0;font-size:1.05rem}
174
+ .meta{color:var(--muted);font-size:0.9rem}
175
+
176
+ /* KPI grid */
177
+ .kpis{display:grid;grid-template-columns:repeat(2,1fr);gap:12px}
178
+ @media (min-width:1024px){ .kpis{grid-template-columns:repeat(4,1fr)} }
179
+ .kpi{
180
+ background:linear-gradient(180deg, rgba(6,182,212,0.03), rgba(15,118,110,0.02));
181
+ padding:12px;border-radius:12px;display:flex;flex-direction:column;gap:6px;
182
+ }
183
+ .kpi small{color:var(--muted)}
184
+ .kpi .value{font-weight:800;font-size:1.2rem;color:var(--accent)}
185
+
186
+ /* Charts area */
187
+ .charts{display:grid;grid-template-columns:1fr;gap:12px}
188
+ @media (min-width:768px){ .charts{grid-template-columns:1fr 340px} }
189
+ .chart{
190
+ background:linear-gradient(180deg, rgba(255,255,255,0.8), rgba(255,255,255,0.95));
191
+ padding:12px;border-radius:12px;
192
+ display:flex;flex-direction:column;gap:8px;
193
+ }
194
+ .chart canvas, .chart svg{width:100%;height:220px}
195
+ .legend{display:flex;gap:12px;flex-wrap:wrap;font-size:0.88rem;color:var(--muted)}
196
+
197
+ /* Table */
198
+ table{width:100%;border-collapse:collapse;font-size:0.95rem}
199
+ th,td{padding:10px;text-align:left;border-bottom:1px solid #eef2f7}
200
+ th{cursor:pointer;background:transparent;color:var(--muted);font-weight:700}
201
+ tr:hover td{background:linear-gradient(90deg,rgba(6,182,212,0.03),transparent)}
202
+
203
+ /* Controls row */
204
+ .controls{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
205
+ .chip{background:var(--glass);padding:8px;border-radius:999px;font-weight:700;color:var(--accent);display:inline-flex;gap:8px;align-items:center}
206
+
207
+ /* Progress & indicators */
208
+ .progress{
209
+ height:10px;background:linear-gradient(90deg,#e6eef0,#f8fafb);
210
+ border-radius:999px;overflow:hidden;
211
+ }
212
+ .progress > i{display:block;height:100%;width:0;background:linear-gradient(90deg,var(--accent),var(--accent-2));transition:width 300ms}
213
+
214
+ /* Collapsible details styled */
215
+ details summary{list-style:none;cursor:pointer;outline:none}
216
+ details summary::-webkit-details-marker{display:none}
217
+ details summary{display:flex;align-items:center;gap:8px;padding:10px;background:linear-gradient(180deg,#fff,#fbfeff);border-radius:10px}
218
+ details[open] summary{background:linear-gradient(90deg, rgba(6,182,212,0.06), rgba(15,118,110,0.03))}
219
+
220
+ /* Footer */
221
+ footer{display:flex;justify-content:space-between;align-items:center;padding:12px;color:var(--muted);font-size:0.9rem}
222
+
223
+ /* Small utilities */
224
+ .muted{color:var(--muted)}
225
+ .pill{padding:6px 10px;border-radius:999px;background:rgba(15,118,110,0.08);color:var(--accent);font-weight:700}
226
+
227
+ /* container queries for cards */
228
+ .card { container-type: inline-size; }
229
+ @container (min-width: 420px) {
230
+ .kpi .label{font-size:0.95rem}
231
+ }
232
+
233
+ /* Tiny responsiveness */
234
+ @media (max-width:420px){
235
+ .kpis{grid-template-columns:repeat(1,1fr)}
236
+ .searchbar input{width:90px}
237
+ }
238
+
239
+ </style>
240
+ </head>
241
+ <body>
242
+ <div class="app" id="app">
243
+ <header>
244
+ <div class="brand">
245
+ <div class="logo">VN</div>
246
+ <div class="title">
247
+ <h1>Vietnam Economic Growth Report 2025</h1>
248
+ <p>Interactive analysis • Data-driven insights • Sources integrated</p>
249
+ </div>
250
+ </div>
251
+
252
+ <div class="tools">
253
+ <div class="searchbar" title="Search report">
254
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none"><path d="M21 21l-4.35-4.35" stroke="#6b7280" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/><circle cx="11" cy="11" r="6" stroke="#6b7280" stroke-width="1.6"/></svg>
255
+ <input id="global-search" placeholder="Search sections, numbers..." />
256
+ </div>
257
+
258
+ <button class="btn" id="copySummary" title="Copy executive summary to clipboard">
259
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none"><rect x="9" y="9" width="11" height="11" rx="2" stroke="#0f766e" stroke-width="1.6"/><rect x="4" y="4" width="11" height="11" rx="2" stroke="#0f766e" stroke-width="1.6"/></svg>
260
+ Export
261
+ </button>
262
+
263
+ <button class="btn ghost" id="toggleFilters" title="Toggle data filters">
264
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none"><path d="M22 6H2l7 8v6l6-4v-2l7-8z" stroke="#0f766e" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>
265
+ Filters
266
+ </button>
267
+ </div>
268
+ </header>
269
+
270
+ <div class="content">
271
+ <nav class="toc" aria-label="Table of contents">
272
+ <h3>Contents</h3>
273
+ <ul id="toc-list">
274
+ <li><a href="#executive" data-target="executive" class="toc-link active"><svg width="14" height="14"><circle cx="7" cy="7" r="6" fill="#06b6d4"/></svg> Executive Summary</a></li>
275
+ <li><a href="#indicators" data-target="indicators" class="toc-link"><svg width="14" height="14"><circle cx="7" cy="7" r="6" fill="#0f766e"/></svg> Key Indicators</a></li>
276
+ <li><a href="#sector" data-target="sector" class="toc-link"><svg width="14" height="14"><circle cx="7" cy="7" r="6" fill="#f59e0b"/></svg> Sectoral Analysis</a></li>
277
+ <li><a href="#risks" data-target="risks" class="toc-link"><svg width="14" height="14"><circle cx="7" cy="7" r="6" fill="#ef4444"/></svg> Challenges & Risks</a></li>
278
+ <li><a href="#history" data-target="history" class="toc-link"><svg width="14" height="14"><circle cx="7" cy="7" r="6" fill="#7c3aed"/></svg> Historical Comparison</a></li>
279
+ <li><a href="#outlook" data-target="outlook" class="toc-link"><svg width="14" height="14"><circle cx="7" cy="7" r="6" fill="#10b981"/></svg> Outlook & Projections</a></li>
280
+ <li><a href="#references" data-target="references" class="toc-link"><svg width="14" height="14"><circle cx="7" cy="7" r="6" fill="#94a3b8"/></svg> Sources & Citations</a></li>
281
+ </ul>
282
+
283
+ <div class="toc-footer">
284
+ <div style="margin-bottom:8px">Reading progress</div>
285
+ <div class="progress" aria-hidden><i id="progress-bar"></i></div>
286
+ <div style="margin-top:10px;font-size:0.86rem">Saved filters: <span id="savedFilters" class="pill">None</span></div>
287
+ </div>
288
+ </nav>
289
+
290
+ <main class="report" id="report">
291
+ <!-- Executive Summary -->
292
+ <section id="executive" class="card" data-title="Executive Summary">
293
+ <div class="card-header">
294
+ <div>
295
+ <h2>Executive Summary</h2>
296
+ <div class="meta">Snapshot of Vietnam's economic performance — H1 2025 and Q2 highlights</div>
297
+ </div>
298
+ <div class="controls">
299
+ <span class="chip">Top-line: 7.52% H1 GDP</span>
300
+ <button class="btn" id="copyExec" title="Copy Executive Summary"><svg width="14" height="14" viewBox="0 0 24 24"><rect x="9" y="9" width="11" height="11" rx="2" stroke="#0f766e" stroke-width="1.6"/><rect x="4" y="4" width="11" height="11" rx="2" stroke="#0f766e" stroke-width="1.6"/></svg> Copy</button>
301
+ </div>
302
+ </div>
303
+
304
+ <div style="display:grid;gap:12px">
305
+ <p id="exec-text" class="muted">
306
+ Vietnam's economy demonstrates robust growth in 2025 with GDP expanding 7.96% in Q2 and 7.52% in the first half — the strongest H1 performance since 2011.
307
+ Growth is led by services and manufacturing, supported by strong FDI inflows, low unemployment and controlled inflation. External risks from trade tensions and tariff policies remain.
308
+ </p>
309
+
310
+ <div style="display:grid;gap:10px;grid-template-columns:1fr 1fr">
311
+ <div class="kpi">
312
+ <small>Q2 2025 GDP (y/y)</small>
313
+ <div class="value" id="k-gdp-q2">7.96%</div>
314
+ <small class="muted">Source: GSO / aggregated</small>
315
+ </div>
316
+
317
+ <div class="kpi">
318
+ <small>H1 2025 GDP (y/y)</small>
319
+ <div class="value" id="k-gdp-h1">7.52%</div>
320
+ <small class="muted">Highest mid-year since 2011</small>
321
+ </div>
322
+ </div>
323
+
324
+ <details>
325
+ <summary>
326
+ <strong>Key takeaways</strong>
327
+ <span style="margin-left:auto;color:var(--muted)">Click to expand</span>
328
+ </summary>
329
+ <div style="padding:10px;display:grid;gap:8px">
330
+ <ul>
331
+ <li>Strong FDI: US$21.51 billion in H1 (up 32.6% y/y) — supporting manufacturing and exports.</li>
332
+ <li>Inflation remains contained (May 3.24%; June 3.57%) within target range.</li>
333
+ <li>Unemployment low at 2.20% in Q1, underpinning domestic consumption.</li>
334
+ </ul>
335
+ </div>
336
+ </details>
337
+ </div>
338
+ </section>
339
+
340
+ <!-- Key Indicators -->
341
+ <section id="indicators" class="card" data-title="Key Indicators">
342
+ <div class="card-header">
343
+ <div>
344
+ <h2>Key Indicators 2025</h2>
345
+ <div class="meta">Actuals, forecasts and comparative metrics</div>
346
+ </div>
347
+ <div class="controls">
348
+ <button class="btn ghost" id="toggleForecasts"><svg width="14" height="14" viewBox="0 0 24 24"><path d="M3 12h18" stroke="#0f766e" stroke-width="1.6" stroke-linecap="round"/><path d="M3 6h12" stroke="#0f766e" stroke-width="1.6" stroke-linecap="round"/><path d="M3 18h8" stroke="#0f766e" stroke-width="1.6" stroke-linecap="round"/></svg> Forecasts</button>
349
+ <button class="btn" id="exportCSV"><svg width="14" height="14" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" stroke="#0f766e" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/><polyline points="7 10 12 15 17 10" stroke="#0f766e" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/><line x1="12" y1="15" x2="12" y2="3" stroke="#0f766e" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg> Export CSV</button>
350
+ </div>
351
+ </div>
352
+
353
+ <div class="kpis" id="indicator-kpis">
354
+ <div class="kpi">
355
+ <small>GDP Q1 2025 (y/y)</small>
356
+ <div class="value">6.90%</div>
357
+ <small class="muted">Actual</small>
358
+ </div>
359
+
360
+ <div class="kpi">
361
+ <small>GDP Q2 2025 (y/y)</small>
362
+ <div class="value">7.96%</div>
363
+ <small class="muted">Actual</small>
364
+ </div>
365
+
366
+ <div class="kpi">
367
+ <small>Inflation (Jun 2025)</small>
368
+ <div class="value">3.57%</div>
369
+ <small class="muted">IMF: 2.9% • ADB: 4.0%</small>
370
+ </div>
371
+
372
+ <div class="kpi">
373
+ <small>Unemployment (Q1 2025)</small>
374
+ <div class="value">2.20%</div>
375
+ <small class="muted">Low level</small>
376
+ </div>
377
+
378
+ <div class="kpi">
379
+ <small>FDI Registered (first 5 months)</small>
380
+ <div class="value">$18.4B</div>
381
+ <small class="muted">Up 51% y/y</small>
382
+ </div>
383
+
384
+ <div class="kpi">
385
+ <small>FDI Disbursed (first 5 months)</small>
386
+ <div class="value">$8.9B</div>
387
+ <small class="muted"></small>
388
+ </div>
389
+
390
+ <div class="kpi">
391
+ <small>Banking sector earnings (2025 est.)</small>
392
+ <div class="value">+17%</div>
393
+ <small class="muted">Credit growth supporting margins</small>
394
+ </div>
395
+
396
+ <div class="kpi">
397
+ <small>Government GDP target 2025</small>
398
+ <div class="value">8.3–8.5%</div>
399
+ <small class="muted">Ambitious vs intl forecasts</small>
400
+ </div>
401
+ </div>
402
+
403
+ <div class="charts">
404
+ <div class="chart" aria-hidden>
405
+ <div style="display:flex;justify-content:space-between;align-items:center">
406
+ <strong>GDP Growth — Q1 (2020–2025)</strong>
407
+ <small class="muted">Interactive: hover for values</small>
408
+ </div>
409
+ <svg id="gdp-sparkline" viewBox="0 0 600 220" preserveAspectRatio="none" style="aspect-ratio: 3 / 1;"></svg>
410
+ <div class="legend" id="gdp-legend"></div>
411
+ </div>
412
+
413
+ <div class="chart">
414
+ <div style="display:flex;justify-content:space-between;align-items:center">
415
+ <strong>Current Periods</strong>
416
+ <small class="muted">Click bars to filter table</small>
417
+ </div>
418
+ <svg id="period-bars" viewBox="0 0 400 220" preserveAspectRatio="none" style="aspect-ratio: 1.2 / 1;"></svg>
419
+ <div style="display:flex;gap:8px;align-items:center;justify-content:space-between">
420
+ <div class="muted">Q1, Q2, H1 comparison</div>
421
+ <div style="display:flex;gap:8px">
422
+ <button id="showAll" class="btn ghost">Reset</button>
423
+ </div>
424
+ </div>
425
+ </div>
426
+ </div>
427
+
428
+ <div style="margin-top:8px">
429
+ <h3 style="margin:0 0 8px 0">Key indicators table</h3>
430
+ <div style="overflow:auto;border-radius:8px">
431
+ <table id="indicators-table" aria-label="Key indicators">
432
+ <thead>
433
+ <tr>
434
+ <th data-key="indicator">Indicator</th>
435
+ <th data-key="value">Value</th>
436
+ <th data-key="period">Period</th>
437
+ <th data-key="source">Source</th>
438
+ </tr>
439
+ </thead>
440
+ <tbody id="indicators-body">
441
+ <!-- populated by JS -->
442
+ </tbody>
443
+ </table>
444
+ </div>
445
+ </div>
446
+ </section>
447
+
448
+ <!-- Sectoral Analysis -->
449
+ <section id="sector" class="card" data-title="Sectoral Analysis">
450
+ <div class="card-header">
451
+ <div>
452
+ <h2>Sectoral Analysis</h2>
453
+ <div class="meta">Primary growth drivers and retail performance</div>
454
+ </div>
455
+ <div class="controls">
456
+ <span class="pill">Services lead</span>
457
+ </div>
458
+ </div>
459
+
460
+ <div style="display:grid;gap:12px">
461
+ <div style="display:grid;grid-template-columns:1fr;gap:12px">
462
+ <div class="chart">
463
+ <div style="display:flex;justify-content:space-between;align-items:center">
464
+ <strong>Sector Contribution (est.)</strong>
465
+ <small class="muted">Hover segments</small>
466
+ </div>
467
+ <svg id="donut" viewBox="0 0 220 220" preserveAspectRatio="xMidYMid meet" style="aspect-ratio:1/1"></svg>
468
+ <div class="legend" id="donut-legend"></div>
469
+ </div>
470
+
471
+ <div class="card" style="padding:12px">
472
+ <strong>Retail Performance</strong>
473
+ <p class="muted">Retail sales in Q1 2025 reached 1.708 quadrillion VND (US$66.83B), up 9.9% y/y.</p>
474
+ <div style="display:flex;gap:12px;align-items:center">
475
+ <div style="flex:1">
476
+ <div class="progress" aria-hidden><i id="retail-progress" style="width:0"></i></div>
477
+ <small class="muted">Year-on-year growth vs baseline</small>
478
+ </div>
479
+ <div style="width:120px;text-align:right">
480
+ <div style="font-weight:800">+9.9%</div>
481
+ <small class="muted">Q1 2025</small>
482
+ </div>
483
+ </div>
484
+ </div>
485
+ </div>
486
+
487
+ <details>
488
+ <summary><strong>Sector notes</strong> <small class="muted" style="margin-left:auto">Expand for details</small></summary>
489
+ <div style="padding:10px">
490
+ <ul>
491
+ <li>Services: largest contributor to GDP, driven by domestic demand and tourism recovery.</li>
492
+ <li>Manufacturing: strong export performance and FDI-supported capacity.</li>
493
+ <li>Banking: projected earnings increase of ~17% due to credit growth and fee income recovery.</li>
494
+ </ul>
495
+ </div>
496
+ </details>
497
+ </div>
498
+ </section>
499
+
500
+ <!-- Risks -->
501
+ <section id="risks" class="card" data-title="Challenges and Risks">
502
+ <div class="card-header">
503
+ <div>
504
+ <h2>Challenges & Risk Factors</h2>
505
+ <div class="meta">External and domestic headwinds to monitor</div>
506
+ </div>
507
+ <div class="controls">
508
+ <button class="btn ghost" id="showRisks">Highlight</button>
509
+ </div>
510
+ </div>
511
+
512
+ <div style="display:grid;gap:8px">
513
+ <ul>
514
+ <li><strong>Global trade tensions:</strong> Pressure on export volumes and supply chains.</li>
515
+ <li><strong>US tariff policies:</strong> Increased costs for export-oriented businesses.</li>
516
+ <li><strong>Geopolitical instability:</strong> Heightened uncertainty for investment.</li>
517
+ <li><strong>FDI concentration:</strong> Risks of overdependence and inflationary pressures.</li>
518
+ <li><strong>Macroeconomic stability:</strong> Need to balance growth with fiscal prudence.</li>
519
+ </ul>
520
+
521
+ <div style="display:flex;gap:12px;align-items:center;flex-wrap:wrap">
522
+ <div style="flex:1">
523
+ <small class="muted">Risk gauge</small>
524
+ <div class="progress" aria-hidden style="margin-top:6px"><i id="risk-bar" style="width:40%"></i></div>
525
+ <small class="muted">Overall risk level: Moderate</small>
526
+ </div>
527
+
528
+ <div style="min-width:220px">
529
+ <small class="muted">Suggested mitigation</small>
530
+ <ol style="margin:6px 0 0 18px">
531
+ <li>Diversify export markets</li>
532
+ <li>Strengthen domestic demand & fiscal buffers</li>
533
+ <li>Promote resilient supply chains</li>
534
+ </ol>
535
+ </div>
536
+ </div>
537
+ </div>
538
+ </section>
539
+
540
+ <!-- Historical Comparison -->
541
+ <section id="history" class="card" data-title="Historical Comparison">
542
+ <div class="card-header">
543
+ <div>
544
+ <h2>Historical Comparison</h2>
545
+ <div class="meta">Q1 year-on-year GDP growth: 2020–2025</div>
546
+ </div>
547
+ <div class="controls">
548
+ <button class="btn ghost" id="toggleSeries">Toggle Series</button>
549
+ </div>
550
+ </div>
551
+
552
+ <div class="chart">
553
+ <div style="display:flex;justify-content:space-between;align-items:center">
554
+ <strong>Q1 GDP Growth (2020–2025)</strong>
555
+ <small class="muted">Interactive sparkline</small>
556
+ </div>
557
+ <svg id="history-line" viewBox="0 0 700 220" preserveAspectRatio="none" style="aspect-ratio: 3.2 / 1;"></svg>
558
+ <div class="legend" id="history-legend"></div>
559
+ </div>
560
+
561
+ <div style="display:flex;gap:12px;align-items:center;flex-wrap:wrap">
562
+ <div class="muted">2024 GDP: 7.1% • 2025 forecasts vary (World Bank 5.8%, ADB 6.6%, IMF 5.2%)</div>
563
+ <div style="margin-left:auto">
564
+ <button class="btn" id="savePref">Save view</button>
565
+ </div>
566
+ </div>
567
+ </section>
568
+
569
+ <!-- Outlook -->
570
+ <section id="outlook" class="card" data-title="Economic Outlook and Projections">
571
+ <div class="card-header">
572
+ <div>
573
+ <h2>Outlook & Projections</h2>
574
+ <div class="meta">Near-term prospects and policy responses</div>
575
+ </div>
576
+ <div class="controls">
577
+ <span class="chip">Cautiously optimistic</span>
578
+ </div>
579
+ </div>
580
+
581
+ <div style="display:grid;gap:10px">
582
+ <p class="muted">Vietnam's economy started 2025 strongly but faces external headwinds. Domestic fundamentals—robust FDI, low unemployment and contained inflation—support a resilient near-term outlook. The government's 8%+ target is ambitious relative to international forecasts.</p>
583
+
584
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
585
+ <div class="card" style="padding:12px">
586
+ <strong>Supporting factors</strong>
587
+ <ul>
588
+ <li>Robust FDI inflows</li>
589
+ <li>Low unemployment supporting consumption</li>
590
+ <li>Controlled inflation</li>
591
+ </ul>
592
+ </div>
593
+
594
+ <div class="card" style="padding:12px">
595
+ <strong>Risk mitigation policies</strong>
596
+ <ul>
597
+ <li>Diversify export markets</li>
598
+ <li>Strengthen domestic demand</li>
599
+ <li>Maintain macro stability and fiscal buffers</li>
600
+ </ul>
601
+ </div>
602
+ </div>
603
+
604
+ <details>
605
+ <summary><strong>Projection scenarios</strong> <small class="muted" style="margin-left:auto">Best/Worse/Base</small></summary>
606
+ <div style="padding:10px">
607
+ <table style="width:100%">
608
+ <thead><tr><th>Scenario</th><th>GDP 2025 (est.)</th><th>Key driver</th></tr></thead>
609
+ <tbody>
610
+ <tr><td>Optimistic</td><td>8.3–8.5%</td><td>Strong domestic demand + FDI</td></tr>
611
+ <tr><td>Base</td><td>6.0–7.0%</td><td>Moderate global growth</td></tr>
612
+ <tr><td>Pessimistic</td><td>4.5–5.5%</td><td>Severe trade disruptions</td></tr>
613
+ </tbody>
614
+ </table>
615
+ </div>
616
+ </details>
617
+ </div>
618
+ </section>
619
+
620
+ <!-- References -->
621
+ <section id="references" class="card" data-title="References and Citations">
622
+ <div class="card-header">
623
+ <div>
624
+ <h2>Sources & Citations</h2>
625
+ <div class="meta">Primary sources used for data and context</div>
626
+ </div>
627
+ <div class="controls">
628
+ <button class="btn" id="copyCite">Copy citations</button>
629
+ </div>
630
+ </div>
631
+
632
+ <div style="display:grid;gap:8px">
633
+ <ol id="sources-list" class="muted">
634
+ <li>Trading Economics - Vietnam GDP Annual Growth Rate — https://tradingeconomics.com/vietnam/gdp-growth-annual</li>
635
+ <li>International Monetary Fund - Vietnam Country Profile — https://www.imf.org/en/Countries/VNM</li>
636
+ <li>World Economics - Vietnam GDP Estimates — https://www.worldeconomics.com/GDP/Vietnam.gdp</li>
637
+ <li>Government of Vietnam - General Statistics Office — https://www.gso.gov.vn/en/</li>
638
+ <li>Wikipedia - Economy of Vietnam — https://en.wikipedia.org/wiki/Economy_of_Vietnam</li>
639
+ <li>And others (ADB, FocusEconomics, Ministry of Planning and Investment, etc.)</li>
640
+ </ol>
641
+ <small class="muted">Click a source to open in a new tab.</small>
642
+ </div>
643
+ </section>
644
+
645
+ <footer>
646
+ <div>Prepared: Vietnam Economic Growth Report 2025 • Interactive Dashboard</div>
647
+ <div class="muted">Data snapshot updated: June 2025</div>
648
+ </footer>
649
+ </main>
650
+ </div>
651
+ </div>
652
+
653
+ <script>
654
+ // Replace feather icons
655
+ feather.replace();
656
+
657
+ // Data model
658
+ const data = {
659
+ gdpQ1: { years: [2020,2021,2022,2023,2024,2025], values: [3.21,4.85,5.42,3.46,5.98,6.93] },
660
+ periodCompare: [
661
+ { label:'Q1 2025', value:6.9, meta:'Actual' },
662
+ { label:'Q2 2025', value:7.96, meta:'Actual' },
663
+ { label:'H1 2025', value:7.52, meta:'Actual' },
664
+ ],
665
+ sectors: [
666
+ { name:'Services', value:45, color:'#06b6d4' },
667
+ { name:'Manufacturing', value:30, color:'#0f766e' },
668
+ { name:'Export Industries', value:15, color:'#f59e0b' },
669
+ { name:'Other', value:10, color:'#7c3aed' }
670
+ ],
671
+ indicators: [
672
+ { indicator:'GDP Q1 2025 (y/y)', value:'6.90%', period:'Q1 2025', source:'GSO' },
673
+ { indicator:'GDP Q2 2025 (y/y)', value:'7.96%', period:'Q2 2025', source:'GSO' },
674
+ { indicator:'H1 2025 GDP (y/y)', value:'7.52%', period:'H1 2025', source:'GSO' },
675
+ { indicator:'Inflation (May 2025)', value:'3.24%', period:'May 2025', source:'GSO/IMF' },
676
+ { indicator:'Inflation (Jun 2025)', value:'3.57%', period:'Jun 2025', source:'GSO/IMF' },
677
+ { indicator:'Unemployment (Q1 2025)', value:'2.20%', period:'Q1 2025', source:'GSO' },
678
+ { indicator:'FDI Registered (first 5 months 2025)', value:'$18.4B', period:'Jan–May 2025', source:'MPI' },
679
+ { indicator:'FDI Disbursed (first 5 months 2025)', value:'$8.9B', period:'Jan–May 2025', source:'MPI' },
680
+ { indicator:'Retail sales Q1 2025', value:'1.708 quadrillion VND', period:'Q1 2025', source:'GSO' },
681
+ { indicator:'Banking sector earnings (2025 est.)', value:'+17%', period:'2025 estimate', source:'Industry' },
682
+ ]
683
+ };
684
+
685
+ // Utility: create elements
686
+ function el(tag, attrs={}, children=[]){
687
+ const e = document.createElement(tag);
688
+ for(const k in attrs){
689
+ if(k==='class') e.className = attrs[k];
690
+ else if(k==='html') e.innerHTML = attrs[k];
691
+ else e.setAttribute(k, attrs[k]);
692
+ }
693
+ (Array.isArray(children)?children:[children]).forEach(c=>{ if(c) e.appendChild(typeof c==='string'?document.createTextNode(c):c) });
694
+ return e;
695
+ }
696
+
697
+ // Populate indicators table
698
+ const tbody = document.getElementById('indicators-body');
699
+ function renderIndicators(list){
700
+ tbody.innerHTML = '';
701
+ list.forEach(row=>{
702
+ const tr = el('tr');
703
+ tr.appendChild(el('td',{},[row.indicator]));
704
+ tr.appendChild(el('td',{},[row.value]));
705
+ tr.appendChild(el('td',{},[row.period]));
706
+ tr.appendChild(el('td',{},[row.source]));
707
+ tbody.appendChild(tr);
708
+ });
709
+ }
710
+ renderIndicators(data.indicators);
711
+
712
+ // Sorting for table
713
+ document.querySelectorAll('#indicators-table th').forEach(th=>{
714
+ th.addEventListener('click',()=>{
715
+ const key = th.getAttribute('data-key');
716
+ const mapping = {indicator:'indicator', value:'value', period:'period', source:'source'};
717
+ const dir = th.dataset.dir === 'asc' ? 'desc' : 'asc';
718
+ th.dataset.dir = dir;
719
+ const sorted = [...data.indicators].sort((a,b)=>{
720
+ let A = a[mapping[key]]; let B = b[mapping[key]];
721
+ // normalize numbers
722
+ if(key==='value'){
723
+ const na = parseFloat(A.replace(/[^0-9\.\-]+/g,'')) || 0;
724
+ const nb = parseFloat(B.replace(/[^0-9\.\-]+/g,'')) || 0;
725
+ if(dir==='asc') return na-nb;
726
+ return nb-na;
727
+ }
728
+ if(dir==='asc') return A.toString().localeCompare(B.toString());
729
+ return B.toString().localeCompare(A.toString());
730
+ });
731
+ renderIndicators(sorted);
732
+ });
733
+ });
734
+
735
+ // Export CSV
736
+ document.getElementById('exportCSV').addEventListener('click', ()=>{
737
+ const rows = [['Indicator','Value','Period','Source'], ...data.indicators.map(r=>[r.indicator,r.value,r.period,r.source])];
738
+ const csv = rows.map(r=>r.map(cell=>`"${String(cell).replace(/"/g,'""')}"`).join(',')).join('\n');
739
+ const blob = new Blob([csv], {type:'text/csv;charset=utf-8;'});
740
+ const url = URL.createObjectURL(blob);
741
+ const a = document.createElement('a');
742
+ a.href = url; a.download = 'vietnam_indicators_2025.csv'; document.body.appendChild(a); a.click();
743
+ URL.revokeObjectURL(url); a.remove();
744
+ });
745
+
746
+ // Copy executive summary to clipboard
747
+ document.getElementById('copyExec').addEventListener('click', async ()=>{
748
+ const text = document.getElementById('exec-text').innerText;
749
+ await navigator.clipboard.writeText(text);
750
+ flash('Executive summary copied to clipboard');
751
+ });
752
+
753
+ // Copy citations
754
+ document.getElementById('copyCite').addEventListener('click', async ()=>{
755
+ const sources = Array.from(document.querySelectorAll('#sources-list li')).map(li=>li.innerText).join('\n');
756
+ await navigator.clipboard.writeText(sources);
757
+ flash('Citations copied to clipboard');
758
+ });
759
+
760
+ // Quick export button: copies a compact summary + key KPIs
761
+ document.getElementById('copySummary').addEventListener('click', async ()=>{
762
+ const exec = document.getElementById('exec-text').innerText;
763
+ const kpis = data.indicators.slice(0,6).map(i=>`${i.indicator}: ${i.value}`).join('\n');
764
+ const payload = exec + "\n\nKey indicators:\n" + kpis;
765
+ await navigator.clipboard.writeText(payload);
766
+ flash('Report summary copied to clipboard');
767
+ });
768
+
769
+ // Flash notification
770
+ function flash(msg){
771
+ const f = el('div',{class:'btn', style:'position:fixed;right:20px;bottom:20px;z-index:999;pointer-events:auto'},[msg]);
772
+ document.body.appendChild(f);
773
+ setTimeout(()=>f.style.transform='translateY(-8px)',50);
774
+ setTimeout(()=>f.remove(),2200);
775
+ }
776
+
777
+ // Draw simple SVG sparkline for GDP Q1
778
+ function drawSparkline(svgEl, series, opts={}){
779
+ const w = svgEl.viewBox.baseVal.width || 600;
780
+ const h = svgEl.viewBox.baseVal.height || 220;
781
+ const pad = 30;
782
+ const values = series.values;
783
+ const years = series.years;
784
+ const max = Math.max(...values) * 1.12;
785
+ const min = Math.min(...values) * 0.9;
786
+ const x = (i)=> pad + (i/(values.length-1))*(w - pad*2);
787
+ const y = (v)=> pad + ((max - v)/(max-min))*(h - pad*2);
788
+
789
+ // clear
790
+ svgEl.innerHTML = '';
791
+ // grid lines
792
+ for(let i=0;i<5;i++){
793
+ const ly = pad + i*((h-pad*2)/4);
794
+ const line = document.createElementNS('http://www.w3.org/2000/svg','line');
795
+ line.setAttribute('x1',pad); line.setAttribute('x2',w-pad);
796
+ line.setAttribute('y1',ly); line.setAttribute('y2',ly);
797
+ line.setAttribute('stroke','#eef2f7'); line.setAttribute('stroke-width','1');
798
+ svgEl.appendChild(line);
799
+ }
800
+
801
+ // polyline
802
+ const pts = values.map((v,i)=>`${x(i)},${y(v)}`).join(' ');
803
+ const poly = document.createElementNS('http://www.w3.org/2000/svg','polyline');
804
+ poly.setAttribute('points', pts);
805
+ poly.setAttribute('fill','none');
806
+ poly.setAttribute('stroke','#0f766e');
807
+ poly.setAttribute('stroke-width','3');
808
+ poly.setAttribute('stroke-linecap','round');
809
+ poly.setAttribute('stroke-linejoin','round');
810
+ svgEl.appendChild(poly);
811
+
812
+ // area fill
813
+ const areaPts = pts + ` ${w-pad},${h-pad} ${pad},${h-pad}`;
814
+ const area = document.createElementNS('http://www.w3.org/2000/svg','path');
815
+ const d = `M ${values.map((v,i)=>`${x(i)} ${y(v)}`).join(' L ')} L ${w-pad} ${h-pad} L ${pad} ${h-pad} Z`;
816
+ area.setAttribute('d', d);
817
+ area.setAttribute('fill','rgba(6,182,212,0.08)');
818
+ svgEl.appendChild(area);
819
+
820
+ // points + interactive tooltip
821
+ const tooltip = el('div',{style:'position:absolute;display:none;padding:8px;background:#fff;border-radius:8px;box-shadow:var(--shadow);font-size:0.9rem;color:#0f172a;'});
822
+ document.body.appendChild(tooltip);
823
+
824
+ values.forEach((v,i)=>{
825
+ const cx = x(i), cy = y(v);
826
+ const c = document.createElementNS('http://www.w3.org/2000/svg','circle');
827
+ c.setAttribute('cx',cx); c.setAttribute('cy',cy); c.setAttribute('r',4);
828
+ c.setAttribute('fill','#06b6d4'); c.setAttribute('stroke','#fff'); c.setAttribute('stroke-width','1.5');
829
+ c.style.cursor='pointer';
830
+ svgEl.appendChild(c);
831
+
832
+ c.addEventListener('mouseenter',(ev)=>{
833
+ tooltip.style.display='block';
834
+ tooltip.innerHTML = `<strong>${years[i]}</strong><div class="muted">${v}%</div>`;
835
+ });
836
+ c.addEventListener('mouseleave',()=> tooltip.style.display='none');
837
+ c.addEventListener('mousemove',(ev)=>{
838
+ tooltip.style.left = (ev.pageX + 12) + 'px';
839
+ tooltip.style.top = (ev.pageY - 18) + 'px';
840
+ });
841
+ });
842
+
843
+ // legend
844
+ const lg = document.getElementById('gdp-legend');
845
+ lg.innerHTML = `<div style="display:flex;gap:8px;align-items:center"><span style="width:12px;height:12px;background:#0f766e;border-radius:4px;display:inline-block"></span><small class="muted">Q1 Growth</small></div>`;
846
+ }
847
+
848
+ // Draw period bars
849
+ function drawPeriodBars(svgEl, series){
850
+ const w = svgEl.viewBox.baseVal.width || 400;
851
+ const h = svgEl.viewBox.baseVal.height || 220;
852
+ const pad = 30;
853
+ const barW = (w - pad*2) / (series.length * 1.8);
854
+ const max = Math.max(...series.map(s=>s.value))*1.15;
855
+ svgEl.innerHTML = '';
856
+
857
+ series.forEach((s,i)=>{
858
+ const bx = pad + i*(barW*1.8);
859
+ const bh = ((s.value)/max)*(h - pad*2);
860
+ const by = h - pad - bh;
861
+
862
+ const rect = document.createElementNS('http://www.w3.org/2000/svg','rect');
863
+ rect.setAttribute('x',bx); rect.setAttribute('y',by);
864
+ rect.setAttribute('width',barW); rect.setAttribute('height',bh);
865
+ rect.setAttribute('fill','#06b6d4'); rect.setAttribute('rx',8);
866
+ rect.style.cursor='pointer';
867
+ svgEl.appendChild(rect);
868
+
869
+ // label
870
+ const lbl = document.createElementNS('http://www.w3.org/2000/svg','text');
871
+ lbl.setAttribute('x',bx + barW/2); lbl.setAttribute('y',h - pad + 18);
872
+ lbl.setAttribute('text-anchor','middle'); lbl.setAttribute('fill','#394155'); lbl.setAttribute('font-size','12');
873
+ lbl.textContent = s.label;
874
+ svgEl.appendChild(lbl);
875
+
876
+ // value text
877
+ const vt = document.createElementNS('http://www.w3.org/2000/svg','text');
878
+ vt.setAttribute('x',bx + barW/2); vt.setAttribute('y',by - 8);
879
+ vt.setAttribute('text-anchor','middle'); vt.setAttribute('fill','#0f172a'); vt.setAttribute('font-weight','700');
880
+ vt.textContent = s.value + '%';
881
+ svgEl.appendChild(vt);
882
+
883
+ rect.addEventListener('click', ()=> {
884
+ // filter table to show matching period
885
+ const filtered = data.indicators.filter(it => it.period.includes(s.label.split(' ')[0]) || it.indicator.includes(s.label.split(' ')[0]));
886
+ if(filtered.length) renderIndicators(filtered);
887
+ flash('Filtered indicators by ' + s.label);
888
+ });
889
+
890
+ });
891
+
892
+ // reset button
893
+ document.getElementById('showAll').addEventListener('click', ()=> {
894
+ renderIndicators(data.indicators);
895
+ });
896
+ }
897
+
898
+ // Draw donut chart
899
+ function drawDonut(svgEl, sectors){
900
+ const w = svgEl.viewBox.baseVal.width || 220;
901
+ const h = svgEl.viewBox.baseVal.height || 220;
902
+ const cx = w/2, cy = h/2;
903
+ const radius = Math.min(w,h)/2 - 16;
904
+ const total = sectors.reduce((s,c)=>s+c.value,0);
905
+ let angle = -Math.PI/2;
906
+ svgEl.innerHTML = '';
907
+
908
+ sectors.forEach((s,i)=>{
909
+ const slice = (s.value/total) * Math.PI*2;
910
+ const x1 = cx + Math.cos(angle) * (radius);
911
+ const y1 = cy + Math.sin(angle) * (radius);
912
+ const angle2 = angle + slice;
913
+ const x2 = cx + Math.cos(angle2) * (radius);
914
+ const y2 = cy + Math.sin(angle2) * (radius);
915
+ const large = slice > Math.PI ? 1 : 0;
916
+ const path = document.createElementNS('http://www.w3.org/2000/svg','path');
917
+ const d = `M ${cx} ${cy} L ${x1} ${y1} A ${radius} ${radius} 0 ${large} 1 ${x2} ${y2} Z`;
918
+ path.setAttribute('d',d);
919
+ path.setAttribute('fill', s.color);
920
+ path.style.cursor='pointer';
921
+ svgEl.appendChild(path);
922
+
923
+ // add small interaction
924
+ path.addEventListener('mouseenter', (ev)=>{
925
+ const tip = document.getElementById('donut-tip') || null;
926
+ showTooltip(ev.pageX, ev.pageY, `${s.name}: ${s.value}%`);
927
+ });
928
+ path.addEventListener('mouseleave', hideTooltip);
929
+
930
+ angle += slice;
931
+ });
932
+
933
+ // hole
934
+ const hole = document.createElementNS('http://www.w3.org/2000/svg','circle');
935
+ hole.setAttribute('cx',cx); hole.setAttribute('cy',cy); hole.setAttribute('r',radius*0.5);
936
+ hole.setAttribute('fill','#fff');
937
+ svgEl.appendChild(hole);
938
+
939
+ // legend
940
+ const legend = document.getElementById('donut-legend');
941
+ legend.innerHTML = '';
942
+ sectors.forEach(s=> {
943
+ const item = el('div',{},[
944
+ el('span',{style:`width:12px;height:12px;background:${s.color};display:inline-block;border-radius:4px;margin-right:6px`}),
945
+ el('small',{class:'muted'},`${s.name} ${s.value}%`)
946
+ ]);
947
+ legend.appendChild(item);
948
+ });
949
+ }
950
+
951
+ // Simple global tooltip
952
+ const globalTip = el('div',{id:'globalTip', style:'position:fixed;display:none;padding:8px;background:white;border-radius:8px;box-shadow:var(--shadow);font-size:0.9rem;pointer-events:none;z-index:999'});
953
+ document.body.appendChild(globalTip);
954
+ function showTooltip(x,y,html){
955
+ globalTip.style.left = (x+12)+'px';
956
+ globalTip.style.top = (y-28)+'px';
957
+ globalTip.innerHTML = html;
958
+ globalTip.style.display = 'block';
959
+ }
960
+ function hideTooltip(){ globalTip.style.display = 'none'; }
961
+
962
+ // Draw history line
963
+ function drawHistory(svgEl, series){
964
+ const w = svgEl.viewBox.baseVal.width || 700;
965
+ const h = svgEl.viewBox.baseVal.height || 220;
966
+ const pad = 40;
967
+ const values = series.values;
968
+ const years = series.years;
969
+ const max = Math.max(...values) * 1.12;
970
+ const min = Math.min(...values) * 0.9;
971
+ const x = (i)=> pad + (i/(values.length-1))*(w - pad*2);
972
+ const y = (v)=> pad + ((max - v)/(max-min))*(h - pad*2);
973
+
974
+ svgEl.innerHTML = '';
975
+ // axes labels
976
+ years.forEach((yr,i)=>{
977
+ const tx = document.createElementNS('http://www.w3.org/2000/svg','text');
978
+ tx.setAttribute('x', x(i));
979
+ tx.setAttribute('y', h - 10);
980
+ tx.setAttribute('text-anchor','middle');
981
+ tx.setAttribute('fill','#64748b');
982
+ tx.setAttribute('font-size','12');
983
+ tx.textContent = yr;
984
+ svgEl.appendChild(tx);
985
+ });
986
+
987
+ // polyline
988
+ const pts = values.map((v,i)=>`${x(i)},${y(v)}`).join(' ');
989
+ const poly = document.createElementNS('http://www.w3.org/2000/svg','polyline');
990
+ poly.setAttribute('points', pts);
991
+ poly.setAttribute('fill','none');
992
+ poly.setAttribute('stroke','#7c3aed');
993
+ poly.setAttribute('stroke-width','3');
994
+ poly.setAttribute('stroke-linecap','round');
995
+ svgEl.appendChild(poly);
996
+
997
+ // points + interactions
998
+ values.forEach((v,i)=>{
999
+ const cx = x(i), cy = y(v);
1000
+ const c = document.createElementNS('http://www.w3.org/2000/svg','circle');
1001
+ c.setAttribute('cx',cx); c.setAttribute('cy',cy); c.setAttribute('r',5);
1002
+ c.setAttribute('fill','#7c3aed'); c.setAttribute('stroke','#fff'); c.setAttribute('stroke-width','1.5');
1003
+ svgEl.appendChild(c);
1004
+ c.addEventListener('mouseenter', (ev)=> showTooltip(ev.pageX, ev.pageY, `<strong>${years[i]}</strong><div class="muted">${v}%</div>`));
1005
+ c.addEventListener('mouseleave', hideTooltip);
1006
+ });
1007
+
1008
+ const lg = document.getElementById('history-legend');
1009
+ lg.innerHTML = `<div style="display:flex;gap:8px;align-items:center"><span style="width:12px;height:12px;background:#7c3aed;border-radius:4px;display:inline-block"></span><small class="muted">Q1 y/y</small></div>`;
1010
+ }
1011
+
1012
+ // Initialize visuals
1013
+ drawSparkline(document.getElementById('gdp-sparkline'), data.gdpQ1);
1014
+ drawPeriodBars(document.getElementById('period-bars'), data.periodCompare);
1015
+ drawDonut(document.getElementById('donut'), data.sectors);
1016
+ drawHistory(document.getElementById('history-line'), data.gdpQ1);
1017
+
1018
+ // Retail progress fill
1019
+ document.getElementById('retail-progress').style.width = '36%';
1020
+
1021
+ // Interactive controls
1022
+ document.getElementById('toggleForecasts').addEventListener('click', ()=>{
1023
+ const body = document.getElementById('indicators-body');
1024
+ // toggle show forecasts rows (simulate)
1025
+ if(body.dataset.showing==='forecasts'){
1026
+ renderIndicators(data.indicators);
1027
+ body.dataset.showing='';
1028
+ } else {
1029
+ const extra = [
1030
+ { indicator:'World Bank 2025 GDP forecast', value:'5.8%', period:'2025 forecast', source:'World Bank' },
1031
+ { indicator:'ADB 2025 GDP forecast', value:'6.6%', period:'2025 forecast', source:'ADB' },
1032
+ { indicator:'IMF 2025 GDP forecast', value:'5.2%', period:'2025 forecast', source:'IMF' },
1033
+ ];
1034
+ renderIndicators([...data.indicators, ...extra]);
1035
+ body.dataset.showing='forecasts';
1036
+ }
1037
+ });
1038
+
1039
+ // TOC smooth scroll & active highlight via IntersectionObserver
1040
+ document.querySelectorAll('.toc-link').forEach(a=>{
1041
+ a.addEventListener('click', (ev)=>{
1042
+ ev.preventDefault();
1043
+ const id = a.dataset.target;
1044
+ document.getElementById(id).scrollIntoView({behavior:'smooth', block:'start'});
1045
+ });
1046
+ });
1047
+
1048
+ const sections = document.querySelectorAll('main.report section');
1049
+ const tocLinks = document.querySelectorAll('.toc-link');
1050
+ const obs = new IntersectionObserver((entries)=>{
1051
+ entries.forEach(entry=>{
1052
+ if(entry.isIntersecting){
1053
+ const id = entry.target.id;
1054
+ tocLinks.forEach(a=>a.classList.toggle('active', a.dataset.target===id));
1055
+ }
1056
+ });
1057
+ }, {root: null, rootMargin: '-30% 0px -55% 0px', threshold: 0});
1058
+ sections.forEach(s=>obs.observe(s));
1059
+
1060
+ // Reading progress bar
1061
+ const progressBar = document.getElementById('progress-bar');
1062
+ window.addEventListener('scroll', ()=>{
1063
+ const doc = document.documentElement;
1064
+ const total = doc.scrollHeight - doc.clientHeight;
1065
+ const pct = (window.scrollY / total) * 100;
1066
+ progressBar.style.width = pct + '%';
1067
+ }, {passive:true});
1068
+
1069
+ // Search/filter across sections (highlights matching sections & table)
1070
+ const globalSearch = document.getElementById('global-search');
1071
+ globalSearch.addEventListener('input', (e)=>{
1072
+ const q = e.target.value.trim().toLowerCase();
1073
+ if(!q){ sections.forEach(s=>s.style.display=''); renderIndicators(data.indicators); return; }
1074
+ sections.forEach(s=>{
1075
+ const txt = s.innerText.toLowerCase();
1076
+ s.style.display = txt.includes(q) ? '' : 'none';
1077
+ });
1078
+ // filter indicators table
1079
+ const filtered = data.indicators.filter(i=> (i.indicator + ' ' + i.value + ' ' + i.period + ' ' + i.source).toLowerCase().includes(q));
1080
+ renderIndicators(filtered);
1081
+ });
1082
+
1083
+ // Save view preferences in localStorage
1084
+ document.getElementById('savePref').addEventListener('click', ()=>{
1085
+ const prefs = {savedAt: Date.now(), view:'default'};
1086
+ localStorage.setItem('reportPrefs', JSON.stringify(prefs));
1087
+ document.getElementById('savedFilters').innerText = 'Saved';
1088
+ flash('View saved');
1089
+ });
1090
+
1091
+ // Restore saved prefs label
1092
+ const prefs = JSON.parse(localStorage.getItem('reportPrefs') || 'null');
1093
+ if(prefs) document.getElementById('savedFilters').innerText = 'Saved';
1094
+
1095
+ // Highlight risks on button click
1096
+ document.getElementById('showRisks').addEventListener('click', ()=>{
1097
+ const sec = document.getElementById('risks');
1098
+ sec.animate([{boxShadow:'none'},{boxShadow:'0 0 0 6px rgba(239,68,68,0.08)'}],{duration:600,iterations:1});
1099
+ sec.scrollIntoView({behavior:'smooth'});
1100
+ flash('Risks highlighted');
1101
+ });
1102
+
1103
+ // Tooltip for donut implemented via global show/hide
1104
+ document.addEventListener('mousemove', ()=>{ /* keep available for chart events */ });
1105
+
1106
+ // Small Intersection observer to animate KPI values (count up)
1107
+ const kpiObserver = new IntersectionObserver((entries)=>{
1108
+ entries.forEach(entry=>{
1109
+ if(entry.isIntersecting){
1110
+ const els = entry.target.querySelectorAll('.kpi .value');
1111
+ els.forEach(v=>{
1112
+ if(v.dataset.animated) return;
1113
+ v.dataset.animated = '1';
1114
+ // animate numbers if numeric
1115
+ const num = parseFloat(v.innerText);
1116
+ if(!isNaN(num)) {
1117
+ const start = Math.max(0, num*0.6);
1118
+ const end = num;
1119
+ let cur = start;
1120
+ const diff = end - start;
1121
+ const dur = 800;
1122
+ const step = 16;
1123
+ const steps = dur/step;
1124
+ let i=0;
1125
+ const iv = setInterval(()=>{
1126
+ i++;
1127
+ const val = start + (diff*(i/steps));
1128
+ v.innerText = (Math.round(val*100)/100) + (v.innerText.includes('%')? '%' : '');
1129
+ if(i>=steps) clearInterval(iv);
1130
+ }, step);
1131
+ }
1132
+ });
1133
+ kpiObserver.unobserve(entry.target);
1134
+ }
1135
+ });
1136
+ }, {threshold:0.2});
1137
+ document.querySelectorAll('section.card').forEach(s=>kpiObserver.observe(s));
1138
+
1139
+ // Table row click -> show contextual section
1140
+ tbody.addEventListener('click', (ev)=>{
1141
+ const tr = ev.target.closest('tr');
1142
+ if(!tr) return;
1143
+ const indicator = tr.children[0].innerText;
1144
+ // simple mapping
1145
+ if(indicator.toLowerCase().includes('fdi')) document.getElementById('sector').scrollIntoView({behavior:'smooth'});
1146
+ else if(indicator.toLowerCase().includes('inflation')) document.getElementById('risks').scrollIntoView({behavior:'smooth'});
1147
+ else document.getElementById('indicators').scrollIntoView({behavior:'smooth'});
1148
+ });
1149
+
1150
+ // Misc: keyboard shortcut "/" focuses search
1151
+ window.addEventListener('keydown', (e)=>{
1152
+ if(e.key === '/') { e.preventDefault(); globalSearch.focus(); }
1153
+ });
1154
+
1155
+ // Small performance: lazy-load heavy visuals on intersection
1156
+ const lazyObs = new IntersectionObserver((entries, observer)=>{
1157
+ entries.forEach(ent=>{
1158
+ if(ent.isIntersecting){
1159
+ // On first view, animate period bars slightly
1160
+ const bars = document.getElementById('period-bars');
1161
+ bars.querySelectorAll('rect').forEach((r,i)=>{
1162
+ r.style.transformOrigin = 'center bottom';
1163
+ r.animate([{transform:'scaleY(0)'},{transform:'scaleY(1)'}], {duration:400 + i*90, easing:'cubic-bezier(.16,1,.3,1)', fill:'forwards'});
1164
+ });
1165
+ observer.unobserve(ent.target);
1166
+ }
1167
+ });
1168
+ }, {root:null, rootMargin:'0px', threshold:0.18});
1169
+ lazyObs.observe(document.getElementById('indicators'));
1170
+
1171
+ // Accessibility: add target=_blank for source links (in references)
1172
+ document.querySelectorAll('#sources-list li').forEach(li=>{
1173
+ const text = li.innerText;
1174
+ const m = text.match(/https?:\/\/\S+/);
1175
+ if(m){
1176
+ const a = document.createElement('a'); a.href = m[0]; a.target='_blank'; a.rel='noopener';
1177
+ a.innerText = m[0];
1178
+ li.innerHTML = li.innerHTML.replace(m[0], '');
1179
+ li.appendChild(a);
1180
+ }
1181
+ });
1182
+
1183
+ // Small responsive: toggle filters panel for mobile (just scroll to sector)
1184
+ document.getElementById('toggleFilters').addEventListener('click', ()=> {
1185
+ document.getElementById('sector').scrollIntoView({behavior:'smooth'});
1186
+ });
1187
+
1188
+ // Provide clean initial focus
1189
+ document.getElementById('app').focus();
1190
+
1191
+ </script>
1192
+ </body>
1193
+ </html>