Spaces:
Running
Running
Upload index.html with huggingface_hub
Browse files- index.html +1459 -18
index.html
CHANGED
|
@@ -1,19 +1,1460 @@
|
|
| 1 |
<!doctype html>
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 Presentation</title>
|
| 7 |
+
|
| 8 |
+
<!-- Font Awesome for icons -->
|
| 9 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" integrity="sha512-yH6fK7Y4W5r8Xwq5mZ4mN6kJm3rQ0y7s8gqO2x1Q7s6Rz5yG3qk9qE5a7Y6vN2uB4t8vZ3F3p2j9h6w=="
|
| 10 |
+
crossorigin="anonymous" referrerpolicy="no-referrer" />
|
| 11 |
+
|
| 12 |
+
<style>
|
| 13 |
+
:root{
|
| 14 |
+
--bg: #f7f9fb;
|
| 15 |
+
--card: #ffffff;
|
| 16 |
+
--muted: #6b7280;
|
| 17 |
+
--accent: linear-gradient(135deg,#0ea5a2 0%,#06b6d4 100%);
|
| 18 |
+
--primary: #0b7285;
|
| 19 |
+
--success: #16a34a;
|
| 20 |
+
--danger: #ef4444;
|
| 21 |
+
--glass: rgba(255,255,255,0.6);
|
| 22 |
+
--radius: 14px;
|
| 23 |
+
--max-width: 1200px;
|
| 24 |
+
--shadow: 0 6px 24px rgba(12,18,31,0.08);
|
| 25 |
+
--kb: 14px;
|
| 26 |
+
--font-sans: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
/* Theme toggling */
|
| 30 |
+
:root[data-theme='dark']{
|
| 31 |
+
--bg: #071022;
|
| 32 |
+
--card: #0b1623;
|
| 33 |
+
--muted: #9aa6b2;
|
| 34 |
+
--primary: #2ad4e0;
|
| 35 |
+
--glass: rgba(255,255,255,0.03);
|
| 36 |
+
color-scheme: dark;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
/* Global styles */
|
| 40 |
+
*{box-sizing:border-box}
|
| 41 |
+
html,body{height:100%;}
|
| 42 |
+
body{
|
| 43 |
+
margin:0;
|
| 44 |
+
background: radial-gradient(1200px 400px at 10% 10%, rgba(6,182,212,0.06), transparent), var(--bg);
|
| 45 |
+
font-family:var(--font-sans);
|
| 46 |
+
color:var(--muted);
|
| 47 |
+
-webkit-font-smoothing:antialiased;
|
| 48 |
+
-moz-osx-font-smoothing:grayscale;
|
| 49 |
+
line-height:1.45;
|
| 50 |
+
font-size:clamp(14px, 1.4vw, 16px);
|
| 51 |
+
padding:24px;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
.container{
|
| 55 |
+
max-width:var(--max-width);
|
| 56 |
+
margin:0 auto;
|
| 57 |
+
display:grid;
|
| 58 |
+
grid-template-columns: 1fr;
|
| 59 |
+
gap:20px;
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
header.site-header{
|
| 63 |
+
display:flex;
|
| 64 |
+
gap:12px;
|
| 65 |
+
align-items:center;
|
| 66 |
+
justify-content:space-between;
|
| 67 |
+
}
|
| 68 |
+
.brand{
|
| 69 |
+
display:flex;
|
| 70 |
+
gap:12px;
|
| 71 |
+
align-items:center;
|
| 72 |
+
}
|
| 73 |
+
.logo{
|
| 74 |
+
width:56px;
|
| 75 |
+
height:56px;
|
| 76 |
+
border-radius:12px;
|
| 77 |
+
background:var(--accent);
|
| 78 |
+
display:flex;
|
| 79 |
+
align-items:center;
|
| 80 |
+
justify-content:center;
|
| 81 |
+
color:white;
|
| 82 |
+
font-weight:700;
|
| 83 |
+
box-shadow:var(--shadow);
|
| 84 |
+
flex:0 0 56px;
|
| 85 |
+
}
|
| 86 |
+
h1{
|
| 87 |
+
margin:0;
|
| 88 |
+
color:var(--primary);
|
| 89 |
+
font-size:clamp(18px,2.6vw,22px);
|
| 90 |
+
}
|
| 91 |
+
p.lead{
|
| 92 |
+
margin:0;
|
| 93 |
+
color:var(--muted);
|
| 94 |
+
font-size:clamp(12px,1.4vw,14px);
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
.controls{
|
| 98 |
+
display:flex;
|
| 99 |
+
gap:10px;
|
| 100 |
+
align-items:center;
|
| 101 |
+
}
|
| 102 |
+
.btn{
|
| 103 |
+
background:var(--card);
|
| 104 |
+
border-radius:10px;
|
| 105 |
+
padding:10px 12px;
|
| 106 |
+
box-shadow:var(--shadow);
|
| 107 |
+
border:1px solid rgba(0,0,0,0.04);
|
| 108 |
+
color:var(--muted);
|
| 109 |
+
cursor:pointer;
|
| 110 |
+
display:inline-flex;
|
| 111 |
+
align-items:center;
|
| 112 |
+
gap:8px;
|
| 113 |
+
font-size:13px;
|
| 114 |
+
}
|
| 115 |
+
.btn.icon{padding:8px}
|
| 116 |
+
.btn.primary{
|
| 117 |
+
background:linear-gradient(180deg, rgba(6,182,212,0.14), rgba(6,182,212,0.04));
|
| 118 |
+
color:var(--primary);
|
| 119 |
+
border:1px solid rgba(6,182,212,0.18);
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
/* Layout for main content with TOC sidebar */
|
| 123 |
+
.layout{
|
| 124 |
+
display:grid;
|
| 125 |
+
gap:18px;
|
| 126 |
+
grid-template-columns: 1fr;
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
/* TOC */
|
| 130 |
+
nav.toc{
|
| 131 |
+
position:sticky;
|
| 132 |
+
top:24px;
|
| 133 |
+
align-self:start;
|
| 134 |
+
background:var(--card);
|
| 135 |
+
border-radius:12px;
|
| 136 |
+
padding:12px;
|
| 137 |
+
box-shadow:var(--shadow);
|
| 138 |
+
display:flex;
|
| 139 |
+
flex-direction:column;
|
| 140 |
+
gap:10px;
|
| 141 |
+
}
|
| 142 |
+
.toc .search{
|
| 143 |
+
display:flex;
|
| 144 |
+
gap:8px;
|
| 145 |
+
align-items:center;
|
| 146 |
+
}
|
| 147 |
+
.toc input[type="search"]{
|
| 148 |
+
border:0;
|
| 149 |
+
outline:0;
|
| 150 |
+
background:var(--glass);
|
| 151 |
+
padding:8px 10px;
|
| 152 |
+
border-radius:8px;
|
| 153 |
+
width:100%;
|
| 154 |
+
color:var(--muted);
|
| 155 |
+
font-size:13px;
|
| 156 |
+
}
|
| 157 |
+
.toc .links{
|
| 158 |
+
display:flex;
|
| 159 |
+
flex-direction:column;
|
| 160 |
+
gap:6px;
|
| 161 |
+
margin-top:6px;
|
| 162 |
+
}
|
| 163 |
+
.toc a{
|
| 164 |
+
text-decoration:none;
|
| 165 |
+
color:var(--muted);
|
| 166 |
+
padding:8px;
|
| 167 |
+
border-radius:8px;
|
| 168 |
+
font-size:13px;
|
| 169 |
+
display:flex;
|
| 170 |
+
gap:8px;
|
| 171 |
+
align-items:center;
|
| 172 |
+
}
|
| 173 |
+
.toc a.active{
|
| 174 |
+
background:linear-gradient(90deg, rgba(6,182,212,0.08), rgba(6,182,212,0.02));
|
| 175 |
+
color:var(--primary);
|
| 176 |
+
font-weight:600;
|
| 177 |
+
}
|
| 178 |
+
.toc .progress-vert{
|
| 179 |
+
height:6px;
|
| 180 |
+
background:linear-gradient(90deg,#e6fdfd,transparent);
|
| 181 |
+
border-radius:99px;
|
| 182 |
+
overflow:hidden;
|
| 183 |
+
margin-top:6px;
|
| 184 |
+
}
|
| 185 |
+
.progress-vert > i{
|
| 186 |
+
display:block;
|
| 187 |
+
height:100%;
|
| 188 |
+
width:0%;
|
| 189 |
+
background:linear-gradient(90deg,#06b6d4,#0ea5a2);
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
/* Main content area */
|
| 193 |
+
main.content{
|
| 194 |
+
display:grid;
|
| 195 |
+
gap:18px;
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
.card{
|
| 199 |
+
background:var(--card);
|
| 200 |
+
border-radius:var(--radius);
|
| 201 |
+
padding:16px;
|
| 202 |
+
box-shadow:var(--shadow);
|
| 203 |
+
border:1px solid rgba(0,0,0,0.04);
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
/* Executive summary */
|
| 207 |
+
.summary{
|
| 208 |
+
display:flex;
|
| 209 |
+
flex-direction:column;
|
| 210 |
+
gap:12px;
|
| 211 |
+
}
|
| 212 |
+
.summary .kpi-row{
|
| 213 |
+
display:flex;
|
| 214 |
+
gap:12px;
|
| 215 |
+
flex-wrap:wrap;
|
| 216 |
+
}
|
| 217 |
+
.kpi{
|
| 218 |
+
flex:1 1 160px;
|
| 219 |
+
background:linear-gradient(180deg, rgba(6,182,212,0.06), rgba(6,182,212,0.02));
|
| 220 |
+
border-radius:10px;
|
| 221 |
+
padding:12px;
|
| 222 |
+
min-width:140px;
|
| 223 |
+
}
|
| 224 |
+
.kpi h3{margin:0;color:var(--primary);font-size:clamp(16px,2.2vw,18px)}
|
| 225 |
+
.kpi p{margin:6px 0 0 0;color:var(--muted);font-weight:600}
|
| 226 |
+
|
| 227 |
+
/* Indicators grid */
|
| 228 |
+
.indicators-grid{
|
| 229 |
+
display:grid;
|
| 230 |
+
gap:12px;
|
| 231 |
+
grid-template-columns: repeat(auto-fit,minmax(220px,1fr));
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
/* Charts area */
|
| 235 |
+
.charts{
|
| 236 |
+
display:grid;
|
| 237 |
+
gap:12px;
|
| 238 |
+
grid-template-columns: 1fr;
|
| 239 |
+
}
|
| 240 |
+
.chart{
|
| 241 |
+
height:260px;
|
| 242 |
+
aspect-ratio: 16 / 9;
|
| 243 |
+
position:relative;
|
| 244 |
+
overflow:visible;
|
| 245 |
+
}
|
| 246 |
+
.chart svg{width:100%;height:100%}
|
| 247 |
+
.tooltip{
|
| 248 |
+
position:fixed;
|
| 249 |
+
pointer-events:none;
|
| 250 |
+
background:var(--card);
|
| 251 |
+
color:var(--muted);
|
| 252 |
+
padding:8px 10px;
|
| 253 |
+
border-radius:8px;
|
| 254 |
+
box-shadow:var(--shadow);
|
| 255 |
+
font-size:13px;
|
| 256 |
+
border:1px solid rgba(0,0,0,0.04);
|
| 257 |
+
transform:translate(-50%,-120%);
|
| 258 |
+
white-space:nowrap;
|
| 259 |
+
z-index:9999;
|
| 260 |
+
display:none;
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
/* Sortable table */
|
| 264 |
+
table{
|
| 265 |
+
width:100%;
|
| 266 |
+
border-collapse:collapse;
|
| 267 |
+
font-size:13px;
|
| 268 |
+
}
|
| 269 |
+
thead th{
|
| 270 |
+
text-align:left;
|
| 271 |
+
padding:10px;
|
| 272 |
+
background:transparent;
|
| 273 |
+
color:var(--muted);
|
| 274 |
+
font-weight:700;
|
| 275 |
+
cursor:pointer;
|
| 276 |
+
}
|
| 277 |
+
tbody td{
|
| 278 |
+
padding:10px;
|
| 279 |
+
border-top:1px solid rgba(0,0,0,0.04);
|
| 280 |
+
color:var(--muted);
|
| 281 |
+
}
|
| 282 |
+
tbody tr:hover td{background:rgba(6,182,212,0.03)}
|
| 283 |
+
|
| 284 |
+
/* Collapsible lists */
|
| 285 |
+
details summary{
|
| 286 |
+
list-style:none;
|
| 287 |
+
cursor:pointer;
|
| 288 |
+
display:flex;
|
| 289 |
+
gap:10px;
|
| 290 |
+
align-items:center;
|
| 291 |
+
font-weight:700;
|
| 292 |
+
color:var(--primary);
|
| 293 |
+
}
|
| 294 |
+
details[open] summary .chev{transform:rotate(180deg)}
|
| 295 |
+
|
| 296 |
+
/* References */
|
| 297 |
+
.references li{margin-bottom:8px}
|
| 298 |
+
.citation{
|
| 299 |
+
display:flex;
|
| 300 |
+
gap:8px;
|
| 301 |
+
align-items:center;
|
| 302 |
+
font-size:13px;
|
| 303 |
+
color:var(--muted);
|
| 304 |
+
}
|
| 305 |
+
.badge{
|
| 306 |
+
background:linear-gradient(90deg,#06b6d4,#0ea5a2);
|
| 307 |
+
color:white;
|
| 308 |
+
padding:6px 8px;
|
| 309 |
+
border-radius:8px;
|
| 310 |
+
font-weight:700;
|
| 311 |
+
font-size:12px;
|
| 312 |
+
}
|
| 313 |
+
|
| 314 |
+
/* Responsive layout adjustments */
|
| 315 |
+
@media (min-width:768px){
|
| 316 |
+
.layout{grid-template-columns: 260px 1fr; align-items:start}
|
| 317 |
+
.container{grid-template-columns: 1fr}
|
| 318 |
+
.charts{grid-template-columns: 1fr 1fr}
|
| 319 |
+
}
|
| 320 |
+
@media (min-width:1024px){
|
| 321 |
+
.container{padding:0}
|
| 322 |
+
.charts{grid-template-columns: 2fr 1fr}
|
| 323 |
+
.chart.large{aspect-ratio: 21/9; height:340px}
|
| 324 |
+
}
|
| 325 |
+
|
| 326 |
+
/* Print-friendly */
|
| 327 |
+
@media print{
|
| 328 |
+
.controls, nav.toc, .btn, .tooltip{display:none}
|
| 329 |
+
body{background:white;padding:0}
|
| 330 |
+
}
|
| 331 |
+
|
| 332 |
+
/* Container query example for a card's content */
|
| 333 |
+
.card:where(:not(:root)){ container-type: inline-size;}
|
| 334 |
+
@container (min-width:420px){
|
| 335 |
+
.card .meta-row{display:flex;justify-content:space-between;align-items:center}
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
+
/* small utilities */
|
| 339 |
+
.muted{color:var(--muted)}
|
| 340 |
+
.small{font-size:12px}
|
| 341 |
+
.right{text-align:right}
|
| 342 |
+
.center{text-align:center}
|
| 343 |
+
.flex{display:flex}
|
| 344 |
+
.gap-8{gap:8px}
|
| 345 |
+
.pill{padding:6px 10px;border-radius:999px;background:var(--glass);font-weight:700;color:var(--muted)}
|
| 346 |
+
.legend{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
|
| 347 |
+
.legend-item{display:flex;gap:8px;align-items:center;font-size:13px}
|
| 348 |
+
.swatch{width:14px;height:14px;border-radius:4px}
|
| 349 |
+
footer{opacity:0.9;color:var(--muted);font-size:13px;text-align:center;padding:12px 0}
|
| 350 |
+
</style>
|
| 351 |
+
</head>
|
| 352 |
+
<body>
|
| 353 |
+
|
| 354 |
+
<div class="container">
|
| 355 |
+
<header class="site-header">
|
| 356 |
+
<div class="brand">
|
| 357 |
+
<div class="logo">VN</div>
|
| 358 |
+
<div>
|
| 359 |
+
<h1>Vietnam Economic Growth Report — 2025</h1>
|
| 360 |
+
<p class="lead">Interactive analysis of recent performance, outlook and risks</p>
|
| 361 |
+
</div>
|
| 362 |
+
</div>
|
| 363 |
+
|
| 364 |
+
<div class="controls" role="toolbar" aria-label="Controls">
|
| 365 |
+
<button class="btn" id="copySummary" title="Copy Executive Summary"><i class="fa fa-copy"></i> Copy Summary</button>
|
| 366 |
+
<button class="btn" id="exportCSV" title="Export indicators as CSV"><i class="fa fa-file-csv"></i> Export CSV</button>
|
| 367 |
+
<button class="btn" id="exportJSON" title="Export report as JSON"><i class="fa fa-code"></i> Export JSON</button>
|
| 368 |
+
<button class="btn" id="printBtn" title="Print view"><i class="fa fa-print"></i></button>
|
| 369 |
+
<button class="btn icon" id="themeToggle" title="Toggle theme"><i class="fa fa-moon"></i></button>
|
| 370 |
+
</div>
|
| 371 |
+
</header>
|
| 372 |
+
|
| 373 |
+
<div class="layout">
|
| 374 |
+
<!-- TOC Sidebar -->
|
| 375 |
+
<nav class="toc card" aria-label="Table of contents">
|
| 376 |
+
<div class="search">
|
| 377 |
+
<i class="fa fa-search" style="color:var(--muted)"></i>
|
| 378 |
+
<input type="search" id="globalSearch" placeholder="Search report..." aria-label="Search report"/>
|
| 379 |
+
</div>
|
| 380 |
+
|
| 381 |
+
<div class="links" id="tocLinks">
|
| 382 |
+
<a href="#executive" data-id="executive"><i class="fa fa-star"></i> Executive Summary</a>
|
| 383 |
+
<a href="#indicators" data-id="indicators"><i class="fa fa-chart-line"></i> Key Indicators</a>
|
| 384 |
+
<a href="#sectors" data-id="sectors"><i class="fa fa-industry"></i> Sectoral Analysis</a>
|
| 385 |
+
<a href="#fdi" data-id="fdi"><i class="fa fa-hand-holding-dollar"></i> FDI & Capital</a>
|
| 386 |
+
<a href="#history" data-id="history"><i class="fa fa-history"></i> Historical Comparison</a>
|
| 387 |
+
<a href="#outlook" data-id="outlook"><i class="fa fa-compass"></i> Economic Outlook</a>
|
| 388 |
+
<a href="#risks" data-id="risks"><i class="fa fa-exclamation-triangle"></i> Challenges & Risks</a>
|
| 389 |
+
<a href="#references" data-id="references"><i class="fa fa-book"></i> References</a>
|
| 390 |
+
</div>
|
| 391 |
+
|
| 392 |
+
<div class="progress-vert" aria-hidden="true">
|
| 393 |
+
<i id="tocProgress"></i>
|
| 394 |
+
</div>
|
| 395 |
+
|
| 396 |
+
<div style="display:flex;gap:8px;margin-top:8px">
|
| 397 |
+
<button class="btn" id="saveBookmark" title="Save current section"><i class="fa fa-bookmark"></i> Save</button>
|
| 398 |
+
<button class="btn" id="gotoBookmark" title="Open saved section"><i class="fa fa-location-arrow"></i> Go</button>
|
| 399 |
+
</div>
|
| 400 |
+
</nav>
|
| 401 |
+
|
| 402 |
+
<!-- Content -->
|
| 403 |
+
<main class="content">
|
| 404 |
+
<!-- Executive Summary -->
|
| 405 |
+
<section id="executive" class="card" tabindex="0" aria-labelledby="executiveTitle">
|
| 406 |
+
<div class="meta-row">
|
| 407 |
+
<div>
|
| 408 |
+
<h2 id="executiveTitle" style="margin:0;color:var(--primary)">Executive Summary</h2>
|
| 409 |
+
<p class="small muted">Snapshot of growth, drivers and immediate outlook</p>
|
| 410 |
+
</div>
|
| 411 |
+
<div class="pill small">Last updated: 2025</div>
|
| 412 |
+
</div>
|
| 413 |
+
|
| 414 |
+
<div class="summary">
|
| 415 |
+
<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>
|
| 416 |
+
|
| 417 |
+
<div class="kpi-row">
|
| 418 |
+
<div class="kpi">
|
| 419 |
+
<h3>7.52%</h3>
|
| 420 |
+
<p>GDP Growth H1 2025 (highest H1 since 2011)</p>
|
| 421 |
+
</div>
|
| 422 |
+
<div class="kpi">
|
| 423 |
+
<h3>7.96%</h3>
|
| 424 |
+
<p>GDP Growth Q2 2025 YoY</p>
|
| 425 |
+
</div>
|
| 426 |
+
<div class="kpi">
|
| 427 |
+
<h3>3.57%</h3>
|
| 428 |
+
<p>Inflation (June 2025)</p>
|
| 429 |
+
</div>
|
| 430 |
+
<div class="kpi">
|
| 431 |
+
<h3>US$21.51B</h3>
|
| 432 |
+
<p>FDI (First Half 2025)</p>
|
| 433 |
+
</div>
|
| 434 |
+
</div>
|
| 435 |
+
|
| 436 |
+
<div style="display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-top:6px">
|
| 437 |
+
<button class="btn primary" id="readMoreExec"><i class="fa fa-eye"></i> Expand Summary</button>
|
| 438 |
+
<div class="muted small">Use the table of contents to navigate sections. Use search to find keywords.</div>
|
| 439 |
+
</div>
|
| 440 |
+
</div>
|
| 441 |
+
</section>
|
| 442 |
+
|
| 443 |
+
<!-- Key Indicators -->
|
| 444 |
+
<section id="indicators" class="card" tabindex="0" aria-labelledby="indicatorsTitle">
|
| 445 |
+
<h2 id="indicatorsTitle" style="margin:0;color:var(--primary)">Key Economic Indicators</h2>
|
| 446 |
+
<p class="small muted">Core datasets and comparisons for 2025</p>
|
| 447 |
+
|
| 448 |
+
<div class="indicators-grid" style="margin-top:12px">
|
| 449 |
+
<div class="card" style="padding:12px">
|
| 450 |
+
<h3 style="margin:0;color:var(--primary)">GDP Series</h3>
|
| 451 |
+
<p class="small muted">Quarterly / mid-year performance</p>
|
| 452 |
+
<div style="display:flex;gap:8px;align-items:center;margin-top:10px;flex-wrap:wrap">
|
| 453 |
+
<div><strong>Q1 2025</strong><div class="muted small">6.9% YoY</div></div>
|
| 454 |
+
<div><strong>Q2 2025</strong><div class="muted small">7.96% YoY</div></div>
|
| 455 |
+
<div><strong>H1 2025</strong><div class="muted small">7.52% YoY</div></div>
|
| 456 |
+
</div>
|
| 457 |
+
</div>
|
| 458 |
+
|
| 459 |
+
<div class="card" style="padding:12px">
|
| 460 |
+
<h3 style="margin:0;color:var(--primary)">Inflation & Labor</h3>
|
| 461 |
+
<p class="small muted">Key macro indicators</p>
|
| 462 |
+
<div style="display:flex;gap:12px;align-items:center;margin-top:10px">
|
| 463 |
+
<div><strong>Inflation</strong><div class="muted small">May: 3.24% • Jun: 3.57%</div></div>
|
| 464 |
+
<div><strong>Unemployment</strong><div class="muted small">Q1 2025: 2.20%</div></div>
|
| 465 |
+
</div>
|
| 466 |
+
</div>
|
| 467 |
+
|
| 468 |
+
<div class="card" style="padding:12px">
|
| 469 |
+
<h3 style="margin:0;color:var(--primary)">FDI</h3>
|
| 470 |
+
<p class="small muted">Capital flows and confidence</p>
|
| 471 |
+
<div style="margin-top:10px">
|
| 472 |
+
<div><strong>Registered (Jan–May)</strong><div class="muted small">US$18.4B (+51% YoY)</div></div>
|
| 473 |
+
<div style="margin-top:6px"><strong>Disbursed (Jan–May)</strong><div class="muted small">US$8.9B</div></div>
|
| 474 |
+
</div>
|
| 475 |
+
</div>
|
| 476 |
+
|
| 477 |
+
<div class="card" style="padding:12px">
|
| 478 |
+
<h3 style="margin:0;color:var(--primary)">Forecasts</h3>
|
| 479 |
+
<p class="small muted">Cross-institution projections (2025)</p>
|
| 480 |
+
<ul style="margin:8px 0 0 16px" class="muted small">
|
| 481 |
+
<li>World Bank: 5.8%</li>
|
| 482 |
+
<li>ADB: 6.6%</li>
|
| 483 |
+
<li>IMF: 5.2%</li>
|
| 484 |
+
<li>Government Target: 8.3–8.5%</li>
|
| 485 |
+
</ul>
|
| 486 |
+
</div>
|
| 487 |
+
</div>
|
| 488 |
+
|
| 489 |
+
<!-- Charts -->
|
| 490 |
+
<div class="charts" style="margin-top:14px">
|
| 491 |
+
<div class="card chart large" id="chartHistoryHolder">
|
| 492 |
+
<h4 style="margin:0;color:var(--primary)">Historical GDP Growth (2020–2025 Q1 series)</h4>
|
| 493 |
+
<p class="small muted">Year-on-year growth in first quarter (2020–2025)</p>
|
| 494 |
+
<div id="chartHistory" class="chart" aria-hidden="false"></div>
|
| 495 |
+
</div>
|
| 496 |
+
|
| 497 |
+
<div class="card chart" id="chartForecastHolder">
|
| 498 |
+
<h4 style="margin:0;color:var(--primary)">2025 Growth Forecasts</h4>
|
| 499 |
+
<p class="small muted">Institutional consensus vs Government target</p>
|
| 500 |
+
<div id="chartForecast" class="chart"></div>
|
| 501 |
+
<div style="margin-top:8px" class="legend" id="forecastLegend"></div>
|
| 502 |
+
</div>
|
| 503 |
+
</div>
|
| 504 |
+
|
| 505 |
+
<!-- Sortable table -->
|
| 506 |
+
<div class="card" style="margin-top:12px">
|
| 507 |
+
<div style="display:flex;justify-content:space-between;align-items:center">
|
| 508 |
+
<h4 style="margin:0;color:var(--primary)">Indicator Table (sortable)</h4>
|
| 509 |
+
<div class="small muted">Click headers to sort • Use export buttons above</div>
|
| 510 |
+
</div>
|
| 511 |
+
|
| 512 |
+
<div style="margin-top:10px;overflow:auto">
|
| 513 |
+
<table id="indicatorTable" aria-label="Key indicators table">
|
| 514 |
+
<thead>
|
| 515 |
+
<tr>
|
| 516 |
+
<th data-key="indicator">Indicator <i class="fa fa-sort"></i></th>
|
| 517 |
+
<th data-key="value">Value</th>
|
| 518 |
+
<th data-key="period">Period</th>
|
| 519 |
+
<th data-key="change">Change</th>
|
| 520 |
+
</tr>
|
| 521 |
+
</thead>
|
| 522 |
+
<tbody></tbody>
|
| 523 |
+
</table>
|
| 524 |
+
</div>
|
| 525 |
+
</div>
|
| 526 |
+
</section>
|
| 527 |
+
|
| 528 |
+
<!-- Sectoral Analysis -->
|
| 529 |
+
<section id="sectors" class="card" tabindex="0" aria-labelledby="sectorsTitle">
|
| 530 |
+
<h2 id="sectorsTitle" style="margin:0;color:var(--primary)">Sectoral Analysis</h2>
|
| 531 |
+
<p class="small muted">Decomposition and performance drivers</p>
|
| 532 |
+
|
| 533 |
+
<div style="display:grid;grid-template-columns:1fr;gap:12px;margin-top:12px">
|
| 534 |
+
<div class="card" style="display:flex;gap:12px;align-items:center;flex-wrap:wrap">
|
| 535 |
+
<div style="flex:1;min-width:220px">
|
| 536 |
+
<h4 style="margin:0;color:var(--primary)">Primary Growth Drivers</h4>
|
| 537 |
+
<ul class="muted small" style="margin-top:8px">
|
| 538 |
+
<li>Services: major contributor</li>
|
| 539 |
+
<li>Manufacturing: recovery & development</li>
|
| 540 |
+
<li>Export industries: export backbone</li>
|
| 541 |
+
<li>Banking: earnings projected +17% in 2025</li>
|
| 542 |
+
</ul>
|
| 543 |
+
</div>
|
| 544 |
+
|
| 545 |
+
<div style="width:320px">
|
| 546 |
+
<div id="donutSectors" class="chart" style="height:220px"></div>
|
| 547 |
+
<div class="legend" id="donutLegend" style="margin-top:10px"></div>
|
| 548 |
+
</div>
|
| 549 |
+
</div>
|
| 550 |
+
|
| 551 |
+
<details class="card" open>
|
| 552 |
+
<summary><i class="chev fa fa-chevron-down"></i> Retail & Consumption</summary>
|
| 553 |
+
<div style="margin-top:8px" class="muted small">
|
| 554 |
+
Retail sales Q1 2025: 1.708 quadrillion VND (~US$66.83B), +9.9% YoY — strong domestic consumption supports services and retail sectors.
|
| 555 |
+
</div>
|
| 556 |
+
</details>
|
| 557 |
+
</div>
|
| 558 |
+
|
| 559 |
+
</section>
|
| 560 |
+
|
| 561 |
+
<!-- FDI & dataset -->
|
| 562 |
+
<section id="fdi" class="card" tabindex="0" aria-labelledby="fdiTitle">
|
| 563 |
+
<h2 id="fdiTitle" style="margin:0;color:var(--primary)">FDI & Capital Flows</h2>
|
| 564 |
+
<p class="small muted">Monthly breakdown and filters</p>
|
| 565 |
+
|
| 566 |
+
<div style="display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-top:12px">
|
| 567 |
+
<label class="muted small">Filter:</label>
|
| 568 |
+
<select id="fdifeature" class="btn" style="padding:8px;border-radius:10px">
|
| 569 |
+
<option value="all">Show all</option>
|
| 570 |
+
<option value="registered">Registered capital</option>
|
| 571 |
+
<option value="disbursed">Disbursed capital</option>
|
| 572 |
+
</select>
|
| 573 |
+
|
| 574 |
+
<div style="margin-left:auto;display:flex;gap:8px">
|
| 575 |
+
<button class="btn" id="copyFDI"><i class="fa fa-clipboard"></i> Copy FDI Table</button>
|
| 576 |
+
<button class="btn" id="downloadFDI"><i class="fa fa-download"></i> Download CSV</button>
|
| 577 |
+
</div>
|
| 578 |
+
</div>
|
| 579 |
+
|
| 580 |
+
<div style="margin-top:12px;overflow:auto" class="card">
|
| 581 |
+
<table id="fdiTable">
|
| 582 |
+
<thead>
|
| 583 |
+
<tr>
|
| 584 |
+
<th>Month</th>
|
| 585 |
+
<th>Registered (US$B)</th>
|
| 586 |
+
<th>Disbursed (US$B)</th>
|
| 587 |
+
</tr>
|
| 588 |
+
</thead>
|
| 589 |
+
<tbody></tbody>
|
| 590 |
+
</table>
|
| 591 |
+
</div>
|
| 592 |
+
|
| 593 |
+
<div class="card" style="margin-top:12px">
|
| 594 |
+
<h4 style="margin:0;color:var(--primary)">FDI Trend</h4>
|
| 595 |
+
<p class="small muted">Registered vs Disbursed (Jan–May 2025)</p>
|
| 596 |
+
<div id="fdiChart" class="chart"></div>
|
| 597 |
+
</div>
|
| 598 |
+
</section>
|
| 599 |
+
|
| 600 |
+
<!-- Historical Comparison -->
|
| 601 |
+
<section id="history" class="card" tabindex="0" aria-labelledby="historyTitle">
|
| 602 |
+
<h2 id="historyTitle" style="margin:0;color:var(--primary)">Historical Comparison</h2>
|
| 603 |
+
<p class="small muted">Contextualizing 2025 against recent years</p>
|
| 604 |
+
|
| 605 |
+
<div style="display:grid;grid-template-columns:1fr;gap:12px;margin-top:12px">
|
| 606 |
+
<div class="card">
|
| 607 |
+
<h4 style="margin:0;color:var(--primary)">Trendline</h4>
|
| 608 |
+
<p class="small muted">First-quarter YoY growth 2020–2025</p>
|
| 609 |
+
<div id="miniHistory" class="chart" style="height:180px"></div>
|
| 610 |
+
</div>
|
| 611 |
+
|
| 612 |
+
<details class="card">
|
| 613 |
+
<summary><i class="chev fa fa-chevron-down"></i> Year-by-year notes</summary>
|
| 614 |
+
<div class="muted small" style="margin-top:8px">
|
| 615 |
+
2024: 7.1% growth overall. 2025 shows a strong start but may moderate with global headwinds. Long-term fundamentals remain resilient.
|
| 616 |
+
</div>
|
| 617 |
+
</details>
|
| 618 |
+
</div>
|
| 619 |
+
</section>
|
| 620 |
+
|
| 621 |
+
<!-- Outlook & projections -->
|
| 622 |
+
<section id="outlook" class="card" tabindex="0" aria-labelledby="outlookTitle">
|
| 623 |
+
<h2 id="outlookTitle" style="margin:0;color:var(--primary)">Economic Outlook & Projections</h2>
|
| 624 |
+
<p class="small muted">Scenarios, risks and recommended mitigations</p>
|
| 625 |
+
|
| 626 |
+
<div style="display:grid;grid-template-columns:1fr;gap:12px;margin-top:12px">
|
| 627 |
+
<div class="card">
|
| 628 |
+
<h4 style="margin:0;color:var(--primary)">Near-term Prospects</h4>
|
| 629 |
+
<ul class="muted small" style="margin-top:8px">
|
| 630 |
+
<li>Strong domestic fundamentals support continued growth.</li>
|
| 631 |
+
<li>Government target (8.3–8.5%) ambitious vs international forecasts.</li>
|
| 632 |
+
<li>Risks centered on trade tensions and geopolitical uncertainty.</li>
|
| 633 |
+
</ul>
|
| 634 |
+
</div>
|
| 635 |
+
|
| 636 |
+
<div class="card">
|
| 637 |
+
<h4 style="margin:0;color:var(--primary)">Policy Responses</h4>
|
| 638 |
+
<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>
|
| 639 |
+
</div>
|
| 640 |
+
</div>
|
| 641 |
+
</section>
|
| 642 |
+
|
| 643 |
+
<!-- Risks -->
|
| 644 |
+
<section id="risks" class="card" tabindex="0" aria-labelledby="risksTitle">
|
| 645 |
+
<h2 id="risksTitle" style="margin:0;color:var(--primary)">Challenges & Risk Factors</h2>
|
| 646 |
+
<p class="small muted">Key risks and suggested mitigations</p>
|
| 647 |
+
|
| 648 |
+
<div style="display:grid;gap:12px;margin-top:12px">
|
| 649 |
+
<div class="card">
|
| 650 |
+
<h4 style="margin:0;color:var(--primary)">Top Risks</h4>
|
| 651 |
+
<ol class="muted small" style="margin-top:8px">
|
| 652 |
+
<li>Global trade tensions affecting exports</li>
|
| 653 |
+
<li>US tariff policies pressuring export-oriented firms</li>
|
| 654 |
+
<li>Geopolitical instability raising uncertainty</li>
|
| 655 |
+
<li>Overreliance on FDI and inflationary pressures</li>
|
| 656 |
+
<li>Need to protect macroeconomic stability while pursuing growth</li>
|
| 657 |
+
</ol>
|
| 658 |
+
</div>
|
| 659 |
+
|
| 660 |
+
<details class="card">
|
| 661 |
+
<summary><i class="chev fa fa-chevron-down"></i> Mitigation Strategies</summary>
|
| 662 |
+
<div class="muted small" style="margin-top:8px">
|
| 663 |
+
Diversify export partners, boost domestic value chains, prudent fiscal policy, and strengthen financial oversight to reduce vulnerabilities.
|
| 664 |
+
</div>
|
| 665 |
+
</details>
|
| 666 |
+
</div>
|
| 667 |
+
</section>
|
| 668 |
+
|
| 669 |
+
<!-- References -->
|
| 670 |
+
<section id="references" class="card" tabindex="0" aria-labelledby="referencesTitle">
|
| 671 |
+
<h2 id="referencesTitle" style="margin:0;color:var(--primary)">Sources & References</h2>
|
| 672 |
+
<p class="small muted">Cited reports, datasets and links</p>
|
| 673 |
+
|
| 674 |
+
<ul class="references" style="margin-top:12px">
|
| 675 |
+
<li>
|
| 676 |
+
<div class="citation">
|
| 677 |
+
<span class="badge">1</span>
|
| 678 |
+
<div style="flex:1">
|
| 679 |
+
Trading Economics - Vietnam GDP Annual Growth Rate — <a href="https://tradingeconomics.com/vietnam/gdp-growth-annual" target="_blank">tradingeconomics.com</a>
|
| 680 |
+
</div>
|
| 681 |
+
<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>
|
| 682 |
+
</div>
|
| 683 |
+
</li>
|
| 684 |
+
|
| 685 |
+
<li>
|
| 686 |
+
<div class="citation">
|
| 687 |
+
<span class="badge">2</span>
|
| 688 |
+
<div style="flex:1">
|
| 689 |
+
IMF - Vietnam Country Profile — <a href="https://www.imf.org/en/Countries/VNM" target="_blank">imf.org</a>
|
| 690 |
+
</div>
|
| 691 |
+
<button class="btn" data-copy="IMF - Vietnam Country Profile — https://www.imf.org/en/Countries/VNM"><i class="fa fa-copy"></i></button>
|
| 692 |
+
</div>
|
| 693 |
+
</li>
|
| 694 |
+
|
| 695 |
+
<li>
|
| 696 |
+
<div class="citation">
|
| 697 |
+
<span class="badge">3</span>
|
| 698 |
+
<div style="flex:1">
|
| 699 |
+
World Economics - Vietnam GDP Estimates — <a href="https://www.worldeconomics.com/GDP/Vietnam.gdp" target="_blank">worldeconomics.com</a>
|
| 700 |
+
</div>
|
| 701 |
+
<button class="btn" data-copy="World Economics - Vietnam GDP Estimates — https://www.worldeconomics.com/GDP/Vietnam.gdp"><i class="fa fa-copy"></i></button>
|
| 702 |
+
</div>
|
| 703 |
+
</li>
|
| 704 |
+
|
| 705 |
+
<li style="margin-top:8px">
|
| 706 |
+
<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>
|
| 707 |
+
</li>
|
| 708 |
+
</ul>
|
| 709 |
+
|
| 710 |
+
<div style="display:flex;gap:8px;margin-top:12px">
|
| 711 |
+
<button class="btn" id="copyRefs"><i class="fa fa-clipboard"></i> Copy All References</button>
|
| 712 |
+
<button class="btn" id="exportRefsJSON"><i class="fa fa-file-export"></i> Export References (JSON)</button>
|
| 713 |
+
</div>
|
| 714 |
+
</section>
|
| 715 |
+
|
| 716 |
+
<footer>
|
| 717 |
+
<div>Prepared from public data sources • Interactive report © 2025</div>
|
| 718 |
+
</footer>
|
| 719 |
+
</main>
|
| 720 |
+
</div>
|
| 721 |
+
</div>
|
| 722 |
+
|
| 723 |
+
<div class="tooltip" id="tooltip"></div>
|
| 724 |
+
|
| 725 |
+
<script>
|
| 726 |
+
/* ===========================
|
| 727 |
+
Data
|
| 728 |
+
===========================*/
|
| 729 |
+
const data = {
|
| 730 |
+
historyQ1: [
|
| 731 |
+
{year:2020, value:3.21},
|
| 732 |
+
{year:2021, value:4.85},
|
| 733 |
+
{year:2022, value:5.42},
|
| 734 |
+
{year:2023, value:3.46},
|
| 735 |
+
{year:2024, value:5.98},
|
| 736 |
+
{year:2025, value:6.93}
|
| 737 |
+
],
|
| 738 |
+
forecasts: [
|
| 739 |
+
{name:"World Bank", value:5.8, color:"#06b6d4"},
|
| 740 |
+
{name:"ADB", value:6.6, color:"#0ea5a2"},
|
| 741 |
+
{name:"IMF", value:5.2, color:"#06a5d4"},
|
| 742 |
+
{name:"Government target", value:8.4, color:"#0b7285"}
|
| 743 |
+
],
|
| 744 |
+
sectors: [
|
| 745 |
+
{name:"Services", value:45, color:"#06b6d4"},
|
| 746 |
+
{name:"Manufacturing", value:30, color:"#0ea5a2"},
|
| 747 |
+
{name:"Agriculture", value:8, color:"#7dd3fc"},
|
| 748 |
+
{name:"Export-related", value:12, color:"#38bdf8"},
|
| 749 |
+
{name:"Other", value:5, color:"#a7f3d0"}
|
| 750 |
+
],
|
| 751 |
+
indicatorsTable:[
|
| 752 |
+
{indicator:"GDP Q1 2025", value:"6.9%", period:"Q1 2025", change:"–"},
|
| 753 |
+
{indicator:"GDP Q2 2025", value:"7.96%", period:"Q2 2025", change:"–"},
|
| 754 |
+
{indicator:"GDP H1 2025", value:"7.52%", period:"H1 2025", change:"Highest H1 since 2011"},
|
| 755 |
+
{indicator:"Inflation May 2025", value:"3.24%", period:"May 2025", change:"–"},
|
| 756 |
+
{indicator:"Inflation Jun 2025", value:"3.57%", period:"Jun 2025", change:"Highest YTD"},
|
| 757 |
+
{indicator:"Unemployment Q1 2025", value:"2.20%", period:"Q1 2025", change:"Down from 2.22%"},
|
| 758 |
+
{indicator:"FDI Registered (Jan–May)", value:"$18.4B", period:"Jan–May 2025", change:"+51% YoY"},
|
| 759 |
+
{indicator:"FDI Disbursed (Jan–May)", value:"$8.9B", period:"Jan–May 2025", change:"–"},
|
| 760 |
+
],
|
| 761 |
+
fdiMonthly:[
|
| 762 |
+
{month:"Jan 2025", registered:5.2, disbursed:2.3},
|
| 763 |
+
{month:"Feb 2025", registered:3.8, disbursed:1.5},
|
| 764 |
+
{month:"Mar 2025", registered:4.1, disbursed:1.8},
|
| 765 |
+
{month:"Apr 2025", registered:2.9, disbursed:1.7},
|
| 766 |
+
{month:"May 2025", registered:2.4, disbursed:1.6} // sums approx to totals
|
| 767 |
+
],
|
| 768 |
+
references:[
|
| 769 |
+
{id:1, text:"Trading Economics - Vietnam GDP Annual Growth Rate — https://tradingeconomics.com/vietnam/gdp-growth-annual"},
|
| 770 |
+
{id:2, text:"IMF - Vietnam Country Profile — https://www.imf.org/en/Countries/VNM"},
|
| 771 |
+
{id:3, text:"World Economics - Vietnam GDP Estimates — https://www.worldeconomics.com/GDP/Vietnam.gdp"},
|
| 772 |
+
{id:4, text:"Government of Vietnam - General Statistics Office — https://www.gso.gov.vn/en/"},
|
| 773 |
+
{id:5, text:"Wikipedia - Economy of Vietnam — https://en.wikipedia.org/wiki/Economy_of_Vietnam"},
|
| 774 |
+
{id:6, text:"ADB - Vietnam Country Partnership — https://www.adb.org/countries/viet-nam/main"}
|
| 775 |
+
]
|
| 776 |
+
};
|
| 777 |
+
|
| 778 |
+
/* ===========================
|
| 779 |
+
Utilities
|
| 780 |
+
===========================*/
|
| 781 |
+
const $ = sel => document.querySelector(sel);
|
| 782 |
+
const $$ = sel => Array.from(document.querySelectorAll(sel));
|
| 783 |
+
const fmt = n => (typeof n === "number" ? n.toFixed(2) : n);
|
| 784 |
+
|
| 785 |
+
/* Smooth scrolling for TOC links */
|
| 786 |
+
$$('.toc a').forEach(a => {
|
| 787 |
+
a.addEventListener('click', e => {
|
| 788 |
+
e.preventDefault();
|
| 789 |
+
const id = a.getAttribute('href').slice(1);
|
| 790 |
+
const target = document.getElementById(id);
|
| 791 |
+
if(target){
|
| 792 |
+
target.scrollIntoView({behavior:'smooth', block:'start'});
|
| 793 |
+
history.replaceState(null, '', '#'+id);
|
| 794 |
+
}
|
| 795 |
+
});
|
| 796 |
+
});
|
| 797 |
+
|
| 798 |
+
/* ===========================
|
| 799 |
+
Theme & LocalStorage
|
| 800 |
+
===========================*/
|
| 801 |
+
const themeToggle = $('#themeToggle');
|
| 802 |
+
const root = document.documentElement;
|
| 803 |
+
const savedTheme = localStorage.getItem('vne_theme') || (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
| 804 |
+
if(savedTheme === 'dark') root.setAttribute('data-theme','dark');
|
| 805 |
+
else root.removeAttribute('data-theme');
|
| 806 |
+
|
| 807 |
+
themeToggle.addEventListener('click', () => {
|
| 808 |
+
if(root.getAttribute('data-theme') === 'dark'){
|
| 809 |
+
root.removeAttribute('data-theme');
|
| 810 |
+
localStorage.setItem('vne_theme','light');
|
| 811 |
+
themeToggle.innerHTML = '<i class="fa fa-moon"></i>';
|
| 812 |
+
} else {
|
| 813 |
+
root.setAttribute('data-theme','dark');
|
| 814 |
+
localStorage.setItem('vne_theme','dark');
|
| 815 |
+
themeToggle.innerHTML = '<i class="fa fa-sun"></i>';
|
| 816 |
+
}
|
| 817 |
+
});
|
| 818 |
+
|
| 819 |
+
/* ===========================
|
| 820 |
+
Populate Indicator Table (sortable)
|
| 821 |
+
===========================*/
|
| 822 |
+
const indicatorTableBody = $('#indicatorTable tbody');
|
| 823 |
+
function renderIndicatorTable(rows){
|
| 824 |
+
indicatorTableBody.innerHTML = rows.map(r => `
|
| 825 |
+
<tr>
|
| 826 |
+
<td>${r.indicator}</td>
|
| 827 |
+
<td>${r.value}</td>
|
| 828 |
+
<td>${r.period}</td>
|
| 829 |
+
<td>${r.change}</td>
|
| 830 |
+
</tr>
|
| 831 |
+
`).join('');
|
| 832 |
+
}
|
| 833 |
+
renderIndicatorTable(data.indicatorsTable);
|
| 834 |
+
|
| 835 |
+
let sortState = {key:null,dir:1};
|
| 836 |
+
$$('#indicatorTable thead th').forEach(th => {
|
| 837 |
+
th.addEventListener('click', () => {
|
| 838 |
+
const key = th.dataset.key;
|
| 839 |
+
if(sortState.key === key) sortState.dir *= -1;
|
| 840 |
+
else { sortState.key = key; sortState.dir = 1; }
|
| 841 |
+
const sorted = [...data.indicatorsTable].sort((a,b) => {
|
| 842 |
+
let va = a[key] || '', vb = b[key] || '';
|
| 843 |
+
// numeric if possible
|
| 844 |
+
const na = parseFloat(String(va).replace(/[^0-9.-]+/g,'')), nb = parseFloat(String(vb).replace(/[^0-9.-]+/g,''));
|
| 845 |
+
if(!isNaN(na) && !isNaN(nb)) return (na-nb)*sortState.dir;
|
| 846 |
+
return String(va).localeCompare(String(vb)) * sortState.dir;
|
| 847 |
+
});
|
| 848 |
+
renderIndicatorTable(sorted);
|
| 849 |
+
});
|
| 850 |
+
});
|
| 851 |
+
|
| 852 |
+
/* ===========================
|
| 853 |
+
Chart Utilities (SVG)
|
| 854 |
+
===========================*/
|
| 855 |
+
function createSVG(w=800,h=300){
|
| 856 |
+
const ns = "http://www.w3.org/2000/svg";
|
| 857 |
+
const svg = document.createElementNS(ns,'svg');
|
| 858 |
+
svg.setAttribute('viewBox',`0 0 ${w} ${h}`);
|
| 859 |
+
svg.setAttribute('width','100%');
|
| 860 |
+
svg.setAttribute('height','100%');
|
| 861 |
+
return svg;
|
| 862 |
+
}
|
| 863 |
+
function clearEl(el){ while(el.firstChild) el.removeChild(el.firstChild); }
|
| 864 |
+
|
| 865 |
+
/* Tooltip */
|
| 866 |
+
const tooltip = $('#tooltip');
|
| 867 |
+
function showTooltip(html,x,y){
|
| 868 |
+
tooltip.style.display = 'block';
|
| 869 |
+
tooltip.innerHTML = html;
|
| 870 |
+
const rect = tooltip.getBoundingClientRect();
|
| 871 |
+
tooltip.style.left = (x)+'px';
|
| 872 |
+
tooltip.style.top = (y)+'px';
|
| 873 |
+
}
|
| 874 |
+
function hideTooltip(){ tooltip.style.display = 'none'; }
|
| 875 |
+
|
| 876 |
+
/* ===========================
|
| 877 |
+
Historical Line Chart (2020–2025)
|
| 878 |
+
===========================*/
|
| 879 |
+
function drawHistoryChart(){
|
| 880 |
+
const holder = $('#chartHistory');
|
| 881 |
+
clearEl(holder);
|
| 882 |
+
const w = 720, h = 320;
|
| 883 |
+
const svg = createSVG(w,h);
|
| 884 |
+
holder.appendChild(svg);
|
| 885 |
+
|
| 886 |
+
const padding = {t:24,r:24,b:36,l:56};
|
| 887 |
+
const innerW = w - padding.l - padding.r;
|
| 888 |
+
const innerH = h - padding.t - padding.b;
|
| 889 |
+
const values = data.historyQ1.map(d => d.value);
|
| 890 |
+
const minV = Math.min(...values) - 1;
|
| 891 |
+
const maxV = Math.max(...values) + 1;
|
| 892 |
+
|
| 893 |
+
// scales
|
| 894 |
+
const xs = idx => padding.l + (idx/(values.length-1))*innerW;
|
| 895 |
+
const ys = v => padding.t + innerH - ((v - minV)/(maxV - minV))*innerH;
|
| 896 |
+
|
| 897 |
+
// grid lines and y-axis ticks
|
| 898 |
+
const ns = "http://www.w3.org/2000/svg";
|
| 899 |
+
for(let t = Math.ceil(minV); t <= Math.floor(maxV); t+=1){
|
| 900 |
+
const y = ys(t);
|
| 901 |
+
const line = document.createElementNS(ns,'line');
|
| 902 |
+
line.setAttribute('x1',padding.l);
|
| 903 |
+
line.setAttribute('x2',w-padding.r);
|
| 904 |
+
line.setAttribute('y1',y);
|
| 905 |
+
line.setAttribute('y2',y);
|
| 906 |
+
line.setAttribute('stroke','rgba(0,0,0,0.04)');
|
| 907 |
+
line.setAttribute('stroke-width','1');
|
| 908 |
+
svg.appendChild(line);
|
| 909 |
+
|
| 910 |
+
const text = document.createElementNS(ns,'text');
|
| 911 |
+
text.setAttribute('x',padding.l-10);
|
| 912 |
+
text.setAttribute('y',y+4);
|
| 913 |
+
text.setAttribute('text-anchor','end');
|
| 914 |
+
text.setAttribute('fill','var(--muted)');
|
| 915 |
+
text.setAttribute('style','font-size:12px');
|
| 916 |
+
text.textContent = t + '%';
|
| 917 |
+
svg.appendChild(text);
|
| 918 |
+
}
|
| 919 |
+
|
| 920 |
+
// path
|
| 921 |
+
let d = '';
|
| 922 |
+
data.historyQ1.forEach((pt,i)=>{
|
| 923 |
+
const x = xs(i), y = ys(pt.value);
|
| 924 |
+
d += (i===0?'M':'L') + x + ' ' + y + ' ';
|
| 925 |
+
});
|
| 926 |
+
const path = document.createElementNS(ns,'path');
|
| 927 |
+
path.setAttribute('d', d);
|
| 928 |
+
path.setAttribute('fill','none');
|
| 929 |
+
path.setAttribute('stroke','#06b6d4');
|
| 930 |
+
path.setAttribute('stroke-width','3');
|
| 931 |
+
path.setAttribute('stroke-linecap','round');
|
| 932 |
+
svg.appendChild(path);
|
| 933 |
+
|
| 934 |
+
// area fill
|
| 935 |
+
const areaD = d + ` L ${padding.l+innerW} ${padding.t+innerH} L ${padding.l} ${padding.t+innerH} Z`;
|
| 936 |
+
const area = document.createElementNS(ns,'path');
|
| 937 |
+
area.setAttribute('d', areaD);
|
| 938 |
+
area.setAttribute('fill','rgba(6,182,212,0.08)');
|
| 939 |
+
svg.insertBefore(area, path);
|
| 940 |
+
|
| 941 |
+
// points and hover
|
| 942 |
+
data.historyQ1.forEach((pt,i)=>{
|
| 943 |
+
const x = xs(i), y = ys(pt.value);
|
| 944 |
+
const circle = document.createElementNS(ns,'circle');
|
| 945 |
+
circle.setAttribute('cx',x);
|
| 946 |
+
circle.setAttribute('cy',y);
|
| 947 |
+
circle.setAttribute('r',5);
|
| 948 |
+
circle.setAttribute('fill','#fff');
|
| 949 |
+
circle.setAttribute('stroke','#06b6d4');
|
| 950 |
+
circle.setAttribute('stroke-width',2);
|
| 951 |
+
circle.style.cursor='pointer';
|
| 952 |
+
svg.appendChild(circle);
|
| 953 |
+
|
| 954 |
+
circle.addEventListener('mouseenter', (e) => {
|
| 955 |
+
const html = `<strong>${pt.year}</strong><div class="muted small">${pt.value}% YoY</div>`;
|
| 956 |
+
const rect = holder.getBoundingClientRect();
|
| 957 |
+
showTooltip(html, rect.left + x, rect.top + y - 8);
|
| 958 |
+
});
|
| 959 |
+
circle.addEventListener('mouseleave', hideTooltip);
|
| 960 |
+
});
|
| 961 |
+
|
| 962 |
+
// labels (years)
|
| 963 |
+
data.historyQ1.forEach((pt,i)=>{
|
| 964 |
+
const x = xs(i), y = padding.t + innerH + 18;
|
| 965 |
+
const nsTxt = document.createElementNS("http://www.w3.org/2000/svg",'text');
|
| 966 |
+
nsTxt.setAttribute('x',x);
|
| 967 |
+
nsTxt.setAttribute('y',y);
|
| 968 |
+
nsTxt.setAttribute('text-anchor','middle');
|
| 969 |
+
nsTxt.setAttribute('fill','var(--muted)');
|
| 970 |
+
nsTxt.setAttribute('style','font-size:12px');
|
| 971 |
+
nsTxt.textContent = pt.year;
|
| 972 |
+
svg.appendChild(nsTxt);
|
| 973 |
+
});
|
| 974 |
+
}
|
| 975 |
+
drawHistoryChart();
|
| 976 |
+
|
| 977 |
+
/* ===========================
|
| 978 |
+
Forecast Bar Chart
|
| 979 |
+
===========================*/
|
| 980 |
+
function drawForecastChart(){
|
| 981 |
+
const holder = $('#chartForecast');
|
| 982 |
+
clearEl(holder);
|
| 983 |
+
const w=420,h=260;
|
| 984 |
+
const svg = createSVG(w,h);
|
| 985 |
+
holder.appendChild(svg);
|
| 986 |
+
const ns = "http://www.w3.org/2000/svg";
|
| 987 |
+
const padding = {t:20,r:20,b:40,l:50};
|
| 988 |
+
const innerW = w - padding.l - padding.r;
|
| 989 |
+
const innerH = h - padding.t - padding.b;
|
| 990 |
+
const maxVal = Math.max(...data.forecasts.map(f=>f.value))+1;
|
| 991 |
+
|
| 992 |
+
data.forecasts.forEach((f,i)=>{
|
| 993 |
+
const barW = innerW / data.forecasts.length * 0.7;
|
| 994 |
+
const gap = innerW / data.forecasts.length;
|
| 995 |
+
const x = padding.l + i*gap + (gap - barW)/2;
|
| 996 |
+
const y = padding.t + innerH - (f.value/maxVal)*innerH;
|
| 997 |
+
const rect = document.createElementNS(ns,'rect');
|
| 998 |
+
rect.setAttribute('x', x);
|
| 999 |
+
rect.setAttribute('y', y);
|
| 1000 |
+
rect.setAttribute('width', barW);
|
| 1001 |
+
rect.setAttribute('height', padding.t + innerH - y);
|
| 1002 |
+
rect.setAttribute('rx',8);
|
| 1003 |
+
rect.setAttribute('fill', f.color);
|
| 1004 |
+
svg.appendChild(rect);
|
| 1005 |
+
|
| 1006 |
+
rect.addEventListener('mouseenter', (ev)=>{
|
| 1007 |
+
const rectEl = holder.getBoundingClientRect();
|
| 1008 |
+
showTooltip(`<strong>${f.name}</strong><div class="muted small">${f.value}%</div>`, rectEl.left + x + barW/2, rectEl.top + y);
|
| 1009 |
+
});
|
| 1010 |
+
rect.addEventListener('mouseleave', hideTooltip);
|
| 1011 |
+
|
| 1012 |
+
// labels
|
| 1013 |
+
const text = document.createElementNS(ns,'text');
|
| 1014 |
+
text.setAttribute('x', x + barW/2);
|
| 1015 |
+
text.setAttribute('y', padding.t + innerH + 18);
|
| 1016 |
+
text.setAttribute('text-anchor','middle');
|
| 1017 |
+
text.setAttribute('fill','var(--muted)');
|
| 1018 |
+
text.setAttribute('style','font-size:12px');
|
| 1019 |
+
text.textContent = f.name.replace('Government target','Gov target');
|
| 1020 |
+
svg.appendChild(text);
|
| 1021 |
+
});
|
| 1022 |
+
|
| 1023 |
+
// y-axis ticks
|
| 1024 |
+
for(let t=0;t<=Math.ceil(maxVal);t+=1){
|
| 1025 |
+
const y = padding.t + innerH - (t/maxVal)*innerH;
|
| 1026 |
+
const tx = document.createElementNS(ns,'text');
|
| 1027 |
+
tx.setAttribute('x',padding.l-8);
|
| 1028 |
+
tx.setAttribute('y',y+4);
|
| 1029 |
+
tx.setAttribute('text-anchor','end');
|
| 1030 |
+
tx.setAttribute('fill','var(--muted)');
|
| 1031 |
+
tx.setAttribute('style','font-size:11px');
|
| 1032 |
+
tx.textContent = t + '%';
|
| 1033 |
+
svg.appendChild(tx);
|
| 1034 |
+
}
|
| 1035 |
+
|
| 1036 |
+
// legend
|
| 1037 |
+
const legend = $('#forecastLegend');
|
| 1038 |
+
legend.innerHTML = '';
|
| 1039 |
+
data.forecasts.forEach(f => {
|
| 1040 |
+
const item = document.createElement('div');
|
| 1041 |
+
item.className='legend-item';
|
| 1042 |
+
item.innerHTML = `<span class="swatch" style="background:${f.color}"></span><div class="small muted">${f.name} — ${f.value}%</div>`;
|
| 1043 |
+
legend.appendChild(item);
|
| 1044 |
+
});
|
| 1045 |
+
}
|
| 1046 |
+
drawForecastChart();
|
| 1047 |
+
|
| 1048 |
+
/* ===========================
|
| 1049 |
+
Donut Chart for Sectors
|
| 1050 |
+
===========================*/
|
| 1051 |
+
function drawDonut(){
|
| 1052 |
+
const holder = $('#donutSectors');
|
| 1053 |
+
clearEl(holder);
|
| 1054 |
+
const w=320,h=220;
|
| 1055 |
+
const svg = createSVG(w,h);
|
| 1056 |
+
holder.appendChild(svg);
|
| 1057 |
+
const ns = "http://www.w3.org/2000/svg";
|
| 1058 |
+
const cx = w/2, cy = h/2 - 10, r = Math.min(w,h)/3;
|
| 1059 |
+
const total = data.sectors.reduce((s,d)=>s+d.value,0);
|
| 1060 |
+
let angle = -Math.PI/2;
|
| 1061 |
+
const donutGroup = document.createElementNS(ns,'g');
|
| 1062 |
+
svg.appendChild(donutGroup);
|
| 1063 |
+
|
| 1064 |
+
data.sectors.forEach((s,idx)=>{
|
| 1065 |
+
const sliceAngle = (s.value/total)*(Math.PI*2);
|
| 1066 |
+
const end = angle + sliceAngle;
|
| 1067 |
+
const x1 = cx + r*Math.cos(angle);
|
| 1068 |
+
const y1 = cy + r*Math.sin(angle);
|
| 1069 |
+
const x2 = cx + r*Math.cos(end);
|
| 1070 |
+
const y2 = cy + r*Math.sin(end);
|
| 1071 |
+
const large = sliceAngle > Math.PI ? 1 : 0;
|
| 1072 |
+
const pathD = `M ${cx} ${cy} L ${x1} ${y1} A ${r} ${r} 0 ${large} 1 ${x2} ${y2} Z`;
|
| 1073 |
+
const path = document.createElementNS(ns,'path');
|
| 1074 |
+
path.setAttribute('d', pathD);
|
| 1075 |
+
path.setAttribute('fill', s.color);
|
| 1076 |
+
path.setAttribute('opacity',0.95);
|
| 1077 |
+
donutGroup.appendChild(path);
|
| 1078 |
+
|
| 1079 |
+
path.addEventListener('mouseenter', () => {
|
| 1080 |
+
const rect = holder.getBoundingClientRect();
|
| 1081 |
+
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)));
|
| 1082 |
+
});
|
| 1083 |
+
path.addEventListener('mouseleave', hideTooltip);
|
| 1084 |
+
|
| 1085 |
+
angle = end;
|
| 1086 |
+
});
|
| 1087 |
+
|
| 1088 |
+
// inner circle to make donut
|
| 1089 |
+
const inner = document.createElementNS(ns,'circle');
|
| 1090 |
+
inner.setAttribute('cx',cx);
|
| 1091 |
+
inner.setAttribute('cy',cy);
|
| 1092 |
+
inner.setAttribute('r',r*0.6);
|
| 1093 |
+
inner.setAttribute('fill','var(--card)');
|
| 1094 |
+
inner.setAttribute('stroke','rgba(0,0,0,0.02)');
|
| 1095 |
+
svg.appendChild(inner);
|
| 1096 |
+
|
| 1097 |
+
// legend mapping with toggles
|
| 1098 |
+
const legend = $('#donutLegend');
|
| 1099 |
+
legend.innerHTML = '';
|
| 1100 |
+
data.sectors.forEach(s=>{
|
| 1101 |
+
const item = document.createElement('div');
|
| 1102 |
+
item.className = 'legend-item';
|
| 1103 |
+
item.innerHTML = `<span class="swatch" style="background:${s.color}"></span><div class="small muted">${s.name} — ${s.value}%</div>`;
|
| 1104 |
+
legend.appendChild(item);
|
| 1105 |
+
});
|
| 1106 |
+
}
|
| 1107 |
+
drawDonut();
|
| 1108 |
+
|
| 1109 |
+
/* ===========================
|
| 1110 |
+
FDI Table and Chart
|
| 1111 |
+
===========================*/
|
| 1112 |
+
function renderFDITable(filter='all'){
|
| 1113 |
+
const tbody = $('#fdiTable tbody');
|
| 1114 |
+
const rows = data.fdiMonthly.map(r => {
|
| 1115 |
+
return `<tr>
|
| 1116 |
+
<td>${r.month}</td>
|
| 1117 |
+
<td>${r.registered.toFixed(2)}</td>
|
| 1118 |
+
<td>${r.disbursed.toFixed(2)}</td>
|
| 1119 |
+
</tr>`;
|
| 1120 |
+
}).join('');
|
| 1121 |
+
tbody.innerHTML = rows;
|
| 1122 |
+
}
|
| 1123 |
+
renderFDITable();
|
| 1124 |
+
|
| 1125 |
+
function drawFDIChart(){
|
| 1126 |
+
const holder = $('#fdiChart');
|
| 1127 |
+
clearEl(holder);
|
| 1128 |
+
const w=640,h=260;
|
| 1129 |
+
const svg = createSVG(w,h);
|
| 1130 |
+
holder.appendChild(svg);
|
| 1131 |
+
const ns = "http://www.w3.org/2000/svg";
|
| 1132 |
+
const padding = {t:20,r:20,b:50,l:50};
|
| 1133 |
+
const innerW = w - padding.l - padding.r;
|
| 1134 |
+
const innerH = h - padding.t - padding.b;
|
| 1135 |
+
const maxV = Math.max(...data.fdiMonthly.flatMap(x=>[x.registered,x.disbursed]))+1;
|
| 1136 |
+
// stacked bars side-by-side
|
| 1137 |
+
data.fdiMonthly.forEach((m,i)=>{
|
| 1138 |
+
const gap = innerW / data.fdiMonthly.length;
|
| 1139 |
+
const barW = gap*0.35;
|
| 1140 |
+
const x = padding.l + i*gap;
|
| 1141 |
+
const yReg = padding.t + innerH - (m.registered/maxV)*innerH;
|
| 1142 |
+
const yDis = padding.t + innerH - (m.disbursed/maxV)*innerH;
|
| 1143 |
+
|
| 1144 |
+
const rectR = document.createElementNS(ns,'rect');
|
| 1145 |
+
rectR.setAttribute('x', x - barW/2);
|
| 1146 |
+
rectR.setAttribute('y', yReg);
|
| 1147 |
+
rectR.setAttribute('width', barW);
|
| 1148 |
+
rectR.setAttribute('height', padding.t + innerH - yReg);
|
| 1149 |
+
rectR.setAttribute('fill','#06b6d4');
|
| 1150 |
+
rectR.setAttribute('rx',6);
|
| 1151 |
+
svg.appendChild(rectR);
|
| 1152 |
+
|
| 1153 |
+
const rectD = document.createElementNS(ns,'rect');
|
| 1154 |
+
rectD.setAttribute('x', x + barW/2);
|
| 1155 |
+
rectD.setAttribute('y', yDis);
|
| 1156 |
+
rectD.setAttribute('width', barW);
|
| 1157 |
+
rectD.setAttribute('height', padding.t + innerH - yDis);
|
| 1158 |
+
rectD.setAttribute('fill','#0ea5a2');
|
| 1159 |
+
rectD.setAttribute('rx',6);
|
| 1160 |
+
svg.appendChild(rectD);
|
| 1161 |
+
|
| 1162 |
+
[rectR,rectD].forEach((el,kindIdx)=>{
|
| 1163 |
+
el.addEventListener('mouseenter',()=>{
|
| 1164 |
+
const rect = holder.getBoundingClientRect();
|
| 1165 |
+
const text = kindIdx===0 ? `Registered: ${m.registered}B` : `Disbursed: ${m.disbursed}B`;
|
| 1166 |
+
showTooltip(`<strong>${m.month}</strong><div class="muted small">${text}</div>`, rect.left + x, rect.top + (kindIdx===0?yReg:yDis));
|
| 1167 |
+
});
|
| 1168 |
+
el.addEventListener('mouseleave', hideTooltip);
|
| 1169 |
+
});
|
| 1170 |
+
|
| 1171 |
+
// label
|
| 1172 |
+
const label = document.createElementNS(ns,'text');
|
| 1173 |
+
label.setAttribute('x', x);
|
| 1174 |
+
label.setAttribute('y', padding.t + innerH + 22);
|
| 1175 |
+
label.setAttribute('text-anchor','middle');
|
| 1176 |
+
label.setAttribute('fill','var(--muted)');
|
| 1177 |
+
label.setAttribute('style','font-size:11px');
|
| 1178 |
+
label.textContent = m.month.split(' ')[0];
|
| 1179 |
+
svg.appendChild(label);
|
| 1180 |
+
});
|
| 1181 |
+
|
| 1182 |
+
// legend
|
| 1183 |
+
const legendDiv = document.createElement('div');
|
| 1184 |
+
legendDiv.style.marginTop = '8px';
|
| 1185 |
+
}
|
| 1186 |
+
drawFDIChart();
|
| 1187 |
+
|
| 1188 |
+
/* ===========================
|
| 1189 |
+
Interactions: search, copy summary, export
|
| 1190 |
+
===========================*/
|
| 1191 |
+
const globalSearch = $('#globalSearch');
|
| 1192 |
+
globalSearch.addEventListener('input', (e) => {
|
| 1193 |
+
const q = e.target.value.trim().toLowerCase();
|
| 1194 |
+
// highlight and open matching sections, show first match
|
| 1195 |
+
const sections = Array.from(document.querySelectorAll('main.content section'));
|
| 1196 |
+
let firstMatch = null;
|
| 1197 |
+
sections.forEach(sec => {
|
| 1198 |
+
const txt = sec.textContent.toLowerCase();
|
| 1199 |
+
if(q && txt.includes(q)){
|
| 1200 |
+
sec.classList.add('highlight');
|
| 1201 |
+
sec.style.boxShadow = '0 6px 30px rgba(6,182,212,0.06)';
|
| 1202 |
+
if(!firstMatch) firstMatch = sec;
|
| 1203 |
+
} else {
|
| 1204 |
+
sec.classList.remove('highlight');
|
| 1205 |
+
sec.style.boxShadow = '';
|
| 1206 |
+
}
|
| 1207 |
+
});
|
| 1208 |
+
if(firstMatch){
|
| 1209 |
+
firstMatch.scrollIntoView({behavior:'smooth', block:'center'});
|
| 1210 |
+
}
|
| 1211 |
+
});
|
| 1212 |
+
|
| 1213 |
+
/* Copy executive summary to clipboard */
|
| 1214 |
+
$('#copySummary').addEventListener('click', async () => {
|
| 1215 |
+
const summary = document.querySelector('#executive .summary p').textContent.trim();
|
| 1216 |
+
try{
|
| 1217 |
+
await navigator.clipboard.writeText(summary);
|
| 1218 |
+
alert('Executive summary copied to clipboard.');
|
| 1219 |
+
}catch(e){
|
| 1220 |
+
console.warn(e);
|
| 1221 |
+
alert('Copy failed — permission denied.');
|
| 1222 |
+
}
|
| 1223 |
+
});
|
| 1224 |
+
|
| 1225 |
+
/* Export indicators to CSV */
|
| 1226 |
+
$('#exportCSV').addEventListener('click', () => {
|
| 1227 |
+
const rows = data.indicatorsTable;
|
| 1228 |
+
const csv = ['Indicator,Value,Period,Change', ...rows.map(r=>`"${r.indicator}","${r.value}","${r.period}","${r.change}"`)].join('\\n');
|
| 1229 |
+
const blob = new Blob([csv], {type:'text/csv'});
|
| 1230 |
+
const url = URL.createObjectURL(blob);
|
| 1231 |
+
const a = document.createElement('a');
|
| 1232 |
+
a.href = url; a.download = 'vietnam_indicators_2025.csv';
|
| 1233 |
+
document.body.appendChild(a); a.click(); a.remove();
|
| 1234 |
+
URL.revokeObjectURL(url);
|
| 1235 |
+
});
|
| 1236 |
+
|
| 1237 |
+
/* Export JSON */
|
| 1238 |
+
$('#exportJSON').addEventListener('click', () => {
|
| 1239 |
+
const payload = {
|
| 1240 |
+
meta:{title:'Vietnam Economic Growth Report 2025', date:2025},
|
| 1241 |
+
data
|
| 1242 |
+
};
|
| 1243 |
+
const blob = new Blob([JSON.stringify(payload,null,2)],{type:'application/json'});
|
| 1244 |
+
const url = URL.createObjectURL(blob);
|
| 1245 |
+
const a = document.createElement('a');
|
| 1246 |
+
a.href = url; a.download = 'vietnam_report_2025.json';
|
| 1247 |
+
document.body.appendChild(a); a.click(); a.remove();
|
| 1248 |
+
URL.revokeObjectURL(url);
|
| 1249 |
+
});
|
| 1250 |
+
|
| 1251 |
+
/* Print */
|
| 1252 |
+
$('#printBtn').addEventListener('click', () => window.print());
|
| 1253 |
+
|
| 1254 |
+
/* Copy FDI table */
|
| 1255 |
+
$('#copyFDI').addEventListener('click', async () => {
|
| 1256 |
+
const txt = data.fdiMonthly.map(r=>`${r.month}: Registered ${r.registered}B — Disbursed ${r.disbursed}B`).join('\\n');
|
| 1257 |
+
try{ await navigator.clipboard.writeText(txt); alert('FDI table copied to clipboard.'); }catch(e){ alert('Copy failed.'); }
|
| 1258 |
+
});
|
| 1259 |
+
|
| 1260 |
+
/* Download FDI CSV */
|
| 1261 |
+
$('#downloadFDI').addEventListener('click', () => {
|
| 1262 |
+
const csv = ['Month,Registered,Disbursed', ...data.fdiMonthly.map(r=>`${r.month},${r.registered},${r.disbursed}`)].join('\\n');
|
| 1263 |
+
const blob = new Blob([csv],{type:'text/csv'});
|
| 1264 |
+
const url = URL.createObjectURL(blob);
|
| 1265 |
+
const a = document.createElement('a');
|
| 1266 |
+
a.href = url; a.download = 'fdi_jan_may_2025.csv';
|
| 1267 |
+
document.body.appendChild(a); a.click(); a.remove();
|
| 1268 |
+
URL.revokeObjectURL(url);
|
| 1269 |
+
});
|
| 1270 |
+
|
| 1271 |
+
/* Copy references buttons */
|
| 1272 |
+
$$('.references button').forEach(btn=>{
|
| 1273 |
+
btn.addEventListener('click', async () => {
|
| 1274 |
+
const txt = btn.dataset.copy;
|
| 1275 |
+
try{ await navigator.clipboard.writeText(txt); alert('Citation copied.'); }catch(e){ alert('Copy failed.'); }
|
| 1276 |
+
});
|
| 1277 |
+
});
|
| 1278 |
+
$('#copyRefs').addEventListener('click', async () => {
|
| 1279 |
+
const all = data.references.map(r=>r.text).join('\\n');
|
| 1280 |
+
try{ await navigator.clipboard.writeText(all); alert('All references copied.'); }catch(e){ alert('Copy failed.'); }
|
| 1281 |
+
});
|
| 1282 |
+
$('#exportRefsJSON').addEventListener('click', () => {
|
| 1283 |
+
const blob = new Blob([JSON.stringify(data.references,null,2)],{type:'application/json'});
|
| 1284 |
+
const url = URL.createObjectURL(blob);
|
| 1285 |
+
const a = document.createElement('a');
|
| 1286 |
+
a.href = url; a.download = 'references.json';
|
| 1287 |
+
document.body.appendChild(a); a.click(); a.remove();
|
| 1288 |
+
URL.revokeObjectURL(url);
|
| 1289 |
+
});
|
| 1290 |
+
|
| 1291 |
+
/* Export entire visible report to JSON (quick) - reuse exportJSON */
|
| 1292 |
+
|
| 1293 |
+
$('#readMoreExec').addEventListener('click', () => {
|
| 1294 |
+
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.`;
|
| 1295 |
+
alert(more);
|
| 1296 |
+
});
|
| 1297 |
+
|
| 1298 |
+
/* ===========================
|
| 1299 |
+
Table filtering (FDI feature)
|
| 1300 |
+
===========================*/
|
| 1301 |
+
$('#fdifeature').addEventListener('change', (e)=>{
|
| 1302 |
+
const v = e.target.value;
|
| 1303 |
+
const tbody = document.querySelectorAll('#fdiTable tbody tr');
|
| 1304 |
+
tbody.forEach(tr => tr.style.display = 'table-row'); // single view in table; filtering shows/hides columns
|
| 1305 |
+
const cells = $('#fdiTable thead').querySelectorAll('th');
|
| 1306 |
+
if(v === 'registered'){
|
| 1307 |
+
cells[1].style.display='table-cell';
|
| 1308 |
+
cells[2].style.display='none';
|
| 1309 |
+
$('#fdiTable tbody tr').forEach(tr=> tr.children[1].style.display='table-cell', tr.children[2].style.display='none');
|
| 1310 |
+
} else if(v === 'disbursed'){
|
| 1311 |
+
cells[1].style.display='none';
|
| 1312 |
+
cells[2].style.display='table-cell';
|
| 1313 |
+
$('#fdiTable tbody tr').forEach(tr=> tr.children[1].style.display='none', tr.children[2].style.display='table-cell');
|
| 1314 |
+
} else {
|
| 1315 |
+
cells[1].style.display='table-cell';
|
| 1316 |
+
cells[2].style.display='table-cell';
|
| 1317 |
+
$('#fdiTable tbody tr').forEach(tr=> tr.children[1].style.display='table-cell', tr.children[2].style.display='table-cell');
|
| 1318 |
+
}
|
| 1319 |
+
});
|
| 1320 |
+
|
| 1321 |
+
/* ===========================
|
| 1322 |
+
TOC Active Link and Progress (IntersectionObserver)
|
| 1323 |
+
===========================*/
|
| 1324 |
+
const sections = Array.from(document.querySelectorAll('main.content section'));
|
| 1325 |
+
const tocLinks = Array.from(document.querySelectorAll('.toc a'));
|
| 1326 |
+
const progressEl = $('#tocProgress');
|
| 1327 |
+
|
| 1328 |
+
const obs = new IntersectionObserver(entries=>{
|
| 1329 |
+
entries.forEach(entry=>{
|
| 1330 |
+
const id = entry.target.id;
|
| 1331 |
+
const link = document.querySelector('.toc a[data-id="'+id+'"]');
|
| 1332 |
+
if(entry.isIntersecting){
|
| 1333 |
+
tocLinks.forEach(l=>l.classList.remove('active'));
|
| 1334 |
+
if(link) link.classList.add('active');
|
| 1335 |
+
localStorage.setItem('vne_last_section', id);
|
| 1336 |
+
}
|
| 1337 |
+
// calculate progress: visible proportion of document
|
| 1338 |
+
const visible = Math.max(0,entry.intersectionRatio);
|
| 1339 |
+
// approximate progress based on scroll position
|
| 1340 |
+
const docHeight = document.body.scrollHeight - window.innerHeight;
|
| 1341 |
+
const scrolled = window.scrollY;
|
| 1342 |
+
const progressPerc = Math.min(100, Math.round((scrolled/docHeight)*100));
|
| 1343 |
+
progressEl.style.width = progressPerc + '%';
|
| 1344 |
+
progressEl.style.display = 'block';
|
| 1345 |
+
// animate width using transform
|
| 1346 |
+
progressEl.style.transform = `translateX(${0}%)`;
|
| 1347 |
+
$('#tocProgress').style.width = progressPerc + '%';
|
| 1348 |
+
});
|
| 1349 |
+
}, {threshold:[0.15,0.45,0.75]});
|
| 1350 |
+
|
| 1351 |
+
sections.forEach(s => obs.observe(s));
|
| 1352 |
+
|
| 1353 |
+
/* Save & go bookmark (stores last open section id) */
|
| 1354 |
+
$('#saveBookmark').addEventListener('click', () => {
|
| 1355 |
+
const active = document.querySelector('.toc a.active');
|
| 1356 |
+
if(active){
|
| 1357 |
+
const id = active.dataset.id;
|
| 1358 |
+
localStorage.setItem('vne_bookmark', id);
|
| 1359 |
+
alert('Saved current section: '+id);
|
| 1360 |
+
} else alert('No active section selected.');
|
| 1361 |
+
});
|
| 1362 |
+
$('#gotoBookmark').addEventListener('click', () => {
|
| 1363 |
+
const id = localStorage.getItem('vne_bookmark');
|
| 1364 |
+
if(id){
|
| 1365 |
+
const target = document.getElementById(id);
|
| 1366 |
+
target.scrollIntoView({behavior:'smooth', block:'start'});
|
| 1367 |
+
} else alert('No bookmark saved.');
|
| 1368 |
+
});
|
| 1369 |
+
|
| 1370 |
+
/* Restore last section if any */
|
| 1371 |
+
window.addEventListener('load', () => {
|
| 1372 |
+
const last = localStorage.getItem('vne_last_section');
|
| 1373 |
+
if(last){
|
| 1374 |
+
const link = document.querySelector('.toc a[data-id="'+last+'"]');
|
| 1375 |
+
if(link) link.classList.add('active');
|
| 1376 |
+
}
|
| 1377 |
+
});
|
| 1378 |
+
|
| 1379 |
+
/* ===========================
|
| 1380 |
+
Small mini history chart
|
| 1381 |
+
===========================*/
|
| 1382 |
+
function drawMiniHistory(){
|
| 1383 |
+
const holder = $('#miniHistory');
|
| 1384 |
+
clearEl(holder);
|
| 1385 |
+
const w=560,h=160;
|
| 1386 |
+
const svg = createSVG(w,h);
|
| 1387 |
+
holder.appendChild(svg);
|
| 1388 |
+
const padding={t:16,b:30,l:40,r:16};
|
| 1389 |
+
const innerW = w-padding.l-padding.r;
|
| 1390 |
+
const innerH = h-padding.t-padding.b;
|
| 1391 |
+
const values = data.historyQ1.map(d=>d.value);
|
| 1392 |
+
const minV = Math.min(...values)-1;
|
| 1393 |
+
const maxV = Math.max(...values)+1;
|
| 1394 |
+
const xs = idx => padding.l + (idx/(values.length-1))*innerW;
|
| 1395 |
+
const ys = v => padding.t + innerH - ((v-minV)/(maxV-minV))*innerH;
|
| 1396 |
+
const ns = "http://www.w3.org/2000/svg";
|
| 1397 |
+
|
| 1398 |
+
let d = '';
|
| 1399 |
+
data.historyQ1.forEach((pt,i)=>{
|
| 1400 |
+
d += (i===0?'M':'L') + xs(i) + ' ' + ys(pt.value) + ' ';
|
| 1401 |
+
});
|
| 1402 |
+
const path = document.createElementNS(ns,'path');
|
| 1403 |
+
path.setAttribute('d',d);
|
| 1404 |
+
path.setAttribute('fill','none');
|
| 1405 |
+
path.setAttribute('stroke','#0ea5a2');
|
| 1406 |
+
path.setAttribute('stroke-width','2');
|
| 1407 |
+
path.setAttribute('stroke-linecap','round');
|
| 1408 |
+
svg.appendChild(path);
|
| 1409 |
+
|
| 1410 |
+
data.historyQ1.forEach((pt,i)=>{
|
| 1411 |
+
const c = document.createElementNS(ns,'circle');
|
| 1412 |
+
c.setAttribute('cx',xs(i));
|
| 1413 |
+
c.setAttribute('cy',ys(pt.value));
|
| 1414 |
+
c.setAttribute('r',3);
|
| 1415 |
+
c.setAttribute('fill','#fff');
|
| 1416 |
+
c.setAttribute('stroke','#0ea5a2');
|
| 1417 |
+
c.setAttribute('stroke-width',1.5);
|
| 1418 |
+
svg.appendChild(c);
|
| 1419 |
+
});
|
| 1420 |
+
}
|
| 1421 |
+
drawMiniHistory();
|
| 1422 |
+
|
| 1423 |
+
/* ===========================
|
| 1424 |
+
Make FDI table rows clickable to copy
|
| 1425 |
+
===========================*/
|
| 1426 |
+
$('#fdiTable tbody').addEventListener('click', async (e) => {
|
| 1427 |
+
let tr = e.target.closest('tr');
|
| 1428 |
+
if(!tr) return;
|
| 1429 |
+
const cells = Array.from(tr.children).map(td=>td.textContent.trim()).join(' | ');
|
| 1430 |
+
try{ await navigator.clipboard.writeText(cells); alert('Row copied: '+cells); }catch(e){ alert('Copy failed'); }
|
| 1431 |
+
});
|
| 1432 |
+
|
| 1433 |
+
/* Accessibility: keyboard navigation for TOC */
|
| 1434 |
+
$$('.toc a').forEach((a,i)=>{
|
| 1435 |
+
a.setAttribute('tabindex',0);
|
| 1436 |
+
a.addEventListener('keydown', (e)=>{
|
| 1437 |
+
if(e.key === 'Enter') a.click();
|
| 1438 |
+
});
|
| 1439 |
+
});
|
| 1440 |
+
|
| 1441 |
+
/* Resize handlers: redraw charts on resize to keep resolution appropriate */
|
| 1442 |
+
let resizeTimer;
|
| 1443 |
+
window.addEventListener('resize', () => {
|
| 1444 |
+
clearTimeout(resizeTimer);
|
| 1445 |
+
resizeTimer = setTimeout(()=>{ drawHistoryChart(); drawForecastChart(); drawDonut(); drawFDIChart(); drawMiniHistory(); }, 200);
|
| 1446 |
+
});
|
| 1447 |
+
|
| 1448 |
+
/* Initialize other UI states */
|
| 1449 |
+
(function init(){
|
| 1450 |
+
renderFDITable();
|
| 1451 |
+
// populate toc active
|
| 1452 |
+
const hash = location.hash.replace('#','');
|
| 1453 |
+
if(hash){
|
| 1454 |
+
const target = document.getElementById(hash);
|
| 1455 |
+
if(target) target.scrollIntoView();
|
| 1456 |
+
}
|
| 1457 |
+
})();
|
| 1458 |
+
</script>
|
| 1459 |
+
</body>
|
| 1460 |
+
</html>
|