diff --git a/bun.lock b/bun.lock
index d44c5db..24ab65a 100644
--- a/bun.lock
+++ b/bun.lock
@@ -9,7 +9,7 @@
},
"devDependencies": {
"@playwright/test": "^1.58.0",
- "@screenly/edge-apps": "^1.2.1",
+ "@screenly/edge-apps": "^1.4.0",
"@types/bun": "^1.3.9",
"bun-types": "^1.3.9",
"chart.js": "^4.4.0",
@@ -46,7 +46,7 @@
"@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="],
- "@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
+ "@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="],
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="],
@@ -124,53 +124,57 @@
"@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="],
- "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="],
+ "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.2" }, "os": "darwin", "cpu": "arm64" }, "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg=="],
- "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="],
+ "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.2" }, "os": "darwin", "cpu": "x64" }, "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w=="],
- "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="],
+ "@img/sharp-freebsd-wasm32": ["@img/sharp-freebsd-wasm32@0.35.3", "", { "dependencies": { "@img/sharp-wasm32": "0.35.3" }, "os": "freebsd" }, "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg=="],
- "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="],
+ "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg=="],
- "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="],
+ "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw=="],
- "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="],
+ "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.2", "", { "os": "linux", "cpu": "arm" }, "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ=="],
- "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="],
+ "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA=="],
- "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="],
+ "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.3.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw=="],
- "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="],
+ "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.3.2", "", { "os": "linux", "cpu": "none" }, "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w=="],
- "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="],
+ "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ=="],
- "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="],
+ "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w=="],
- "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="],
+ "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw=="],
- "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="],
+ "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ=="],
- "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="],
+ "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.2" }, "os": "linux", "cpu": "arm" }, "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA=="],
- "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="],
+ "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.2" }, "os": "linux", "cpu": "arm64" }, "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ=="],
- "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="],
+ "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.3.2" }, "os": "linux", "cpu": "ppc64" }, "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA=="],
- "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="],
+ "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.3.2" }, "os": "linux", "cpu": "none" }, "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ=="],
- "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="],
+ "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.2" }, "os": "linux", "cpu": "s390x" }, "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw=="],
- "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="],
+ "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.2" }, "os": "linux", "cpu": "x64" }, "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA=="],
- "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="],
+ "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" }, "os": "linux", "cpu": "arm64" }, "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w=="],
- "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="],
+ "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.2" }, "os": "linux", "cpu": "x64" }, "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg=="],
- "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="],
+ "@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.3", "", { "dependencies": { "@emnapi/runtime": "^1.11.1" } }, "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w=="],
- "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="],
+ "@img/sharp-webcontainers-wasm32": ["@img/sharp-webcontainers-wasm32@0.35.3", "", { "dependencies": { "@img/sharp-wasm32": "0.35.3" }, "cpu": "none" }, "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q=="],
- "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="],
+ "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.35.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w=="],
+
+ "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw=="],
+
+ "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.3", "", { "os": "win32", "cpu": "x64" }, "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
@@ -420,7 +424,7 @@
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="],
- "@screenly/edge-apps": ["@screenly/edge-apps@1.2.1", "", { "dependencies": { "@eslint/js": "^10.0.1", "@photostructure/tz-lookup": "^11.5.0", "@sentry/browser": "^10.59.0", "@tailwindcss/vite": "^4.2.1", "country-locale-map": "^1.9.12", "dayjs": "^1.11.19", "eslint": "^10.0.3", "jiti": "^2.6.1", "jsdom": "^28.1.0", "offline-geocode-city": "^1.0.2", "panic-overlay": "^1.0.51", "sharp": "^0.34.5", "tailwindcss": "^4.2.1", "typescript": "^5.9.3", "typescript-eslint": "^8.57.0", "vite": "^8.1.2", "yaml": "^2.8.2" }, "peerDependencies": { "@playwright/test": "^1.58.0" }, "optionalPeers": ["@playwright/test"], "bin": { "edge-apps-scripts": "bin/edge-apps-scripts.js" } }, "sha512-9CTA8eaYtVlrACHWU9uhml3Yf7hIgc6mELmXhqDdHKDC2fmrgma3sAOpvr0sBjTJr37QRvpeqUjlZ9BJlwT0nQ=="],
+ "@screenly/edge-apps": ["@screenly/edge-apps@1.4.0", "", { "dependencies": { "@eslint/js": "^10.0.1", "@photostructure/tz-lookup": "^11.5.0", "@sentry/browser": "^10.59.0", "@tailwindcss/vite": "^4.2.1", "country-locale-map": "^1.9.12", "dayjs": "^1.11.19", "eslint": "^10.0.3", "jiti": "^2.6.1", "jsdom": "^28.1.0", "offline-geocode-city": "^1.0.2", "panic-overlay": "^1.0.51", "sharp": "^0.35.3", "tailwindcss": "^4.2.1", "typescript": "^5.9.3", "typescript-eslint": "^8.57.0", "vite": "^8.1.2", "yaml": "^2.8.2" }, "peerDependencies": { "@playwright/test": "^1.58.0" }, "optionalPeers": ["@playwright/test"], "bin": { "edge-apps-scripts": "bin/edge-apps-scripts.js" } }, "sha512-+tMvSLoZQ/rmkmrY+gmlQpFpWN4NgRa+TDQN83pDyo6xg/2tC2DQuZoHwl6xTSZ2P4q2mIhhqeC6+VV+Ltaktg=="],
"@sentry/browser": ["@sentry/browser@10.66.0", "", { "dependencies": { "@sentry/browser-utils": "10.66.0", "@sentry/conventions": "^0.16.0", "@sentry/core": "10.66.0", "@sentry/feedback": "10.66.0", "@sentry/replay": "10.66.0", "@sentry/replay-canvas": "10.66.0" } }, "sha512-MaPoBqKvI7O3UhexSJ/KO/o6T4Tr3A+vQWLZYTos0mxd59jVKMKbbJ6LxM/0uWQ5JAO2XYC/kCMRvk6VqQ5QZw=="],
@@ -880,11 +884,11 @@
"saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="],
- "semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="],
+ "semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="],
"setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="],
- "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="],
+ "sharp": ["sharp@0.35.3", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.3", "@img/sharp-darwin-x64": "0.35.3", "@img/sharp-freebsd-wasm32": "0.35.3", "@img/sharp-libvips-darwin-arm64": "1.3.2", "@img/sharp-libvips-darwin-x64": "1.3.2", "@img/sharp-libvips-linux-arm": "1.3.2", "@img/sharp-libvips-linux-arm64": "1.3.2", "@img/sharp-libvips-linux-ppc64": "1.3.2", "@img/sharp-libvips-linux-riscv64": "1.3.2", "@img/sharp-libvips-linux-s390x": "1.3.2", "@img/sharp-libvips-linux-x64": "1.3.2", "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", "@img/sharp-libvips-linuxmusl-x64": "1.3.2", "@img/sharp-linux-arm": "0.35.3", "@img/sharp-linux-arm64": "0.35.3", "@img/sharp-linux-ppc64": "0.35.3", "@img/sharp-linux-riscv64": "0.35.3", "@img/sharp-linux-s390x": "0.35.3", "@img/sharp-linux-x64": "0.35.3", "@img/sharp-linuxmusl-arm64": "0.35.3", "@img/sharp-linuxmusl-x64": "0.35.3", "@img/sharp-webcontainers-wasm32": "0.35.3", "@img/sharp-win32-arm64": "0.35.3", "@img/sharp-win32-ia32": "0.35.3", "@img/sharp-win32-x64": "0.35.3" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q=="],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
@@ -982,9 +986,19 @@
"@jsheaven/easybuild/typescript": ["typescript@4.9.5", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g=="],
+ "@parcel/core/semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="],
+
+ "@parcel/node-resolver-core/semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="],
+
+ "@parcel/package-manager/semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="],
+
"@parcel/source-map/detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
- "@rolldown/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="],
+ "@parcel/transformer-babel/semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="],
+
+ "@parcel/transformer-js/semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="],
+
+ "@parcel/transformer-postcss/semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="],
@@ -1000,6 +1014,8 @@
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
+ "@typescript-eslint/typescript-estree/semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="],
+
"@typescript-eslint/typescript-estree/tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
"anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
diff --git a/index.html b/index.html
index efc29be..e1d6683 100644
--- a/index.html
+++ b/index.html
@@ -6,35 +6,40 @@
Salesforce Analytics - Screenly Edge App
-
+
-
+
-
+
-
-
-
+
-
+
-
-
Something went wrong
+
+
Something went wrong
The Salesforce content could not be loaded. Please check your
configuration or contact your administrator.
-
diff --git a/package.json b/package.json
index 3de75da..94f5408 100644
--- a/package.json
+++ b/package.json
@@ -24,7 +24,7 @@
"prettier": "./.prettierrc.json",
"devDependencies": {
"@playwright/test": "^1.58.0",
- "@screenly/edge-apps": "^1.1.2",
+ "@screenly/edge-apps": "^1.4.0",
"@types/bun": "^1.3.9",
"bun-types": "^1.3.9",
"chart.js": "^4.4.0",
diff --git a/screenshots/dashboard-1080x1920.webp b/screenshots/dashboard-1080x1920.webp
index eb43b96..4ad73ab 100644
Binary files a/screenshots/dashboard-1080x1920.webp and b/screenshots/dashboard-1080x1920.webp differ
diff --git a/screenshots/dashboard-1280x720.webp b/screenshots/dashboard-1280x720.webp
index 63156ee..64e1b1c 100644
Binary files a/screenshots/dashboard-1280x720.webp and b/screenshots/dashboard-1280x720.webp differ
diff --git a/screenshots/dashboard-1920x1080.webp b/screenshots/dashboard-1920x1080.webp
index 0c0275c..26568c4 100644
Binary files a/screenshots/dashboard-1920x1080.webp and b/screenshots/dashboard-1920x1080.webp differ
diff --git a/screenshots/dashboard-2160x3840.webp b/screenshots/dashboard-2160x3840.webp
index 3697598..5212779 100644
Binary files a/screenshots/dashboard-2160x3840.webp and b/screenshots/dashboard-2160x3840.webp differ
diff --git a/screenshots/dashboard-2160x4096.webp b/screenshots/dashboard-2160x4096.webp
index b17c216..75719a9 100644
Binary files a/screenshots/dashboard-2160x4096.webp and b/screenshots/dashboard-2160x4096.webp differ
diff --git a/screenshots/dashboard-3840x2160.webp b/screenshots/dashboard-3840x2160.webp
index 3c3ef7b..556a767 100644
Binary files a/screenshots/dashboard-3840x2160.webp and b/screenshots/dashboard-3840x2160.webp differ
diff --git a/screenshots/dashboard-4096x2160.webp b/screenshots/dashboard-4096x2160.webp
index 507672a..c3bf9b6 100644
Binary files a/screenshots/dashboard-4096x2160.webp and b/screenshots/dashboard-4096x2160.webp differ
diff --git a/screenshots/dashboard-480x800.webp b/screenshots/dashboard-480x800.webp
index 79077c6..defa33f 100644
Binary files a/screenshots/dashboard-480x800.webp and b/screenshots/dashboard-480x800.webp differ
diff --git a/screenshots/dashboard-720x1280.webp b/screenshots/dashboard-720x1280.webp
index 0d54734..913a728 100644
Binary files a/screenshots/dashboard-720x1280.webp and b/screenshots/dashboard-720x1280.webp differ
diff --git a/screenshots/dashboard-800x480.webp b/screenshots/dashboard-800x480.webp
index 5e978e3..d60b399 100644
Binary files a/screenshots/dashboard-800x480.webp and b/screenshots/dashboard-800x480.webp differ
diff --git a/screenshots/dashboard-labels-1080x1920.webp b/screenshots/dashboard-labels-1080x1920.webp
index 907c86d..bf2594f 100644
Binary files a/screenshots/dashboard-labels-1080x1920.webp and b/screenshots/dashboard-labels-1080x1920.webp differ
diff --git a/screenshots/dashboard-labels-1920x1080.webp b/screenshots/dashboard-labels-1920x1080.webp
index 5bdb1e4..782463f 100644
Binary files a/screenshots/dashboard-labels-1920x1080.webp and b/screenshots/dashboard-labels-1920x1080.webp differ
diff --git a/screenshots/error-2160x3840.webp b/screenshots/error-2160x3840.webp
index 3465fb2..0fa858e 100644
Binary files a/screenshots/error-2160x3840.webp and b/screenshots/error-2160x3840.webp differ
diff --git a/screenshots/error-3840x2160.webp b/screenshots/error-3840x2160.webp
index c67bf68..eccda2d 100644
Binary files a/screenshots/error-3840x2160.webp and b/screenshots/error-3840x2160.webp differ
diff --git a/screenshots/error-not-found-3840x2160.webp b/screenshots/error-not-found-3840x2160.webp
index cfce75c..8d297db 100644
Binary files a/screenshots/error-not-found-3840x2160.webp and b/screenshots/error-not-found-3840x2160.webp differ
diff --git a/screenshots/report-2160x3840.webp b/screenshots/report-2160x3840.webp
index 693ef86..f05730a 100644
Binary files a/screenshots/report-2160x3840.webp and b/screenshots/report-2160x3840.webp differ
diff --git a/screenshots/report-3840x2160.webp b/screenshots/report-3840x2160.webp
index 4eb4e8e..9e1a9cc 100644
Binary files a/screenshots/report-3840x2160.webp and b/screenshots/report-3840x2160.webp differ
diff --git a/screenshots/report-labels-1080x1920.webp b/screenshots/report-labels-1080x1920.webp
index 65d045d..1faa364 100644
Binary files a/screenshots/report-labels-1080x1920.webp and b/screenshots/report-labels-1080x1920.webp differ
diff --git a/screenshots/report-labels-1920x1080.webp b/screenshots/report-labels-1920x1080.webp
index 31efb36..ffbe4a8 100644
Binary files a/screenshots/report-labels-1920x1080.webp and b/screenshots/report-labels-1920x1080.webp differ
diff --git a/src/css/style.css b/src/css/style.css
index b33d3fd..1443841 100644
--- a/src/css/style.css
+++ b/src/css/style.css
@@ -1,309 +1,120 @@
-@import '@screenly/edge-apps/styles';
+@layer theme, base, components, utilities;
-:root {
- --color-bg: #111;
- --color-surface: #1a1a1a;
- --color-border: #2a2a2a;
- --color-text-primary: #f2f2f3;
- --color-text-secondary: #dadadb;
- --color-text-muted: #9d9d9f;
- --color-error: #ff6b6b;
- --color-error-bg: rgba(255, 80, 80, 0.08);
- --color-error-border: rgba(255, 80, 80, 0.2);
- --card-radius: 1.5rem;
- --card-padding: 1.75rem 2rem;
- --grid-gap: 1.25rem;
-}
-
-* {
- box-sizing: border-box;
- margin: 0;
- padding: 0;
-}
-
-body {
- background: var(--color-bg);
- color: var(--color-text-primary);
- font-family: 'Inter', system-ui, sans-serif;
- overflow: hidden;
-}
-
-auto-scaler {
- position: relative;
- z-index: var(--z-index-content);
-}
-
-#app {
- display: flex;
- flex-direction: column;
- width: 100%;
- height: 100%;
- padding: 1.25rem;
- gap: 1.25rem;
-}
-
-.header {
- flex-shrink: 0;
- border-radius: 0.75rem;
- padding: 0.5rem 1.25rem;
- background: var(--color-surface);
- border: 0.0625rem solid var(--color-border);
-}
-
-main {
- flex: 1;
- display: flex;
- flex-direction: column;
- min-height: 0;
- overflow: hidden;
-}
-
-/* Dashboard screen */
-
-#dashboard-screen {
- flex: 1;
- display: flex;
- flex-direction: column;
- min-height: 0;
- overflow: hidden;
- gap: 1.25rem;
-}
-
-#dashboard-title {
- font-size: 2rem;
- font-weight: 600;
- color: var(--color-text-primary);
- letter-spacing: -0.03em;
- flex-shrink: 0;
-}
-
-#charts-grid {
- display: grid;
- gap: var(--grid-gap);
- flex: 1;
- overflow: auto;
- align-content: start;
-}
+@import 'tailwindcss/theme.css' layer(theme);
+@import '@screenly/edge-apps/styles' layer(base);
+@import 'tailwindcss/utilities.css' layer(utilities);
-.chart-card {
- background: var(--color-surface);
- border: 0.0625rem solid var(--color-border);
- border-radius: var(--card-radius);
- padding: var(--card-padding);
- display: flex;
- flex-direction: column;
- gap: 1rem;
- min-height: 0;
- overflow: hidden;
-}
-
-.chart-title {
- font-size: 0.75rem;
- font-weight: 600;
- letter-spacing: 0.08em;
- text-transform: uppercase;
- color: var(--color-text-muted);
- flex-shrink: 0;
-}
-
-.chart-container {
- flex: 1;
- position: relative;
- min-height: 0;
- container-type: size;
-}
-
-.report-chart-card {
- min-height: 28rem;
-}
-
-.report-chart-container {
- min-height: 22rem;
-}
+@layer components {
+ .header {
+ @apply flex-shrink-0 rounded-xl py-2 px-5 bg-[#1a1a1a] border border-[#2a2a2a];
+ }
-.report-stat-card {
- min-height: 16rem;
-}
+ .dashboard-screen {
+ @apply flex-1 flex-col min-h-0 overflow-hidden gap-5;
+ }
-.report-table-card {
- min-height: 20rem;
-}
+ .dashboard-title {
+ @apply text-[2rem] font-semibold text-[#f2f2f3] tracking-[-0.03em] flex-shrink-0;
+ }
-/* Error screen */
+ .charts-grid {
+ @apply grid gap-5 flex-1 overflow-auto content-start;
+ }
-#error-screen {
- flex: 1;
- display: flex;
- align-items: center;
- justify-content: center;
-}
+ .error-screen {
+ @apply flex-1 items-center justify-center;
+ }
-.error-card {
- background: var(--color-surface);
- border: 0.0625rem solid var(--color-error-border);
- border-radius: var(--card-radius);
- display: flex;
- flex-direction: row;
- overflow: hidden;
- max-width: 60rem;
- width: 100%;
-}
+ .error-card {
+ @apply flex flex-row portrait:flex-col overflow-hidden max-w-[60rem] portrait:max-w-full w-full rounded-3xl bg-[#1a1a1a] border border-[rgba(255,80,80,0.2)];
+ }
-.error-card-left {
- background: var(--color-error-bg);
- border-right: 0.0625rem solid var(--color-error-border);
- padding: 2.5rem 2rem;
- display: flex;
- flex-direction: column;
- justify-content: flex-start;
- gap: 1rem;
- flex: 0 0 20rem;
-}
+ .error-aside {
+ @apply bg-[rgba(255,80,80,0.08)] border-[rgba(255,80,80,0.2)] border-r portrait:border-r-0 portrait:border-b py-10 px-8 flex flex-col justify-start gap-4 flex-[0_0_20rem] portrait:flex-none;
+ }
-.error-card-left h2 {
- font-size: 2rem;
- font-weight: 700;
- line-height: 1.2;
- letter-spacing: -0.03em;
- color: var(--color-error);
-}
+ .error-title {
+ @apply text-[2rem] font-bold leading-[1.2] tracking-[-0.03em] text-[#ff6b6b];
+ }
-.error-card-left p {
- font-size: 1rem;
- color: var(--color-text-secondary);
- line-height: 1.6;
-}
+ .error-body {
+ @apply flex-1 py-10 px-8 flex flex-col justify-start gap-3;
+ }
-.error-card-right {
- flex: 1;
- padding: 2.5rem 2rem;
- display: flex;
- flex-direction: column;
- justify-content: flex-start;
- gap: 0.75rem;
-}
+ .error-label {
+ @apply text-xs font-semibold text-[#9d9d9f] uppercase tracking-[0.08em];
+ }
-.error-detail-label {
- font-size: 0.75rem;
- font-weight: 600;
- color: var(--color-text-muted);
- text-transform: uppercase;
- letter-spacing: 0.08em;
-}
+ .error-aside p,
+ .error-message {
+ @apply text-base text-[#dadadb] leading-[1.6];
+ }
-.error-detail-message {
- font-size: 1rem;
- color: var(--color-text-secondary);
- line-height: 1.6;
- word-break: break-word;
-}
+ .error-message {
+ @apply break-words;
+ }
-/* Metric component */
+ .chart-card {
+ @apply bg-[#1a1a1a] border border-[#2a2a2a] rounded-3xl py-7 px-8 flex flex-col gap-4 min-h-0 overflow-hidden;
+ }
-.metric-value {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 100%;
- height: 100%;
- font-size: clamp(2rem, 30cqh, 20rem);
- font-weight: 700;
- letter-spacing: -0.04em;
- color: var(--color-text-primary);
- text-align: center;
-}
+ .chart-title {
+ @apply text-xs font-semibold tracking-[0.08em] uppercase text-[#9d9d9f] flex-shrink-0;
+ }
-/* Empty state */
+ .chart-container {
+ @apply flex-1 relative min-h-0 [container-type:size];
+ }
-.empty-state {
- color: var(--color-text-muted);
- font-size: 0.875rem;
- text-align: center;
- margin: auto;
-}
+ .report-chart-card {
+ @apply min-h-[28rem];
+ }
-/* Data table */
+ .report-chart-container {
+ @apply min-h-[22rem];
+ }
-.table-scroll {
- width: 100%;
- height: 100%;
- overflow: auto;
-}
+ .report-table-card {
+ @apply min-h-80;
+ }
-.data-table {
- width: 100%;
- border-collapse: collapse;
- color: var(--color-text-secondary);
-}
+ .report-stat-card {
+ @apply min-h-64;
+ }
-.data-table th {
- padding: 0.75rem 1rem;
- text-align: left;
- font-size: 0.75rem;
- font-weight: 600;
- letter-spacing: 0.08em;
- text-transform: uppercase;
- color: var(--color-text-muted);
- white-space: nowrap;
- background-image: linear-gradient(var(--color-border), var(--color-border));
- background-size: 100% 0.0625rem;
- background-position: center bottom;
- background-repeat: no-repeat;
-}
+ .report-stat {
+ @apply flex flex-col justify-center h-full min-h-64 text-center gap-3;
+ }
-.data-table td {
- padding: 0.75rem 1rem;
- font-size: 0.9rem;
- text-align: left;
- white-space: nowrap;
- min-width: 8rem;
- background-image: linear-gradient(var(--color-border), var(--color-border));
- background-size: 100% 0.0625rem;
- background-position: center bottom;
- background-repeat: no-repeat;
-}
+ .report-stat-value {
+ @apply text-[clamp(3rem,7vw,6rem)] font-bold leading-[0.95] tracking-[-0.06em] text-[#f2f2f3];
+ }
-.data-table tbody tr:last-child td {
- background-image: none;
-}
+ .report-stat-label {
+ @apply text-sm font-semibold tracking-[0.08em] uppercase text-[#9d9d9f];
+ }
-/* Stat card */
+ .metric-value {
+ @apply flex items-center justify-center w-full h-full text-[clamp(2rem,30cqh,20rem)] font-bold tracking-[-0.04em] text-[#f2f2f3] text-center;
+ }
-.report-stat {
- display: flex;
- flex-direction: column;
- justify-content: center;
- height: 100%;
- min-height: 16rem;
- text-align: center;
- gap: 0.75rem;
-}
+ .empty-state {
+ @apply text-[#9d9d9f] text-sm text-center m-auto;
+ }
-.report-stat-value {
- font-size: clamp(3rem, 7vw, 6rem);
- font-weight: 700;
- line-height: 0.95;
- letter-spacing: -0.06em;
- color: var(--color-text-primary);
-}
+ .table-scroll {
+ @apply w-full h-full overflow-auto;
+ }
-.report-stat-label {
- font-size: 0.875rem;
- font-weight: 600;
- letter-spacing: 0.08em;
- text-transform: uppercase;
- color: var(--color-text-muted);
-}
+ .data-table {
+ @apply w-full border-collapse text-[#dadadb];
+ }
-@media (orientation: portrait) {
- .error-card {
- flex-direction: column;
- max-width: 100%;
+ .data-table th {
+ @apply px-4 py-3 text-left text-xs font-semibold tracking-[0.08em] uppercase text-[#9d9d9f] whitespace-nowrap bg-[linear-gradient(#2a2a2a,#2a2a2a)] [background-size:100%_0.0625rem] bg-bottom bg-no-repeat;
}
- .error-card-left {
- flex: none;
- border-right: none;
- border-bottom: 0.0625rem solid var(--color-error-border);
+ .data-table td {
+ @apply px-4 py-3 text-[0.9rem] text-left whitespace-nowrap min-w-32 bg-[linear-gradient(#2a2a2a,#2a2a2a)] [background-size:100%_0.0625rem] bg-bottom bg-no-repeat group-last:bg-none;
}
}
diff --git a/src/render/report.ts b/src/render/report.ts
index 3d759b6..8cbe0dc 100644
--- a/src/render/report.ts
+++ b/src/render/report.ts
@@ -34,11 +34,11 @@ export function appendReportCard(
card.style.gridColumn = '1 / span 12'
if (options?.cardClassName) {
- card.classList.add(options.cardClassName)
+ card.classList.add(...options.cardClassName.split(' '))
}
if (options?.containerClassName) {
- contentContainer.classList.add(options.containerClassName)
+ contentContainer.classList.add(...options.containerClassName.split(' '))
}
chartsGrid.appendChild(card)
diff --git a/src/render/table.ts b/src/render/table.ts
index 1c7bde6..51dd1b6 100644
--- a/src/render/table.ts
+++ b/src/render/table.ts
@@ -43,6 +43,7 @@ export function renderTable(
const tbody = document.createElement('tbody')
for (const row of rows) {
const tr = document.createElement('tr')
+ tr.className = 'group'
for (let i = 0; i < detailColumns.length; i++) {
const td = document.createElement('td')
const cell = row.dataCells[i]