diff --git a/package-lock.json b/package-lock.json
index d4eb7f9..4c7fce0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7,6 +7,12 @@
"": {
"name": "map-journal",
"version": "0.0.0",
+ "dependencies": {
+ "d3": "^7.9.0",
+ "flag-icons": "^7.5.0",
+ "topojson-client": "^3.1.0",
+ "world-atlas": "^2.0.2"
+ },
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^7.1.2",
"svelte": "^5.55.5",
@@ -488,6 +494,416 @@
"node": ">=6"
}
},
+ "node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/d3": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz",
+ "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "3",
+ "d3-axis": "3",
+ "d3-brush": "3",
+ "d3-chord": "3",
+ "d3-color": "3",
+ "d3-contour": "4",
+ "d3-delaunay": "6",
+ "d3-dispatch": "3",
+ "d3-drag": "3",
+ "d3-dsv": "3",
+ "d3-ease": "3",
+ "d3-fetch": "3",
+ "d3-force": "3",
+ "d3-format": "3",
+ "d3-geo": "3",
+ "d3-hierarchy": "3",
+ "d3-interpolate": "3",
+ "d3-path": "3",
+ "d3-polygon": "3",
+ "d3-quadtree": "3",
+ "d3-random": "3",
+ "d3-scale": "4",
+ "d3-scale-chromatic": "3",
+ "d3-selection": "3",
+ "d3-shape": "3",
+ "d3-time": "3",
+ "d3-time-format": "4",
+ "d3-timer": "3",
+ "d3-transition": "3",
+ "d3-zoom": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-array": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
+ "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
+ "license": "ISC",
+ "dependencies": {
+ "internmap": "1 - 2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-axis": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
+ "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-brush": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
+ "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "3",
+ "d3-transition": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-chord": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
+ "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-path": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-contour": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
+ "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
+ "license": "ISC",
+ "dependencies": {
+ "delaunator": "5"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dispatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+ "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-drag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
+ "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-selection": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dsv": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
+ "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
+ "license": "ISC",
+ "dependencies": {
+ "commander": "7",
+ "iconv-lite": "0.6",
+ "rw": "1"
+ },
+ "bin": {
+ "csv2json": "bin/dsv2json.js",
+ "csv2tsv": "bin/dsv2dsv.js",
+ "dsv2dsv": "bin/dsv2dsv.js",
+ "dsv2json": "bin/dsv2json.js",
+ "json2csv": "bin/json2dsv.js",
+ "json2dsv": "bin/json2dsv.js",
+ "json2tsv": "bin/json2dsv.js",
+ "tsv2csv": "bin/dsv2dsv.js",
+ "tsv2json": "bin/dsv2json.js"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-ease": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+ "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-fetch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
+ "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dsv": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-force": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
+ "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-quadtree": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-format": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz",
+ "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-geo": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
+ "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2.5.0 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-hierarchy": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
+ "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-polygon": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
+ "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-quadtree": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
+ "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-random": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
+ "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale-chromatic": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
+ "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-interpolate": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-selection": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
+ "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-shape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-path": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+ "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-time": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-transition": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
+ "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-dispatch": "1 - 3",
+ "d3-ease": "1 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "d3-selection": "2 - 3"
+ }
+ },
+ "node_modules/d3-zoom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
+ "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "2 - 3",
+ "d3-transition": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
@@ -498,6 +914,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/delaunator": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz",
+ "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==",
+ "license": "ISC",
+ "dependencies": {
+ "robust-predicates": "^3.0.2"
+ }
+ },
"node_modules/detect-libc": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
@@ -558,6 +983,12 @@
}
}
},
+ "node_modules/flag-icons": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/flag-icons/-/flag-icons-7.5.0.tgz",
+ "integrity": "sha512-kd+MNXviFIg5hijH766tt+3x76ele1AXlo4zDdCxIvqWZhKt4T83bOtxUOOMlTx/EcFdUMH5yvQgYlFh1EqqFg==",
+ "license": "MIT"
+ },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -573,6 +1004,27 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/internmap": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/is-reference": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
@@ -940,6 +1392,12 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/robust-predicates": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz",
+ "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==",
+ "license": "Unlicense"
+ },
"node_modules/rolldown": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz",
@@ -974,6 +1432,18 @@
"@rolldown/binding-win32-x64-msvc": "1.0.3"
}
},
+ "node_modules/rw": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+ "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -1029,6 +1499,26 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
+ "node_modules/topojson-client": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz",
+ "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==",
+ "license": "ISC",
+ "dependencies": {
+ "commander": "2"
+ },
+ "bin": {
+ "topo2geo": "bin/topo2geo",
+ "topomerge": "bin/topomerge",
+ "topoquantize": "bin/topoquantize"
+ }
+ },
+ "node_modules/topojson-client/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "license": "MIT"
+ },
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -1135,6 +1625,12 @@
}
}
},
+ "node_modules/world-atlas": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/world-atlas/-/world-atlas-2.0.2.tgz",
+ "integrity": "sha512-IXfV0qwlKXpckz1FhwXVwKRjiIhOnWttOskm5CtxMsjgE/MXAYRHWJqgXOpM8IkcPBoXnyTU5lFHcYa5ChG0LQ==",
+ "license": "ISC"
+ },
"node_modules/zimmerframe": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz",
diff --git a/package.json b/package.json
index 0d60aba..c363719 100644
--- a/package.json
+++ b/package.json
@@ -12,5 +12,11 @@
"@sveltejs/vite-plugin-svelte": "^7.1.2",
"svelte": "^5.55.5",
"vite": "^8.0.12"
+ },
+ "dependencies": {
+ "d3": "^7.9.0",
+ "flag-icons": "^7.5.0",
+ "topojson-client": "^3.1.0",
+ "world-atlas": "^2.0.2"
}
}
diff --git a/src/App.svelte b/src/App.svelte
index d6a918d..35ecae6 100644
--- a/src/App.svelte
+++ b/src/App.svelte
@@ -1,14 +1,36 @@
-
-
-
+ (screen = s)}>
+ {#if screen === 'worldmap'}
+
+ {:else}
+
+ {/if}
+
diff --git a/src/app.css b/src/app.css
index 527d4fb..26d4db9 100644
--- a/src/app.css
+++ b/src/app.css
@@ -1,296 +1,128 @@
+@import url('https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,200;12..96,300;12..96,400&display=swap');
+
+/* ── Color tokens ─────────────────────────────────────────── */
:root {
- --text: #6b6375;
- --text-h: #08060d;
- --bg: #fff;
- --border: #e5e4e7;
- --code-bg: #f4f3ec;
- --accent: #aa3bff;
- --accent-bg: rgba(170, 59, 255, 0.1);
- --accent-border: rgba(170, 59, 255, 0.5);
- --social-bg: rgba(244, 243, 236, 0.5);
- --shadow:
- rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;
+ /* Indigo & Lavender palette */
+ --accent: #6366f1; /* indigo-500 */
+ --accent-dark: #4f46e5; /* indigo-600 */
+ --accent-light: #818cf8; /* indigo-400 */
+ --accent-bg: rgba(99, 102, 241, 0.08);
+ --accent-border: rgba(99, 102, 241, 0.35);
- --sans: system-ui, 'Segoe UI', Roboto, sans-serif;
- --heading: system-ui, 'Segoe UI', Roboto, sans-serif;
- --mono: ui-monospace, Consolas, monospace;
+ --lavender: #c4b5fd; /* violet-300 — lavender highlight */
+ --lavender-bg: rgba(196, 181, 253, 0.15);
- font: 18px/145% var(--sans);
- letter-spacing: 0.18px;
- color-scheme: light dark;
+ /* Neutrals */
+ --text: #6b7280; /* body copy */
+ --text-h: #1e1b4b; /* headings — deep indigo-950 */
+ --text-sub: #9ca3af; /* captions / secondary */
+ --bg: #fafafe; /* faint indigo tint on white */
+ --bg-raised: #ffffff;
+ --border: #e0e7ff; /* indigo-100 */
+ --shadow: rgba(99, 102, 241, 0.12) 0 4px 24px -4px;
+
+ /* Typography */
+ --sans: 'Bricolage Grotesque', system-ui, sans-serif;
+ --heading: 'Bricolage Grotesque', system-ui, sans-serif;
+ --mono: ui-monospace, Consolas, monospace;
+
+ /* Type scale */
+ --text-xs: 11px;
+ --text-sm: 13px;
+ --text-base: 14px;
+ --text-md: 16px;
+ --text-lg: 20px;
+ --text-xl: 26px;
+ --text-2xl: 32px;
+
+ font-family: var(--sans);
+ font-size: var(--text-base);
+ line-height: 1.6;
+ font-weight: 400;
+ letter-spacing: 0.01em;
color: var(--text);
background: var(--bg);
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-
- @media (max-width: 1024px) {
- font-size: 16px;
- }
}
+/* ── Dark mode ────────────────────────────────────────────── */
@media (prefers-color-scheme: dark) {
:root {
- --text: #9ca3af;
- --text-h: #f3f4f6;
- --bg: #16171d;
- --border: #2e303a;
- --code-bg: #1f2028;
- --accent: #c084fc;
- --accent-bg: rgba(192, 132, 252, 0.15);
- --accent-border: rgba(192, 132, 252, 0.5);
- --social-bg: rgba(47, 48, 58, 0.5);
- --shadow:
- rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
- }
+ --accent: #818cf8;
+ --accent-dark: #6366f1;
+ --accent-light: #a5b4fc;
+ --accent-bg: rgba(129, 140, 248, 0.12);
+ --accent-border: rgba(129, 140, 248, 0.35);
- #social .button-icon {
- filter: invert(1) brightness(2);
+ --lavender: #a78bfa;
+ --lavender-bg: rgba(167, 139, 250, 0.12);
+
+ --text: #9ca3af;
+ --text-h: #e0e7ff;
+ --text-sub: #6b7280;
+ --bg: #0f0e1a;
+ --bg-raised: #16152a;
+ --border: #2e2a5e;
+ --shadow: rgba(0, 0, 0, 0.5) 0 4px 24px -4px;
}
}
-body {
+/* ── Reset ────────────────────────────────────────────────── */
+*, *::before, *::after {
+ box-sizing: border-box;
margin: 0;
+ padding: 0;
}
-h1,
-h2 {
- font-family: var(--heading);
- font-weight: 500;
+html, body, #app {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+}
+
+/* ── Text hierarchy ───────────────────────────────────────── */
+h1 {
+ font-size: var(--text-2xl);
+ font-weight: 400;
+ line-height: 1.15;
+ letter-spacing: -0.6px;
color: var(--text-h);
}
-h1 {
- font-size: 56px;
- letter-spacing: -1.68px;
- margin: 32px 0;
- @media (max-width: 1024px) {
- font-size: 36px;
- margin: 20px 0;
- }
-}
h2 {
- font-size: 24px;
- line-height: 118%;
- letter-spacing: -0.24px;
- margin: 0 0 8px;
- @media (max-width: 1024px) {
- font-size: 20px;
- }
+ font-size: var(--text-xl);
+ font-weight: 400;
+ line-height: 1.2;
+ letter-spacing: -0.4px;
+ color: var(--text-h);
}
+
+h3 {
+ font-size: var(--text-lg);
+ font-weight: 300;
+ line-height: 1.3;
+ color: var(--text-h);
+}
+
+h4, h5, h6 {
+ font-size: var(--text-md);
+ font-weight: 300;
+ color: var(--text-h);
+}
+
p {
margin: 0;
+ color: var(--text);
}
-code,
-.counter {
- font-family: var(--mono);
- display: inline-flex;
- border-radius: 4px;
- color: var(--text-h);
-}
-
-code {
- font-size: 15px;
- line-height: 135%;
- padding: 4px 8px;
- background: var(--code-bg);
-}
-
-.counter {
- font-size: 16px;
- padding: 5px 10px;
- border-radius: 5px;
+/* Eyebrow / label style */
+.label-xs {
+ font-size: var(--text-xs);
+ font-weight: 600;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
color: var(--accent);
- background: var(--accent-bg);
- border: 2px solid transparent;
- transition: border-color 0.3s;
- margin-bottom: 24px;
-
- &:hover {
- border-color: var(--accent-border);
- }
- &:focus-visible {
- outline: 2px solid var(--accent);
- outline-offset: 2px;
- }
-}
-
-.hero {
- position: relative;
-
- .base,
- .framework,
- .vite {
- inset-inline: 0;
- margin: 0 auto;
- }
-
- .base {
- width: 170px;
- position: relative;
- z-index: 0;
- }
-
- .framework,
- .vite {
- position: absolute;
- }
-
- .framework {
- z-index: 1;
- top: 34px;
- height: 28px;
- transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
- scale(1.4);
- }
-
- .vite {
- z-index: 0;
- top: 107px;
- height: 26px;
- width: auto;
- transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
- scale(0.8);
- }
-}
-
-#app {
- width: 1126px;
- max-width: 100%;
- margin: 0 auto;
- text-align: center;
- border-inline: 1px solid var(--border);
- min-height: 100svh;
- display: flex;
- flex-direction: column;
- box-sizing: border-box;
-}
-
-#center {
- display: flex;
- flex-direction: column;
- gap: 25px;
- place-content: center;
- place-items: center;
- flex-grow: 1;
-
- @media (max-width: 1024px) {
- padding: 32px 20px 24px;
- gap: 18px;
- }
-}
-
-#next-steps {
- display: flex;
- border-top: 1px solid var(--border);
- text-align: left;
-
- & > div {
- flex: 1 1 0;
- padding: 32px;
- @media (max-width: 1024px) {
- padding: 24px 20px;
- }
- }
-
- .icon {
- margin-bottom: 16px;
- width: 22px;
- height: 22px;
- }
-
- @media (max-width: 1024px) {
- flex-direction: column;
- text-align: center;
- }
-}
-
-#docs {
- border-right: 1px solid var(--border);
-
- @media (max-width: 1024px) {
- border-right: none;
- border-bottom: 1px solid var(--border);
- }
-}
-
-#next-steps ul {
- list-style: none;
- padding: 0;
- display: flex;
- gap: 8px;
- margin: 32px 0 0;
-
- .logo {
- height: 18px;
- }
-
- a {
- color: var(--text-h);
- font-size: 16px;
- border-radius: 6px;
- background: var(--social-bg);
- display: flex;
- padding: 6px 12px;
- align-items: center;
- gap: 8px;
- text-decoration: none;
- transition: box-shadow 0.3s;
-
- &:hover {
- box-shadow: var(--shadow);
- }
- .button-icon {
- height: 18px;
- width: 18px;
- }
- }
-
- @media (max-width: 1024px) {
- margin-top: 20px;
- flex-wrap: wrap;
- justify-content: center;
-
- li {
- flex: 1 1 calc(50% - 8px);
- }
-
- a {
- width: 100%;
- justify-content: center;
- box-sizing: border-box;
- }
- }
-}
-
-#spacer {
- height: 88px;
- border-top: 1px solid var(--border);
- @media (max-width: 1024px) {
- height: 48px;
- }
-}
-
-.ticks {
- position: relative;
- width: 100%;
-
- &::before,
- &::after {
- content: '';
- position: absolute;
- top: -4.5px;
- border: 5px solid transparent;
- }
-
- &::before {
- left: 0;
- border-left-color: var(--border);
- }
- &::after {
- right: 0;
- border-right-color: var(--border);
- }
}
diff --git a/src/lib/layout/Footer.svelte b/src/lib/layout/Footer.svelte
new file mode 100644
index 0000000..c0a1d01
--- /dev/null
+++ b/src/lib/layout/Footer.svelte
@@ -0,0 +1,24 @@
+
+
+
+
+
diff --git a/src/lib/layout/Layout.svelte b/src/lib/layout/Layout.svelte
new file mode 100644
index 0000000..ecff737
--- /dev/null
+++ b/src/lib/layout/Layout.svelte
@@ -0,0 +1,33 @@
+
+
+
+
+
+ {@render children()}
+
+
+
+
+
diff --git a/src/lib/layout/TopBar.svelte b/src/lib/layout/TopBar.svelte
new file mode 100644
index 0000000..3149de7
--- /dev/null
+++ b/src/lib/layout/TopBar.svelte
@@ -0,0 +1,62 @@
+
+
+
+
+
diff --git a/src/lib/layout/selection.svelte.js b/src/lib/layout/selection.svelte.js
new file mode 100644
index 0000000..c6a0d7f
--- /dev/null
+++ b/src/lib/layout/selection.svelte.js
@@ -0,0 +1,28 @@
+let selected = $state(new Set());
+let totalCountries = $state(0);
+
+export function toggle(id) {
+ const next = new Set(selected);
+ if (next.has(id)) {
+ next.delete(id);
+ } else {
+ next.add(id);
+ }
+ selected = next;
+}
+
+export function clearAll() {
+ selected = new Set();
+}
+
+export function getSelected() {
+ return selected;
+}
+
+export function setTotalCount(n) {
+ totalCountries = n;
+}
+
+export function getTotalCount() {
+ return totalCountries;
+}
diff --git a/src/lib/shared/PhotoGallery.svelte b/src/lib/shared/PhotoGallery.svelte
index 8eb37cf..aa14030 100644
--- a/src/lib/shared/PhotoGallery.svelte
+++ b/src/lib/shared/PhotoGallery.svelte
@@ -153,7 +153,7 @@
top: 14px;
right: 14px;
font-size: 12px;
- font-weight: 500;
+ font-weight: 300;
color: #fff;
background: rgba(0,0,0,0.45);
padding: 3px 10px;
diff --git a/src/lib/timeline/JournalDetail.svelte b/src/lib/timeline/JournalDetail.svelte
index e09bd2f..ac36318 100644
--- a/src/lib/timeline/JournalDetail.svelte
+++ b/src/lib/timeline/JournalDetail.svelte
@@ -82,7 +82,7 @@
align-items: center;
gap: 6px;
font-size: 14px;
- font-weight: 500;
+ font-weight: 300;
color: var(--text, #6b6375);
background: none;
border: none;
@@ -91,7 +91,7 @@
margin-bottom: 28px;
transition: color 0.15s;
}
- .back-btn:hover { color: var(--accent, #aa3bff); }
+ .back-btn:hover { color: var(--accent); }
.hero-gallery-wrap {
border-radius: 16px;
@@ -110,17 +110,17 @@
.badge {
font-size: 12px;
- font-weight: 500;
+ font-weight: 300;
padding: 4px 10px;
border-radius: 20px;
}
- .loc-badge { background: var(--accent-bg, rgba(170,59,255,0.08)); color: var(--accent, #aa3bff); }
+ .loc-badge { background: var(--accent-bg); color: var(--accent); }
.trip-badge--solo { background: rgba(245,158,11,0.12); color: #b45309; }
.trip-badge--friends { background: rgba(59,130,246,0.12); color: #1d4ed8; }
.detail-title {
font-size: 28px;
- font-weight: 700;
+ font-weight: 400;
color: var(--text-h, #08060d);
margin: 0 0 20px;
letter-spacing: -0.6px;
@@ -130,7 +130,7 @@
.stats-row { display: flex; align-items: center; gap: 20px; margin-bottom: 24px; }
.stat { display: flex; flex-direction: column; gap: 2px; }
.stat-label { font-size: 11px; letter-spacing: 1.5px; text-transform: uppercase; color: var(--text, #6b6375); }
- .stat-value { font-size: 15px; font-weight: 600; color: var(--text-h, #08060d); }
+ .stat-value { font-size: 15px; font-weight: 300; color: var(--text-h, #08060d); }
.stat-divider { width: 1px; height: 32px; background: var(--border, #e5e4e7); }
.section-divider { border: none; border-top: 1px solid var(--border, #e5e4e7); margin: 24px 0; }
@@ -140,13 +140,13 @@
.song-row { display: flex; align-items: center; gap: 14px; }
.song-icon-wrap {
width: 44px; height: 44px; border-radius: 50%;
- background: var(--accent-bg, rgba(170,59,255,0.08));
- color: var(--accent, #aa3bff);
+ background: var(--accent-bg);
+ color: var(--accent);
display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.song-text { display: flex; flex-direction: column; gap: 2px; }
.song-label { font-size: 11px; letter-spacing: 1.5px; text-transform: uppercase; color: var(--text, #6b6375); }
- .song-name { font-size: 15px; font-weight: 600; color: var(--text-h, #08060d); }
+ .song-name { font-size: 15px; font-weight: 300; color: var(--text-h, #08060d); }
.song-artist { font-size: 13px; color: var(--text, #6b6375); }
@media (max-width: 600px) {
diff --git a/src/lib/timeline/JournalSummary.svelte b/src/lib/timeline/JournalSummary.svelte
new file mode 100644
index 0000000..de0de9d
--- /dev/null
+++ b/src/lib/timeline/JournalSummary.svelte
@@ -0,0 +1,215 @@
+
+
+{#if stats}
+
+
+
My Journey
+
{stats.yearRange}
+
+
+
+
+
+
+ {stats.countries.length}
+ Countries
+
+
+ {stats.cities.length}
+ Cities
+
+
+ {stats.totalDays}
+ Days abroad
+
+
+ {stats.tripCount}
+ Trips
+
+
+
+
+
+
+
+ Most visited
+ {stats.topCountry[0]}
+ {stats.topCountry[1]} {stats.topCountry[1] === 1 ? 'trip' : 'trips'}
+
+
+
+
+ Latest trip
+ {stats.latest.location.city}
+ {stats.latest.location.country}
+
+
+
+
+
+
+
Trip style
+
+
+ {stats.soloPct}% Solo
+ {100 - stats.soloPct}% Friends
+
+
+
+
+{/if}
+
+
diff --git a/src/lib/timeline/TimelineCard.svelte b/src/lib/timeline/TimelineCard.svelte
index 3af7554..21993ee 100644
--- a/src/lib/timeline/TimelineCard.svelte
+++ b/src/lib/timeline/TimelineCard.svelte
@@ -1,6 +1,4 @@
-
-
-
- ·
- {entry.location.city}, {entry.location.country}
- ·
- {entry.days} {entry.days === 1 ? 'day' : 'days'}
-
-
e.key === 'Enter' && onClick()}>
+
e.key === 'Enter' && onClick()}>
-
-
-
-
{entry.title}
- {#if entry.memo}
-
{entry.memo}
+
+
0}>
+
+ {#if mainPhoto}
+

+ {:else}
+
{/if}
-
-
-
{entry.song.title}
-
·
-
{entry.song.artist}
-
- {entry.tripType === 'solo' ? 'Solo' : 'With Friends'}
-
+
+
+
+
+
+ {entry.tripType === 'solo' ? 'Solo' : 'With Friends'}
+
+
+
+ {entry.location.city}
+ {entry.location.country}
+ {formatDate(entry.date)} · {entry.days} {entry.days === 1 ? 'day' : 'days'}
+
+
+ {#if thumbPhotos.length > 0}
+
+ {#each thumbPhotos as photo, i}
+
+

+ {#if i === 2 && extraCount > 0}
+
+ {/if}
+
+ {/each}
+
+ {/if}
+
diff --git a/src/lib/timeline/TimelineToolbar.svelte b/src/lib/timeline/TimelineToolbar.svelte
index 93900ec..3e173b1 100644
--- a/src/lib/timeline/TimelineToolbar.svelte
+++ b/src/lib/timeline/TimelineToolbar.svelte
@@ -41,13 +41,13 @@
font-size: 11px;
letter-spacing: 3px;
text-transform: uppercase;
- color: var(--accent, #aa3bff);
+ color: var(--accent);
margin: 0 0 6px;
}
.page-title {
font-size: 32px;
- font-weight: 700;
+ font-weight: 400;
color: var(--text-h, #08060d);
margin: 0;
letter-spacing: -0.8px;
@@ -69,7 +69,7 @@
background-repeat: no-repeat;
background-position: right 10px center;
}
- select:focus { outline: 2px solid var(--accent, #aa3bff); outline-offset: 2px; }
+ select:focus { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (max-width: 600px) {
.page-title { font-size: 26px; }
diff --git a/src/lib/timeline/TimelineView.svelte b/src/lib/timeline/TimelineView.svelte
index cb54b78..4d9f541 100644
--- a/src/lib/timeline/TimelineView.svelte
+++ b/src/lib/timeline/TimelineView.svelte
@@ -4,6 +4,7 @@
import TimelineToolbar from './TimelineToolbar.svelte';
import TimelineCard from './TimelineCard.svelte';
import JournalDetail from './JournalDetail.svelte';
+ import JournalSummary from './JournalSummary.svelte';
/** @type {import('../stores/journalStore.js').JournalEntry|null} */
let selected = $state(null);
@@ -27,38 +28,86 @@
return 0;
});
});
+
+ function getYear(iso) {
+ return new Date(iso).getFullYear();
+ }
-{#if selected}
-
(selected = null)} />
-{:else}
-
- (sortKey = k)} />
+
- {#if sortedEntries.length === 0}
-
No journal entries yet.
- {:else}
-
- {#each sortedEntries as entry (entry.id)}
- (selected = entry)} />
- {/each}
-
- {/if}
+ {#if selected}
+
+
+ (selected = null)} />
+
+ {:else}
+
+
-
-
-{/if}
+
+
(sortKey = k)} />
+
+ {#if sortedEntries.length === 0}
+ No journal entries yet.
+ {:else}
+
+ {#each sortedEntries as entry, i (entry.id)}
+ {#if i === 0 || getYear(entry.date) !== getYear(sortedEntries[i - 1].date)}
+ -
+ {getYear(entry.date)}
+
+ {/if}
+ (selected = entry)} />
+ {/each}
+
+ {/if}
+
+
+
+ {/if}
+
+
diff --git a/src/lib/world-map/StatsPanel.svelte b/src/lib/world-map/StatsPanel.svelte
new file mode 100644
index 0000000..cdfe792
--- /dev/null
+++ b/src/lib/world-map/StatsPanel.svelte
@@ -0,0 +1,307 @@
+
+
+
+
+
+ {#if !collapsed}
+
+
your statistics
+
+
visited countries
+
+
+
{total} / {grandTotal}
+
+
+
+
+
by continent
+ {#each CONTINENTS as continent}
+ {@const contTotal = continentTotals[continent]}
+
+
+ {continent}
+ {counts[continent]}/{contTotal}
+
+ {/each}
+
+
+ {#if segments.length > 0}
+
+ {:else}
+
+ {/if}
+
+
+
+
+
Contains all UN countries, Kosovo, Hong Kong and Taiwan
+
+ {/if}
+
+
+
diff --git a/src/lib/world-map/WorldMap.svelte b/src/lib/world-map/WorldMap.svelte
new file mode 100644
index 0000000..46571f4
--- /dev/null
+++ b/src/lib/world-map/WorldMap.svelte
@@ -0,0 +1,159 @@
+
+
+
+
+
diff --git a/src/lib/world-map/continents.js b/src/lib/world-map/continents.js
new file mode 100644
index 0000000..7a5ad24
--- /dev/null
+++ b/src/lib/world-map/continents.js
@@ -0,0 +1,212 @@
+export const CONTINENTS = ['Europe', 'Asia', 'Africa', 'N. America', 'S. America', 'Oceania'];
+
+const map = {
+ '004': 'Asia', // Afghanistan
+ '008': 'Europe', // Albania
+ '012': 'Africa', // Algeria
+ '020': 'Europe', // Andorra
+ '024': 'Africa', // Angola
+ '028': 'N. America', // Antigua and Barb.
+ '031': 'Asia', // Azerbaijan
+ '032': 'S. America', // Argentina
+ '036': 'Oceania', // Australia
+ '040': 'Europe', // Austria
+ '044': 'N. America', // Bahamas
+ '048': 'Asia', // Bahrain
+ '050': 'Asia', // Bangladesh
+ '051': 'Asia', // Armenia
+ '052': 'N. America', // Barbados
+ '056': 'Europe', // Belgium
+ '064': 'Asia', // Bhutan
+ '068': 'S. America', // Bolivia
+ '070': 'Europe', // Bosnia and Herz.
+ '072': 'Africa', // Botswana
+ '076': 'S. America', // Brazil
+ '084': 'N. America', // Belize
+ '090': 'Oceania', // Solomon Is.
+ '096': 'Asia', // Brunei
+ '100': 'Europe', // Bulgaria
+ '104': 'Asia', // Myanmar
+ '108': 'Africa', // Burundi
+ '112': 'Europe', // Belarus
+ '116': 'Asia', // Cambodia
+ '120': 'Africa', // Cameroon
+ '124': 'N. America', // Canada
+ '132': 'Africa', // Cabo Verde
+ '140': 'Africa', // Central African Rep.
+ '144': 'Asia', // Sri Lanka
+ '148': 'Africa', // Chad
+ '152': 'S. America', // Chile
+ '156': 'Asia', // China
+ '158': 'Asia', // Taiwan
+ '170': 'S. America', // Colombia
+ '174': 'Africa', // Comoros
+ '178': 'Africa', // Congo
+ '180': 'Africa', // Dem. Rep. Congo
+ '188': 'N. America', // Costa Rica
+ '191': 'Europe', // Croatia
+ '192': 'N. America', // Cuba
+ '196': 'Asia', // Cyprus
+ '203': 'Europe', // Czechia
+ '204': 'Africa', // Benin
+ '208': 'Europe', // Denmark
+ '212': 'N. America', // Dominica
+ '214': 'N. America', // Dominican Rep.
+ '218': 'S. America', // Ecuador
+ '222': 'N. America', // El Salvador
+ '226': 'Africa', // Eq. Guinea
+ '231': 'Africa', // Ethiopia
+ '232': 'Africa', // Eritrea
+ '233': 'Europe', // Estonia
+ '242': 'Oceania', // Fiji
+ '246': 'Europe', // Finland
+ '250': 'Europe', // France
+ '262': 'Africa', // Djibouti
+ '266': 'Africa', // Gabon
+ '268': 'Asia', // Georgia
+ '270': 'Africa', // Gambia
+ '275': 'Asia', // Palestine
+ '276': 'Europe', // Germany
+ '288': 'Africa', // Ghana
+ '296': 'Oceania', // Kiribati
+ '300': 'Europe', // Greece
+ '308': 'N. America', // Grenada
+ '320': 'N. America', // Guatemala
+ '324': 'Africa', // Guinea
+ '328': 'S. America', // Guyana
+ '332': 'N. America', // Haiti
+ '336': 'Europe', // Vatican
+ '340': 'N. America', // Honduras
+ '344': 'Asia', // Hong Kong
+ '348': 'Europe', // Hungary
+ '352': 'Europe', // Iceland
+ '356': 'Asia', // India
+ '360': 'Asia', // Indonesia
+ '364': 'Asia', // Iran
+ '368': 'Asia', // Iraq
+ '372': 'Europe', // Ireland
+ '376': 'Asia', // Israel
+ '380': 'Europe', // Italy
+ '384': 'Africa', // Cote d'Ivoire
+ '388': 'N. America', // Jamaica
+ '392': 'Asia', // Japan
+ '398': 'Asia', // Kazakhstan
+ '400': 'Asia', // Jordan
+ '404': 'Africa', // Kenya
+ '408': 'Asia', // North Korea
+ '410': 'Asia', // South Korea
+ '414': 'Asia', // Kuwait
+ '417': 'Asia', // Kyrgyzstan
+ '418': 'Asia', // Laos
+ '422': 'Asia', // Lebanon
+ '426': 'Africa', // Lesotho
+ '428': 'Europe', // Latvia
+ '430': 'Africa', // Liberia
+ '434': 'Africa', // Libya
+ '438': 'Europe', // Liechtenstein
+ '440': 'Europe', // Lithuania
+ '442': 'Europe', // Luxembourg
+ '450': 'Africa', // Madagascar
+ '454': 'Africa', // Malawi
+ '458': 'Asia', // Malaysia
+ '462': 'Asia', // Maldives
+ '466': 'Africa', // Mali
+ '470': 'Europe', // Malta
+ '478': 'Africa', // Mauritania
+ '480': 'Africa', // Mauritius
+ '484': 'N. America', // Mexico
+ '492': 'Europe', // Monaco
+ '496': 'Asia', // Mongolia
+ '498': 'Europe', // Moldova
+ '499': 'Europe', // Montenegro
+ '504': 'Africa', // Morocco
+ '508': 'Africa', // Mozambique
+ '512': 'Asia', // Oman
+ '516': 'Africa', // Namibia
+ '520': 'Oceania', // Nauru
+ '524': 'Asia', // Nepal
+ '528': 'Europe', // Netherlands
+ '548': 'Oceania', // Vanuatu
+ '554': 'Oceania', // New Zealand
+ '558': 'N. America', // Nicaragua
+ '562': 'Africa', // Niger
+ '566': 'Africa', // Nigeria
+ '578': 'Europe', // Norway
+ '583': 'Oceania', // Micronesia
+ '584': 'Oceania', // Marshall Is.
+ '585': 'Oceania', // Palau
+ '586': 'Asia', // Pakistan
+ '591': 'N. America', // Panama
+ '598': 'Oceania', // Papua New Guinea
+ '600': 'S. America', // Paraguay
+ '604': 'S. America', // Peru
+ '608': 'Asia', // Philippines
+ '616': 'Europe', // Poland
+ '620': 'Europe', // Portugal
+ '624': 'Africa', // Guinea-Bissau
+ '626': 'Asia', // Timor-Leste
+ '634': 'Asia', // Qatar
+ '642': 'Europe', // Romania
+ '643': 'Europe', // Russia
+ '646': 'Africa', // Rwanda
+ '659': 'N. America', // St. Kitts and Nevis
+ '662': 'N. America', // Saint Lucia
+ '670': 'N. America', // St. Vin. and Gren.
+ '674': 'Europe', // San Marino
+ '678': 'Africa', // Sao Tome and Principe
+ '682': 'Asia', // Saudi Arabia
+ '686': 'Africa', // Senegal
+ '688': 'Europe', // Serbia
+ '690': 'Africa', // Seychelles
+ '694': 'Africa', // Sierra Leone
+ '702': 'Asia', // Singapore
+ '703': 'Europe', // Slovakia
+ '704': 'Asia', // Vietnam
+ '705': 'Europe', // Slovenia
+ '706': 'Africa', // Somalia
+ '710': 'Africa', // South Africa
+ '716': 'Africa', // Zimbabwe
+ '724': 'Europe', // Spain
+ '728': 'Africa', // S. Sudan
+ '729': 'Africa', // Sudan
+ '732': 'Africa', // W. Sahara
+ '740': 'S. America', // Suriname
+ '748': 'Africa', // eSwatini
+ '752': 'Europe', // Sweden
+ '756': 'Europe', // Switzerland
+ '760': 'Asia', // Syria
+ '762': 'Asia', // Tajikistan
+ '764': 'Asia', // Thailand
+ '768': 'Africa', // Togo
+ '776': 'Oceania', // Tonga
+ '780': 'N. America', // Trinidad and Tobago
+ '784': 'Asia', // United Arab Emirates
+ '788': 'Africa', // Tunisia
+ '792': 'Asia', // Turkey
+ '795': 'Asia', // Turkmenistan
+ '800': 'Africa', // Uganda
+ '804': 'Europe', // Ukraine
+ '807': 'Europe', // Macedonia
+ '818': 'Africa', // Egypt
+ '826': 'Europe', // United Kingdom
+ '834': 'Africa', // Tanzania
+ '840': 'N. America', // United States of America
+ '854': 'Africa', // Burkina Faso
+ '858': 'S. America', // Uruguay
+ '860': 'Asia', // Uzbekistan
+ '862': 'S. America', // Venezuela
+ '882': 'Oceania', // Samoa
+ '887': 'Asia', // Yemen
+ '894': 'Africa', // Zambia
+ 'XK': 'Europe', // Kosovo
+};
+
+export const continentTotals = CONTINENTS.reduce((acc, c) => (acc[c] = 0, acc), {});
+for (const id of Object.keys(map)) {
+ const cont = map[id];
+ if (cont) continentTotals[cont]++;
+}
+
+export function getContinent(id) {
+ return map[id] ?? null;
+}