diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8d36e3be68f696db797915902f9ea258839196b7..b90f3ebb07cef39d1e943bf704b5454751e9bef5 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -16,8 +16,11 @@
         c, t = get_summary(process_folder())
         using Printf
         @printf "Test coverage %.2f%%\n" 100c / t'
-Julia 1.9:
-  image: julia:1.9
+Julia 1.10:
+  image: julia:1.10
+  extends:
+    - .script
+Julia 1.11:
+  image: julia:1.11
   extends:
     - .script
-    - .coverage
diff --git a/Manifest-v1.11.toml b/Manifest-v1.11.toml
new file mode 100644
index 0000000000000000000000000000000000000000..f2d4c37b4ed7ab406a7b3b4bf344e8d1dbbfd2ed
--- /dev/null
+++ b/Manifest-v1.11.toml
@@ -0,0 +1,1756 @@
+# This file is machine-generated - editing it directly is not advised
+
+julia_version = "1.11.1"
+manifest_format = "2.0"
+project_hash = "fccf84810b56f9ba8947369bbaf414d318bc47f2"
+
+[[deps.AbstractFFTs]]
+deps = ["LinearAlgebra"]
+git-tree-sha1 = "d92ad398961a3ed262d8bf04a1a2b8340f915fef"
+uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c"
+version = "1.5.0"
+weakdeps = ["ChainRulesCore", "Test"]
+
+    [deps.AbstractFFTs.extensions]
+    AbstractFFTsChainRulesCoreExt = "ChainRulesCore"
+    AbstractFFTsTestExt = "Test"
+
+[[deps.AbstractTrees]]
+git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177"
+uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
+version = "0.4.5"
+
+[[deps.Adapt]]
+deps = ["LinearAlgebra", "Requires"]
+git-tree-sha1 = "50c3c56a52972d78e8be9fd135bfb91c9574c140"
+uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
+version = "4.1.1"
+weakdeps = ["StaticArrays"]
+
+    [deps.Adapt.extensions]
+    AdaptStaticArraysExt = "StaticArrays"
+
+[[deps.AdaptivePredicates]]
+git-tree-sha1 = "7e651ea8d262d2d74ce75fdf47c4d63c07dba7a6"
+uuid = "35492f91-a3bd-45ad-95db-fcad7dcfedb7"
+version = "1.2.0"
+
+[[deps.AliasTables]]
+deps = ["PtrArrays", "Random"]
+git-tree-sha1 = "9876e1e164b144ca45e9e3198d0b689cadfed9ff"
+uuid = "66dad0bd-aa9a-41b7-9441-69ab47430ed8"
+version = "1.1.3"
+
+[[deps.Animations]]
+deps = ["Colors"]
+git-tree-sha1 = "e81c509d2c8e49592413bfb0bb3b08150056c79d"
+uuid = "27a7e980-b3e6-11e9-2bcd-0b925532e340"
+version = "0.4.1"
+
+[[deps.ArgTools]]
+uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"
+version = "1.1.2"
+
+[[deps.Artifacts]]
+uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
+version = "1.11.0"
+
+[[deps.Automa]]
+deps = ["PrecompileTools", "SIMD", "TranscodingStreams"]
+git-tree-sha1 = "a8f503e8e1a5f583fbef15a8440c8c7e32185df2"
+uuid = "67c07d97-cdcb-5c2c-af73-a7f9c32a568b"
+version = "1.1.0"
+
+[[deps.AxisAlgorithms]]
+deps = ["LinearAlgebra", "Random", "SparseArrays", "WoodburyMatrices"]
+git-tree-sha1 = "01b8ccb13d68535d73d2b0c23e39bd23155fb712"
+uuid = "13072b0f-2c55-5437-9ae7-d433b7a33950"
+version = "1.1.0"
+
+[[deps.AxisArrays]]
+deps = ["Dates", "IntervalSets", "IterTools", "RangeArrays"]
+git-tree-sha1 = "16351be62963a67ac4083f748fdb3cca58bfd52f"
+uuid = "39de3d68-74b9-583c-8d2d-e117c070f3a9"
+version = "0.4.7"
+
+[[deps.Base64]]
+uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
+version = "1.11.0"
+
+[[deps.Bessels]]
+git-tree-sha1 = "4435559dc39793d53a9e3d278e185e920b4619ef"
+uuid = "0e736298-9ec6-45e8-9647-e4fc86a2fe38"
+version = "0.2.8"
+
+[[deps.BitFlags]]
+git-tree-sha1 = "0691e34b3bb8be9307330f88d1a3c3f25466c24d"
+uuid = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35"
+version = "0.1.9"
+
+[[deps.Bonito]]
+deps = ["Base64", "CodecZlib", "Colors", "Dates", "Deno_jll", "HTTP", "Hyperscript", "LinearAlgebra", "Markdown", "MsgPack", "Observables", "RelocatableFolders", "SHA", "Sockets", "Tables", "ThreadPools", "URIs", "UUIDs", "WidgetsBase"]
+git-tree-sha1 = "d7635780a8cfe0cb43c075276fd358c5b166695e"
+uuid = "824d6782-a2ef-11e9-3a09-e5662e0c26f8"
+version = "3.2.4"
+
+[[deps.BufferedStreams]]
+git-tree-sha1 = "6863c5b7fc997eadcabdbaf6c5f201dc30032643"
+uuid = "e1450e63-4bb3-523b-b2a4-4ffa8c0fd77d"
+version = "1.2.2"
+
+[[deps.Bzip2_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
+git-tree-sha1 = "8873e196c2eb87962a2048b3b8e08946535864a1"
+uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0"
+version = "1.0.8+2"
+
+[[deps.CEnum]]
+git-tree-sha1 = "389ad5c84de1ae7cf0e28e381131c98ea87d54fc"
+uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82"
+version = "0.5.0"
+
+[[deps.CRC32c]]
+uuid = "8bf52ea8-c179-5cab-976a-9e18b702a9bc"
+version = "1.11.0"
+
+[[deps.CRlibm_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
+git-tree-sha1 = "e329286945d0cfc04456972ea732551869af1cfc"
+uuid = "4e9b3aee-d8a1-5a3d-ad8b-7d824db253f0"
+version = "1.0.1+0"
+
+[[deps.Cairo_jll]]
+deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"]
+git-tree-sha1 = "009060c9a6168704143100f36ab08f06c2af4642"
+uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a"
+version = "1.18.2+1"
+
+[[deps.ChainRulesCore]]
+deps = ["Compat", "LinearAlgebra"]
+git-tree-sha1 = "3e4b134270b372f2ed4d4d0e936aabaefc1802bc"
+uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
+version = "1.25.0"
+weakdeps = ["SparseArrays"]
+
+    [deps.ChainRulesCore.extensions]
+    ChainRulesCoreSparseArraysExt = "SparseArrays"
+
+[[deps.CircularArrays]]
+deps = ["OffsetArrays"]
+git-tree-sha1 = "e24a6f390e5563583bb4315c73035b5b3f3e7ab4"
+uuid = "7a955b69-7140-5f4e-a0ed-f168c5e2e749"
+version = "1.4.0"
+
+[[deps.CodecZlib]]
+deps = ["TranscodingStreams", "Zlib_jll"]
+git-tree-sha1 = "bce6804e5e6044c6daab27bb533d1295e4a2e759"
+uuid = "944b1d66-785c-5afd-91f1-9de20f533193"
+version = "0.7.6"
+
+[[deps.ColorBrewer]]
+deps = ["Colors", "JSON", "Test"]
+git-tree-sha1 = "61c5334f33d91e570e1d0c3eb5465835242582c4"
+uuid = "a2cac450-b92f-5266-8821-25eda20663c8"
+version = "0.4.0"
+
+[[deps.ColorSchemes]]
+deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"]
+git-tree-sha1 = "13951eb68769ad1cd460cdb2e64e5e95f1bf123d"
+uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
+version = "3.27.0"
+
+[[deps.ColorTypes]]
+deps = ["FixedPointNumbers", "Random"]
+git-tree-sha1 = "b10d0b65641d57b8b4d5e234446582de5047050d"
+uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
+version = "0.11.5"
+
+[[deps.ColorVectorSpace]]
+deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"]
+git-tree-sha1 = "a1f44953f2382ebb937d60dafbe2deea4bd23249"
+uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4"
+version = "0.10.0"
+weakdeps = ["SpecialFunctions"]
+
+    [deps.ColorVectorSpace.extensions]
+    SpecialFunctionsExt = "SpecialFunctions"
+
+[[deps.Colors]]
+deps = ["ColorTypes", "FixedPointNumbers", "Reexport"]
+git-tree-sha1 = "362a287c3aa50601b0bc359053d5c2468f0e7ce0"
+uuid = "5ae59095-9a9b-59fe-a467-6f913c188581"
+version = "0.12.11"
+
+[[deps.Compat]]
+deps = ["TOML", "UUIDs"]
+git-tree-sha1 = "8ae8d32e09f0dcf42a36b90d4e17f5dd2e4c4215"
+uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
+version = "4.16.0"
+weakdeps = ["Dates", "LinearAlgebra"]
+
+    [deps.Compat.extensions]
+    CompatLinearAlgebraExt = "LinearAlgebra"
+
+[[deps.CompilerSupportLibraries_jll]]
+deps = ["Artifacts", "Libdl"]
+uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
+version = "1.1.1+0"
+
+[[deps.ConcurrentUtilities]]
+deps = ["Serialization", "Sockets"]
+git-tree-sha1 = "ea32b83ca4fefa1768dc84e504cc0a94fb1ab8d1"
+uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb"
+version = "2.4.2"
+
+[[deps.ConstructionBase]]
+git-tree-sha1 = "76219f1ed5771adbb096743bff43fb5fdd4c1157"
+uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9"
+version = "1.5.8"
+weakdeps = ["IntervalSets", "LinearAlgebra", "StaticArrays"]
+
+    [deps.ConstructionBase.extensions]
+    ConstructionBaseIntervalSetsExt = "IntervalSets"
+    ConstructionBaseLinearAlgebraExt = "LinearAlgebra"
+    ConstructionBaseStaticArraysExt = "StaticArrays"
+
+[[deps.Contour]]
+git-tree-sha1 = "439e35b0b36e2e5881738abc8857bd92ad6ff9a8"
+uuid = "d38c429a-6771-53c6-b99e-75d170b6e991"
+version = "0.6.3"
+
+[[deps.DataAPI]]
+git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe"
+uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
+version = "1.16.0"
+
+[[deps.DataStructures]]
+deps = ["Compat", "InteractiveUtils", "OrderedCollections"]
+git-tree-sha1 = "1d0a14036acb104d9e89698bd408f63ab58cdc82"
+uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
+version = "0.18.20"
+
+[[deps.DataValueInterfaces]]
+git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6"
+uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464"
+version = "1.0.0"
+
+[[deps.Dates]]
+deps = ["Printf"]
+uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"
+version = "1.11.0"
+
+[[deps.DelaunayTriangulation]]
+deps = ["AdaptivePredicates", "EnumX", "ExactPredicates", "PrecompileTools", "Random"]
+git-tree-sha1 = "89df54fbe66e5872d91d8c2cd3a375f660c3fd64"
+uuid = "927a84f5-c5f4-47a5-9785-b46e178433df"
+version = "1.6.1"
+
+[[deps.DelimitedFiles]]
+deps = ["Mmap"]
+git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae"
+uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab"
+version = "1.9.1"
+
+[[deps.Deno_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "cd6756e833c377e0ce9cd63fb97689a255f12323"
+uuid = "04572ae6-984a-583e-9378-9577a1c2574d"
+version = "1.33.4+0"
+
+[[deps.Distances]]
+deps = ["LinearAlgebra", "Statistics", "StatsAPI"]
+git-tree-sha1 = "c7e3a542b999843086e2f29dac96a618c105be1d"
+uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
+version = "0.10.12"
+weakdeps = ["ChainRulesCore", "SparseArrays"]
+
+    [deps.Distances.extensions]
+    DistancesChainRulesCoreExt = "ChainRulesCore"
+    DistancesSparseArraysExt = "SparseArrays"
+
+[[deps.Distributed]]
+deps = ["Random", "Serialization", "Sockets"]
+uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"
+version = "1.11.0"
+
+[[deps.Distributions]]
+deps = ["AliasTables", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns"]
+git-tree-sha1 = "3101c32aab536e7a27b1763c0797dba151b899ad"
+uuid = "31c24e10-a181-5473-b8eb-7969acd0382f"
+version = "0.25.113"
+
+    [deps.Distributions.extensions]
+    DistributionsChainRulesCoreExt = "ChainRulesCore"
+    DistributionsDensityInterfaceExt = "DensityInterface"
+    DistributionsTestExt = "Test"
+
+    [deps.Distributions.weakdeps]
+    ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
+    DensityInterface = "b429d917-457f-4dbc-8f4c-0cc954292b1d"
+    Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
+
+[[deps.DocOpt]]
+deps = ["Dates", "Printf"]
+git-tree-sha1 = "9c2681389ed8d9cf6193b1ba31796f12456b111a"
+uuid = "968ba79b-81e4-546f-ab3a-2eecfa62a9db"
+version = "0.5.0"
+
+[[deps.DocStringExtensions]]
+deps = ["LibGit2"]
+git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d"
+uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
+version = "0.9.3"
+
+[[deps.Downloads]]
+deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"]
+uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
+version = "1.6.0"
+
+[[deps.EarCut_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
+git-tree-sha1 = "e3290f2d49e661fbd94046d7e3726ffcb2d41053"
+uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5"
+version = "2.2.4+0"
+
+[[deps.EnumX]]
+git-tree-sha1 = "bdb1942cd4c45e3c678fd11569d5cccd80976237"
+uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56"
+version = "1.0.4"
+
+[[deps.ExactPredicates]]
+deps = ["IntervalArithmetic", "Random", "StaticArrays"]
+git-tree-sha1 = "b3f2ff58735b5f024c392fde763f29b057e4b025"
+uuid = "429591f6-91af-11e9-00e2-59fbe8cec110"
+version = "2.2.8"
+
+[[deps.ExceptionUnwrapping]]
+deps = ["Test"]
+git-tree-sha1 = "dcb08a0d93ec0b1cdc4af184b26b591e9695423a"
+uuid = "460bff9d-24e4-43bc-9d9f-a8973cb893f4"
+version = "0.1.10"
+
+[[deps.Expat_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "1c6317308b9dc757616f0b5cb379db10494443a7"
+uuid = "2e619515-83b5-522b-bb60-26c02a35a201"
+version = "2.6.2+0"
+
+[[deps.Extents]]
+git-tree-sha1 = "81023caa0021a41712685887db1fc03db26f41f5"
+uuid = "411431e0-e8b7-467b-b5e0-f676ba4f2910"
+version = "0.1.4"
+
+[[deps.FFMPEG_jll]]
+deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "PCRE2_jll", "Zlib_jll", "libaom_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"]
+git-tree-sha1 = "8cc47f299902e13f90405ddb5bf87e5d474c0d38"
+uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5"
+version = "6.1.2+0"
+
+[[deps.FFTW]]
+deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"]
+git-tree-sha1 = "4820348781ae578893311153d69049a93d05f39d"
+uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
+version = "1.8.0"
+
+[[deps.FFTW_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
+git-tree-sha1 = "4d81ed14783ec49ce9f2e168208a12ce1815aa25"
+uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a"
+version = "3.3.10+1"
+
+[[deps.FileIO]]
+deps = ["Pkg", "Requires", "UUIDs"]
+git-tree-sha1 = "62ca0547a14c57e98154423419d8a342dca75ca9"
+uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
+version = "1.16.4"
+
+[[deps.FilePaths]]
+deps = ["FilePathsBase", "MacroTools", "Reexport", "Requires"]
+git-tree-sha1 = "919d9412dbf53a2e6fe74af62a73ceed0bce0629"
+uuid = "8fc22ac5-c921-52a6-82fd-178b2807b824"
+version = "0.8.3"
+
+[[deps.FilePathsBase]]
+deps = ["Compat", "Dates"]
+git-tree-sha1 = "7878ff7172a8e6beedd1dea14bd27c3c6340d361"
+uuid = "48062228-2e41-5def-b9a4-89aafe57970f"
+version = "0.9.22"
+weakdeps = ["Mmap", "Test"]
+
+    [deps.FilePathsBase.extensions]
+    FilePathsBaseMmapExt = "Mmap"
+    FilePathsBaseTestExt = "Test"
+
+[[deps.FileWatching]]
+uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"
+version = "1.11.0"
+
+[[deps.FillArrays]]
+deps = ["LinearAlgebra"]
+git-tree-sha1 = "6a70198746448456524cb442b8af316927ff3e1a"
+uuid = "1a297f60-69ca-5386-bcde-b61e274b549b"
+version = "1.13.0"
+weakdeps = ["PDMats", "SparseArrays", "Statistics"]
+
+    [deps.FillArrays.extensions]
+    FillArraysPDMatsExt = "PDMats"
+    FillArraysSparseArraysExt = "SparseArrays"
+    FillArraysStatisticsExt = "Statistics"
+
+[[deps.FixedPointNumbers]]
+deps = ["Statistics"]
+git-tree-sha1 = "05882d6995ae5c12bb5f36dd2ed3f61c98cbb172"
+uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
+version = "0.8.5"
+
+[[deps.Fontconfig_jll]]
+deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Zlib_jll"]
+git-tree-sha1 = "db16beca600632c95fc8aca29890d83788dd8b23"
+uuid = "a3f928ae-7b40-5064-980b-68af3947d34b"
+version = "2.13.96+0"
+
+[[deps.Format]]
+git-tree-sha1 = "9c68794ef81b08086aeb32eeaf33531668d5f5fc"
+uuid = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8"
+version = "1.3.7"
+
+[[deps.FreeType]]
+deps = ["CEnum", "FreeType2_jll"]
+git-tree-sha1 = "907369da0f8e80728ab49c1c7e09327bf0d6d999"
+uuid = "b38be410-82b0-50bf-ab77-7b57e271db43"
+version = "4.1.1"
+
+[[deps.FreeType2_jll]]
+deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"]
+git-tree-sha1 = "5c1d8ae0efc6c2e7b1fc502cbe25def8f661b7bc"
+uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7"
+version = "2.13.2+0"
+
+[[deps.FreeTypeAbstraction]]
+deps = ["ColorVectorSpace", "Colors", "FreeType", "GeometryBasics"]
+git-tree-sha1 = "84dfe824bd6fdf2a5d73bb187ff31b5549b2a79c"
+uuid = "663a7486-cb36-511b-a19d-713bb74d65c9"
+version = "0.10.4"
+
+[[deps.FriBidi_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "1ed150b39aebcc805c26b93a8d0122c940f64ce2"
+uuid = "559328eb-81f9-559d-9380-de523a88c83c"
+version = "1.0.14+0"
+
+[[deps.GeoFormatTypes]]
+git-tree-sha1 = "59107c179a586f0fe667024c5eb7033e81333271"
+uuid = "68eda718-8dee-11e9-39e7-89f7f65f511f"
+version = "0.4.2"
+
+[[deps.GeoInterface]]
+deps = ["Extents", "GeoFormatTypes"]
+git-tree-sha1 = "826b4fd69438d9ce4d2b19de6bc2f970f45f0f88"
+uuid = "cf35fbd7-0cd7-5166-be24-54bfbe79505f"
+version = "1.3.8"
+
+[[deps.GeometryBasics]]
+deps = ["EarCut_jll", "Extents", "GeoInterface", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"]
+git-tree-sha1 = "b62f2b2d76cee0d61a2ef2b3118cd2a3215d3134"
+uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326"
+version = "0.4.11"
+
+[[deps.Gettext_jll]]
+deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"]
+git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046"
+uuid = "78b55507-aeef-58d4-861c-77aaff3498b1"
+version = "0.21.0+0"
+
+[[deps.Giflib_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "0224cce99284d997f6880a42ef715a37c99338d1"
+uuid = "59f7168a-df46-5410-90c8-f2779963d0ec"
+version = "5.2.2+0"
+
+[[deps.Glib_jll]]
+deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"]
+git-tree-sha1 = "674ff0db93fffcd11a3573986e550d66cd4fd71f"
+uuid = "7746bdde-850d-59dc-9ae8-88ece973131d"
+version = "2.80.5+0"
+
+[[deps.Graphite2_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
+git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011"
+uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472"
+version = "1.3.14+0"
+
+[[deps.GridLayoutBase]]
+deps = ["GeometryBasics", "InteractiveUtils", "Observables"]
+git-tree-sha1 = "fc713f007cff99ff9e50accba6373624ddd33588"
+uuid = "3955a311-db13-416c-9275-1d80ed98e5e9"
+version = "0.11.0"
+
+[[deps.Grisu]]
+git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2"
+uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe"
+version = "1.0.2"
+
+[[deps.HDF5]]
+deps = ["Compat", "HDF5_jll", "Libdl", "MPIPreferences", "Mmap", "Preferences", "Printf", "Random", "Requires", "UUIDs"]
+git-tree-sha1 = "e856eef26cf5bf2b0f95f8f4fc37553c72c8641c"
+uuid = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f"
+version = "0.17.2"
+
+    [deps.HDF5.extensions]
+    MPIExt = "MPI"
+
+    [deps.HDF5.weakdeps]
+    MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195"
+
+[[deps.HDF5_jll]]
+deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "LazyArtifacts", "LibCURL_jll", "Libdl", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "OpenSSL_jll", "TOML", "Zlib_jll", "libaec_jll"]
+git-tree-sha1 = "38c8874692d48d5440d5752d6c74b0c6b0b60739"
+uuid = "0234f1f7-429e-5d53-9886-15a909be8d59"
+version = "1.14.2+1"
+
+[[deps.HTTP]]
+deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"]
+git-tree-sha1 = "bc3f416a965ae61968c20d0ad867556367f2817d"
+uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
+version = "1.10.9"
+
+[[deps.HarfBuzz_jll]]
+deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll"]
+git-tree-sha1 = "401e4f3f30f43af2c8478fc008da50096ea5240f"
+uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566"
+version = "8.3.1+0"
+
+[[deps.Hwloc_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "dd3b49277ec2bb2c6b94eb1604d4d0616016f7a6"
+uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8"
+version = "2.11.2+0"
+
+[[deps.HypergeometricFunctions]]
+deps = ["LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"]
+git-tree-sha1 = "7c4195be1649ae622304031ed46a2f4df989f1eb"
+uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a"
+version = "0.3.24"
+
+[[deps.Hyperscript]]
+deps = ["Test"]
+git-tree-sha1 = "179267cfa5e712760cd43dcae385d7ea90cc25a4"
+uuid = "47d2ed2b-36de-50cf-bf87-49c2cf4b8b91"
+version = "0.0.5"
+
+[[deps.ImageAxes]]
+deps = ["AxisArrays", "ImageBase", "ImageCore", "Reexport", "SimpleTraits"]
+git-tree-sha1 = "2e4520d67b0cef90865b3ef727594d2a58e0e1f8"
+uuid = "2803e5a7-5153-5ecf-9a86-9b4c37f5f5ac"
+version = "0.6.11"
+
+[[deps.ImageBase]]
+deps = ["ImageCore", "Reexport"]
+git-tree-sha1 = "eb49b82c172811fd2c86759fa0553a2221feb909"
+uuid = "c817782e-172a-44cc-b673-b171935fbb9e"
+version = "0.1.7"
+
+[[deps.ImageCore]]
+deps = ["ColorVectorSpace", "Colors", "FixedPointNumbers", "MappedArrays", "MosaicViews", "OffsetArrays", "PaddedViews", "PrecompileTools", "Reexport"]
+git-tree-sha1 = "b2a7eaa169c13f5bcae8131a83bc30eff8f71be0"
+uuid = "a09fc81d-aa75-5fe9-8630-4744c3626534"
+version = "0.10.2"
+
+[[deps.ImageIO]]
+deps = ["FileIO", "IndirectArrays", "JpegTurbo", "LazyModules", "Netpbm", "OpenEXR", "PNGFiles", "QOI", "Sixel", "TiffImages", "UUIDs", "WebP"]
+git-tree-sha1 = "696144904b76e1ca433b886b4e7edd067d76cbf7"
+uuid = "82e4d734-157c-48bb-816b-45c225c6df19"
+version = "0.6.9"
+
+[[deps.ImageMetadata]]
+deps = ["AxisArrays", "ImageAxes", "ImageBase", "ImageCore"]
+git-tree-sha1 = "355e2b974f2e3212a75dfb60519de21361ad3cb7"
+uuid = "bc367c6b-8a6b-528e-b4bd-a4b897500b49"
+version = "0.9.9"
+
+[[deps.Imath_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "0936ba688c6d201805a83da835b55c61a180db52"
+uuid = "905a6f67-0a94-5f89-b386-d35d92009cd1"
+version = "3.1.11+0"
+
+[[deps.IndirectArrays]]
+git-tree-sha1 = "012e604e1c7458645cb8b436f8fba789a51b257f"
+uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959"
+version = "1.0.0"
+
+[[deps.Inflate]]
+git-tree-sha1 = "d1b1b796e47d94588b3757fe84fbf65a5ec4a80d"
+uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9"
+version = "0.1.5"
+
+[[deps.IntelOpenMP_jll]]
+deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"]
+git-tree-sha1 = "10bd689145d2c3b2a9844005d01087cc1194e79e"
+uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0"
+version = "2024.2.1+0"
+
+[[deps.InteractiveUtils]]
+deps = ["Markdown"]
+uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
+version = "1.11.0"
+
+[[deps.Interpolations]]
+deps = ["Adapt", "AxisAlgorithms", "ChainRulesCore", "LinearAlgebra", "OffsetArrays", "Random", "Ratios", "Requires", "SharedArrays", "SparseArrays", "StaticArrays", "WoodburyMatrices"]
+git-tree-sha1 = "88a101217d7cb38a7b481ccd50d21876e1d1b0e0"
+uuid = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59"
+version = "0.15.1"
+weakdeps = ["Unitful"]
+
+    [deps.Interpolations.extensions]
+    InterpolationsUnitfulExt = "Unitful"
+
+[[deps.IntervalArithmetic]]
+deps = ["CRlibm_jll", "LinearAlgebra", "MacroTools", "RoundingEmulator"]
+git-tree-sha1 = "24c095b1ec7ee58b936985d31d5df92f9b9cfebb"
+uuid = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253"
+version = "0.22.19"
+
+    [deps.IntervalArithmetic.extensions]
+    IntervalArithmeticDiffRulesExt = "DiffRules"
+    IntervalArithmeticForwardDiffExt = "ForwardDiff"
+    IntervalArithmeticIntervalSetsExt = "IntervalSets"
+    IntervalArithmeticRecipesBaseExt = "RecipesBase"
+
+    [deps.IntervalArithmetic.weakdeps]
+    DiffRules = "b552c78f-8df3-52c6-915a-8e097449b14b"
+    ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
+    IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953"
+    RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
+
+[[deps.IntervalSets]]
+git-tree-sha1 = "dba9ddf07f77f60450fe5d2e2beb9854d9a49bd0"
+uuid = "8197267c-284f-5f27-9208-e0e47529a953"
+version = "0.7.10"
+weakdeps = ["Random", "RecipesBase", "Statistics"]
+
+    [deps.IntervalSets.extensions]
+    IntervalSetsRandomExt = "Random"
+    IntervalSetsRecipesBaseExt = "RecipesBase"
+    IntervalSetsStatisticsExt = "Statistics"
+
+[[deps.InverseFunctions]]
+git-tree-sha1 = "a779299d77cd080bf77b97535acecd73e1c5e5cb"
+uuid = "3587e190-3f89-42d0-90ee-14403ec27112"
+version = "0.1.17"
+weakdeps = ["Dates", "Test"]
+
+    [deps.InverseFunctions.extensions]
+    InverseFunctionsDatesExt = "Dates"
+    InverseFunctionsTestExt = "Test"
+
+[[deps.IrrationalConstants]]
+git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2"
+uuid = "92d709cd-6900-40b7-9082-c6be49f344b6"
+version = "0.2.2"
+
+[[deps.Isoband]]
+deps = ["isoband_jll"]
+git-tree-sha1 = "f9b6d97355599074dc867318950adaa6f9946137"
+uuid = "f1662d9f-8043-43de-a69a-05efc1cc6ff4"
+version = "0.1.1"
+
+[[deps.IterTools]]
+git-tree-sha1 = "42d5f897009e7ff2cf88db414a389e5ed1bdd023"
+uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
+version = "1.10.0"
+
+[[deps.IteratorInterfaceExtensions]]
+git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856"
+uuid = "82899510-4779-5014-852e-03e436cf321d"
+version = "1.0.0"
+
+[[deps.JLLWrappers]]
+deps = ["Artifacts", "Preferences"]
+git-tree-sha1 = "be3dc50a92e5a386872a493a10050136d4703f9b"
+uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
+version = "1.6.1"
+
+[[deps.JSON]]
+deps = ["Dates", "Mmap", "Parsers", "Unicode"]
+git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a"
+uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
+version = "0.21.4"
+
+[[deps.JSON3]]
+deps = ["Dates", "Mmap", "Parsers", "PrecompileTools", "StructTypes", "UUIDs"]
+git-tree-sha1 = "1d322381ef7b087548321d3f878cb4c9bd8f8f9b"
+uuid = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
+version = "1.14.1"
+
+    [deps.JSON3.extensions]
+    JSON3ArrowExt = ["ArrowTypes"]
+
+    [deps.JSON3.weakdeps]
+    ArrowTypes = "31f734f8-188a-4ce0-8406-c8a06bd891cd"
+
+[[deps.JpegTurbo]]
+deps = ["CEnum", "FileIO", "ImageCore", "JpegTurbo_jll", "TOML"]
+git-tree-sha1 = "fa6d0bcff8583bac20f1ffa708c3913ca605c611"
+uuid = "b835a17e-a41a-41e7-81f0-2f016b05efe0"
+version = "0.1.5"
+
+[[deps.JpegTurbo_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "25ee0be4d43d0269027024d75a24c24d6c6e590c"
+uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8"
+version = "3.0.4+0"
+
+[[deps.KernelDensity]]
+deps = ["Distributions", "DocStringExtensions", "FFTW", "Interpolations", "StatsBase"]
+git-tree-sha1 = "7d703202e65efa1369de1279c162b915e245eed1"
+uuid = "5ab0869b-81aa-558d-bb23-cbf5423bbe9b"
+version = "0.6.9"
+
+[[deps.LAME_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "170b660facf5df5de098d866564877e119141cbd"
+uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d"
+version = "3.100.2+0"
+
+[[deps.LERC_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "36bdbc52f13a7d1dcb0f3cd694e01677a515655b"
+uuid = "88015f11-f218-50d7-93a8-a6af411a945d"
+version = "4.0.0+0"
+
+[[deps.LLVMOpenMP_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "78211fb6cbc872f77cad3fc0b6cf647d923f4929"
+uuid = "1d63c593-3942-5779-bab2-d838dc0a180e"
+version = "18.1.7+0"
+
+[[deps.LZO_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "854a9c268c43b77b0a27f22d7fab8d33cdb3a731"
+uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac"
+version = "2.10.2+1"
+
+[[deps.LaTeXStrings]]
+git-tree-sha1 = "dda21b8cbd6a6c40d9d02a73230f9d70fed6918c"
+uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
+version = "1.4.0"
+
+[[deps.LazyArtifacts]]
+deps = ["Artifacts", "Pkg"]
+uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3"
+version = "1.11.0"
+
+[[deps.LazyModules]]
+git-tree-sha1 = "a560dd966b386ac9ae60bdd3a3d3a326062d3c3e"
+uuid = "8cdb02fc-e678-4876-92c5-9defec4f444e"
+version = "0.3.1"
+
+[[deps.LibCURL]]
+deps = ["LibCURL_jll", "MozillaCACerts_jll"]
+uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21"
+version = "0.6.4"
+
+[[deps.LibCURL_jll]]
+deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"]
+uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0"
+version = "8.6.0+0"
+
+[[deps.LibGit2]]
+deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"]
+uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"
+version = "1.11.0"
+
+[[deps.LibGit2_jll]]
+deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"]
+uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5"
+version = "1.7.2+0"
+
+[[deps.LibSSH2_jll]]
+deps = ["Artifacts", "Libdl", "MbedTLS_jll"]
+uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8"
+version = "1.11.0+1"
+
+[[deps.Libdl]]
+uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
+version = "1.11.0"
+
+[[deps.Libffi_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
+git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290"
+uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490"
+version = "3.2.2+1"
+
+[[deps.Libgcrypt_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll"]
+git-tree-sha1 = "8be878062e0ffa2c3f67bb58a595375eda5de80b"
+uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4"
+version = "1.11.0+0"
+
+[[deps.Libglvnd_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"]
+git-tree-sha1 = "6f73d1dd803986947b2c750138528a999a6c7733"
+uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29"
+version = "1.6.0+0"
+
+[[deps.Libgpg_error_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "c6ce1e19f3aec9b59186bdf06cdf3c4fc5f5f3e6"
+uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8"
+version = "1.50.0+0"
+
+[[deps.Libiconv_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "61dfdba58e585066d8bce214c5a51eaa0539f269"
+uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531"
+version = "1.17.0+1"
+
+[[deps.Libmount_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "0c4f9c4f1a50d8f35048fa0532dabbadf702f81e"
+uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9"
+version = "2.40.1+0"
+
+[[deps.Libtiff_jll]]
+deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "XZ_jll", "Zlib_jll", "Zstd_jll"]
+git-tree-sha1 = "b404131d06f7886402758c9ce2214b636eb4d54a"
+uuid = "89763e89-9b03-5906-acba-b20f662cd828"
+version = "4.7.0+0"
+
+[[deps.Libuuid_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "5ee6203157c120d79034c748a2acba45b82b8807"
+uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700"
+version = "2.40.1+0"
+
+[[deps.LinearAlgebra]]
+deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"]
+uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
+version = "1.11.0"
+
+[[deps.LogExpFunctions]]
+deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"]
+git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea"
+uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
+version = "0.3.28"
+
+    [deps.LogExpFunctions.extensions]
+    LogExpFunctionsChainRulesCoreExt = "ChainRulesCore"
+    LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables"
+    LogExpFunctionsInverseFunctionsExt = "InverseFunctions"
+
+    [deps.LogExpFunctions.weakdeps]
+    ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
+    ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0"
+    InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112"
+
+[[deps.Logging]]
+uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
+version = "1.11.0"
+
+[[deps.LoggingExtras]]
+deps = ["Dates", "Logging"]
+git-tree-sha1 = "f02b56007b064fbfddb4c9cd60161b6dd0f40df3"
+uuid = "e6f89c97-d47a-5376-807f-9c37f3926c36"
+version = "1.1.0"
+
+[[deps.MAT]]
+deps = ["BufferedStreams", "CodecZlib", "HDF5", "SparseArrays"]
+git-tree-sha1 = "1d2dd9b186742b0f317f2530ddcbf00eebb18e96"
+uuid = "23992714-dd62-5051-b70f-ba57cb901cac"
+version = "0.10.7"
+
+[[deps.MKL_jll]]
+deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"]
+git-tree-sha1 = "f046ccd0c6db2832a9f639e2c669c6fe867e5f4f"
+uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7"
+version = "2024.2.0+0"
+
+[[deps.MPICH_jll]]
+deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"]
+git-tree-sha1 = "7715e65c47ba3941c502bffb7f266a41a7f54423"
+uuid = "7cb0a576-ebde-5e09-9194-50597f1243b4"
+version = "4.2.3+0"
+
+[[deps.MPIPreferences]]
+deps = ["Libdl", "Preferences"]
+git-tree-sha1 = "c105fe467859e7f6e9a852cb15cb4301126fac07"
+uuid = "3da0fdf6-3ccc-4f1b-acd9-58baa6c99267"
+version = "0.1.11"
+
+[[deps.MPItrampoline_jll]]
+deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"]
+git-tree-sha1 = "70e830dab5d0775183c99fc75e4c24c614ed7142"
+uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748"
+version = "5.5.1+0"
+
+[[deps.MacroTools]]
+deps = ["Markdown", "Random"]
+git-tree-sha1 = "2fa9ee3e63fd3a4f7a9a4f4744a52f4856de82df"
+uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
+version = "0.5.13"
+
+[[deps.Makie]]
+deps = ["Animations", "Base64", "CRC32c", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "Dates", "DelaunayTriangulation", "Distributions", "DocStringExtensions", "Downloads", "FFMPEG_jll", "FileIO", "FilePaths", "FixedPointNumbers", "Format", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageBase", "ImageIO", "InteractiveUtils", "Interpolations", "IntervalSets", "InverseFunctions", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MacroTools", "MakieCore", "Markdown", "MathTeXEngine", "Observables", "OffsetArrays", "Packing", "PlotUtils", "PolygonOps", "PrecompileTools", "Printf", "REPL", "Random", "RelocatableFolders", "Scratch", "ShaderAbstractions", "Showoff", "SignedDistanceFields", "SparseArrays", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "TriplotBase", "UnicodeFun", "Unitful"]
+git-tree-sha1 = "f7907907eb914138cc9e9ee66ab46f7a9efac8e8"
+uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
+version = "0.21.15"
+
+[[deps.MakieCore]]
+deps = ["ColorTypes", "GeometryBasics", "IntervalSets", "Observables"]
+git-tree-sha1 = "4604f03e5b057e8e62a95a44929cafc9585b0fe9"
+uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b"
+version = "0.8.9"
+
+[[deps.MappedArrays]]
+git-tree-sha1 = "2dab0221fe2b0f2cb6754eaa743cc266339f527e"
+uuid = "dbb5928d-eab1-5f90-85c2-b9b0edb7c900"
+version = "0.4.2"
+
+[[deps.Markdown]]
+deps = ["Base64"]
+uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
+version = "1.11.0"
+
+[[deps.MathTeXEngine]]
+deps = ["AbstractTrees", "Automa", "DataStructures", "FreeTypeAbstraction", "GeometryBasics", "LaTeXStrings", "REPL", "RelocatableFolders", "UnicodeFun"]
+git-tree-sha1 = "f45c8916e8385976e1ccd055c9874560c257ab13"
+uuid = "0a4f8689-d25c-4efe-a92b-7142dfc1aa53"
+version = "0.6.2"
+
+[[deps.MbedTLS]]
+deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "NetworkOptions", "Random", "Sockets"]
+git-tree-sha1 = "c067a280ddc25f196b5e7df3877c6b226d390aaf"
+uuid = "739be429-bea8-5141-9913-cc70e7f3736d"
+version = "1.1.9"
+
+[[deps.MbedTLS_jll]]
+deps = ["Artifacts", "Libdl"]
+uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"
+version = "2.28.6+0"
+
+[[deps.Meshes]]
+deps = ["Bessels", "CircularArrays", "Distances", "IterTools", "LinearAlgebra", "NearestNeighbors", "Random", "Rotations", "SparseArrays", "StaticArrays", "StatsBase", "Tables", "TransformsBase"]
+git-tree-sha1 = "a1a152787767f3393362276452897605584964b1"
+uuid = "eacbb407-ea5a-433e-ab97-5258b1ca43fa"
+version = "0.29.0"
+
+[[deps.MicrosoftMPI_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
+git-tree-sha1 = "f12a29c4400ba812841c6ace3f4efbb6dbb3ba01"
+uuid = "9237b28f-5490-5468-be7b-bb81f5f5e6cf"
+version = "10.1.4+2"
+
+[[deps.Missings]]
+deps = ["DataAPI"]
+git-tree-sha1 = "ec4f7fbeab05d7747bdf98eb74d130a2a2ed298d"
+uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28"
+version = "1.2.0"
+
+[[deps.Mmap]]
+uuid = "a63ad114-7e13-5084-954f-fe012c677804"
+version = "1.11.0"
+
+[[deps.MosaicViews]]
+deps = ["MappedArrays", "OffsetArrays", "PaddedViews", "StackViews"]
+git-tree-sha1 = "7b86a5d4d70a9f5cdf2dacb3cbe6d251d1a61dbe"
+uuid = "e94cdb99-869f-56ef-bcf0-1ae2bcbe0389"
+version = "0.3.4"
+
+[[deps.MozillaCACerts_jll]]
+uuid = "14a3606d-f60d-562e-9121-12d972cd8159"
+version = "2023.12.12"
+
+[[deps.MsgPack]]
+deps = ["Serialization"]
+git-tree-sha1 = "f5db02ae992c260e4826fe78c942954b48e1d9c2"
+uuid = "99f44e22-a591-53d1-9472-aa23ef4bd671"
+version = "1.2.1"
+
+[[deps.NearestNeighbors]]
+deps = ["Distances", "StaticArrays"]
+git-tree-sha1 = "3cebfc94a0754cc329ebc3bab1e6c89621e791ad"
+uuid = "b8a86587-4115-5ab1-83bc-aa920d37bbce"
+version = "0.4.20"
+
+[[deps.Netpbm]]
+deps = ["FileIO", "ImageCore", "ImageMetadata"]
+git-tree-sha1 = "d92b107dbb887293622df7697a2223f9f8176fcd"
+uuid = "f09324ee-3d7c-5217-9330-fc30815ba969"
+version = "1.1.1"
+
+[[deps.NetworkOptions]]
+uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"
+version = "1.2.0"
+
+[[deps.NyxWidgets]]
+deps = ["Bonito", "Colors", "Format", "LazyArtifacts", "Observables"]
+git-tree-sha1 = "936f80aa61413c47da00f96abbc0186078698bca"
+repo-rev = "main"
+repo-url = "https://gitlab.com/dbc-nyx/NyxWidgets.jl"
+uuid = "c288fd06-43d3-4b04-8307-797133353e2e"
+version = "0.1.1"
+
+[[deps.Observables]]
+git-tree-sha1 = "7438a59546cf62428fc9d1bc94729146d37a7225"
+uuid = "510215fc-4207-5dde-b226-833fc4488ee2"
+version = "0.5.5"
+
+[[deps.ObservationPolicies]]
+deps = ["Observables"]
+git-tree-sha1 = "d54b4c26b41238806c172f85e1913736c2583b0a"
+repo-rev = "main"
+repo-url = "https://gitlab.com/dbc-nyx/ObservationPolicies.jl"
+uuid = "6317928a-6b1a-42e8-b853-b8e2fc3e9ca3"
+version = "0.2.4"
+
+[[deps.OffsetArrays]]
+git-tree-sha1 = "1a27764e945a152f7ca7efa04de513d473e9542e"
+uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
+version = "1.14.1"
+weakdeps = ["Adapt"]
+
+    [deps.OffsetArrays.extensions]
+    OffsetArraysAdaptExt = "Adapt"
+
+[[deps.Ogg_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
+git-tree-sha1 = "887579a3eb005446d514ab7aeac5d1d027658b8f"
+uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051"
+version = "1.3.5+1"
+
+[[deps.OpenBLAS_jll]]
+deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"]
+uuid = "4536629a-c528-5b80-bd46-f80d51c5b363"
+version = "0.3.27+1"
+
+[[deps.OpenEXR]]
+deps = ["Colors", "FileIO", "OpenEXR_jll"]
+git-tree-sha1 = "327f53360fdb54df7ecd01e96ef1983536d1e633"
+uuid = "52e1d378-f018-4a11-a4be-720524705ac7"
+version = "0.3.2"
+
+[[deps.OpenEXR_jll]]
+deps = ["Artifacts", "Imath_jll", "JLLWrappers", "Libdl", "Zlib_jll"]
+git-tree-sha1 = "8292dd5c8a38257111ada2174000a33745b06d4e"
+uuid = "18a262bb-aa17-5467-a713-aee519bc75cb"
+version = "3.2.4+0"
+
+[[deps.OpenLibm_jll]]
+deps = ["Artifacts", "Libdl"]
+uuid = "05823500-19ac-5b8b-9628-191a04bc5112"
+version = "0.8.1+2"
+
+[[deps.OpenMPI_jll]]
+deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML", "Zlib_jll"]
+git-tree-sha1 = "bfce6d523861a6c562721b262c0d1aaeead2647f"
+uuid = "fe0851c0-eecd-5654-98d4-656369965a5c"
+version = "5.0.5+0"
+
+[[deps.OpenSSL]]
+deps = ["BitFlags", "Dates", "MozillaCACerts_jll", "OpenSSL_jll", "Sockets"]
+git-tree-sha1 = "38cb508d080d21dc1128f7fb04f20387ed4c0af4"
+uuid = "4d8831e6-92b7-49fb-bdf8-b643e874388c"
+version = "1.4.3"
+
+[[deps.OpenSSL_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "7493f61f55a6cce7325f197443aa80d32554ba10"
+uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95"
+version = "3.0.15+1"
+
+[[deps.OpenSpecFun_jll]]
+deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"]
+git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1"
+uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e"
+version = "0.5.5+0"
+
+[[deps.Opus_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "6703a85cb3781bd5909d48730a67205f3f31a575"
+uuid = "91d4177d-7536-5919-b921-800302f37372"
+version = "1.3.3+0"
+
+[[deps.OrderedCollections]]
+git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5"
+uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
+version = "1.6.3"
+
+[[deps.PCRE2_jll]]
+deps = ["Artifacts", "Libdl"]
+uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15"
+version = "10.42.0+1"
+
+[[deps.PDMats]]
+deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"]
+git-tree-sha1 = "949347156c25054de2db3b166c52ac4728cbad65"
+uuid = "90014a1f-27ba-587c-ab20-58faa44d9150"
+version = "0.11.31"
+
+[[deps.PNGFiles]]
+deps = ["Base64", "CEnum", "ImageCore", "IndirectArrays", "OffsetArrays", "libpng_jll"]
+git-tree-sha1 = "67186a2bc9a90f9f85ff3cc8277868961fb57cbd"
+uuid = "f57f5aa1-a3ce-4bc8-8ab9-96f992907883"
+version = "0.4.3"
+
+[[deps.Packing]]
+deps = ["GeometryBasics"]
+git-tree-sha1 = "ec3edfe723df33528e085e632414499f26650501"
+uuid = "19eb6ba3-879d-56ad-ad62-d5c202156566"
+version = "0.5.0"
+
+[[deps.PaddedViews]]
+deps = ["OffsetArrays"]
+git-tree-sha1 = "0fac6313486baae819364c52b4f483450a9d793f"
+uuid = "5432bcbf-9aad-5242-b902-cca2824c8663"
+version = "0.5.12"
+
+[[deps.Parsers]]
+deps = ["Dates", "PrecompileTools", "UUIDs"]
+git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821"
+uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
+version = "2.8.1"
+
+[[deps.Pixman_jll]]
+deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"]
+git-tree-sha1 = "35621f10a7531bc8fa58f74610b1bfb70a3cfc6b"
+uuid = "30392449-352a-5448-841d-b1acce4e97dc"
+version = "0.43.4+0"
+
+[[deps.Pkg]]
+deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"]
+uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
+version = "1.11.0"
+weakdeps = ["REPL"]
+
+    [deps.Pkg.extensions]
+    REPLExt = "REPL"
+
+[[deps.PkgVersion]]
+deps = ["Pkg"]
+git-tree-sha1 = "f9501cc0430a26bc3d156ae1b5b0c1b47af4d6da"
+uuid = "eebad327-c553-4316-9ea0-9fa01ccd7688"
+version = "0.3.3"
+
+[[deps.PlanarLarvae]]
+deps = ["DelimitedFiles", "HDF5", "JSON3", "LinearAlgebra", "MAT", "Meshes", "OrderedCollections", "Random", "SHA", "StaticArrays", "Statistics", "StatsBase", "StructTypes"]
+git-tree-sha1 = "d964d040e319fe3bd9140e5bf91d648de6acc96f"
+repo-rev = "main"
+repo-url = "https://gitlab.pasteur.fr/nyx/planarlarvae.jl"
+uuid = "c2615984-ef14-4d40-b148-916c85b43307"
+version = "0.16.0"
+
+[[deps.PlotUtils]]
+deps = ["ColorSchemes", "Colors", "Dates", "PrecompileTools", "Printf", "Random", "Reexport", "StableRNGs", "Statistics"]
+git-tree-sha1 = "3ca9a356cd2e113c420f2c13bea19f8d3fb1cb18"
+uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043"
+version = "1.4.3"
+
+[[deps.PolygonOps]]
+git-tree-sha1 = "77b3d3605fc1cd0b42d95eba87dfcd2bf67d5ff6"
+uuid = "647866c9-e3ac-4575-94e7-e3d426903924"
+version = "0.1.2"
+
+[[deps.PrecompileTools]]
+deps = ["Preferences"]
+git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f"
+uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
+version = "1.2.1"
+
+[[deps.Preferences]]
+deps = ["TOML"]
+git-tree-sha1 = "9306f6085165d270f7e3db02af26a400d580f5c6"
+uuid = "21216c6a-2e73-6563-6e65-726566657250"
+version = "1.4.3"
+
+[[deps.Printf]]
+deps = ["Unicode"]
+uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"
+version = "1.11.0"
+
+[[deps.ProgressMeter]]
+deps = ["Distributed", "Printf"]
+git-tree-sha1 = "8f6bc219586aef8baf0ff9a5fe16ee9c70cb65e4"
+uuid = "92933f4c-e287-5a05-a399-4b506db050ca"
+version = "1.10.2"
+
+[[deps.PtrArrays]]
+git-tree-sha1 = "77a42d78b6a92df47ab37e177b2deac405e1c88f"
+uuid = "43287f4e-b6f4-7ad1-bb20-aadabca52c3d"
+version = "1.2.1"
+
+[[deps.QOI]]
+deps = ["ColorTypes", "FileIO", "FixedPointNumbers"]
+git-tree-sha1 = "18e8f4d1426e965c7b532ddd260599e1510d26ce"
+uuid = "4b34888f-f399-49d4-9bb3-47ed5cae4e65"
+version = "1.0.0"
+
+[[deps.QuadGK]]
+deps = ["DataStructures", "LinearAlgebra"]
+git-tree-sha1 = "cda3b045cf9ef07a08ad46731f5a3165e56cf3da"
+uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
+version = "2.11.1"
+
+    [deps.QuadGK.extensions]
+    QuadGKEnzymeExt = "Enzyme"
+
+    [deps.QuadGK.weakdeps]
+    Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9"
+
+[[deps.Quaternions]]
+deps = ["LinearAlgebra", "Random", "RealDot"]
+git-tree-sha1 = "994cc27cdacca10e68feb291673ec3a76aa2fae9"
+uuid = "94ee1d12-ae83-5a48-8b1c-48b8ff168ae0"
+version = "0.7.6"
+
+[[deps.REPL]]
+deps = ["InteractiveUtils", "Markdown", "Sockets", "StyledStrings", "Unicode"]
+uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
+version = "1.11.0"
+
+[[deps.Random]]
+deps = ["SHA"]
+uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
+version = "1.11.0"
+
+[[deps.RangeArrays]]
+git-tree-sha1 = "b9039e93773ddcfc828f12aadf7115b4b4d225f5"
+uuid = "b3c3ace0-ae52-54e7-9d0b-2c1406fd6b9d"
+version = "0.3.2"
+
+[[deps.Ratios]]
+deps = ["Requires"]
+git-tree-sha1 = "1342a47bf3260ee108163042310d26f2be5ec90b"
+uuid = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439"
+version = "0.4.5"
+weakdeps = ["FixedPointNumbers"]
+
+    [deps.Ratios.extensions]
+    RatiosFixedPointNumbersExt = "FixedPointNumbers"
+
+[[deps.RealDot]]
+deps = ["LinearAlgebra"]
+git-tree-sha1 = "9f0a1b71baaf7650f4fa8a1d168c7fb6ee41f0c9"
+uuid = "c1ae055f-0cd5-4b69-90a6-9a35b1a98df9"
+version = "0.1.0"
+
+[[deps.RecipesBase]]
+deps = ["PrecompileTools"]
+git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff"
+uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
+version = "1.3.4"
+
+[[deps.Reexport]]
+git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b"
+uuid = "189a3867-3050-52da-a836-e630ba90ab69"
+version = "1.2.2"
+
+[[deps.RelocatableFolders]]
+deps = ["SHA", "Scratch"]
+git-tree-sha1 = "ffdaf70d81cf6ff22c2b6e733c900c3321cab864"
+uuid = "05181044-ff0b-4ac5-8273-598c1e38db00"
+version = "1.0.1"
+
+[[deps.Requires]]
+deps = ["UUIDs"]
+git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7"
+uuid = "ae029012-a4dd-5104-9daa-d747884805df"
+version = "1.3.0"
+
+[[deps.Rmath]]
+deps = ["Random", "Rmath_jll"]
+git-tree-sha1 = "852bd0f55565a9e973fcfee83a84413270224dc4"
+uuid = "79098fc4-a85e-5d69-aa6a-4863f24498fa"
+version = "0.8.0"
+
+[[deps.Rmath_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "58cdd8fb2201a6267e1db87ff148dd6c1dbd8ad8"
+uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f"
+version = "0.5.1+0"
+
+[[deps.Rotations]]
+deps = ["LinearAlgebra", "Quaternions", "Random", "StaticArrays"]
+git-tree-sha1 = "5680a9276685d392c87407df00d57c9924d9f11e"
+uuid = "6038ab10-8711-5258-84ad-4b1120ba62dc"
+version = "1.7.1"
+weakdeps = ["RecipesBase"]
+
+    [deps.Rotations.extensions]
+    RotationsRecipesBaseExt = "RecipesBase"
+
+[[deps.RoundingEmulator]]
+git-tree-sha1 = "40b9edad2e5287e05bd413a38f61a8ff55b9557b"
+uuid = "5eaf0fd0-dfba-4ccb-bf02-d820a40db705"
+version = "0.2.1"
+
+[[deps.SHA]]
+uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
+version = "0.7.0"
+
+[[deps.SIMD]]
+deps = ["PrecompileTools"]
+git-tree-sha1 = "98ca7c29edd6fc79cd74c61accb7010a4e7aee33"
+uuid = "fdea26ae-647d-5447-a871-4b548cad5224"
+version = "3.6.0"
+
+[[deps.Scratch]]
+deps = ["Dates"]
+git-tree-sha1 = "3bac05bc7e74a75fd9cba4295cde4045d9fe2386"
+uuid = "6c6a2e73-6563-6170-7368-637461726353"
+version = "1.2.1"
+
+[[deps.Serialization]]
+uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
+version = "1.11.0"
+
+[[deps.ShaderAbstractions]]
+deps = ["ColorTypes", "FixedPointNumbers", "GeometryBasics", "LinearAlgebra", "Observables", "StaticArrays", "StructArrays", "Tables"]
+git-tree-sha1 = "79123bc60c5507f035e6d1d9e563bb2971954ec8"
+uuid = "65257c39-d410-5151-9873-9b3e5be5013e"
+version = "0.4.1"
+
+[[deps.SharedArrays]]
+deps = ["Distributed", "Mmap", "Random", "Serialization"]
+uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383"
+version = "1.11.0"
+
+[[deps.Showoff]]
+deps = ["Dates", "Grisu"]
+git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de"
+uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f"
+version = "1.0.3"
+
+[[deps.SignedDistanceFields]]
+deps = ["Random", "Statistics", "Test"]
+git-tree-sha1 = "d263a08ec505853a5ff1c1ebde2070419e3f28e9"
+uuid = "73760f76-fbc4-59ce-8f25-708e95d2df96"
+version = "0.4.0"
+
+[[deps.SimpleBufferStream]]
+git-tree-sha1 = "f305871d2f381d21527c770d4788c06c097c9bc1"
+uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7"
+version = "1.2.0"
+
+[[deps.SimpleTraits]]
+deps = ["InteractiveUtils", "MacroTools"]
+git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231"
+uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d"
+version = "0.9.4"
+
+[[deps.Sixel]]
+deps = ["Dates", "FileIO", "ImageCore", "IndirectArrays", "OffsetArrays", "REPL", "libsixel_jll"]
+git-tree-sha1 = "2da10356e31327c7096832eb9cd86307a50b1eb6"
+uuid = "45858cf5-a6b0-47a3-bbea-62219f50df47"
+version = "0.1.3"
+
+[[deps.Sockets]]
+uuid = "6462fe0b-24de-5631-8697-dd941f90decc"
+version = "1.11.0"
+
+[[deps.SortingAlgorithms]]
+deps = ["DataStructures"]
+git-tree-sha1 = "66e0a8e672a0bdfca2c3f5937efb8538b9ddc085"
+uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c"
+version = "1.2.1"
+
+[[deps.SparseArrays]]
+deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"]
+uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
+version = "1.11.0"
+
+[[deps.SpecialFunctions]]
+deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"]
+git-tree-sha1 = "2f5d4697f21388cbe1ff299430dd169ef97d7e14"
+uuid = "276daf66-3868-5448-9aa4-cd146d93841b"
+version = "2.4.0"
+weakdeps = ["ChainRulesCore"]
+
+    [deps.SpecialFunctions.extensions]
+    SpecialFunctionsChainRulesCoreExt = "ChainRulesCore"
+
+[[deps.StableRNGs]]
+deps = ["Random"]
+git-tree-sha1 = "83e6cce8324d49dfaf9ef059227f91ed4441a8e5"
+uuid = "860ef19b-820b-49d6-a774-d7a799459cd3"
+version = "1.0.2"
+
+[[deps.StackViews]]
+deps = ["OffsetArrays"]
+git-tree-sha1 = "46e589465204cd0c08b4bd97385e4fa79a0c770c"
+uuid = "cae243ae-269e-4f55-b966-ac2d0dc13c15"
+version = "0.1.1"
+
+[[deps.StaticArrays]]
+deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"]
+git-tree-sha1 = "777657803913ffc7e8cc20f0fd04b634f871af8f"
+uuid = "90137ffa-7385-5640-81b9-e52037218182"
+version = "1.9.8"
+weakdeps = ["ChainRulesCore", "Statistics"]
+
+    [deps.StaticArrays.extensions]
+    StaticArraysChainRulesCoreExt = "ChainRulesCore"
+    StaticArraysStatisticsExt = "Statistics"
+
+[[deps.StaticArraysCore]]
+git-tree-sha1 = "192954ef1208c7019899fbf8049e717f92959682"
+uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c"
+version = "1.4.3"
+
+[[deps.Statistics]]
+deps = ["LinearAlgebra"]
+git-tree-sha1 = "ae3bb1eb3bba077cd276bc5cfc337cc65c3075c0"
+uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
+version = "1.11.1"
+weakdeps = ["SparseArrays"]
+
+    [deps.Statistics.extensions]
+    SparseArraysExt = ["SparseArrays"]
+
+[[deps.StatsAPI]]
+deps = ["LinearAlgebra"]
+git-tree-sha1 = "1ff449ad350c9c4cbc756624d6f8a8c3ef56d3ed"
+uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0"
+version = "1.7.0"
+
+[[deps.StatsBase]]
+deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"]
+git-tree-sha1 = "5cf7606d6cef84b543b483848d4ae08ad9832b21"
+uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
+version = "0.34.3"
+
+[[deps.StatsFuns]]
+deps = ["HypergeometricFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"]
+git-tree-sha1 = "b423576adc27097764a90e163157bcfc9acf0f46"
+uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c"
+version = "1.3.2"
+weakdeps = ["ChainRulesCore", "InverseFunctions"]
+
+    [deps.StatsFuns.extensions]
+    StatsFunsChainRulesCoreExt = "ChainRulesCore"
+    StatsFunsInverseFunctionsExt = "InverseFunctions"
+
+[[deps.StructArrays]]
+deps = ["ConstructionBase", "DataAPI", "Tables"]
+git-tree-sha1 = "f4dc295e983502292c4c3f951dbb4e985e35b3be"
+uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a"
+version = "0.6.18"
+
+    [deps.StructArrays.extensions]
+    StructArraysAdaptExt = "Adapt"
+    StructArraysGPUArraysCoreExt = "GPUArraysCore"
+    StructArraysSparseArraysExt = "SparseArrays"
+    StructArraysStaticArraysExt = "StaticArrays"
+
+    [deps.StructArrays.weakdeps]
+    Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
+    GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527"
+    SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
+    StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
+
+[[deps.StructTypes]]
+deps = ["Dates", "UUIDs"]
+git-tree-sha1 = "159331b30e94d7b11379037feeb9b690950cace8"
+uuid = "856f2bd8-1eba-4b0a-8007-ebc267875bd4"
+version = "1.11.0"
+
+[[deps.StyledStrings]]
+uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b"
+version = "1.11.0"
+
+[[deps.SuiteSparse]]
+deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"]
+uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9"
+
+[[deps.SuiteSparse_jll]]
+deps = ["Artifacts", "Libdl", "libblastrampoline_jll"]
+uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c"
+version = "7.7.0+0"
+
+[[deps.TOML]]
+deps = ["Dates"]
+uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
+version = "1.0.3"
+
+[[deps.TableTraits]]
+deps = ["IteratorInterfaceExtensions"]
+git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39"
+uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c"
+version = "1.0.1"
+
+[[deps.Tables]]
+deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "OrderedCollections", "TableTraits"]
+git-tree-sha1 = "598cd7c1f68d1e205689b1c2fe65a9f85846f297"
+uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
+version = "1.12.0"
+
+[[deps.Tar]]
+deps = ["ArgTools", "SHA"]
+uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"
+version = "1.10.0"
+
+[[deps.TensorCore]]
+deps = ["LinearAlgebra"]
+git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6"
+uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50"
+version = "0.1.1"
+
+[[deps.Test]]
+deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
+uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
+version = "1.11.0"
+
+[[deps.ThreadPools]]
+deps = ["Printf", "RecipesBase", "Statistics"]
+git-tree-sha1 = "50cb5f85d5646bc1422aa0238aa5bfca99ca9ae7"
+uuid = "b189fb0b-2eb5-4ed4-bc0c-d34c51242431"
+version = "2.1.1"
+
+[[deps.TidyObservables]]
+deps = ["Observables"]
+git-tree-sha1 = "0589ec7397374678942cae9aa356b4bb6c1e9bf4"
+repo-rev = "main"
+repo-url = "https://gitlab.com/dbc-nyx/tidyobservables.jl"
+uuid = "c8131bbd-73a8-4254-a42d-d5d4c5febb31"
+version = "0.1.1"
+
+[[deps.TiffImages]]
+deps = ["ColorTypes", "DataStructures", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "Mmap", "OffsetArrays", "PkgVersion", "ProgressMeter", "SIMD", "UUIDs"]
+git-tree-sha1 = "6ee0c220d0aecad18792c277ae358129cc50a475"
+uuid = "731e570b-9d59-4bfa-96dc-6df516fadf69"
+version = "0.11.0"
+
+[[deps.TranscodingStreams]]
+git-tree-sha1 = "0c45878dcfdcfa8480052b6ab162cdd138781742"
+uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa"
+version = "0.11.3"
+
+[[deps.TransformsBase]]
+deps = ["AbstractTrees"]
+git-tree-sha1 = "2412fb54902b0063c69c2bcfbec6b571120cc856"
+uuid = "28dd2a49-a57a-4bfb-84ca-1a49db9b96b8"
+version = "0.1.2"
+
+[[deps.TriplotBase]]
+git-tree-sha1 = "4d4ed7f294cda19382ff7de4c137d24d16adc89b"
+uuid = "981d1d27-644d-49a2-9326-4793e63143c3"
+version = "0.1.0"
+
+[[deps.URIs]]
+git-tree-sha1 = "67db6cc7b3821e19ebe75791a9dd19c9b1188f2b"
+uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4"
+version = "1.5.1"
+
+[[deps.UUIDs]]
+deps = ["Random", "SHA"]
+uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
+version = "1.11.0"
+
+[[deps.Unicode]]
+uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
+version = "1.11.0"
+
+[[deps.UnicodeFun]]
+deps = ["REPL"]
+git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf"
+uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1"
+version = "0.4.1"
+
+[[deps.Unitful]]
+deps = ["Dates", "LinearAlgebra", "Random"]
+git-tree-sha1 = "d95fe458f26209c66a187b1114df96fd70839efd"
+uuid = "1986cc42-f94f-5a68-af5c-568840ba703d"
+version = "1.21.0"
+weakdeps = ["ConstructionBase", "InverseFunctions"]
+
+    [deps.Unitful.extensions]
+    ConstructionBaseUnitfulExt = "ConstructionBase"
+    InverseFunctionsUnitfulExt = "InverseFunctions"
+
+[[deps.WGLMakie]]
+deps = ["Bonito", "Colors", "FileIO", "FreeTypeAbstraction", "GeometryBasics", "Hyperscript", "LinearAlgebra", "Makie", "Observables", "PNGFiles", "PrecompileTools", "RelocatableFolders", "ShaderAbstractions", "StaticArrays"]
+git-tree-sha1 = "13cab94d885d7760d487d7e2f30c2d6df4643880"
+uuid = "276b4fcb-3e11-5398-bf8b-a0c2d153d008"
+version = "0.10.15"
+
+[[deps.WebP]]
+deps = ["CEnum", "ColorTypes", "FileIO", "FixedPointNumbers", "ImageCore", "libwebp_jll"]
+git-tree-sha1 = "f1f6d497ff84039deeb37f264396dac0c2250497"
+uuid = "e3aaa7dc-3e4b-44e0-be63-ffb868ccd7c1"
+version = "0.1.2"
+
+[[deps.WidgetsBase]]
+deps = ["Observables"]
+git-tree-sha1 = "30a1d631eb06e8c868c559599f915a62d55c2601"
+uuid = "eead4739-05f7-45a1-878c-cee36b57321c"
+version = "0.1.4"
+
+[[deps.WoodburyMatrices]]
+deps = ["LinearAlgebra", "SparseArrays"]
+git-tree-sha1 = "c1a7aa6219628fcd757dede0ca95e245c5cd9511"
+uuid = "efce3f68-66dc-5838-9240-27a6d6f5f9b6"
+version = "1.0.0"
+
+[[deps.XML2_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"]
+git-tree-sha1 = "6a451c6f33a176150f315726eba8b92fbfdb9ae7"
+uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a"
+version = "2.13.4+0"
+
+[[deps.XSLT_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "XML2_jll", "Zlib_jll"]
+git-tree-sha1 = "a54ee957f4c86b526460a720dbc882fa5edcbefc"
+uuid = "aed1982a-8fda-507f-9586-7b0439959a61"
+version = "1.1.41+0"
+
+[[deps.XZ_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "15e637a697345f6743674f1322beefbc5dcd5cfc"
+uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800"
+version = "5.6.3+0"
+
+[[deps.Xorg_libX11_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"]
+git-tree-sha1 = "afead5aba5aa507ad5a3bf01f58f82c8d1403495"
+uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc"
+version = "1.8.6+0"
+
+[[deps.Xorg_libXau_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "6035850dcc70518ca32f012e46015b9beeda49d8"
+uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec"
+version = "1.0.11+0"
+
+[[deps.Xorg_libXdmcp_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "34d526d318358a859d7de23da945578e8e8727b7"
+uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05"
+version = "1.1.4+0"
+
+[[deps.Xorg_libXext_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"]
+git-tree-sha1 = "d2d1a5c49fae4ba39983f63de6afcbea47194e85"
+uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3"
+version = "1.3.6+0"
+
+[[deps.Xorg_libXrender_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"]
+git-tree-sha1 = "47e45cd78224c53109495b3e324df0c37bb61fbe"
+uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa"
+version = "0.9.11+0"
+
+[[deps.Xorg_libpthread_stubs_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "8fdda4c692503d44d04a0603d9ac0982054635f9"
+uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74"
+version = "0.1.1+0"
+
+[[deps.Xorg_libxcb_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"]
+git-tree-sha1 = "bcd466676fef0878338c61e655629fa7bbc69d8e"
+uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b"
+version = "1.17.0+0"
+
+[[deps.Xorg_xtrans_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "e92a1a012a10506618f10b7047e478403a046c77"
+uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10"
+version = "1.5.0+0"
+
+[[deps.Zlib_jll]]
+deps = ["Libdl"]
+uuid = "83775a58-1f1d-513f-b197-d71354ab007a"
+version = "1.2.13+1"
+
+[[deps.Zstd_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "555d1076590a6cc2fdee2ef1469451f872d8b41b"
+uuid = "3161d3a3-bdf6-5164-811a-617609db77b4"
+version = "1.5.6+1"
+
+[[deps.isoband_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
+git-tree-sha1 = "51b5eeb3f98367157a7a12a1fb0aa5328946c03c"
+uuid = "9a68df92-36a6-505f-a73e-abb412b6bfb4"
+version = "0.2.3+0"
+
+[[deps.libaec_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "46bf7be2917b59b761247be3f317ddf75e50e997"
+uuid = "477f73a3-ac25-53e9-8cc3-50b2fa2566f0"
+version = "1.1.2+0"
+
+[[deps.libaom_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "1827acba325fdcdf1d2647fc8d5301dd9ba43a9d"
+uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b"
+version = "3.9.0+0"
+
+[[deps.libass_jll]]
+deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Zlib_jll"]
+git-tree-sha1 = "e17c115d55c5fbb7e52ebedb427a0dca79d4484e"
+uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0"
+version = "0.15.2+0"
+
+[[deps.libblastrampoline_jll]]
+deps = ["Artifacts", "Libdl"]
+uuid = "8e850b90-86db-534c-a0d3-1478176c7d93"
+version = "5.11.0+0"
+
+[[deps.libfdk_aac_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "8a22cf860a7d27e4f3498a0fe0811a7957badb38"
+uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280"
+version = "2.0.3+0"
+
+[[deps.libpng_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"]
+git-tree-sha1 = "b70c870239dc3d7bc094eb2d6be9b73d27bef280"
+uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f"
+version = "1.6.44+0"
+
+[[deps.libsixel_jll]]
+deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "libpng_jll"]
+git-tree-sha1 = "7dfa0fd9c783d3d0cc43ea1af53d69ba45c447df"
+uuid = "075b6546-f08a-558a-be8f-8157d0f608a5"
+version = "1.10.3+1"
+
+[[deps.libvorbis_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"]
+git-tree-sha1 = "490376214c4721cdaca654041f635213c6165cb3"
+uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a"
+version = "1.3.7+2"
+
+[[deps.libwebp_jll]]
+deps = ["Artifacts", "Giflib_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Libtiff_jll", "libpng_jll"]
+git-tree-sha1 = "ccbb625a89ec6195856a50aa2b668a5c08712c94"
+uuid = "c5f90fcd-3b7e-5836-afba-fc50a0988cb2"
+version = "1.4.0+0"
+
+[[deps.nghttp2_jll]]
+deps = ["Artifacts", "Libdl"]
+uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d"
+version = "1.59.0+0"
+
+[[deps.oneTBB_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "7d0ea0f4895ef2f5cb83645fa689e52cb55cf493"
+uuid = "1317d2d5-d96f-522e-a858-c73665f53c3e"
+version = "2021.12.0+0"
+
+[[deps.p7zip_jll]]
+deps = ["Artifacts", "Libdl"]
+uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0"
+version = "17.4.0+2"
+
+[[deps.x264_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "35976a1216d6c066ea32cba2150c4fa682b276fc"
+uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a"
+version = "10164.0.0+0"
+
+[[deps.x265_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "dcc541bb19ed5b0ede95581fb2e41ecf179527d2"
+uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76"
+version = "3.6.0+0"
diff --git a/Manifest.toml b/Manifest.toml
index 46f7deabd22ffeb010ad88b55b91e6219e087baa..d02ed0ce6003ec651f18217bf255430a7306468b 100644
--- a/Manifest.toml
+++ b/Manifest.toml
@@ -1,8 +1,8 @@
 # This file is machine-generated - editing it directly is not advised
 
-julia_version = "1.10.3"
+julia_version = "1.10.6"
 manifest_format = "2.0"
-project_hash = "cb952709b0f0743fc5b93ca3d08c93ff9353644c"
+project_hash = "0fb14e688a33d3d8ba0bbce1542d1ada113967c7"
 
 [[deps.AbstractFFTs]]
 deps = ["LinearAlgebra"]
@@ -22,14 +22,19 @@ version = "0.4.5"
 
 [[deps.Adapt]]
 deps = ["LinearAlgebra", "Requires"]
-git-tree-sha1 = "6a55b747d1812e699320963ffde36f1ebdda4099"
+git-tree-sha1 = "50c3c56a52972d78e8be9fd135bfb91c9574c140"
 uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
-version = "4.0.4"
+version = "4.1.1"
 weakdeps = ["StaticArrays"]
 
     [deps.Adapt.extensions]
     AdaptStaticArraysExt = "StaticArrays"
 
+[[deps.AdaptivePredicates]]
+git-tree-sha1 = "7e651ea8d262d2d74ce75fdf47c4d63c07dba7a6"
+uuid = "35492f91-a3bd-45ad-95db-fcad7dcfedb7"
+version = "1.2.0"
+
 [[deps.AliasTables]]
 deps = ["PtrArrays", "Random"]
 git-tree-sha1 = "9876e1e164b144ca45e9e3198d0b689cadfed9ff"
@@ -50,10 +55,10 @@ version = "1.1.1"
 uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
 
 [[deps.Automa]]
-deps = ["TranscodingStreams"]
-git-tree-sha1 = "ef9997b3d5547c48b41c7bd8899e812a917b409d"
+deps = ["PrecompileTools", "SIMD", "TranscodingStreams"]
+git-tree-sha1 = "a8f503e8e1a5f583fbef15a8440c8c7e32185df2"
 uuid = "67c07d97-cdcb-5c2c-af73-a7f9c32a568b"
-version = "0.8.4"
+version = "1.1.0"
 
 [[deps.AxisAlgorithms]]
 deps = ["LinearAlgebra", "Random", "SparseArrays", "WoodburyMatrices"]
@@ -75,39 +80,53 @@ git-tree-sha1 = "4435559dc39793d53a9e3d278e185e920b4619ef"
 uuid = "0e736298-9ec6-45e8-9647-e4fc86a2fe38"
 version = "0.2.8"
 
+[[deps.BitFlags]]
+git-tree-sha1 = "0691e34b3bb8be9307330f88d1a3c3f25466c24d"
+uuid = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35"
+version = "0.1.9"
+
+[[deps.Bonito]]
+deps = ["Base64", "CodecZlib", "Colors", "Dates", "Deno_jll", "HTTP", "Hyperscript", "LinearAlgebra", "Markdown", "MsgPack", "Observables", "RelocatableFolders", "SHA", "Sockets", "Tables", "ThreadPools", "URIs", "UUIDs", "WidgetsBase"]
+git-tree-sha1 = "d7635780a8cfe0cb43c075276fd358c5b166695e"
+uuid = "824d6782-a2ef-11e9-3a09-e5662e0c26f8"
+version = "3.2.4"
+
 [[deps.BufferedStreams]]
-git-tree-sha1 = "4ae47f9a4b1dc19897d3743ff13685925c5202ec"
+git-tree-sha1 = "6863c5b7fc997eadcabdbaf6c5f201dc30032643"
 uuid = "e1450e63-4bb3-523b-b2a4-4ffa8c0fd77d"
-version = "1.2.1"
+version = "1.2.2"
 
 [[deps.Bzip2_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
-git-tree-sha1 = "9e2a6b69137e6969bab0152632dcb3bc108c8bdd"
+git-tree-sha1 = "8873e196c2eb87962a2048b3b8e08946535864a1"
 uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0"
-version = "1.0.8+1"
+version = "1.0.8+2"
 
 [[deps.CEnum]]
 git-tree-sha1 = "389ad5c84de1ae7cf0e28e381131c98ea87d54fc"
 uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82"
 version = "0.5.0"
 
+[[deps.CRC32c]]
+uuid = "8bf52ea8-c179-5cab-976a-9e18b702a9bc"
+
+[[deps.CRlibm_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
+git-tree-sha1 = "e329286945d0cfc04456972ea732551869af1cfc"
+uuid = "4e9b3aee-d8a1-5a3d-ad8b-7d824db253f0"
+version = "1.0.1+0"
+
 [[deps.Cairo_jll]]
 deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"]
-git-tree-sha1 = "a2f1c8c668c8e3cb4cca4e57a8efdb09067bb3fd"
+git-tree-sha1 = "009060c9a6168704143100f36ab08f06c2af4642"
 uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a"
-version = "1.18.0+2"
-
-[[deps.Calculus]]
-deps = ["LinearAlgebra"]
-git-tree-sha1 = "f641eb0a4f00c343bbc32346e1217b86f3ce9dad"
-uuid = "49dc2e85-a5d0-5ad3-a950-438e2897f1b9"
-version = "0.5.1"
+version = "1.18.2+1"
 
 [[deps.ChainRulesCore]]
 deps = ["Compat", "LinearAlgebra"]
-git-tree-sha1 = "575cd02e080939a33b6df6c5853d14924c08e35b"
+git-tree-sha1 = "3e4b134270b372f2ed4d4d0e936aabaefc1802bc"
 uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
-version = "1.23.0"
+version = "1.25.0"
 weakdeps = ["SparseArrays"]
 
     [deps.ChainRulesCore.extensions]
@@ -121,9 +140,9 @@ version = "1.4.0"
 
 [[deps.CodecZlib]]
 deps = ["TranscodingStreams", "Zlib_jll"]
-git-tree-sha1 = "59939d8a997469ee05c4b4944560a820f9ba0d73"
+git-tree-sha1 = "bce6804e5e6044c6daab27bb533d1295e4a2e759"
 uuid = "944b1d66-785c-5afd-91f1-9de20f533193"
-version = "0.7.4"
+version = "0.7.6"
 
 [[deps.ColorBrewer]]
 deps = ["Colors", "JSON", "Test"]
@@ -133,9 +152,9 @@ version = "0.4.0"
 
 [[deps.ColorSchemes]]
 deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"]
-git-tree-sha1 = "4b270d6465eb21ae89b732182c20dc165f8bf9f2"
+git-tree-sha1 = "c785dfb1b3bfddd1da557e861b919819b82bbe5b"
 uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
-version = "3.25.0"
+version = "3.27.1"
 
 [[deps.ColorTypes]]
 deps = ["FixedPointNumbers", "Random"]
@@ -144,10 +163,14 @@ uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
 version = "0.11.5"
 
 [[deps.ColorVectorSpace]]
-deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "SpecialFunctions", "Statistics", "TensorCore"]
-git-tree-sha1 = "600cc5508d66b78aae350f7accdb58763ac18589"
+deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"]
+git-tree-sha1 = "a1f44953f2382ebb937d60dafbe2deea4bd23249"
 uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4"
-version = "0.9.10"
+version = "0.10.0"
+weakdeps = ["SpecialFunctions"]
+
+    [deps.ColorVectorSpace.extensions]
+    SpecialFunctionsExt = "SpecialFunctions"
 
 [[deps.Colors]]
 deps = ["ColorTypes", "FixedPointNumbers", "Reexport"]
@@ -157,9 +180,9 @@ version = "0.12.11"
 
 [[deps.Compat]]
 deps = ["TOML", "UUIDs"]
-git-tree-sha1 = "b1c55339b7c6c350ee89f2c1604299660525b248"
+git-tree-sha1 = "8ae8d32e09f0dcf42a36b90d4e17f5dd2e4c4215"
 uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
-version = "4.15.0"
+version = "4.16.0"
 weakdeps = ["Dates", "LinearAlgebra"]
 
     [deps.Compat.extensions]
@@ -170,15 +193,21 @@ deps = ["Artifacts", "Libdl"]
 uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
 version = "1.1.1+0"
 
+[[deps.ConcurrentUtilities]]
+deps = ["Serialization", "Sockets"]
+git-tree-sha1 = "ea32b83ca4fefa1768dc84e504cc0a94fb1ab8d1"
+uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb"
+version = "2.4.2"
+
 [[deps.ConstructionBase]]
-deps = ["LinearAlgebra"]
-git-tree-sha1 = "260fd2400ed2dab602a7c15cf10c1933c59930a2"
+git-tree-sha1 = "76219f1ed5771adbb096743bff43fb5fdd4c1157"
 uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9"
-version = "1.5.5"
-weakdeps = ["IntervalSets", "StaticArrays"]
+version = "1.5.8"
+weakdeps = ["IntervalSets", "LinearAlgebra", "StaticArrays"]
 
     [deps.ConstructionBase.extensions]
     ConstructionBaseIntervalSetsExt = "IntervalSets"
+    ConstructionBaseLinearAlgebraExt = "LinearAlgebra"
     ConstructionBaseStaticArraysExt = "StaticArrays"
 
 [[deps.Contour]]
@@ -206,17 +235,29 @@ version = "1.0.0"
 deps = ["Printf"]
 uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"
 
+[[deps.DelaunayTriangulation]]
+deps = ["AdaptivePredicates", "EnumX", "ExactPredicates", "PrecompileTools", "Random"]
+git-tree-sha1 = "89df54fbe66e5872d91d8c2cd3a375f660c3fd64"
+uuid = "927a84f5-c5f4-47a5-9785-b46e178433df"
+version = "1.6.1"
+
 [[deps.DelimitedFiles]]
 deps = ["Mmap"]
 git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae"
 uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab"
 version = "1.9.1"
 
+[[deps.Deno_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "cd6756e833c377e0ce9cd63fb97689a255f12323"
+uuid = "04572ae6-984a-583e-9378-9577a1c2574d"
+version = "1.33.4+0"
+
 [[deps.Distances]]
 deps = ["LinearAlgebra", "Statistics", "StatsAPI"]
-git-tree-sha1 = "66c4c81f259586e8f002eacebc177e1fb06363b0"
+git-tree-sha1 = "c7e3a542b999843086e2f29dac96a618c105be1d"
 uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
-version = "0.10.11"
+version = "0.10.12"
 weakdeps = ["ChainRulesCore", "SparseArrays"]
 
     [deps.Distances.extensions]
@@ -229,9 +270,9 @@ uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"
 
 [[deps.Distributions]]
 deps = ["AliasTables", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns"]
-git-tree-sha1 = "22c595ca4146c07b16bcf9c8bea86f731f7109d2"
+git-tree-sha1 = "3101c32aab536e7a27b1763c0797dba151b899ad"
 uuid = "31c24e10-a181-5473-b8eb-7969acd0382f"
-version = "0.25.108"
+version = "0.25.113"
 
     [deps.Distributions.extensions]
     DistributionsChainRulesCoreExt = "ChainRulesCore"
@@ -260,18 +301,29 @@ deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"]
 uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
 version = "1.6.0"
 
-[[deps.DualNumbers]]
-deps = ["Calculus", "NaNMath", "SpecialFunctions"]
-git-tree-sha1 = "5837a837389fccf076445fce071c8ddaea35a566"
-uuid = "fa6b7ba4-c1ee-5f82-b5fc-ecf0adba8f74"
-version = "0.6.8"
-
 [[deps.EarCut_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
 git-tree-sha1 = "e3290f2d49e661fbd94046d7e3726ffcb2d41053"
 uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5"
 version = "2.2.4+0"
 
+[[deps.EnumX]]
+git-tree-sha1 = "bdb1942cd4c45e3c678fd11569d5cccd80976237"
+uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56"
+version = "1.0.4"
+
+[[deps.ExactPredicates]]
+deps = ["IntervalArithmetic", "Random", "StaticArrays"]
+git-tree-sha1 = "b3f2ff58735b5f024c392fde763f29b057e4b025"
+uuid = "429591f6-91af-11e9-00e2-59fbe8cec110"
+version = "2.2.8"
+
+[[deps.ExceptionUnwrapping]]
+deps = ["Test"]
+git-tree-sha1 = "dcb08a0d93ec0b1cdc4af184b26b591e9695423a"
+uuid = "460bff9d-24e4-43bc-9d9f-a8973cb893f4"
+version = "0.1.10"
+
 [[deps.Expat_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl"]
 git-tree-sha1 = "1c6317308b9dc757616f0b5cb379db10494443a7"
@@ -279,21 +331,15 @@ uuid = "2e619515-83b5-522b-bb60-26c02a35a201"
 version = "2.6.2+0"
 
 [[deps.Extents]]
-git-tree-sha1 = "2140cd04483da90b2da7f99b2add0750504fc39c"
+git-tree-sha1 = "81023caa0021a41712685887db1fc03db26f41f5"
 uuid = "411431e0-e8b7-467b-b5e0-f676ba4f2910"
-version = "0.1.2"
-
-[[deps.FFMPEG]]
-deps = ["FFMPEG_jll"]
-git-tree-sha1 = "b57e3acbe22f8484b4b5ff66a7499717fe1a9cc8"
-uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a"
-version = "0.4.1"
+version = "0.1.4"
 
 [[deps.FFMPEG_jll]]
 deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "PCRE2_jll", "Zlib_jll", "libaom_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"]
-git-tree-sha1 = "466d45dc38e15794ec7d5d63ec03d776a9aff36e"
+git-tree-sha1 = "8cc47f299902e13f90405ddb5bf87e5d474c0d38"
 uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5"
-version = "4.4.4+1"
+version = "6.1.2+0"
 
 [[deps.FFTW]]
 deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"]
@@ -303,24 +349,41 @@ version = "1.8.0"
 
 [[deps.FFTW_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
-git-tree-sha1 = "c6033cc3892d0ef5bb9cd29b7f2f0331ea5184ea"
+git-tree-sha1 = "4d81ed14783ec49ce9f2e168208a12ce1815aa25"
 uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a"
-version = "3.3.10+0"
+version = "3.3.10+1"
 
 [[deps.FileIO]]
 deps = ["Pkg", "Requires", "UUIDs"]
-git-tree-sha1 = "82d8afa92ecf4b52d78d869f038ebfb881267322"
+git-tree-sha1 = "91e0e5c68d02bcdaae76d3c8ceb4361e8f28d2e9"
 uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
-version = "1.16.3"
+version = "1.16.5"
+
+[[deps.FilePaths]]
+deps = ["FilePathsBase", "MacroTools", "Reexport", "Requires"]
+git-tree-sha1 = "919d9412dbf53a2e6fe74af62a73ceed0bce0629"
+uuid = "8fc22ac5-c921-52a6-82fd-178b2807b824"
+version = "0.8.3"
+
+[[deps.FilePathsBase]]
+deps = ["Compat", "Dates"]
+git-tree-sha1 = "7878ff7172a8e6beedd1dea14bd27c3c6340d361"
+uuid = "48062228-2e41-5def-b9a4-89aafe57970f"
+version = "0.9.22"
+weakdeps = ["Mmap", "Test"]
+
+    [deps.FilePathsBase.extensions]
+    FilePathsBaseMmapExt = "Mmap"
+    FilePathsBaseTestExt = "Test"
 
 [[deps.FileWatching]]
 uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"
 
 [[deps.FillArrays]]
 deps = ["LinearAlgebra"]
-git-tree-sha1 = "0653c0a2396a6da5bc4766c43041ef5fd3efbe57"
+git-tree-sha1 = "6a70198746448456524cb442b8af316927ff3e1a"
 uuid = "1a297f60-69ca-5386-bcde-b61e274b549b"
-version = "1.11.0"
+version = "1.13.0"
 weakdeps = ["PDMats", "SparseArrays", "Statistics"]
 
     [deps.FillArrays.extensions]
@@ -345,12 +408,6 @@ git-tree-sha1 = "9c68794ef81b08086aeb32eeaf33531668d5f5fc"
 uuid = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8"
 version = "1.3.7"
 
-[[deps.Formatting]]
-deps = ["Logging", "Printf"]
-git-tree-sha1 = "fb409abab2caf118986fc597ba84b50cbaf00b87"
-uuid = "59287772-0a20-5a39-b81b-1366585eb4c0"
-version = "0.4.3"
-
 [[deps.FreeType]]
 deps = ["CEnum", "FreeType2_jll"]
 git-tree-sha1 = "907369da0f8e80728ab49c1c7e09327bf0d6d999"
@@ -365,9 +422,9 @@ version = "2.13.2+0"
 
 [[deps.FreeTypeAbstraction]]
 deps = ["ColorVectorSpace", "Colors", "FreeType", "GeometryBasics"]
-git-tree-sha1 = "b5c7fe9cea653443736d264b85466bad8c574f4a"
+git-tree-sha1 = "77e2b094e61d939f9626181ab23d0b76e78f9fd3"
 uuid = "663a7486-cb36-511b-a19d-713bb74d65c9"
-version = "0.9.9"
+version = "0.10.5"
 
 [[deps.FriBidi_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl"]
@@ -375,11 +432,16 @@ git-tree-sha1 = "1ed150b39aebcc805c26b93a8d0122c940f64ce2"
 uuid = "559328eb-81f9-559d-9380-de523a88c83c"
 version = "1.0.14+0"
 
+[[deps.GeoFormatTypes]]
+git-tree-sha1 = "59107c179a586f0fe667024c5eb7033e81333271"
+uuid = "68eda718-8dee-11e9-39e7-89f7f65f511f"
+version = "0.4.2"
+
 [[deps.GeoInterface]]
-deps = ["Extents"]
-git-tree-sha1 = "801aef8228f7f04972e596b09d4dba481807c913"
+deps = ["Extents", "GeoFormatTypes"]
+git-tree-sha1 = "826b4fd69438d9ce4d2b19de6bc2f970f45f0f88"
 uuid = "cf35fbd7-0cd7-5166-be24-54bfbe79505f"
-version = "1.3.4"
+version = "1.3.8"
 
 [[deps.GeometryBasics]]
 deps = ["EarCut_jll", "Extents", "GeoInterface", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"]
@@ -393,23 +455,17 @@ git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046"
 uuid = "78b55507-aeef-58d4-861c-77aaff3498b1"
 version = "0.21.0+0"
 
-[[deps.Ghostscript_jll]]
-deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
-git-tree-sha1 = "43ba3d3c82c18d88471cfd2924931658838c9d8f"
-uuid = "61579ee1-b43e-5ca0-a5da-69d92c66a64b"
-version = "9.55.0+4"
+[[deps.Giflib_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "0224cce99284d997f6880a42ef715a37c99338d1"
+uuid = "59f7168a-df46-5410-90c8-f2779963d0ec"
+version = "5.2.2+0"
 
 [[deps.Glib_jll]]
 deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"]
-git-tree-sha1 = "7c82e6a6cd34e9d935e9aa4051b66c6ff3af59ba"
+git-tree-sha1 = "674ff0db93fffcd11a3573986e550d66cd4fd71f"
 uuid = "7746bdde-850d-59dc-9ae8-88ece973131d"
-version = "2.80.2+0"
-
-[[deps.Graphics]]
-deps = ["Colors", "LinearAlgebra", "NaNMath"]
-git-tree-sha1 = "d61890399bc535850c4bf08e4e0d3a7ad0f21cbd"
-uuid = "a2bd30eb-e257-5431-a919-1863eab51364"
-version = "1.1.2"
+version = "2.80.5+0"
 
 [[deps.Graphite2_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
@@ -419,9 +475,9 @@ version = "1.3.14+0"
 
 [[deps.GridLayoutBase]]
 deps = ["GeometryBasics", "InteractiveUtils", "Observables"]
-git-tree-sha1 = "f57a64794b336d4990d90f80b147474b869b1bc4"
+git-tree-sha1 = "fc713f007cff99ff9e50accba6373624ddd33588"
 uuid = "3955a311-db13-416c-9275-1d80ed98e5e9"
-version = "0.9.2"
+version = "0.11.0"
 
 [[deps.Grisu]]
 git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2"
@@ -447,76 +503,64 @@ uuid = "0234f1f7-429e-5d53-9886-15a909be8d59"
 version = "1.14.2+1"
 
 [[deps.HTTP]]
-deps = ["Base64", "Dates", "IniFile", "Logging", "MbedTLS", "NetworkOptions", "Sockets", "URIs"]
-git-tree-sha1 = "0fa77022fe4b511826b39c894c90daf5fce3334a"
+deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"]
+git-tree-sha1 = "1336e07ba2eb75614c99496501a8f4b233e9fafe"
 uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
-version = "0.9.17"
+version = "1.10.10"
 
 [[deps.HarfBuzz_jll]]
-deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"]
-git-tree-sha1 = "129acf094d168394e80ee1dc4bc06ec835e510a3"
+deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll"]
+git-tree-sha1 = "401e4f3f30f43af2c8478fc008da50096ea5240f"
 uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566"
-version = "2.8.1+1"
+version = "8.3.1+0"
 
 [[deps.Hwloc_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl"]
-git-tree-sha1 = "ca0f6bf568b4bfc807e7537f081c81e35ceca114"
+git-tree-sha1 = "50aedf345a709ab75872f80a2779568dc0bb461b"
 uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8"
-version = "2.10.0+0"
+version = "2.11.2+1"
 
 [[deps.HypergeometricFunctions]]
-deps = ["DualNumbers", "LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"]
-git-tree-sha1 = "f218fe3736ddf977e0e772bc9a586b2383da2685"
+deps = ["LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"]
+git-tree-sha1 = "b1c2585431c382e3fe5805874bda6aea90a95de9"
 uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a"
-version = "0.3.23"
+version = "0.3.25"
 
 [[deps.Hyperscript]]
 deps = ["Test"]
-git-tree-sha1 = "8d511d5b81240fc8e6802386302675bdf47737b9"
+git-tree-sha1 = "179267cfa5e712760cd43dcae385d7ea90cc25a4"
 uuid = "47d2ed2b-36de-50cf-bf87-49c2cf4b8b91"
-version = "0.0.4"
+version = "0.0.5"
 
 [[deps.ImageAxes]]
 deps = ["AxisArrays", "ImageBase", "ImageCore", "Reexport", "SimpleTraits"]
-git-tree-sha1 = "2e4520d67b0cef90865b3ef727594d2a58e0e1f8"
+git-tree-sha1 = "e12629406c6c4442539436581041d372d69c55ba"
 uuid = "2803e5a7-5153-5ecf-9a86-9b4c37f5f5ac"
-version = "0.6.11"
+version = "0.6.12"
 
 [[deps.ImageBase]]
 deps = ["ImageCore", "Reexport"]
-git-tree-sha1 = "b51bb8cae22c66d0f6357e3bcb6363145ef20835"
+git-tree-sha1 = "eb49b82c172811fd2c86759fa0553a2221feb909"
 uuid = "c817782e-172a-44cc-b673-b171935fbb9e"
-version = "0.1.5"
+version = "0.1.7"
 
 [[deps.ImageCore]]
-deps = ["AbstractFFTs", "ColorVectorSpace", "Colors", "FixedPointNumbers", "Graphics", "MappedArrays", "MosaicViews", "OffsetArrays", "PaddedViews", "Reexport"]
-git-tree-sha1 = "acf614720ef026d38400b3817614c45882d75500"
+deps = ["ColorVectorSpace", "Colors", "FixedPointNumbers", "MappedArrays", "MosaicViews", "OffsetArrays", "PaddedViews", "PrecompileTools", "Reexport"]
+git-tree-sha1 = "8c193230235bbcee22c8066b0374f63b5683c2d3"
 uuid = "a09fc81d-aa75-5fe9-8630-4744c3626534"
-version = "0.9.4"
+version = "0.10.5"
 
 [[deps.ImageIO]]
-deps = ["FileIO", "IndirectArrays", "JpegTurbo", "LazyModules", "Netpbm", "OpenEXR", "PNGFiles", "QOI", "Sixel", "TiffImages", "UUIDs"]
-git-tree-sha1 = "437abb322a41d527c197fa800455f79d414f0a3c"
+deps = ["FileIO", "IndirectArrays", "JpegTurbo", "LazyModules", "Netpbm", "OpenEXR", "PNGFiles", "QOI", "Sixel", "TiffImages", "UUIDs", "WebP"]
+git-tree-sha1 = "696144904b76e1ca433b886b4e7edd067d76cbf7"
 uuid = "82e4d734-157c-48bb-816b-45c225c6df19"
-version = "0.6.8"
-
-[[deps.ImageMagick]]
-deps = ["FileIO", "ImageCore", "ImageMagick_jll", "InteractiveUtils", "Libdl", "Pkg", "Random"]
-git-tree-sha1 = "5bc1cb62e0c5f1005868358db0692c994c3a13c6"
-uuid = "6218d12a-5da1-5696-b52f-db25d2ecc6d1"
-version = "1.2.1"
-
-[[deps.ImageMagick_jll]]
-deps = ["Artifacts", "Ghostscript_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "OpenJpeg_jll", "Zlib_jll", "libpng_jll"]
-git-tree-sha1 = "d65554bad8b16d9562050c67e7223abf91eaba2f"
-uuid = "c73af94c-d91f-53ed-93a7-00f77d67a9d7"
-version = "6.9.13+0"
+version = "0.6.9"
 
 [[deps.ImageMetadata]]
 deps = ["AxisArrays", "ImageAxes", "ImageBase", "ImageCore"]
-git-tree-sha1 = "355e2b974f2e3212a75dfb60519de21361ad3cb7"
+git-tree-sha1 = "2a81c3897be6fbcde0802a0ebe6796d0562f63ec"
 uuid = "bc367c6b-8a6b-528e-b4bd-a4b897500b49"
-version = "0.9.9"
+version = "0.9.10"
 
 [[deps.Imath_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl"]
@@ -530,20 +574,15 @@ uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959"
 version = "1.0.0"
 
 [[deps.Inflate]]
-git-tree-sha1 = "ea8031dea4aff6bd41f1df8f2fdfb25b33626381"
+git-tree-sha1 = "d1b1b796e47d94588b3757fe84fbf65a5ec4a80d"
 uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9"
-version = "0.1.4"
-
-[[deps.IniFile]]
-git-tree-sha1 = "f550e6e32074c939295eb5ea6de31849ac2c9625"
-uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f"
-version = "0.5.1"
+version = "0.1.5"
 
 [[deps.IntelOpenMP_jll]]
-deps = ["Artifacts", "JLLWrappers", "Libdl"]
-git-tree-sha1 = "be50fe8df3acbffa0274a744f1a99d29c45a57f4"
+deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"]
+git-tree-sha1 = "10bd689145d2c3b2a9844005d01087cc1194e79e"
 uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0"
-version = "2024.1.0+0"
+version = "2024.2.1+0"
 
 [[deps.InteractiveUtils]]
 deps = ["Markdown"]
@@ -554,27 +593,49 @@ deps = ["Adapt", "AxisAlgorithms", "ChainRulesCore", "LinearAlgebra", "OffsetArr
 git-tree-sha1 = "88a101217d7cb38a7b481ccd50d21876e1d1b0e0"
 uuid = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59"
 version = "0.15.1"
+weakdeps = ["Unitful"]
 
     [deps.Interpolations.extensions]
     InterpolationsUnitfulExt = "Unitful"
 
-    [deps.Interpolations.weakdeps]
-    Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
+[[deps.IntervalArithmetic]]
+deps = ["CRlibm_jll", "LinearAlgebra", "MacroTools", "RoundingEmulator"]
+git-tree-sha1 = "24c095b1ec7ee58b936985d31d5df92f9b9cfebb"
+uuid = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253"
+version = "0.22.19"
+
+    [deps.IntervalArithmetic.extensions]
+    IntervalArithmeticDiffRulesExt = "DiffRules"
+    IntervalArithmeticForwardDiffExt = "ForwardDiff"
+    IntervalArithmeticIntervalSetsExt = "IntervalSets"
+    IntervalArithmeticRecipesBaseExt = "RecipesBase"
+
+    [deps.IntervalArithmetic.weakdeps]
+    DiffRules = "b552c78f-8df3-52c6-915a-8e097449b14b"
+    ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
+    IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953"
+    RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
 
 [[deps.IntervalSets]]
 git-tree-sha1 = "dba9ddf07f77f60450fe5d2e2beb9854d9a49bd0"
 uuid = "8197267c-284f-5f27-9208-e0e47529a953"
 version = "0.7.10"
+weakdeps = ["Random", "RecipesBase", "Statistics"]
 
     [deps.IntervalSets.extensions]
     IntervalSetsRandomExt = "Random"
     IntervalSetsRecipesBaseExt = "RecipesBase"
     IntervalSetsStatisticsExt = "Statistics"
 
-    [deps.IntervalSets.weakdeps]
-    Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
-    RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
-    Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
+[[deps.InverseFunctions]]
+git-tree-sha1 = "a779299d77cd080bf77b97535acecd73e1c5e5cb"
+uuid = "3587e190-3f89-42d0-90ee-14403ec27112"
+version = "0.1.17"
+weakdeps = ["Dates", "Test"]
+
+    [deps.InverseFunctions.extensions]
+    InverseFunctionsDatesExt = "Dates"
+    InverseFunctionsTestExt = "Test"
 
 [[deps.IrrationalConstants]]
 git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2"
@@ -599,9 +660,9 @@ version = "1.0.0"
 
 [[deps.JLLWrappers]]
 deps = ["Artifacts", "Preferences"]
-git-tree-sha1 = "7e5d6779a1e09a36db2a7b6cff50942a0a7d0fca"
+git-tree-sha1 = "be3dc50a92e5a386872a493a10050136d4703f9b"
 uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
-version = "1.5.0"
+version = "1.6.1"
 
 [[deps.JSON]]
 deps = ["Dates", "Mmap", "Parsers", "Unicode"]
@@ -611,9 +672,9 @@ version = "0.21.4"
 
 [[deps.JSON3]]
 deps = ["Dates", "Mmap", "Parsers", "PrecompileTools", "StructTypes", "UUIDs"]
-git-tree-sha1 = "eb3edce0ed4fa32f75a0a11217433c31d56bd48b"
+git-tree-sha1 = "1d322381ef7b087548321d3f878cb4c9bd8f8f9b"
 uuid = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
-version = "1.14.0"
+version = "1.14.1"
 
     [deps.JSON3.extensions]
     JSON3ArrowExt = ["ArrowTypes"]
@@ -621,12 +682,6 @@ version = "1.14.0"
     [deps.JSON3.weakdeps]
     ArrowTypes = "31f734f8-188a-4ce0-8406-c8a06bd891cd"
 
-[[deps.JSServe]]
-deps = ["Base64", "CodecZlib", "Colors", "HTTP", "Hyperscript", "JSON3", "LinearAlgebra", "Markdown", "MsgPack", "Observables", "RelocatableFolders", "SHA", "Sockets", "Tables", "Test", "UUIDs", "WebSockets", "WidgetsBase"]
-git-tree-sha1 = "4cd7c5f723cad3cbbdfb295215e45b15b6924a19"
-uuid = "824d6782-a2ef-11e9-3a09-e5662e0c26f9"
-version = "1.2.9"
-
 [[deps.JpegTurbo]]
 deps = ["CEnum", "FileIO", "ImageCore", "JpegTurbo_jll", "TOML"]
 git-tree-sha1 = "fa6d0bcff8583bac20f1ffa708c3913ca605c611"
@@ -635,9 +690,9 @@ version = "0.1.5"
 
 [[deps.JpegTurbo_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl"]
-git-tree-sha1 = "c84a835e1a09b289ffcd2271bf2a337bbdda6637"
+git-tree-sha1 = "25ee0be4d43d0269027024d75a24c24d6c6e590c"
 uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8"
-version = "3.0.3+0"
+version = "3.0.4+0"
 
 [[deps.KernelDensity]]
 deps = ["Distributions", "DocStringExtensions", "FFTW", "Interpolations", "StatsBase"]
@@ -652,27 +707,27 @@ uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d"
 version = "3.100.2+0"
 
 [[deps.LERC_jll]]
-deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
-git-tree-sha1 = "bf36f528eec6634efc60d7ec062008f171071434"
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "36bdbc52f13a7d1dcb0f3cd694e01677a515655b"
 uuid = "88015f11-f218-50d7-93a8-a6af411a945d"
-version = "3.0.0+1"
+version = "4.0.0+0"
 
 [[deps.LLVMOpenMP_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl"]
-git-tree-sha1 = "d986ce2d884d49126836ea94ed5bfb0f12679713"
+git-tree-sha1 = "78211fb6cbc872f77cad3fc0b6cf647d923f4929"
 uuid = "1d63c593-3942-5779-bab2-d838dc0a180e"
-version = "15.0.7+0"
+version = "18.1.7+0"
 
 [[deps.LZO_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl"]
-git-tree-sha1 = "70c5da094887fd2cae843b8db33920bac4b6f07d"
+git-tree-sha1 = "854a9c268c43b77b0a27f22d7fab8d33cdb3a731"
 uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac"
-version = "2.10.2+0"
+version = "2.10.2+1"
 
 [[deps.LaTeXStrings]]
-git-tree-sha1 = "50901ebc375ed41dbf8058da26f9de442febbbec"
+git-tree-sha1 = "dda21b8cbd6a6c40d9d02a73230f9d70fed6918c"
 uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
-version = "1.3.1"
+version = "1.4.0"
 
 [[deps.LazyArtifacts]]
 deps = ["Artifacts", "Pkg"]
@@ -718,21 +773,27 @@ version = "3.2.2+1"
 
 [[deps.Libgcrypt_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll"]
-git-tree-sha1 = "9fd170c4bbfd8b935fdc5f8b7aa33532c991a673"
+git-tree-sha1 = "8be878062e0ffa2c3f67bb58a595375eda5de80b"
 uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4"
-version = "1.8.11+0"
+version = "1.11.0+0"
+
+[[deps.Libglvnd_jll]]
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"]
+git-tree-sha1 = "6f73d1dd803986947b2c750138528a999a6c7733"
+uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29"
+version = "1.6.0+0"
 
 [[deps.Libgpg_error_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl"]
-git-tree-sha1 = "fbb1f2bef882392312feb1ede3615ddc1e9b99ed"
+git-tree-sha1 = "c6ce1e19f3aec9b59186bdf06cdf3c4fc5f5f3e6"
 uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8"
-version = "1.49.0+0"
+version = "1.50.0+0"
 
 [[deps.Libiconv_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl"]
-git-tree-sha1 = "f9557a255370125b405568f9767d6d195822a175"
+git-tree-sha1 = "61dfdba58e585066d8bce214c5a51eaa0539f269"
 uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531"
-version = "1.17.0+0"
+version = "1.17.0+1"
 
 [[deps.Libmount_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl"]
@@ -742,9 +803,9 @@ version = "2.40.1+0"
 
 [[deps.Libtiff_jll]]
 deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "XZ_jll", "Zlib_jll", "Zstd_jll"]
-git-tree-sha1 = "6355fb9a4d22d867318db186fd09b09b35bd2ed7"
+git-tree-sha1 = "b404131d06f7886402758c9ce2214b636eb4d54a"
 uuid = "89763e89-9b03-5906-acba-b20f662cd828"
-version = "4.6.0+0"
+version = "4.7.0+0"
 
 [[deps.Libuuid_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl"]
@@ -756,17 +817,11 @@ version = "2.40.1+0"
 deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"]
 uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
 
-[[deps.LittleCMS_jll]]
-deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll"]
-git-tree-sha1 = "fa7fd067dca76cadd880f1ca937b4f387975a9f5"
-uuid = "d3a379c0-f9a3-5b72-a4c0-6bf4d2e8af0f"
-version = "2.16.0+0"
-
 [[deps.LogExpFunctions]]
 deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"]
-git-tree-sha1 = "18144f3e9cbe9b15b070288eef858f71b291ce37"
+git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea"
 uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
-version = "0.3.27"
+version = "0.3.28"
 
     [deps.LogExpFunctions.extensions]
     LogExpFunctionsChainRulesCoreExt = "ChainRulesCore"
@@ -781,6 +836,12 @@ version = "0.3.27"
 [[deps.Logging]]
 uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
 
+[[deps.LoggingExtras]]
+deps = ["Dates", "Logging"]
+git-tree-sha1 = "f02b56007b064fbfddb4c9cd60161b6dd0f40df3"
+uuid = "e6f89c97-d47a-5376-807f-9c37f3926c36"
+version = "1.1.0"
+
 [[deps.MAT]]
 deps = ["BufferedStreams", "CodecZlib", "HDF5", "SparseArrays"]
 git-tree-sha1 = "1d2dd9b186742b0f317f2530ddcbf00eebb18e96"
@@ -789,15 +850,15 @@ version = "0.10.7"
 
 [[deps.MKL_jll]]
 deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"]
-git-tree-sha1 = "80b2833b56d466b3858d565adcd16a4a05f2089b"
+git-tree-sha1 = "f046ccd0c6db2832a9f639e2c669c6fe867e5f4f"
 uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7"
-version = "2024.1.0+0"
+version = "2024.2.0+0"
 
 [[deps.MPICH_jll]]
 deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"]
-git-tree-sha1 = "4099bb6809ac109bfc17d521dad33763bcf026b7"
+git-tree-sha1 = "7715e65c47ba3941c502bffb7f266a41a7f54423"
 uuid = "7cb0a576-ebde-5e09-9194-50597f1243b4"
-version = "4.2.1+1"
+version = "4.2.3+0"
 
 [[deps.MPIPreferences]]
 deps = ["Libdl", "Preferences"]
@@ -807,9 +868,9 @@ version = "0.1.11"
 
 [[deps.MPItrampoline_jll]]
 deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"]
-git-tree-sha1 = "8c35d5420193841b2f367e658540e8d9e0601ed0"
+git-tree-sha1 = "70e830dab5d0775183c99fc75e4c24c614ed7142"
 uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748"
-version = "5.4.0+0"
+version = "5.5.1+0"
 
 [[deps.MacroTools]]
 deps = ["Markdown", "Random"]
@@ -818,17 +879,16 @@ uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
 version = "0.5.13"
 
 [[deps.Makie]]
-deps = ["Animations", "Base64", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "Distributions", "DocStringExtensions", "FFMPEG", "FileIO", "FixedPointNumbers", "Formatting", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageIO", "IntervalSets", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MakieCore", "Markdown", "Match", "MathTeXEngine", "Observables", "OffsetArrays", "Packing", "PlotUtils", "PolygonOps", "Printf", "Random", "RelocatableFolders", "Serialization", "Showoff", "SignedDistanceFields", "SparseArrays", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "UnicodeFun"]
-git-tree-sha1 = "b0323393a7190c9bf5b03af442fc115756df8e59"
-pinned = true
+deps = ["Animations", "Base64", "CRC32c", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "Dates", "DelaunayTriangulation", "Distributions", "DocStringExtensions", "Downloads", "FFMPEG_jll", "FileIO", "FilePaths", "FixedPointNumbers", "Format", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageBase", "ImageIO", "InteractiveUtils", "Interpolations", "IntervalSets", "InverseFunctions", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MacroTools", "MakieCore", "Markdown", "MathTeXEngine", "Observables", "OffsetArrays", "Packing", "PlotUtils", "PolygonOps", "PrecompileTools", "Printf", "REPL", "Random", "RelocatableFolders", "Scratch", "ShaderAbstractions", "Showoff", "SignedDistanceFields", "SparseArrays", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "TriplotBase", "UnicodeFun", "Unitful"]
+git-tree-sha1 = "5e4e0e027642293da251bf35dac408d692ccba8b"
 uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
-version = "0.17.13"
+version = "0.21.16"
 
 [[deps.MakieCore]]
-deps = ["Observables"]
-git-tree-sha1 = "fbf705d2bdea8fc93f1ae8ca2965d8e03d4ca98c"
+deps = ["ColorTypes", "GeometryBasics", "IntervalSets", "Observables"]
+git-tree-sha1 = "ae4dbe0fcf1594ed98594e5f4ee685295a2a6f74"
 uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b"
-version = "0.4.0"
+version = "0.8.10"
 
 [[deps.MappedArrays]]
 git-tree-sha1 = "2dab0221fe2b0f2cb6754eaa743cc266339f527e"
@@ -839,16 +899,11 @@ version = "0.4.2"
 deps = ["Base64"]
 uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
 
-[[deps.Match]]
-git-tree-sha1 = "1d9bc5c1a6e7ee24effb93f175c9342f9154d97f"
-uuid = "7eb4fadd-790c-5f42-8a69-bfa0b872bfbf"
-version = "1.2.0"
-
 [[deps.MathTeXEngine]]
-deps = ["AbstractTrees", "Automa", "DataStructures", "FreeTypeAbstraction", "GeometryBasics", "LaTeXStrings", "REPL", "RelocatableFolders", "Test"]
-git-tree-sha1 = "114ef48a73aea632b8aebcb84f796afcc510ac7c"
+deps = ["AbstractTrees", "Automa", "DataStructures", "FreeTypeAbstraction", "GeometryBasics", "LaTeXStrings", "REPL", "RelocatableFolders", "UnicodeFun"]
+git-tree-sha1 = "f45c8916e8385976e1ccd055c9874560c257ab13"
 uuid = "0a4f8689-d25c-4efe-a92b-7142dfc1aa53"
-version = "0.4.3"
+version = "0.6.2"
 
 [[deps.MbedTLS]]
 deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "NetworkOptions", "Random", "Sockets"]
@@ -898,17 +953,11 @@ git-tree-sha1 = "f5db02ae992c260e4826fe78c942954b48e1d9c2"
 uuid = "99f44e22-a591-53d1-9472-aa23ef4bd671"
 version = "1.2.1"
 
-[[deps.NaNMath]]
-deps = ["OpenLibm_jll"]
-git-tree-sha1 = "0877504529a3e5c3343c6f8b4c0381e57e4387e4"
-uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3"
-version = "1.0.2"
-
 [[deps.NearestNeighbors]]
 deps = ["Distances", "StaticArrays"]
-git-tree-sha1 = "ded64ff6d4fdd1cb68dfcbb818c69e144a5b2e4c"
+git-tree-sha1 = "8a3271d8309285f4db73b4f662b1b290c715e85e"
 uuid = "b8a86587-4115-5ab1-83bc-aa920d37bbce"
-version = "0.4.16"
+version = "0.4.21"
 
 [[deps.Netpbm]]
 deps = ["FileIO", "ImageCore", "ImageMetadata"]
@@ -920,6 +969,14 @@ version = "1.1.1"
 uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"
 version = "1.2.0"
 
+[[deps.NyxWidgets]]
+deps = ["Bonito", "Colors", "Format", "LazyArtifacts", "Observables"]
+git-tree-sha1 = "936f80aa61413c47da00f96abbc0186078698bca"
+repo-rev = "main"
+repo-url = "https://gitlab.com/dbc-nyx/NyxWidgets.jl"
+uuid = "c288fd06-43d3-4b04-8307-797133353e2e"
+version = "0.1.1"
+
 [[deps.Observables]]
 git-tree-sha1 = "7438a59546cf62428fc9d1bc94729146d37a7225"
 uuid = "510215fc-4207-5dde-b226-833fc4488ee2"
@@ -929,14 +986,14 @@ version = "0.5.5"
 deps = ["Observables"]
 git-tree-sha1 = "d54b4c26b41238806c172f85e1913736c2583b0a"
 repo-rev = "main"
-repo-url = "https://gitlab.com/dbc-nyx/observationpolicies.jl"
+repo-url = "https://gitlab.com/dbc-nyx/ObservationPolicies.jl"
 uuid = "6317928a-6b1a-42e8-b853-b8e2fc3e9ca3"
 version = "0.2.4"
 
 [[deps.OffsetArrays]]
-git-tree-sha1 = "e64b4f5ea6b7389f6f046d13d4896a8f9c1ba71e"
+git-tree-sha1 = "1a27764e945a152f7ca7efa04de513d473e9542e"
 uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
-version = "1.14.0"
+version = "1.14.1"
 weakdeps = ["Adapt"]
 
     [deps.OffsetArrays.extensions]
@@ -955,9 +1012,9 @@ version = "0.3.23+4"
 
 [[deps.OpenEXR]]
 deps = ["Colors", "FileIO", "OpenEXR_jll"]
-git-tree-sha1 = "327f53360fdb54df7ecd01e96ef1983536d1e633"
+git-tree-sha1 = "97db9e07fe2091882c765380ef58ec553074e9c7"
 uuid = "52e1d378-f018-4a11-a4be-720524705ac7"
-version = "0.3.2"
+version = "0.3.3"
 
 [[deps.OpenEXR_jll]]
 deps = ["Artifacts", "Imath_jll", "JLLWrappers", "Libdl", "Zlib_jll"]
@@ -965,12 +1022,6 @@ git-tree-sha1 = "8292dd5c8a38257111ada2174000a33745b06d4e"
 uuid = "18a262bb-aa17-5467-a713-aee519bc75cb"
 version = "3.2.4+0"
 
-[[deps.OpenJpeg_jll]]
-deps = ["Artifacts", "JLLWrappers", "Libdl", "Libtiff_jll", "LittleCMS_jll", "libpng_jll"]
-git-tree-sha1 = "f4cb457ffac5f5cf695699f82c537073958a6a6c"
-uuid = "643b3616-a352-519d-856d-80112ee9badc"
-version = "2.5.2+0"
-
 [[deps.OpenLibm_jll]]
 deps = ["Artifacts", "Libdl"]
 uuid = "05823500-19ac-5b8b-9628-191a04bc5112"
@@ -978,15 +1029,21 @@ version = "0.8.1+2"
 
 [[deps.OpenMPI_jll]]
 deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML", "Zlib_jll"]
-git-tree-sha1 = "a9de2f1fc98b92f8856c640bf4aec1ac9b2a0d86"
+git-tree-sha1 = "bfce6d523861a6c562721b262c0d1aaeead2647f"
 uuid = "fe0851c0-eecd-5654-98d4-656369965a5c"
-version = "5.0.3+0"
+version = "5.0.5+0"
+
+[[deps.OpenSSL]]
+deps = ["BitFlags", "Dates", "MozillaCACerts_jll", "OpenSSL_jll", "Sockets"]
+git-tree-sha1 = "38cb508d080d21dc1128f7fb04f20387ed4c0af4"
+uuid = "4d8831e6-92b7-49fb-bdf8-b643e874388c"
+version = "1.4.3"
 
 [[deps.OpenSSL_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl"]
-git-tree-sha1 = "3da7367955dcc5c54c1ba4d402ccdc09a1a3e046"
+git-tree-sha1 = "7493f61f55a6cce7325f197443aa80d32554ba10"
 uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95"
-version = "3.0.13+1"
+version = "3.0.15+1"
 
 [[deps.OpenSpecFun_jll]]
 deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"]
@@ -995,10 +1052,10 @@ uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e"
 version = "0.5.5+0"
 
 [[deps.Opus_jll]]
-deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
-git-tree-sha1 = "51a08fb14ec28da2ec7a927c4337e4332c2a4720"
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "6703a85cb3781bd5909d48730a67205f3f31a575"
 uuid = "91d4177d-7536-5919-b921-800302f37372"
-version = "1.3.2+0"
+version = "1.3.3+0"
 
 [[deps.OrderedCollections]]
 git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5"
@@ -1024,9 +1081,9 @@ version = "0.4.3"
 
 [[deps.Packing]]
 deps = ["GeometryBasics"]
-git-tree-sha1 = "1155f6f937fa2b94104162f01fa400e192e4272f"
+git-tree-sha1 = "ec3edfe723df33528e085e632414499f26650501"
 uuid = "19eb6ba3-879d-56ad-ad62-d5c202156566"
-version = "0.4.2"
+version = "0.5.0"
 
 [[deps.PaddedViews]]
 deps = ["OffsetArrays"]
@@ -1066,10 +1123,10 @@ uuid = "c2615984-ef14-4d40-b148-916c85b43307"
 version = "0.16.0"
 
 [[deps.PlotUtils]]
-deps = ["ColorSchemes", "Colors", "Dates", "PrecompileTools", "Printf", "Random", "Reexport", "Statistics"]
-git-tree-sha1 = "7b1a9df27f072ac4c9c7cbe5efb198489258d1f5"
+deps = ["ColorSchemes", "Colors", "Dates", "PrecompileTools", "Printf", "Random", "Reexport", "StableRNGs", "Statistics"]
+git-tree-sha1 = "3ca9a356cd2e113c420f2c13bea19f8d3fb1cb18"
 uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043"
-version = "1.4.1"
+version = "1.4.3"
 
 [[deps.PolygonOps]]
 git-tree-sha1 = "77b3d3605fc1cd0b42d95eba87dfcd2bf67d5ff6"
@@ -1094,26 +1151,32 @@ uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"
 
 [[deps.ProgressMeter]]
 deps = ["Distributed", "Printf"]
-git-tree-sha1 = "763a8ceb07833dd51bb9e3bbca372de32c0605ad"
+git-tree-sha1 = "8f6bc219586aef8baf0ff9a5fe16ee9c70cb65e4"
 uuid = "92933f4c-e287-5a05-a399-4b506db050ca"
-version = "1.10.0"
+version = "1.10.2"
 
 [[deps.PtrArrays]]
-git-tree-sha1 = "f011fbb92c4d401059b2212c05c0601b70f8b759"
+git-tree-sha1 = "77a42d78b6a92df47ab37e177b2deac405e1c88f"
 uuid = "43287f4e-b6f4-7ad1-bb20-aadabca52c3d"
-version = "1.2.0"
+version = "1.2.1"
 
 [[deps.QOI]]
 deps = ["ColorTypes", "FileIO", "FixedPointNumbers"]
-git-tree-sha1 = "18e8f4d1426e965c7b532ddd260599e1510d26ce"
+git-tree-sha1 = "8b3fc30bc0390abdce15f8822c889f669baed73d"
 uuid = "4b34888f-f399-49d4-9bb3-47ed5cae4e65"
-version = "1.0.0"
+version = "1.0.1"
 
 [[deps.QuadGK]]
 deps = ["DataStructures", "LinearAlgebra"]
-git-tree-sha1 = "9b23c31e76e333e6fb4c1595ae6afa74966a729e"
+git-tree-sha1 = "cda3b045cf9ef07a08ad46731f5a3165e56cf3da"
 uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
-version = "2.9.4"
+version = "2.11.1"
+
+    [deps.QuadGK.extensions]
+    QuadGKEnzymeExt = "Enzyme"
+
+    [deps.QuadGK.weakdeps]
+    Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9"
 
 [[deps.Quaternions]]
 deps = ["LinearAlgebra", "Random", "RealDot"]
@@ -1150,6 +1213,12 @@ git-tree-sha1 = "9f0a1b71baaf7650f4fa8a1d168c7fb6ee41f0c9"
 uuid = "c1ae055f-0cd5-4b69-90a6-9a35b1a98df9"
 version = "0.1.0"
 
+[[deps.RecipesBase]]
+deps = ["PrecompileTools"]
+git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff"
+uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
+version = "1.3.4"
+
 [[deps.Reexport]]
 git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b"
 uuid = "189a3867-3050-52da-a836-e630ba90ab69"
@@ -1157,9 +1226,9 @@ version = "1.2.2"
 
 [[deps.RelocatableFolders]]
 deps = ["SHA", "Scratch"]
-git-tree-sha1 = "307761d71804208c0c62abdbd0ea6822aa5bbefd"
+git-tree-sha1 = "ffdaf70d81cf6ff22c2b6e733c900c3321cab864"
 uuid = "05181044-ff0b-4ac5-8273-598c1e38db00"
-version = "0.2.0"
+version = "1.0.1"
 
 [[deps.Requires]]
 deps = ["UUIDs"]
@@ -1169,27 +1238,30 @@ version = "1.3.0"
 
 [[deps.Rmath]]
 deps = ["Random", "Rmath_jll"]
-git-tree-sha1 = "f65dcb5fa46aee0cf9ed6274ccbd597adc49aa7b"
+git-tree-sha1 = "852bd0f55565a9e973fcfee83a84413270224dc4"
 uuid = "79098fc4-a85e-5d69-aa6a-4863f24498fa"
-version = "0.7.1"
+version = "0.8.0"
 
 [[deps.Rmath_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl"]
-git-tree-sha1 = "d483cd324ce5cf5d61b77930f0bbd6cb61927d21"
+git-tree-sha1 = "58cdd8fb2201a6267e1db87ff148dd6c1dbd8ad8"
 uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f"
-version = "0.4.2+0"
+version = "0.5.1+0"
 
 [[deps.Rotations]]
 deps = ["LinearAlgebra", "Quaternions", "Random", "StaticArrays"]
 git-tree-sha1 = "5680a9276685d392c87407df00d57c9924d9f11e"
 uuid = "6038ab10-8711-5258-84ad-4b1120ba62dc"
 version = "1.7.1"
+weakdeps = ["RecipesBase"]
 
     [deps.Rotations.extensions]
     RotationsRecipesBaseExt = "RecipesBase"
 
-    [deps.Rotations.weakdeps]
-    RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
+[[deps.RoundingEmulator]]
+git-tree-sha1 = "40b9edad2e5287e05bd413a38f61a8ff55b9557b"
+uuid = "5eaf0fd0-dfba-4ccb-bf02-d820a40db705"
+version = "0.2.1"
 
 [[deps.SHA]]
 uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
@@ -1197,9 +1269,9 @@ version = "0.7.0"
 
 [[deps.SIMD]]
 deps = ["PrecompileTools"]
-git-tree-sha1 = "2803cab51702db743f3fda07dd1745aadfbf43bd"
+git-tree-sha1 = "52af86e35dd1b177d051b12681e1c581f53c281b"
 uuid = "fdea26ae-647d-5447-a871-4b548cad5224"
-version = "3.5.0"
+version = "3.7.0"
 
 [[deps.Scratch]]
 deps = ["Dates"]
@@ -1212,9 +1284,9 @@ uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
 
 [[deps.ShaderAbstractions]]
 deps = ["ColorTypes", "FixedPointNumbers", "GeometryBasics", "LinearAlgebra", "Observables", "StaticArrays", "StructArrays", "Tables"]
-git-tree-sha1 = "6b5bba824b515ec026064d1e7f5d61432e954b71"
+git-tree-sha1 = "79123bc60c5507f035e6d1d9e563bb2971954ec8"
 uuid = "65257c39-d410-5151-9873-9b3e5be5013e"
-version = "0.2.9"
+version = "0.4.1"
 
 [[deps.SharedArrays]]
 deps = ["Distributed", "Mmap", "Random", "Serialization"]
@@ -1232,6 +1304,11 @@ git-tree-sha1 = "d263a08ec505853a5ff1c1ebde2070419e3f28e9"
 uuid = "73760f76-fbc4-59ce-8f25-708e95d2df96"
 version = "0.4.0"
 
+[[deps.SimpleBufferStream]]
+git-tree-sha1 = "f305871d2f381d21527c770d4788c06c097c9bc1"
+uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7"
+version = "1.2.0"
+
 [[deps.SimpleTraits]]
 deps = ["InteractiveUtils", "MacroTools"]
 git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231"
@@ -1268,6 +1345,12 @@ weakdeps = ["ChainRulesCore"]
     [deps.SpecialFunctions.extensions]
     SpecialFunctionsChainRulesCoreExt = "ChainRulesCore"
 
+[[deps.StableRNGs]]
+deps = ["Random"]
+git-tree-sha1 = "83e6cce8324d49dfaf9ef059227f91ed4441a8e5"
+uuid = "860ef19b-820b-49d6-a774-d7a799459cd3"
+version = "1.0.2"
+
 [[deps.StackViews]]
 deps = ["OffsetArrays"]
 git-tree-sha1 = "46e589465204cd0c08b4bd97385e4fa79a0c770c"
@@ -1276,9 +1359,9 @@ version = "0.1.1"
 
 [[deps.StaticArrays]]
 deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"]
-git-tree-sha1 = "9ae599cd7529cfce7fea36cf00a62cfc56f0f37c"
+git-tree-sha1 = "777657803913ffc7e8cc20f0fd04b634f871af8f"
 uuid = "90137ffa-7385-5640-81b9-e52037218182"
-version = "1.9.4"
+version = "1.9.8"
 weakdeps = ["ChainRulesCore", "Statistics"]
 
     [deps.StaticArrays.extensions]
@@ -1286,9 +1369,9 @@ weakdeps = ["ChainRulesCore", "Statistics"]
     StaticArraysStatisticsExt = "Statistics"
 
 [[deps.StaticArraysCore]]
-git-tree-sha1 = "36b3d696ce6366023a0ea192b4cd442268995a0d"
+git-tree-sha1 = "192954ef1208c7019899fbf8049e717f92959682"
 uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c"
-version = "1.4.2"
+version = "1.4.3"
 
 [[deps.Statistics]]
 deps = ["LinearAlgebra", "SparseArrays"]
@@ -1303,24 +1386,21 @@ version = "1.7.0"
 
 [[deps.StatsBase]]
 deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"]
-git-tree-sha1 = "d1bf48bfcc554a3761a133fe3a9bb01488e06916"
+git-tree-sha1 = "5cf7606d6cef84b543b483848d4ae08ad9832b21"
 uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
-version = "0.33.21"
+version = "0.34.3"
 
 [[deps.StatsFuns]]
 deps = ["HypergeometricFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"]
-git-tree-sha1 = "cef0472124fab0695b58ca35a77c6fb942fdab8a"
+git-tree-sha1 = "b423576adc27097764a90e163157bcfc9acf0f46"
 uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c"
-version = "1.3.1"
+version = "1.3.2"
+weakdeps = ["ChainRulesCore", "InverseFunctions"]
 
     [deps.StatsFuns.extensions]
     StatsFunsChainRulesCoreExt = "ChainRulesCore"
     StatsFunsInverseFunctionsExt = "InverseFunctions"
 
-    [deps.StatsFuns.weakdeps]
-    ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
-    InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112"
-
 [[deps.StructArrays]]
 deps = ["ConstructionBase", "DataAPI", "Tables"]
 git-tree-sha1 = "f4dc295e983502292c4c3f951dbb4e985e35b3be"
@@ -1341,9 +1421,9 @@ version = "0.6.18"
 
 [[deps.StructTypes]]
 deps = ["Dates", "UUIDs"]
-git-tree-sha1 = "ca4bccb03acf9faaf4137a9abc1881ed1841aa70"
+git-tree-sha1 = "159331b30e94d7b11379037feeb9b690950cace8"
 uuid = "856f2bd8-1eba-4b0a-8007-ebc267875bd4"
-version = "1.10.0"
+version = "1.11.0"
 
 [[deps.SuiteSparse]]
 deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"]
@@ -1366,10 +1446,10 @@ uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c"
 version = "1.0.1"
 
 [[deps.Tables]]
-deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits"]
-git-tree-sha1 = "cb76cf677714c095e535e3501ac7954732aeea2d"
+deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "OrderedCollections", "TableTraits"]
+git-tree-sha1 = "598cd7c1f68d1e205689b1c2fe65a9f85846f297"
 uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
-version = "1.11.1"
+version = "1.12.0"
 
 [[deps.Tar]]
 deps = ["ArgTools", "SHA"]
@@ -1386,25 +1466,30 @@ version = "0.1.1"
 deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
 uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
 
+[[deps.ThreadPools]]
+deps = ["Printf", "RecipesBase", "Statistics"]
+git-tree-sha1 = "50cb5f85d5646bc1422aa0238aa5bfca99ca9ae7"
+uuid = "b189fb0b-2eb5-4ed4-bc0c-d34c51242431"
+version = "2.1.1"
+
 [[deps.TidyObservables]]
 deps = ["Observables"]
 git-tree-sha1 = "0589ec7397374678942cae9aa356b4bb6c1e9bf4"
 repo-rev = "main"
-repo-url = "https://gitlab.com/dbc-nyx/tidyobservables.jl"
+repo-url = "https://gitlab.com/dbc-nyx/TidyObservables.jl"
 uuid = "c8131bbd-73a8-4254-a42d-d5d4c5febb31"
 version = "0.1.1"
 
 [[deps.TiffImages]]
 deps = ["ColorTypes", "DataStructures", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "Mmap", "OffsetArrays", "PkgVersion", "ProgressMeter", "SIMD", "UUIDs"]
-git-tree-sha1 = "bc7fd5c91041f44636b2c134041f7e5263ce58ae"
+git-tree-sha1 = "0248b1b2210285652fbc67fd6ced9bf0394bcfec"
 uuid = "731e570b-9d59-4bfa-96dc-6df516fadf69"
-version = "0.10.0"
+version = "0.11.1"
 
 [[deps.TranscodingStreams]]
-deps = ["Random", "Test"]
-git-tree-sha1 = "9a6ae7ed916312b41236fcef7e0af564ef934769"
+git-tree-sha1 = "0c45878dcfdcfa8480052b6ab162cdd138781742"
 uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa"
-version = "0.9.13"
+version = "0.11.3"
 
 [[deps.TransformsBase]]
 deps = ["AbstractTrees"]
@@ -1412,6 +1497,11 @@ git-tree-sha1 = "2412fb54902b0063c69c2bcfbec6b571120cc856"
 uuid = "28dd2a49-a57a-4bfb-84ca-1a49db9b96b8"
 version = "0.1.2"
 
+[[deps.TriplotBase]]
+git-tree-sha1 = "4d4ed7f294cda19382ff7de4c137d24d16adc89b"
+uuid = "981d1d27-644d-49a2-9326-4793e63143c3"
+version = "0.1.0"
+
 [[deps.URIs]]
 git-tree-sha1 = "67db6cc7b3821e19ebe75791a9dd19c9b1188f2b"
 uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4"
@@ -1430,17 +1520,28 @@ git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf"
 uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1"
 version = "0.4.1"
 
+[[deps.Unitful]]
+deps = ["Dates", "LinearAlgebra", "Random"]
+git-tree-sha1 = "d95fe458f26209c66a187b1114df96fd70839efd"
+uuid = "1986cc42-f94f-5a68-af5c-568840ba703d"
+version = "1.21.0"
+weakdeps = ["ConstructionBase", "InverseFunctions"]
+
+    [deps.Unitful.extensions]
+    ConstructionBaseUnitfulExt = "ConstructionBase"
+    InverseFunctionsUnitfulExt = "InverseFunctions"
+
 [[deps.WGLMakie]]
-deps = ["Colors", "FileIO", "FreeTypeAbstraction", "GeometryBasics", "Hyperscript", "ImageMagick", "JSServe", "LinearAlgebra", "Makie", "Observables", "RelocatableFolders", "ShaderAbstractions", "StaticArrays"]
-git-tree-sha1 = "0c7a980515d2072b1bc094668b5ca94671d020de"
+deps = ["Bonito", "Colors", "FileIO", "FreeTypeAbstraction", "GeometryBasics", "Hyperscript", "LinearAlgebra", "Makie", "Observables", "PNGFiles", "PrecompileTools", "RelocatableFolders", "ShaderAbstractions", "StaticArrays"]
+git-tree-sha1 = "8ac9150de978e8215e7c9ab437d6f0a673748999"
 uuid = "276b4fcb-3e11-5398-bf8b-a0c2d153d008"
-version = "0.6.13"
+version = "0.10.16"
 
-[[deps.WebSockets]]
-deps = ["Base64", "Dates", "HTTP", "Logging", "Sockets"]
-git-tree-sha1 = "f91a602e25fe6b89afc93cf02a4ae18ee9384ce3"
-uuid = "104b5d7c-a370-577a-8038-80a2059c5097"
-version = "1.5.9"
+[[deps.WebP]]
+deps = ["CEnum", "ColorTypes", "FileIO", "FixedPointNumbers", "ImageCore", "libwebp_jll"]
+git-tree-sha1 = "aa1ca3c47f119fbdae8770c29820e5e6119b83f2"
+uuid = "e3aaa7dc-3e4b-44e0-be63-ffb868ccd7c1"
+version = "0.1.3"
 
 [[deps.WidgetsBase]]
 deps = ["Observables"]
@@ -1456,21 +1557,21 @@ version = "1.0.0"
 
 [[deps.XML2_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"]
-git-tree-sha1 = "52ff2af32e591541550bd753c0da8b9bc92bb9d9"
+git-tree-sha1 = "6a451c6f33a176150f315726eba8b92fbfdb9ae7"
 uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a"
-version = "2.12.7+0"
+version = "2.13.4+0"
 
 [[deps.XSLT_jll]]
-deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"]
-git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a"
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "XML2_jll", "Zlib_jll"]
+git-tree-sha1 = "a54ee957f4c86b526460a720dbc882fa5edcbefc"
 uuid = "aed1982a-8fda-507f-9586-7b0439959a61"
-version = "1.1.34+0"
+version = "1.1.41+0"
 
 [[deps.XZ_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl"]
-git-tree-sha1 = "ac88fb95ae6447c8dda6a5503f3bafd496ae8632"
+git-tree-sha1 = "15e637a697345f6743674f1322beefbc5dcd5cfc"
 uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800"
-version = "5.4.6+0"
+version = "5.6.3+0"
 
 [[deps.Xorg_libX11_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"]
@@ -1510,9 +1611,9 @@ version = "0.1.1+0"
 
 [[deps.Xorg_libxcb_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"]
-git-tree-sha1 = "b4bfde5d5b652e22b9c790ad00af08b6d042b97d"
+git-tree-sha1 = "bcd466676fef0878338c61e655629fa7bbc69d8e"
 uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b"
-version = "1.15.0+0"
+version = "1.17.0+0"
 
 [[deps.Xorg_xtrans_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl"]
@@ -1527,9 +1628,9 @@ version = "1.2.13+1"
 
 [[deps.Zstd_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl"]
-git-tree-sha1 = "e678132f07ddb5bfa46857f0d7620fb9be675d3b"
+git-tree-sha1 = "555d1076590a6cc2fdee2ef1469451f872d8b41b"
 uuid = "3161d3a3-bdf6-5164-811a-617609db77b4"
-version = "1.5.6+0"
+version = "1.5.6+1"
 
 [[deps.isoband_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
@@ -1550,39 +1651,45 @@ uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b"
 version = "3.9.0+0"
 
 [[deps.libass_jll]]
-deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
-git-tree-sha1 = "5982a94fcba20f02f42ace44b9894ee2b140fe47"
+deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Zlib_jll"]
+git-tree-sha1 = "e17c115d55c5fbb7e52ebedb427a0dca79d4484e"
 uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0"
-version = "0.15.1+0"
+version = "0.15.2+0"
 
 [[deps.libblastrampoline_jll]]
 deps = ["Artifacts", "Libdl"]
 uuid = "8e850b90-86db-534c-a0d3-1478176c7d93"
-version = "5.8.0+1"
+version = "5.11.0+0"
 
 [[deps.libfdk_aac_jll]]
-deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
-git-tree-sha1 = "daacc84a041563f965be61859a36e17c4e4fcd55"
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "8a22cf860a7d27e4f3498a0fe0811a7957badb38"
 uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280"
-version = "2.0.2+0"
+version = "2.0.3+0"
 
 [[deps.libpng_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"]
-git-tree-sha1 = "d7015d2e18a5fd9a4f47de711837e980519781a4"
+git-tree-sha1 = "b70c870239dc3d7bc094eb2d6be9b73d27bef280"
 uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f"
-version = "1.6.43+1"
+version = "1.6.44+0"
 
 [[deps.libsixel_jll]]
 deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "libpng_jll"]
-git-tree-sha1 = "d4f63314c8aa1e48cd22aa0c17ed76cd1ae48c3c"
+git-tree-sha1 = "7dfa0fd9c783d3d0cc43ea1af53d69ba45c447df"
 uuid = "075b6546-f08a-558a-be8f-8157d0f608a5"
-version = "1.10.3+0"
+version = "1.10.3+1"
 
 [[deps.libvorbis_jll]]
 deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"]
-git-tree-sha1 = "b910cb81ef3fe6e78bf6acee440bda86fd6ae00c"
+git-tree-sha1 = "490376214c4721cdaca654041f635213c6165cb3"
 uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a"
-version = "1.3.7+1"
+version = "1.3.7+2"
+
+[[deps.libwebp_jll]]
+deps = ["Artifacts", "Giflib_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Libtiff_jll", "libpng_jll"]
+git-tree-sha1 = "ccbb625a89ec6195856a50aa2b668a5c08712c94"
+uuid = "c5f90fcd-3b7e-5836-afba-fc50a0988cb2"
+version = "1.4.0+0"
 
 [[deps.nghttp2_jll]]
 deps = ["Artifacts", "Libdl"]
@@ -1601,13 +1708,13 @@ uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0"
 version = "17.4.0+2"
 
 [[deps.x264_jll]]
-deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
-git-tree-sha1 = "4fea590b89e6ec504593146bf8b988b2c00922b2"
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "35976a1216d6c066ea32cba2150c4fa682b276fc"
 uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a"
-version = "2021.5.5+0"
+version = "10164.0.0+0"
 
 [[deps.x265_jll]]
-deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
-git-tree-sha1 = "ee567a171cce03570d77ad3a43e90218e38937a9"
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "dcc541bb19ed5b0ede95581fb2e41ecf179527d2"
 uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76"
-version = "3.5.0+0"
+version = "3.6.0+0"
diff --git a/Project.toml b/Project.toml
index 115c84b1db4d403ba75f00adc5a0e23cbed88330..64da699db05a973147106bdfa26c67f49c02551a 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,19 +1,20 @@
 name = "LarvaTagger"
 uuid = "8b3b36f1-dfed-446e-8561-ea19fe966a4d"
 authors = ["François Laurent", "Institut Pasteur"]
-version = "0.18.5"
+version = "0.19.0"
 
 [deps]
+Bonito = "824d6782-a2ef-11e9-3a09-e5662e0c26f8"
 Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
 Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
 DocOpt = "968ba79b-81e4-546f-ab3a-2eecfa62a9db"
 Format = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8"
-JSServe = "824d6782-a2ef-11e9-3a09-e5662e0c26f9"
 LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
 Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
 Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
 Meshes = "eacbb407-ea5a-433e-ab97-5258b1ca43fa"
 NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce"
+NyxWidgets = "c288fd06-43d3-4b04-8307-797133353e2e"
 Observables = "510215fc-4207-5dde-b226-833fc4488ee2"
 ObservationPolicies = "6317928a-6b1a-42e8-b853-b8e2fc3e9ca3"
 OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
@@ -25,8 +26,11 @@ TidyObservables = "c8131bbd-73a8-4254-a42d-d5d4c5febb31"
 WGLMakie = "276b4fcb-3e11-5398-bf8b-a0c2d153d008"
 
 [compat]
-Makie = "< 0.18.0"
+Bonito = "< 4.0.0"
+NyxWidgets = "0.1.1"
+ObservationPolicies = "0.2.4"
 PlanarLarvae = ">= 0.11.2"
+TidyObservables = "0.1.1"
 julia = "1.6"
 
 [extras]
diff --git a/README.md b/README.md
index 5636442e7d09ab77e21cb0b498b7f0100701e828..eaf29e8fa886e47c79baaca4a86a7abd4cc05cbd 100644
--- a/README.md
+++ b/README.md
@@ -143,6 +143,7 @@ mkdir LarvaTagger && cd $_ && \
 julia --project=. -e 'using Pkg; Pkg.add([ \
   PackageSpec(url="https://gitlab.com/dbc-nyx/ObservationPolicies.jl"), \
   PackageSpec(url="https://gitlab.com/dbc-nyx/TidyObservables.jl"), \
+  PackageSpec(url="https://gitlab.com/dbc-nyx/NyxWidgets.jl"), \
   PackageSpec(url="https://gitlab.pasteur.fr/nyx/PlanarLarvae.jl"), \
   PackageSpec(url="https://gitlab.pasteur.fr/nyx/LarvaTagger.jl")])'
 ```
diff --git a/scripts/larvatagger b/scripts/larvatagger
index b46c90c643b4b4deec6ee5ccc84ae6b0bdf61ff4..5a0a65aed7cc2f537d6e5701784cba00852c3d00 100755
--- a/scripts/larvatagger
+++ b/scripts/larvatagger
@@ -52,7 +52,7 @@ import|merge|--version|-V)
 LarvaTagger
 
 Usage:
-  larvatagger open <file-path> [--backends=<path>] [--port=<number>] [--quiet] [--viewer] [--browser] [--view-factor=<real>] [--manual-label=<label>]
+  larvatagger open [<file-path>] [--backends=<path>] [--port=<number>] [--quiet] [--viewer] [--browser] [--view-factor=<real>] [--manual-label=<label>]
   larvatagger import <input-path> [<output-file>] [--id=<id>] [--framerate=<fps>] [--pixelsize=<μm>] [--overrides=<comma-separated-list>] [--default-label=<label>] [--manual-label=<label>] [--decode] [--copy-labels]
   larvatagger train <backend-path> <data-path> <model-instance> [--pretrained-model=<instance>] [--labels=<comma-separated-list>] [--sample-size=<N>] [--balancing-strategy=<strategy>] [--class-weights=<csv>] [--manual-label=<label>] [--layers=<N>] [--iterations=<N>] [--seed=<seed>] [--debug]
   larvatagger train <backend-path> <data-path> <model-instance> --fine-tune=<instance> [--balancing-strategy=<strategy>] [--manual-label=<label>] [--iterations=<N>] [--seed=<seed>] [--debug]
@@ -101,6 +101,7 @@ Commands:
 
   open      Launch the server-based GUI.
 
+    The optional positional argument <file-path> can also be the data root directory.
     Backends defined in LarvaTagger project root directory are automatically found. Other
     backend locations can be specified with the --backends argument.
 
diff --git a/scripts/larvatagger-gui.jl b/scripts/larvatagger-gui.jl
index 1bc01e8db4d536ee67bbd9cdbfb5144ce6954b03..36ac2abbfd3b0379a3e28d9d6bd84a44444d9d56 100755
--- a/scripts/larvatagger-gui.jl
+++ b/scripts/larvatagger-gui.jl
@@ -9,7 +9,9 @@ fi
 FLAGS=
 if [ "$1" = "--sysimage" -o "$1" = "-J" ]; then FLAGS="--sysimage $2 "; shift 2; fi
 if [ "${1:0:2}" = "-J" ]; then FLAGS="$1 "; shift; fi
-if [ -n "$1" -a -f "$1" ]; then FLAGS="$FLAGS -iq "; fi
+HELP=
+for i in "$@"; do if [ "$i" = "-h" -o "$i" = "--help" ]; then HELP=1; break; fi; done
+if [ -z $HELP ]; then FLAGS="$FLAGS -iq "; fi
 if [ -z "$JULIA" ]; then JULIA=julia; fi
     exec $JULIA --project="$PROJECT_DIR" --color=yes --startup-file=no $FLAGS\
     "${BASH_SOURCE[0]}" "$@"
diff --git a/src/LarvaTagger.jl b/src/LarvaTagger.jl
index b3484f945e8f30276e0fd401583c7cc814e09862..00d344cc349bbf9d23082cd6168c350f5e7cb788 100644
--- a/src/LarvaTagger.jl
+++ b/src/LarvaTagger.jl
@@ -3,13 +3,13 @@ module LarvaTagger
 using PlanarLarvae, PlanarLarvae.Datasets, PlanarLarvae.Formats
 using ObservationPolicies
 using TidyObservables
+using NyxWidgets, NyxWidgets.Players, NyxWidgets.FileBrowsers, NyxWidgets.FilePickers
+import NyxWidgets.Base: lowerdom, dom_id, dom_selector
 
 using Logging
-Logging.disable_logging(Logging.Warn) # prior to loading old libraries
-using JSServe, WGLMakie
-using JSServe: evaljs, onjs
+using Bonito, WGLMakie
+using Bonito: evaljs, onjs
 using Makie
-Logging.disable_logging(Logging.Debug) # restore default
 using Format
 using Colors
 using StaticArrays
diff --git a/src/cli.jl b/src/cli.jl
index c0a25fbc05090bc4e0ee55eb5c23388c8b7e62d6..23e8fdb34ca5ea3b672fe5da51891cb9c72a8d57 100644
--- a/src/cli.jl
+++ b/src/cli.jl
@@ -9,7 +9,7 @@ using .Toolkit
 usage = """Larva Tagger.
 
 Usage:
-  larvatagger.jl open <file-path> [--backends=<path>] [--port=<number>] [--quiet] [--viewer] [--browser] [--view-factor=<real>] [--manual-label=<label>]
+  larvatagger.jl open [<file-path>] [--backends=<path>] [--port=<number>] [--quiet] [--viewer] [--browser] [--view-factor=<real>] [--manual-label=<label>]
   larvatagger.jl import <input-path> [<output-file>] [--id=<id>] [--framerate=<fps>] [--pixelsize=<μm>] [--overrides=<comma-separated-list>] [--default-label=<label>] [--manual-label=<label>] [--decode] [--copy-labels]
   larvatagger.jl train <backend-path> <data-path> <model-instance> [--pretrained-model=<instance>] [--labels=<comma-separated-list>] [--sample-size=<N>] [--balancing-strategy=<strategy>] [--class-weights=<csv>] [--manual-label=<label>] [--layers=<N>] [--iterations=<N>] [--seed=<seed>] [--debug]
   larvatagger.jl train <backend-path> <data-path> <model-instance> --fine-tune=<instance> [--balancing-strategy=<strategy>] [--manual-label=<label>] [--iterations=<N>] [--seed=<seed>] [--debug]
@@ -58,6 +58,7 @@ Commands:
 
   open      Launch the server-based GUI.
 
+    The optional positional argument <file-path> can also be the data root directory.
     Backends defined in LarvaTagger project root directory are automatically found. Other
     backend locations can be specified with the --backends argument.
 
diff --git a/src/cli_open.jl b/src/cli_open.jl
index f46a52a26e98d40c4effd6775a2c873f2ffbf8aa..6f10ed66ad59f670a10d2e9d4e5e60061321ac3c 100644
--- a/src/cli_open.jl
+++ b/src/cli_open.jl
@@ -2,14 +2,14 @@ module GUI
 
 using DocOpt
 using LarvaTagger
-using JSServe: JSServe, Server
+using Bonito: Bonito, Server
 
 export main
 
 usage = """LarvaTagger.jl - launch the server-based GUI.
 
 Usage:
-  larvatagger-gui.jl <file-path> [--backends=<path>] [--port=<number>] [--quiet] [--viewer] [--browser] [--view-factor=<real>] [--manual-label=<label>]
+  larvatagger-gui.jl [<file-path>] [--backends=<path>] [--port=<number>] [--quiet] [--viewer] [--browser] [--view-factor=<real>] [--manual-label=<label>]
   larvatagger-gui.jl -h | --help
 
 Options:
@@ -22,6 +22,7 @@ Options:
   --view-factor=<real>    Scaling factor for the larva views; default is 2 on macOS, 1 elsewhere.
   --manual-label=<label>  Secondary label for manually labelled data [default: edited].
 
+The optional positional argument <file-path> can also be the data root directory.
 Backends defined in LarvaTagger project root directory are automatically found. Other
 backend locations can be specified with the --backends argument.
 """
@@ -47,9 +48,17 @@ function main(args=ARGS; exit_on_error=false)
 
     verbose = !parsed_args["--quiet"]
     infile = parsed_args["<file-path>"]
-    if !isfile(infile)
-        @error "File not found; did you specify a file path?" infile
-        exit()
+    if isempty(infile)
+        infile = nothing
+    elseif !isfile(infile)
+        if isdir(infile)
+            dataroot = infile
+            infile = nothing
+            cd(dataroot)
+        else
+            @error "File not found; did you specify a file path?" infile
+            exit()
+        end
     end
 
     kwargs = Dict{Symbol, Any}()
@@ -75,7 +84,7 @@ function main(args=ARGS; exit_on_error=false)
     port = isnothing(port) ? 9284 : parse(Int, port)
     server = Server(app, "0.0.0.0", port)
     if parsed_args["--browser"]
-        JSServe.openurl("http://127.0.0.1:$(port)")
+        Bonito.openurl("http://127.0.0.1:$(port)")
     end
     if verbose
         @info "The server is ready at http://127.0.0.1:$(port)"
diff --git a/src/controllers.jl b/src/controllers.jl
index bf5906beb44e138f07047e0b5b5149c79ef70ccd..0482579159b7448a9deb1759c8bec5c9ae96a2ea 100644
--- a/src/controllers.jl
+++ b/src/controllers.jl
@@ -170,7 +170,7 @@ struct LarvaController
     model::Dict{<:Integer, LarvaModel}
     tag_lut::AbstractObservable{<:TagLUT}
     activelarva::AbstractObservable{<:ActiveLarva}
-    player::AbstractAnimator
+    player::AbstractPlayer
     # could make LarvaController mutable instead...
     boundingbox::Ref{<:NTuple{4, <:AbstractFloat}}
     medianlarvasize::AbstractFloat
@@ -179,7 +179,7 @@ end
 function LarvaController(main::ControllerHub,
         model::Dict{<:Integer, LarvaModel},
         tag_lut::AbstractObservable{<:TagLUT},
-        player::AbstractAnimator)
+        player::AbstractPlayer)
     activelarva = Observable{ActiveLarva}(nothing)
     boundingbox = Ref((0.0, 10.0, 0.0, 10.0))
     mediansize = medianlarvasize(model)
@@ -190,7 +190,7 @@ function LarvaController(main::ControllerHub,
         model::Dict{<:Integer, LarvaModel},
         tag_lut::TagLUT,
         times::Vector{PlanarLarvae.Time})
-    player = playcontroller(times)
+    player = timecontroller(times)
     LarvaController(main, model, Observable(tag_lut), player)
 end
 
@@ -216,7 +216,7 @@ setboundingbox!(c::LarvaController, bb) = (c.boundingbox[] = bb)
 getmedianlarvasize(c) = getmedianlarvasize(gethub(c)[:larva])
 getmedianlarvasize(c::LarvaController) = c.medianlarvasize
 
-getplayer(c::AbstractAnimator) = c
+getplayer(c::AbstractPlayer) = c
 getplayer(c::LarvaController) = c.player
 getplayer(c) = getplayer(gethub(c))
 getplayer(hub::ControllerHub) = haskey(hub, :player) ? hub[:player] : getplayer(hub[:larva])
@@ -307,9 +307,9 @@ end
 setbounds!(view::Axis, lb, ub) = limits!(view, lb[1], ub[1], lb[2], ub[2])
 
 settimebounds!(c, larva) = settimebounds!(getplayer(c), larva)
-settimebounds!(::AbstractAnimator, _) = nothing
+settimebounds!(::AbstractPlayer, _) = nothing
 unsettimebounds!(c) = unsettimebounds!(getplayer(c))
-unsettimebounds!(::AbstractAnimator) = nothing
+unsettimebounds!(::AbstractPlayer) = nothing
 
 function slave(master::Observable, policy::ObservationPolicy=IndependentObservables())
     newobservable(policy, master)
@@ -362,8 +362,6 @@ function slave(master::LarvaController,
                     master.medianlarvasize)
 end
 
-stop!(controller::LarvaController) = stop!(controller.player)
-
 # history
 
 transactions(c) = get!(gethub(c), :transactions, Transaction[])
diff --git a/src/editor.jl b/src/editor.jl
index 61c33882a40150683a38145aa3c2ab10f7f9e8c2..abf8b403d3f060476ffa83f071b7b1e165e7cbf9 100644
--- a/src/editor.jl
+++ b/src/editor.jl
@@ -10,13 +10,12 @@ struct EditorView
     twooptiondialog::TwoOptionDialog
 end
 
-function JSServe.jsrender(session::Session, ev::EditorView)
+function Bonito.jsrender(session::Session, ev::EditorView)
     r(session, LarvaTaggerJS)
-    r(session, LarvaTaggerCSS)
     r(session, LoadAwesomeCSS)
-    r(session, JSServe.TailwindCSS)
+    r(session, Bonito.TailwindCSS)
     evaljs(session, js"window.addEventListener('beforeunload', LarvaTagger.confirmDialog)")
-    r(session,
+    dom = r(session,
       DOM.div(ev.larvaviewer,
               cp(ControlPanel(ev.tagfilter; id="tag-panel", title="Tags"),
                  ControlPanel(ev.larvafilter; id="larva-panel", title="Tracks"),
@@ -25,6 +24,8 @@ function JSServe.jsrender(session::Session, ev::EditorView)
               ev.loadanimation,
               ev.twooptiondialog;
               class="flex flex-row"))
+    r(session, LarvaTaggerCSS)
+    return dom
 end
 
 projectdir = dirname(Base.active_project())
@@ -33,16 +34,17 @@ function larvaeditor(path=nothing;
         allow_multiple_tags::Union{Nothing, Bool}=nothing,
         backend_directory::AbstractString=projectdir,
         manualtag::Union{Nothing, String, Symbol}="edited",
+        title="LarvaTagger",
         kwargs...)
 
     # to (re-)load a file, the app is reloaded with the filepath as sole information
     # from previous session
     input = Ref{Union{Nothing, String}}(path)
 
-    App() do session::Session
+    App(title=title) do session::Session
 
         controller = ControllerHub()
-        # used for method dispatch (here `Session` is taken to represent JSServe)
+        # used for method dispatch (here `Session` is taken to represent Bonito)
         controller[:frontend] = Session
 
         tryopenfile(controller, input)
@@ -59,7 +61,7 @@ function larvaeditor(path=nothing;
                             loadanimation(controller),
                             twooptiondialog(controller))
 
-        dom = JSServe.jsrender(session, editor)
+        dom = Bonito.jsrender(session, editor)
 
         turn_load_animation_off(controller)
 
diff --git a/src/files.jl b/src/files.jl
index 273e385e54911b683ebd1532a84c8e14b5dc7038..397c78160d2df38634a5202d36675cc61b279c54 100644
--- a/src/files.jl
+++ b/src/files.jl
@@ -1,4 +1,5 @@
 
+# deprecated; may be replaced by FileBrowsers.Model.FileBrowser
 struct WorkingDirectory
     controller
     root::String # could be hidden (not stored)
@@ -20,40 +21,40 @@ function workingdir(controller, root::String, path::String=""; secure::Bool=true
         startswith(path, "..") && throw(ArgumentError("path not below root: $path"))
     end
     wd = WorkingDirectory(controller, root, path)
-    on(wd.path, Val(1)) do newpath
-        try
-            if isempty(newpath)
-                newpath = "."
-            elseif isabspath(newpath)
-                fullpath = newpath
-                newpath = relpath(newpath, root)
-            end
-            fullpath = joinpath(root, newpath)
-            if secure
-                fullpath = realpath(fullpath)
-            end
-            if startswith(relpath(fullpath, root), "..")
-                @logmsg SecurityAlert "Path outside of chroot" path=newpath client=identifyclient(controller)
-                throw(Exception)
-            elseif !isdir(fullpath)
-                @debug "Path is not a directory" path=newpath
-                tryopenfile(controller, fullpath; reload=true)
-                throw(Exception)
-            end
-            return Validate(newpath)
-        catch
-            return Invalidate()
-        end
-    end
-    on(wd.path.model) do dir
-        @assert !isempty(dir)
-        content = [entry for entry in readdir(joinpath(root, dir))
-                   if entry[1] != '.']
-        if dir != "."
-            pushfirst!(content, "..")
-        end
-        wd.content[] = content
-    end
+    # on(wd.path, Val(1)) do newpath
+    #     try
+    #         if isempty(newpath)
+    #             newpath = "."
+    #         elseif isabspath(newpath)
+    #             fullpath = newpath
+    #             newpath = relpath(newpath, root)
+    #         end
+    #         fullpath = joinpath(root, newpath)
+    #         if secure
+    #             fullpath = realpath(fullpath)
+    #         end
+    #         if startswith(relpath(fullpath, root), "..")
+    #             @logmsg SecurityAlert "Path outside of chroot" path=newpath client=identifyclient(controller)
+    #             throw(Exception)
+    #         elseif !isdir(fullpath)
+    #             @debug "Path is not a directory" path=newpath
+    #             tryopenfile(controller, fullpath; reload=true)
+    #             throw(Exception)
+    #         end
+    #         return Validate(newpath)
+    #     catch
+    #         return Invalidate()
+    #     end
+    # end
+    # on(wd.path.model) do dir
+    #     @assert !isempty(dir)
+    #     content = [entry for entry in readdir(joinpath(root, dir))
+    #                if entry[1] != '.']
+    #     if dir != "."
+    #         pushfirst!(content, "..")
+    #     end
+    #     wd.content[] = content
+    # end
     return wd
 end
 
@@ -483,3 +484,25 @@ function getoutputfile(controller)
     return outputfile
 end
 
+const valid_inputfile_formats = (".mat", ".spine", ".outline", ".csv", ".label", ".json",
+                                 ".labels")
+
+function saveinputfile(path, content)
+    parts = splitext(path)
+    if length(parts) == 2 && parts[2] in valid_inputfile_formats
+        if ispath(path)
+            if isfile(path)
+                @warn "File already exists" path
+            else
+                @error "File already exists and is not a regular file" path
+                return
+            end
+        end
+        open(path, "w") do f
+            write(f, content)
+        end
+        Taggers.check_permissions(path)
+    else
+        @error "File format not admitted" fileparts=parts
+    end
+end
diff --git a/src/larvatagger.css b/src/larvatagger.css
index 6d9bfd011eaabccc09a2cf2f5a04fdf7ad1cdd38..0ead1806f7f3b3194136418789a3d16ca34ad755 100644
--- a/src/larvatagger.css
+++ b/src/larvatagger.css
@@ -58,6 +58,11 @@ canvas {
 	background: var(--theme-main-color);
 }
 
+.cp-tab .cp-tab-switch:checked ~ .control-panel .nyx-icon-button div {
+  /* restore default style, otherwise overridden by an above rule */
+  background-color: var(--nyx-icon-fill-color);
+}
+
 div.scrollable {
 	position: relative;
 	margin-right: 0;
@@ -87,16 +92,18 @@ table {
 	width: 30rem;
 }
 
+.nyx-filebrowser {
+  width: unset;
+  box-shadow: unset;
+  padding: unset;
+  margin: unset;
+}
+
 #larva-panel th {
 	padding-left: 1rem;
 	padding-right: 1rem;
 }
 
-#filemenu-panel input:first-child {
-	border-width: 0.0625rem;
-	padding: 0.2rem;
-}
-
 #metadata-panel input {
 	border-width: 0.0625rem;
 	margin: 0.0625rem 0.0625rem 0 0;
@@ -175,12 +182,9 @@ input[type=checkbox] {
 	border-width: 8px;
 }
 
-.player button {
+.nyx-player {
 	margin-left: 0.25rem;
 }
-.player input[type=range]:focus {
-	outline: none;
-}
 
 #tag-selector {
 	width: 100%;
diff --git a/src/larvatagger.js b/src/larvatagger.js
index 51b1478c28e87ce5887df5b73f831020d3d73a1c..d65fb7dfd6428106854c18bced86df6aa57e887a 100644
--- a/src/larvatagger.js
+++ b/src/larvatagger.js
@@ -1,8 +1,4 @@
 const LarvaTagger = (function () {
-	function toggle(jlstate) {
-		const buttonstate = JSServe.get_observable(jlstate);
-		JSServe.update_obs(jlstate, !buttonstate);
-	}
 
 	function timeSlider(element, input, stepmin, stepmax) {
 		const cooldown = 0.1; // in seconds
@@ -24,21 +20,21 @@ const LarvaTagger = (function () {
 				slavepos = heldpos;
 				heldpos = null;
 				// check bounds
-				mastermax = JSServe.get_observable(stepmax);
+				mastermax = stepmax.value;
 				if (mastermax < slavepos) {
 					slavepos = mastermax;
 					element.value = String(slavepos);
 				} else {
-					mastermin = JSServe.get_observable(stepmin);
+					mastermin = stepmin.value;
 					if (slavepos < mastermin) {
 						slavepos = mastermin;
 						element.value = String(slavepos);
 					}
 				}
 				// update
-				masterpos = JSServe.get_observable(input);
+				masterpos = input.value;
 				if (slavepos != masterpos) {
-					JSServe.update_obs(input, slavepos);
+					input.notify(slavepos);
 				}
 				// start cooldown
 				window.setTimeout(this.timeout, cooldown);
@@ -55,7 +51,7 @@ const LarvaTagger = (function () {
 	function focusOnTimeSlider(view) {
 		if (view === undefined)
 			view = document.getElementById("trackviewer");
-		view.querySelector("input[type=range]").focus()
+		//view.querySelector("input[type=range]").focus()
 	}
 
 	function discardLarvaEdits(checkbox, observer, label) {
@@ -64,7 +60,7 @@ const LarvaTagger = (function () {
 		} else {
 			var answer = confirm(`Discard changes for larva ${label}?`);
 			if (answer) {
-				JSServe.update_obs(observer, false);
+				observer.notify(false);
 			} else {
 				checkbox.checked = true;
 			}
@@ -79,7 +75,7 @@ const LarvaTagger = (function () {
 			const includeallcheckbox = document.getElementById('includeall');
 			includeallcheckbox.checked = false;
 		}
-		JSServe.update_obs(jlstate, jsstate.checked);
+		jlstate.notify(jsstate.checked);
 	}
 
 	function includeAllLarvae(jsstate, jlstate, button) {
@@ -91,7 +87,7 @@ const LarvaTagger = (function () {
 			button = document.getElementById('saveas');
 		}
 		button.disabled = !jsstate.checked;
-		JSServe.update_obs(jlstate, jsstate.checked);
+		jlstate.notify(jsstate.checked);
 	}
 
 	function insertNewTag(table, html) {
@@ -111,22 +107,24 @@ const LarvaTagger = (function () {
 		jlTagSelection = obs;
 	}
 	function toggleTagAtPointer(event) {
-		var that = event.target,
-		    scroll = that.parentElement.scrollTop;
-		event.preventDefault();
-		that.selected = !that.selected;
-		setTimeout(function(){that.parentElement.scrollTop = scroll;}, 0);
-		JSServe.update_obs(jlTagSelection, that.value);
+    if (event !== undefined) {
+      var that = event.target,
+          scroll = that.parentElement.scrollTop;
+      event.preventDefault();
+      that.selected = !that.selected;
+      setTimeout(function(){that.parentElement.scrollTop = scroll;}, 0);
+      jlTagSelection.notify(that.value);
+    }
 		return false;
 	}
 
 	function setOutputFilename(obs) {
-		var defaultfilepath = JSServe.get_observable(obs);
+		var defaultfilepath = obs.value;
 		if (defaultfilepath === null) {
 			defaultfilepath = "{yyyymmdd_HHMMSS}.label";
 		}
 		let filepath = prompt("File path: ", defaultfilepath);
-		JSServe.update_obs(obs, filepath);
+		obs.notify(filepath);
 	}
 
 	function updateSelectOptions(selectElement, selectOptions, jlObserver) {
@@ -139,7 +137,7 @@ const LarvaTagger = (function () {
 			option.text = selectOptions[i];
 			if (jlObserver !== undefined) {
 				option.ondblclick = () => {
-					JSServe.update_obs(jlObserver, selectOptions[i]);
+					jlObserver.notify(selectOptions[i]);
 				};
 			}
 			selectElement.append(option);
@@ -154,7 +152,7 @@ const LarvaTagger = (function () {
 			let files = Array.from(input.files);
 			if (files.length > 0) {
 				console.log(files);
-				JSServe.update_obs(jlObserver, files);
+				jlObserver.notify(files);
 			}
 		};
 		input.click();
@@ -179,7 +177,6 @@ const LarvaTagger = (function () {
 	}
 
 	return {
-		toggle,
 		timeSlider,
 		focusOnTimeSlider,
 		discardLarvaEdits,
diff --git a/src/models.jl b/src/models.jl
index 2c537415dc328090bbf2133ac7e5b982be09c88c..9910a28cadded6c41161031c487c93d567b6fc9e 100644
--- a/src/models.jl
+++ b/src/models.jl
@@ -310,7 +310,8 @@ end
 
 medianlarvasize(larvae::Dict; n::Int=100) = medianlarvasize(collect(values(larvae)); n=n)
 
-function medianlarvasize(larvae::Vector{LarvaModel}; n::Int=100)
+function medianlarvasize(larvae::Vector{LarvaModel}; n::Int=100)::Float32
+    isempty(larvae) && return 0.0f0
     larvae = Random.shuffle(larvae)
     sizes = Float64[]
     sizehint!(sizes, n)
@@ -324,7 +325,8 @@ function medianlarvasize(larvae::Vector{LarvaModel}; n::Int=100)
     return median(sizes)
 end
 
-function simultaneouslarvae(larvae)
+function simultaneouslarvae(larvae)::Int
+    isempty(larvae) && return 0
     laststep = maximum([larva.alignedsteps[end] for larva in values(larvae)])
     n = 0
     for step in 1:20:laststep
diff --git a/src/players.jl b/src/players.jl
index ff1f5fd5ae8ed5fa98a213c2aee584ace3fc1903..0cf3f3a05f6717c2d43db81e842cd13c6736f8b0 100644
--- a/src/players.jl
+++ b/src/players.jl
@@ -1,250 +1,71 @@
 
-abstract type AbstractAnimator end
-
-function stop! end
-function play! end
-function pause! end
-function playpause! end
-function isplaying end
-
-"""
-Plans are to make `Animator` not mutable; don't rely on its mutability.
-"""
-mutable struct Animator <: AbstractAnimator
-    times::Vector{PlanarLarvae.Time}
-    timestep::Observable
-    speed::Observable
-    isplaying::Observable
-end
-
-function playcontroller end
-
-function bind! end
-
-function playercontrols end
-
-## implementation
-
 const O{T} = Union{T, Observable{T}}
+using PlanarLarvae: Time
 asobservable(arg) = Observable(arg)
 asobservable(arg::Observable) = arg
 
-Animator(times::Vector{PlanarLarvae.Time},
-         step::O{Int}=1,
-         speed::O{<:AbstractFloat}=1.0,
-        ) = Animator(times,
-                     asobservable(step),
-                     asobservable(speed),
-                     Observable(false),
-                    )
-
-isplaying(controller::AbstractAnimator) = controller.isplaying[]
-isplaying!(controller::AbstractAnimator, state::Bool) = (controller.isplaying[] = state)
-
-function playnow!(controller::AbstractAnimator)
-    @info "Playing" speed=controller.speed[]
-    ts = controller.times
-    step = controller.timestep[]
-    stop = length(ts)
-    #
-    lag, n = 0, 0
-    clock_t = time()
-    #
-    while step<stop
-        t, t′ = ts[step], ts[step + 1]
-        dt = t′ - t
-        dt′= dt / controller.speed[] - lag
-        0 < dt′ && sleep(dt′)
-        #
-        step′ = ObservationPolicies.input(controller.timestep)[]
-        if isplaying(controller)
-            if step == step′
-                step′ = step + 1
-                @async controller.timestep[] = step′
-            end
-        else
-            break
-        end
-        # adjust lag correction
-        clock_t′ = time()
-        lag′= clock_t′- clock_t - dt
-        lag = (lag * n + lag′) / (n + 1)
-        @debug "Step timing" expected=dt actual=clock_t′-clock_t adjustment=lag
-        #
-        step = step′
-        clock_t = clock_t′
-        n += 1
-    end
-    @info "Pause/stop"
-    pause!(controller)
-end
-
-function bind!(controller::Animator)
-    on(controller.isplaying) do playnow
-        if playnow
-            @async playnow!(controller)
-        end
-    end
-    return controller
-end
-
-function bind!(controller::Animator, timeslider::Makie.Slider)
-    controller.timestep = timeslider.selected_index
-    #bind!(controller)
-end
-
-function bind!(controller::Animator, scene)
-    on(events(scene).window_open) do isopen
-        if !isopen
-            stop!(controller)
-        end
-    end
-    return controller
-end
-
-play!(controller::AbstractAnimator) = (controller.isplaying[] = true)
-pause!(controller::AbstractAnimator) = (controller.isplaying[] = false)
-playpause!(controller::AbstractAnimator) = (controller.isplaying[] = !controller.isplaying[])
-stop!(controller) = pause!(controller)
-
-function playcontroller(times; step::O{Int}=1, speed::O{<:AbstractFloat}=1.0)
-    controller = Animator(times, step, speed)
-    bind!(controller)
-    return controller
-end
-
-gettime(controller::AbstractAnimator, step::Int) = controller.times[step]
-
-struct TimeController <: AbstractAnimator
-    times::Vector{Float64}
-    time::AbstractObservable{Float64}
-    timestep::AbstractObservable{Int64}
+struct TimeController <: AbstractPlayer
+    player::AbstractPlayer
     stepmin::AbstractObservable{Int64}
     stepmax::AbstractObservable{Int64}
     boundreached::AbstractObservable{Bool}
-    playing::AbstractObservable{Bool}
-    speed::AbstractObservable{Float64}
 end
 
-isplaying(controller::TimeController) = controller.playing[]
-play!(controller::TimeController) = (controller.playing[] = true)
-pause!(controller::TimeController) = (controller.playing[] = false)
-playpause!(controller::TimeController) = (controller.playing[] = !controller.playing[])
-
-function stepforward!(controller::TimeController)
-    step = controller.timestep[]
-    if step < controller.stepmax[]
-        controller.timestep[] = step + 1
-    elseif isplaying(controller)
-        pause!(controller)
-    end
+function TimeController(player::AbstractPlayer)
+    stepmin = Observable(min(player))
+    stepmax = Observable(max(player))
+    TimeController(player, stepmin, stepmax, Observable(false))
 end
 
-function stepbackward!(controller::TimeController)
-    step = controller.timestep[]
-    if controller.stepmin[] < step
-        controller.timestep[] = step - 1
-    elseif isplaying(controller)
-        pause!(controller)
-    end
+Players.timestep(controller::TimeController) = timestep(controller.player)
+Players.isplaying(controller::TimeController) = isplaying(controller.player)
+Base.min(controller::TimeController) = controller.stepmin[]
+Base.max(controller::TimeController) = controller.stepmax[]
+Players.timestamps(controller::TimeController) = timestamps(controller.player)
+Players.speed(controller::TimeController) = Players.speed(controller.player)
+Players.repeat(controller::TimeController) = Players.repeat(controller.player)
+Players.Model.task(controller::TimeController) = Players.Model.task(controller.player)
+function Players.Model.task!(controller::TimeController, task)
+    Players.Model.task!(controller.player, task)
 end
 
 function timecontroller(times::Vector{Float64}; speed=1.0)
-    issorted(times) || throw(ArgumentError("timestamps are not sorted"))
-    initialstep = 1
-    timestep = newobservable(Cooldown(0.1), initialstep)
-    time = Observable(times[initialstep])
-    nsteps = length(times)
-    stepmin = Observable(1)
-    stepmax = Observable(nsteps)
-    boundreached = Observable(true)
-    initialized = Ref(false) # useful for skipping a "Time bound reached" message at startup
-    #
-    on(timestep) do step
-        min, max = stepmin[], stepmax[]
-        if step < min
-            timestep.val = min
-            @warn "step < stepmin"
-        elseif max < step
-            timestep.val = max
-            @warn "step > stepmax"
-        else
-            time[] = times[step]
-            if step == min || step == max
-                boundreached[] = true
-            elseif boundreached[]
-                boundreached[] = false
-            end
-        end
-    end
-    on(boundreached) do b
-        b && initialized[] && @info "Time bound reached"
-        initialized[] = true
-    end
-    on(stepmin) do bound
-        0 < bound || throw(DomainError("stepmin < 1"))
-        if timestep[] < bound
-            timestep[] = bound
-        end
-    end
-    on(stepmax) do bound
-        bound <= nsteps || throw(DomainError("stepmax > #steps=$(nsteps)"))
-        if bound < timestep[]
-            timestep[] = bound
-        end
-    end
-    #
-    playing = Observable(false)
-    if !(speed isa AbstractObservable)
-        speed = Observable(speed)
-    end
-    controller = TimeController(times, time, timestep,
-                                stepmin, stepmax, boundreached,
-                                playing, speed)
-    on(playing) do playnow
-        if playnow
-            @async playnow!(controller)
-        end
-    end
-    return controller
+    player = Player(times; speed=speed, loop=nothing)
+    ctrl = TimeController(player)
+    Players.Model.bind(ctrl, Players.Model.basicloop)
+    return ctrl
 end
 
 function slave(master::TimeController, policy::ObservationPolicy=IndependentObservables)
-    slave_timestep = newobservable(policy, master.timestep)
-    slave_time = map(slave_timestep) do step
-        master.times[step]
-    end
-    TimeController(master.times,
-                   slave_time,
-                   slave_timestep,
-                   master.stepmin,
-                   master.stepmax,
-                   master.boundreached,
-                   master.playing,
-                   master.speed)
-end
-
-isslave(controller::TimeController) = ObservationPolicies.haspolicy(controller.timestep)
-
-function stop!(controller::TimeController)
-    pause!(controller)
-    if isslave(controller)
-        # duck typing
-        try
-            ObservationPolicies.stop!(controller.timestep.policy)
-        catch
-            @debug begin
-                typ = typeof(controller.timestep.policy)
-                "`stop!` not implemented for slave controller $(typ)"
-            end
-        end
-    end
+    slave_timestep = newobservable(policy, timestep(master))
+    slave_player = Player(timestamps(master),
+                          slave_timestep,
+                          Players.speed(master),
+                          isplaying(master),
+                          Players.repeat(master),
+                          player.task)
+    TimeController(slave_player, master.stepmin, master.stepmax, master.boundreached)
 end
 
-gettimes(c::AbstractAnimator) = c.times
-gettimestep(c::AbstractAnimator) = c.timestep
-settimestep!(c::AbstractAnimator, t) = (c.timestep[] = t)
-
+isslave(controller::TimeController) = ObservationPolicies.haspolicy(timestep(controller))
+
+# function stop!(controller::TimeController)
+#     pause(controller)
+#     if isslave(controller)
+#         policy = timestep(controller).policy
+#         # duck typing
+#         try
+#             ObservationPolicies.stop!(policy)
+#         catch
+#             @debug "`stop!` not implemented for slave controller $(typeof(policy))"
+#         end
+#     end
+# end
+
+# aliases
+gettimes(c::AbstractPlayer) = timestamps(c)[]
+gettimestep(c::AbstractPlayer) = timestep(c)
+settimestep!(c::AbstractPlayer, t) = (timestep(c)[] = t)
 gettimes(c) = gettimes(getplayer(c))
 gettimestep(c) = gettimestep(getplayer(c))
 settimestep!(c, t) = settimestep!(getplayer(c), t)
@@ -256,6 +77,6 @@ function settimebounds!(c::TimeController, larva)
 end
 function unsettimebounds!(c::TimeController)
     c.stepmin[] = 1
-    c.stepmax[] = length(c.times)
+    c.stepmax[] = length(timestamps(c)[])
     @debug "Unbounding time" stepmin=c.stepmin[] stepmax=c.stepmax[]
 end
diff --git a/src/plots.jl b/src/plots.jl
index 104d85655e7879bf56ea429146331184c37907ba..4a76a07e37abd5b8b02ec19cc83924fb95b49504 100644
--- a/src/plots.jl
+++ b/src/plots.jl
@@ -25,8 +25,6 @@ withalpha(outline_color::Observable, alpha_value::Observable) = lift(withalpha,
 
 const MinimalLarva = Tuple{PathOrOutline, PathOrOutline, Color, Bool}
 
-const wgl = startswith(string(Makie.current_backend[]), "WGL")
-
 function Makie.plot!(plot::LarvaPlot{<:MinimalLarva})
     path = plot[1]
     shape_outline = plot[2]
@@ -41,13 +39,6 @@ function Makie.plot!(plot::LarvaPlot{<:MinimalLarva})
            linewidth=plot.shape_linewidth,
            visible=visibility)
     translate!(plot.plots[end], Makie.Vec3f(0, 0, 1))
-    if !wgl
-        poly!(plot, shape_outline;
-              color=withalpha(shape_color, plot.shape_alpha),
-              transparency=true,
-              visible=visibility)
-        translate!(plot.plots[end], Makie.Vec3f(0, 0, 0.5))
-    end
 end
 
 """
@@ -169,7 +160,6 @@ function SingleLarvaView(larvae::Vector{LarvaModel}, controller; editabletags::B
 
     tags = gettags(controller)
     timestep = gettimestep(controller)
-    player = getplayer(controller)
 
     f = on(usertags[]) do _
         _, color = gettag(tags[], model[], timestep[], fallback_color)
@@ -207,7 +197,6 @@ function SingleLarvaView(larvae::Vector{LarvaModel}, controller; editabletags::B
             shape_color.val = html_color(color)
             #
             visible[] = true # notify all subplots
-            timestep == last_timestep && pause!(player)
         elseif visible[]
             visible[] = false
         end
@@ -599,12 +588,19 @@ function DecoratedLarvae(larvae::Vector{DecoratedLarva})
             end
         end
     end
-    center = Meshes.coordinates ∘ Meshes.centroid
-    centers = cat((center(larva.activearea) for larva in larvae)...; dims=2)
-    norm2 = sum(centers .* centers, dims=1)
+    centers = zeros(Float32, (2, 0))
+    norm2 = zeros(Float32, (1, 0))
+    if ~isempty(larvae)
+        center = Meshes.coordinates ∘ Meshes.centroid
+        centers = cat((center(larva.activearea) for larva in larvae)...; dims=2)
+        norm2 = sum(centers .* centers, dims=1)
+    end
     DecoratedLarvae(larvae, centers, norm2, hovered_larva, hovering_active)
 end
 
+Base.isempty(larvae::DecoratedLarvae) = isempty(larvae.larvae)
+Base.length(larvae::DecoratedLarvae) = length(larvae.larvae)
+
 function find(larvae::DecoratedLarvae, position)
     pos = Vector(position)'
     p2 = pos * pos'
@@ -641,7 +637,13 @@ end
 
 Meshes.boundingbox(larva::StatefulLarva) = Meshes.boundingbox(larva.model)
 Meshes.boundingbox(larva::DecoratedLarva) = Meshes.boundingbox(larva.larva)
-Meshes.boundingbox(larvae::DecoratedLarvae) = Meshes.boundingbox(map(Meshes.boundingbox, larvae.larvae))
+function Meshes.boundingbox(larvae::DecoratedLarvae)
+    if isempty(larvae)
+        Meshes.Box(Meshes.Point(0.0, 1.0), Meshes.Point(0.0, 1.0))
+    else
+        Meshes.boundingbox(map(Meshes.boundingbox, larvae.larvae))
+    end
+end
 
 function setmouseevents!(scene, plot::DecoratedLarvae, ctrl; consume=false)
     on(events(scene).mousebutton) do mb
diff --git a/src/viewer.jl b/src/viewer.jl
index 7463492dd4eef02ba03de68d9bd57efd5c12df6a..b2344b44c0937f0cb60ae6270712619a7ce975aa 100644
--- a/src/viewer.jl
+++ b/src/viewer.jl
@@ -1,51 +1,58 @@
 
 struct ViewerView
     assayplot::AssayViewer
-    trackplot::TrackViewer
+    trackplot::Union{Nothing, TrackViewer}
 end
 
-function JSServe.jsrender(session::Session, vv::ViewerView)
-    controller = vv.trackplot.plot.controller
+function Bonito.jsrender(session::Session, vv::ViewerView)
     delayed_controller = vv.assayplot.plot.controller
 
-    assaydom = r(session, vv.assayplot)
-    trackdom = r(session, vv.trackplot)
-
-    on(larvaevents(controller).activated) do _
-        vv.assayplot.visible[] = false
-        JSServe.evaljs(session,
-                       js"""
-                       const assayviewer = $(assaydom);
-                       const trackviewer = $(trackdom);
-                       assayviewer.style.display = "none";
-                       trackviewer.style.display = "block";
-                       """)
-    end
-    on(larvaevents(controller).deactivated) do _
-        vv.assayplot.visible[] = true
-        JSServe.evaljs(session,
-                       js"""
-                       const assayviewer = $(assaydom);
-                       const trackviewer = $(trackdom);
-                       trackviewer.style.display = "none";
-                       assayviewer.style.display = "block";
-                       """)
-    end
-
     on(session.on_close) do closed
         if closed
-            stop!(delayed_controller)
+            pause(delayed_controller.player)
         end
     end
 
-    r(session,
-      DOM.div(JSServe.TailwindCSS, assaydom, trackdom; class="flex flex-row"))
+    assaydom = r(session, vv.assayplot)
+    trackdom = nothing
+
+    if !isnothing(vv.trackplot)
+        controller = vv.trackplot.plot.controller
+        trackdom = r(session, vv.trackplot)
+
+        on(session, larvaevents(controller).activated) do _
+            vv.assayplot.visible[] = false
+            evaljs(session,
+                js"""
+                const assayviewer = $(assaydom);
+                const trackviewer = $(trackdom);
+                assayviewer.style.display = "none";
+                trackviewer.style.display = "block";
+                """)
+        end
+        on(session, larvaevents(controller).deactivated) do _
+            vv.assayplot.visible[] = true
+            evaljs(session,
+                js"""
+                const assayviewer = $(assaydom);
+                const trackviewer = $(trackdom);
+                trackviewer.style.display = "none";
+                assayviewer.style.display = "block";
+                """)
+        end
+
+        r(session,
+          DOM.div(Bonito.TailwindCSS, assaydom, trackdom; class="flex flex-row"))
+
+    else
+        r(session, DOM.div(Bonito.TailwindCSS, assaydom))
+    end
 end
 
 function larvaviewer(path::String; allow_multiple_tags::Union{Nothing, Bool}=false,
-    kwargs...)
+    title="LarvaTagger", kwargs...)
 
-    App() do session::Session
+    App(title=title) do session::Session
 
         controller = ControllerHub()
 
@@ -54,9 +61,12 @@ function larvaviewer(path::String; allow_multiple_tags::Union{Nothing, Bool}=fal
         viewer = larvaviewer(controller; multipletags=allow_multiple_tags, kwargs...)
 
         r(session, LarvaTaggerJS)
+
+        dom = Bonito.jsrender(session, viewer)
+
         r(session, LarvaTaggerCSS)
 
-        JSServe.jsrender(session, viewer)
+        return dom
     end
 end
 
@@ -65,9 +75,14 @@ function larvaviewer(controller;
         multipletags::Union{Nothing, Bool}=false,
         viewfactor::Real=1,
     )
-    model = getlarvae(controller)
-    times = gettimes(controller)
-    tag_lut = gettags(controller)
+    model = LarvaModel[]
+    times = PlanarLarvae.Time[0.0, 1.0]
+    tag_lut = Observable(TagLUT())
+    if haskey(controller, :larva)
+        model = getlarvae(controller)
+        times = gettimes(controller)
+        tag_lut = gettags(controller)
+    end
 
     controller[:player] = player = timecontroller(times)
     controller[:larva] = larva = LarvaController(controller, model, tag_lut, player)
diff --git a/src/wgl.jl b/src/wgl.jl
index 66408bbd99a3ed35ee7d82005f1836f9672c153d..c21d40fc2d8abf281f35b0a8be23e0374dcfbc00 100644
--- a/src/wgl.jl
+++ b/src/wgl.jl
@@ -1,28 +1,15 @@
 getsession(c) = gethub(c)[:session]
 setsession!(c, session) = (gethub(c)[:session] = session)
 
-const r = JSServe.jsrender
+const r = Bonito.jsrender
 
-const LarvaTaggerJS = JSServe.Dependency(:LarvaTagger,
-                                         [joinpath(@__DIR__, "larvatagger.js")])
-const LarvaTaggerCSS = JSServe.Asset(joinpath(@__DIR__, "larvatagger.css"))
-const LoadAwesomeCSS = JSServe.Asset(joinpath(@__DIR__, "loadawesome.css"))
+const LarvaTaggerJS = Bonito.Asset(joinpath(@__DIR__, "larvatagger.js"))
+const LarvaTaggerCSS = Bonito.Asset(joinpath(@__DIR__, "larvatagger.css"))
+const LoadAwesomeCSS = Bonito.Asset(joinpath(@__DIR__, "loadawesome.css"))
 
 scrollable(elements...) = DOM.div(DOM.table(elements...);
                                   class="scrollable", scrolling="auto")
 
-const Trigger = Observable{Bool}
-const Toggle = Observable{Bool}
-
-function simpletrigger(cls::DataType, args...)
-    click = Trigger()
-    on(click) do _
-        #click.val = false
-        @debug "Click on $cls"
-    end
-    return cls(click, args...)
-end
-
 function with_attributes(kwargs; class="", style="", kwargs′...)
     attributes = Dict{Symbol, Any}(kwargs′)
     merge!(attributes, Dict{Symbol, Any}(kwargs))
@@ -42,36 +29,19 @@ function with_attributes(kwargs; class="", style="", kwargs′...)
     return attributes
 end
 
-css(lines...) = join(lines, "; ")
-
 css_button = "button"
 
-function DOMtrigger(label, trigger, attributes)
-    DOM.button(label;
-               type="button",
-               onclick=js"""
-               JSServe.update_obs($trigger, true);
-               """,
-               attributes...
-              )
-end
-
-DOMtrigger(label, trigger; kwargs...) = DOMtrigger(label, trigger, with_attributes(kwargs; class=css_button))
-
-function DOMtoggle(label, jlstate, attributes)
-    DOM.button(label;
-               type="button",
-               onclick=js"LarvaTagger.toggle($jlstate)",
-               attributes...
-              )
-end
-
 struct ControlPanel
     id::String
     title::String
     active::Bool
     elements
     attributes
+    dom_id
+end
+
+function ControlPanel(id::String, title::String, active::Bool, elements, attributes)
+    ControlPanel(id, title, active, elements, attributes, dom_id())
 end
 
 function ControlPanel(elements...; id::String, title::String, active::Bool=false, attributes...)
@@ -79,34 +49,23 @@ function ControlPanel(elements...; id::String, title::String, active::Bool=false
     ControlPanel(id, title, active, elements, attributes)
 end
 
-function JSServe.jsrender(session::Session, panel::ControlPanel)
+function lowerdom(panel::ControlPanel)
     tabid = "select-" * panel.id
     escaped_attrs = (:for=>tabid,)
-    r(session,
-      DOM.div(DOM.input(; type="radio", name="tab", id=tabid, class="cp-tab-switch",
-                          checked=panel.active),
-              DOM.label(panel.title; class="cp-tab-label", escaped_attrs...),
-              DOM.div(panel.elements...; id=panel.id, panel.attributes...);
-              class="cp-tab"))
+    return DOM.div(DOM.input(; type="radio", name="tab", id=tabid, class="cp-tab-switch",
+                             checked=panel.active),
+                   DOM.label(panel.title; class="cp-tab-label", escaped_attrs...),
+                   DOM.div(panel.elements...; id=panel.id, panel.attributes...);
+                   class="cp-tab")
 end
+Bonito.jsrender(session::Session, panel::ControlPanel) = lowerdom(session, panel)
 
 cp(panels...) = DOM.div(panels...; class="cp-tabs")
 
-struct HomeButton
-    click::Trigger
-    attributes
-end
-
-function homebutton(kwargs...)
-    simpletrigger(HomeButton,
-                  with_attributes(kwargs;
-                                  class=css_button,
-                                  style="position:absolute; top:1.5625rem; right:2.5rem;"))
-end
-
-function JSServe.jsrender(session::Session, b::HomeButton)
-    r(session,
-      DOMtrigger("⌂", b.click, b.attributes))
+function homebutton()
+    NyxWidgets.Button("⌂";
+                      class=css_button,
+                      style="position:absolute; top:1.5625rem; right:2.5rem;")
 end
 
 struct AssayPlot
@@ -114,22 +73,30 @@ struct AssayPlot
     figure::Figure
     axis::Axis
     larvae::DecoratedLarvae
-    homebutton::HomeButton
+    homebutton::NyxWidgets.Button
+    dom_id
+end
+
+function AssayPlot(controller, figure::Figure, axis::Axis, larvae::DecoratedLarvae,
+        homebutton::NyxWidgets.Button)
+    AssayPlot(controller, figure, axis, larvae, homebutton, dom_id())
 end
 
 function AssayPlot(ctrl, larvae::DecoratedLarvae; size=FIGSIZE)
     gethub(ctrl)[:decoratedlarvae] = larvae
-    fig = Figure(resolution=size)
+    fig = Figure(size=size)
     width = 0.1f0 # try to get 1px
     color = RGBAf(0, 0, 0, 0.36)
     ax = Axis(fig.layout[1, 1], aspect=DataAspect(), xgridwidth=width, ygridwidth=width,
               xgridcolor=color, ygridcolor=color)
     autosize!(ax, size)
-    plot = larvaplot!(ax, larvae)
-    setkeyboardevents!(plot.parent, getplayer(ctrl))
-    setbounds!(ax, ctrl, larvae)
+    if !isempty(larvae)
+        plot = larvaplot!(ax, larvae)
+        setkeyboardevents!(plot.parent, getplayer(ctrl))
+        setbounds!(ax, ctrl, larvae)
+    end
     button = homebutton()
-    on(button.click) do _
+    on(button) do _
         setbounds!(ax, ctrl, larvae)
     end
     AssayPlot(ctrl, fig, ax, larvae, button)
@@ -162,12 +129,8 @@ function assayplot(data, ctrl, args...; kwargs...)
     return plot
 end
 
-function JSServe.jsrender(session::Session, p::AssayPlot)
-    r(session,
-      DOM.div(r(session, p.homebutton),
-              p.figure;
-              class="relative"))
-end
+lowerdom(p::AssayPlot) = DOM.div(p.homebutton, p.figure; class="relative")
+Bonito.jsrender(session::Session, p::AssayPlot) = lowerdom(session, p)
 
 function setmouseevents!(plot::AssayPlot; kwargs...)
     setmouseevents!(plot.axis.scene, plot.larvae, plot.controller; kwargs...)
@@ -179,13 +142,19 @@ struct TrackPlot
     axis::Axis
     larvae::Vector{LarvaModel}
     view::SingleLarvaView
-    homebutton::HomeButton
+    homebutton::NyxWidgets.Button
+    dom_id
+end
+
+function TrackPlot(controller, figure, axis::Axis, larvae::Vector{LarvaModel},
+        view::SingleLarvaView, homebutton::NyxWidgets.Button)
+    TrackPlot(controller, figure, axis, larvae, view, homebutton, dom_id())
 end
 
 function TrackPlot(controller, larvae::Vector{LarvaModel};
         size=FIGSIZE, editabletags::Bool=true)
     view = SingleLarvaView(larvae, controller; editabletags=editabletags)
-    fig = Figure(resolution=size)
+    fig = Figure(size=size)
     width = 0.1f0 # try to get 1px
     color = RGBAf(0, 0, 0, 0.36)
     ax = Axis(fig.layout[1, 1], aspect=DataAspect(), xgridwidth=width, ygridwidth=width,
@@ -205,7 +174,7 @@ function TrackPlot(controller, larvae::Vector{LarvaModel};
         end
     end
     button = homebutton()
-    on(button.click) do _
+    on(button) do _
         deactivatelarva!(controller)
     end
     TrackPlot(controller, fig, ax, larvae, view, button)
@@ -215,212 +184,38 @@ TrackPlot(ctrl, model::Dict{<:Integer, LarvaModel}, args...; kwargs...) = TrackP
 
 trackplot(model, controller; kwargs...) = TrackPlot(controller, model; kwargs...)
 
-function JSServe.jsrender(session::Session, p::TrackPlot)
-    # TODO: read client window size
-    r(session,
-      DOM.div(r(session, p.homebutton),
-              p.figure;
-              class="relative"))
-end
-
-struct PlayPauseButton
-    playing::AbstractObservable{Bool}
-    attributes::Dict{Symbol, Any}
-end
-
-function playpausebutton(ctrl::TimeController; kwargs...)
-    PlayPauseButton(ctrl.playing, with_attributes(kwargs; class=css_button))
-end
-
-function JSServe.jsrender(session::Session, b::PlayPauseButton)
-    r(session,
-      DOMtoggle("Play/Pause", b.playing, b.attributes))
-end
-
-struct StepForwardButton
-    click::Trigger
-    attributes::Dict{Symbol, Any}
-end
-
-function stepforwardbutton(ctrl::TimeController; kwargs...)
-    button = simpletrigger(StepForwardButton, with_attributes(kwargs; class=css_button))
-    on(button.click) do _
-        stepforward!(ctrl)
-    end
-    return button
-end
-
-function JSServe.jsrender(session::Session, b::StepForwardButton)
-    r(session,
-      DOMtrigger(">", b.click, b.attributes))
-end
-
-struct StepBackwardButton
-    click::Trigger
-    attributes::Dict{Symbol, Any}
-end
-
-function stepbackwardbutton(ctrl::TimeController; kwargs...)
-    button = simpletrigger(StepBackwardButton, with_attributes(kwargs; class=css_button))
-    on(button.click) do _
-        stepbackward!(ctrl)
-    end
-    return button
-end
-
-function JSServe.jsrender(session::Session, b::StepBackwardButton)
-    r(session,
-      DOMtrigger("<", b.click, b.attributes))
-end
-
-struct TimeSlider
-    master::TimeController
-    attributes::Dict{Symbol, Any}
-end
-
-function timeslider(master::TimeController; kwargs...)
-    TimeSlider(master,
-               with_attributes(kwargs;
-                               style="width: 100%",
-                               class="ml-1 mr-1"))
-end
-timeslider(times::Vector{Float64}; kwargs...) = timeslider(timecontroller(times); kwargs...)
-
-function JSServe.jsrender(session::Session, ts::TimeSlider)
-    master = ts.master
-    input = ObservationPolicies.input(master.timestep)
-    output = ObservationPolicies.output(master.timestep)
-    moveslider = js"LarvaTagger.timeSlider(this, $input, $(master.stepmin), $(master.stepmax)).moveto(value)"
-    slider = DOM.input(type="range",
-                       min=1,
-                       max=length(master.times),
-                       value=output,
-                       step=1,
-                       oninput=moveslider;
-                       ts.attributes...)
-    JSServe.onload(session, slider, js"(slider)=>{ slider.value = 1; }")
-    r(session, slider)
-end
-
-struct TimeLabel
-    time::AbstractObservable{Float64}
-    ndecimals::Int
-    attributes::Dict{Symbol, Any}
-end
-
-function timelabel(time::AbstractObservable{Float64};
-                   ndecimals=2, kwargs...)
-    TimeLabel(time,
-              ndecimals,
-              with_attributes(kwargs,
-                              style=css("width: 12.5rem",
-                                        "display: flex",
-                                        "flex-direction: row",
-                                        "align-items: center")))
-end
-timelabel(master::TimeController; kwargs...) = timelabel(master.time; kwargs...)
-
-function JSServe.jsrender(session::Session, tl::TimeLabel)
-    fmt = FormatExpr("t={: .$(tl.ndecimals)f} s")
-    formatter = t -> format(fmt, t)
-    r(session,
-      DOM.label(lift(formatter, tl.time);
-                tl.attributes...))
-end
-
-struct PlaybackSpeedList
-    backspeed::AbstractObservable{Float64}
-    frontspeed::AbstractObservable{Float64}
-    attributes::Dict{Symbol, Any}
-end
-
-function playbackspeedlist(animator; kwargs...)
-    backspeed = animator.speed
-    frontspeed = map(a -> a, backspeed)
-    PlaybackSpeedList(backspeed, frontspeed, with_attributes(kwargs; class="speed"))
-end
-
-function JSServe.jsrender(session::Session, sl::PlaybackSpeedList)
-    dom = r(session,
-            DOM.select(DOM.option("0.25"; value=0.25),
-                       DOM.option("0.5"; value=0.5),
-                       DOM.option("0.75"; value=0.75),
-                       DOM.option("1.0"; value=1.0),
-                       DOM.option("1.25"; value=1.25),
-                       DOM.option("1.5"; value=1.5),
-                       DOM.option("2.0"; value=2.0);
-                       onchange=js"JSServe.update_obs($(sl.backspeed), parseFloat(this.value))",
-                       sl.attributes...))
-    on(sl.frontspeed) do speed
-        frontspeed = if speed < 0.375
-            "0.25"
-        elseif speed < 0.625
-            "0.5"
-        elseif speed < 0.875
-            "0.75"
-        elseif speed < 1.125
-            "1.0"
-        elseif speed < 1.375
-            "1.25"
-        elseif speed < 1.75
-            "1.5"
-        else
-            "2.0"
-        end
-        evaljs(session, js"$(dom).value = $frontspeed;")
-    end
-    notify(sl.frontspeed)
-    return dom
-end
-
-struct Player
-    controller::TimeController
-    timeslider::TimeSlider
-    timelabel::TimeLabel
-    playpausebutton::PlayPauseButton
-    stepbackwardbutton::StepBackwardButton
-    stepforwardbutton::StepForwardButton
-    playbackspeedlist::PlaybackSpeedList
-    attributes::Dict{Symbol, Any}
-end
-
+lowerdom(p::TrackPlot) = DOM.div(p.homebutton, p.figure; class="relative")
+Bonito.jsrender(session::Session, p::TrackPlot) = lowerdom(session, p)
 
 player(times::Vector; kwargs...) = player(timecontroller(times); kwargs...)
 
 player(controller; kwargs...) = player(getplayer(controller); kwargs...)
 
 function player(animator::TimeController; kwargs...)
-    Player(animator,
-           timeslider(animator),
-           timelabel(animator),
-           playpausebutton(animator),
-           stepbackwardbutton(animator),
-           stepforwardbutton(animator),
-           playbackspeedlist(animator),
-           with_attributes(kwargs;
-                           class="flex flex-row player",
-                           style="width: 100%; height: 2.5rem"),
-          )
-end
-
-function JSServe.jsrender(session::Session, p::Player)
-    r(session,
-      DOM.div(r(session, p.playpausebutton),
-              r(session, p.stepbackwardbutton),
-              r(session, p.stepforwardbutton),
-              r(session, p.playbackspeedlist),
-              r(session, p.timeslider),
-              r(session, p.timelabel);
-              onmouseenter=js"LarvaTagger.focusOnTimeSlider(this)",
-              p.attributes...
-             ))
-end
-
-mutable struct AssayViewer
+    step = timestep(animator)
+    stepmin = animator.stepmin
+    stepmax = animator.stepmax
+    playbackspeeds = ["0.25", "0.5", "0.75", "1.0", "1.25", "1.5", "2"]
+    view = PlayerView(animator, playbackspeeds)
+    oninput = js"""(evt) => {
+        var step = evt.srcElement.value;
+        LarvaTagger.timeSlider(evt.srcElement, $step, $stepmin, $stepmax).moveto(step)
+    }"""
+    # set oninput attribute in immutable structs
+    slider = view.timeslider
+    slider = Players.TimeSlider(slider.player, slider.index, slider.playpause, oninput,
+                                slider.attributes, view.dom_id)
+    view = PlayerView(view.player, view.playpause, view.stepforward, view.stepbackward,
+                      view.repeat, view.speedselector, slider, view.timelabel,
+                      view.attributes, view.dom_id)
+    return view
+end
+
+struct AssayViewer
     plot::AssayPlot
-    player::Player
+    player::PlayerView
     visible::AbstractObservable{Bool}
-    dom
+    dom_id
 end
 
 function assayviewer(controller; kwargs...)
@@ -434,18 +229,14 @@ function assayviewer(controller; kwargs...)
             notify(gettimestep(controller))
         end
     end
-    AssayViewer(plot, play, visible, nothing)
+    AssayViewer(plot, play, visible, dom_id())
 end
 
-function JSServe.jsrender(session::Session, av::AssayViewer)
-    dom = DOM.div(r(session, av.plot),
-                  r(session, av.player);
-                  class="flex flex-col",
-                  onmouseenter=js"LarvaTagger.focusOnTimeSlider(this)",
-                 )
-    av.dom = dom
-    return r(session, dom)
+function lowerdom(av::AssayViewer)
+    focus = js"(evt)=>{ LarvaTagger.focusOnTimeSlider(evt.srcElement); }"
+    return DOM.div(av.plot, av.player; class="flex flex-col", onmouseenter=focus)
 end
+Bonito.jsrender(session::Session, av::AssayViewer) = lowerdom(session, av)
 
 struct LarvaInfo
     controller
@@ -455,6 +246,7 @@ struct LarvaInfo
     reviewed::AbstractObservable{Bool}
     edited::AbstractObservable{Bool}
     included::AbstractObservable{Bool}
+    dom_id
 end
 
 function larvainfo(controller, id)
@@ -516,28 +308,30 @@ function larvainfo(controller, id)
               clicked,
               reviewed,
               edited,
-              included)
+              included,
+              dom_id())
 end
 
-JSServe.jsrender(session::Session, li::LarvaInfo) = r(session, prerender(li))
+Bonito.jsrender(session::Session, li::LarvaInfo) = lowerdom(session, li)
 
-function prerender(li::LarvaInfo)
+function lowerdom(li::LarvaInfo)
     label = "#$(li.id)"
-    discard_larva_edits = js"LarvaTagger.discardLarvaEdits(this, $(li.edited), $label)"
     label = DOM.label(label,
-                      onmouseenter=js"JSServe.update_obs($(li.hovered), true)",
-                      onmouseleave=js"JSServe.update_obs($(li.hovered), false)",
-                      onmouseup=js"JSServe.update_obs($(li.clicked), true)")
+                      onmouseenter=js"()=>{ $(li.hovered).notify(true); }",
+                      onmouseleave=js"()=>{ $(li.hovered).notify(false); }",
+                      onmouseup=js"()=>{ $(li.clicked).notify(true); }")
+    toggle_reviewed = js"(evt)=>{ $(li.reviewed).notify(evt.srcElement.checked); }"
     reviewed_checkbox = DOM.input(type="checkbox",
                                   checked=li.reviewed,
-                                  onchange=js"""
-                                  JSServe.update_obs($(li.reviewed), this.checked);
-                                  """)
+                                  onchange=toggle_reviewed)
+    discard_larva_edits = js"""(evt)=>{
+        LarvaTagger.discardLarvaEdits(evt.srcElement, $(li.edited), $label);
+    }"""
     edited_checkbox = DOM.input(type="checkbox",
                                 checked=li.edited,
                                 # onchange is not triggered from Julia
                                 onchange=discard_larva_edits)
-    include = js"LarvaTagger.includeLarva(this, $(li.included))"
+    include = js"(evt)=>{ LarvaTagger.includeLarva(evt.srcElement, $(li.included)); }"
     included_checkbox = DOM.input(type="checkbox",
                                   checked=li.included,
                                   class="included",
@@ -554,6 +348,12 @@ struct LarvaFilter
     includeall::AbstractObservable{Bool}
     saveas::AbstractObservable{Bool}
     attributes
+    dom_id
+end
+
+function LarvaFilter(controller, entries::Vector{LarvaInfo},
+        includeall::AbstractObservable{Bool}, saveas::AbstractObservable{Bool}, attributes)
+    LarvaFilter(controller, entries, includeall, saveas, attributes, dom_id())
 end
 
 function larvafilter(controller::ControllerHub; kwargs...)
@@ -586,18 +386,21 @@ end
 
 getlarvafilter(controller) = gethub(controller)[:larvafilter]
 
-function JSServe.jsrender(session::Session, lf::LarvaFilter)
-    outputfile = getoutputfile(lf.controller)
+
+function lowerdom(lf::LarvaFilter)
+    outputfilename = getoutputfile(lf.controller).name
     disabled = map(!, lf.saveas)
-    button = r(session,
-               DOM.button("Save as...";
-                          type="button",
-                          id="saveas",
-                          onclick=js"LarvaTagger.setOutputFilename($(outputfile.name))",
-                          disabled=disabled,
-                          style="width: 100%",
-                          class=css_button))
-    includeall = js"LarvaTagger.includeAllLarvae(this, $(lf.includeall), $button)"
+    setoutputfilename = js"()=>{ LarvaTagger.setOutputFilename($outputfilename); }"
+    button = DOM.button("Save as...";
+                        type="button",
+                        id="saveas",
+                        onclick=setoutputfilename,
+                        disabled=disabled,
+                        style="width: 100%",
+                        class=css_button)
+    includeall = js"""(evt)=>{
+        LarvaTagger.includeAllLarvae(evt.srcElement, $(lf.includeall), $button);
+    }"""
     table = scrollable(DOM.tr(DOM.th(""),
                               DOM.th("Reviewed"),
                               DOM.th("Edited"),
@@ -610,15 +413,19 @@ function JSServe.jsrender(session::Session, lf::LarvaFilter)
                                                id="includeall",
                                                onchange=includeall),
                                      style="text-align: center;")),
-                       prerender.(lf.entries)...)
-    JSServe.onload(session, table, js"LarvaTagger.uncheckAllCheckboxes")
-    r(session,
-      DOM.div(table,
-              button;
-              with_attributes(lf.attributes; class="panel")...))
+                       lowerdom.(lf.entries)...)
+    return DOM.div(table,
+                   button;
+                   with_attributes(lf.attributes; class="panel")...)
 end
 
-abstract type AbstractView end
+function Bonito.jsrender(session::Session, lf::LarvaFilter)
+    node = lowerdom(session, lf)
+    Bonito.onload(session, node, js"""(parent)=>{
+        LarvaTagger.uncheckAllCheckboxes(parent.firstElementChild);
+    }""")
+    return node
+end
 
 mutable struct TagController
     hub::ControllerHub
@@ -646,32 +453,21 @@ end
 
 getmanualtag(c) = gethub(c)[:tag].manualtag
 
-function setcallbacks!(controller, tag, checkbox, label, colorpicker)
-    session = getsession(controller)
-    JSServe.attribute_render(session, label, "value", tag.name)
-    JSServe.attribute_render(session, colorpicker, "value", tag.color)
-    #
-    on(tagevents(controller).renamefailed) do (name, failure)
-        name == tag.name[] || return
-        @info "Reverting to \"$(name)\" after failure: \"$(failure)\""
-        evaljs(session,
-               js"""
-               const label = $(JSServe.selector(label));
-               label.value = $(name);
-               """)
-    end
-end
-
 newtagname(::TagController) = NoTag
 
 struct IndividualTagView
     controller::TagController
     tag::ObservableTag # view
+    dom_id
+end
+
+function IndividualTagView(controller::TagController, tag::ObservableTag)
+    IndividualTagView(controller, tag, dom_id())
 end
 
 tagview(controller, tag) = IndividualTagView(controller, tag)
 
-function JSServe.jsrender(session::Session, ti::IndividualTagView)
+function lowerdom(ti::IndividualTagView)
     tag = ti.tag
     active = tag.active
     disabled = map(!, active)
@@ -680,73 +476,63 @@ function JSServe.jsrender(session::Session, ti::IndividualTagView)
     #
     label = DOM.input(type="text",
                       value=name[],
-                      onchange=js"""
-                      JSServe.update_obs($(name), this.value);
-                      """,
+                      onchange=js"(evt)=>{ $name.notify(evt.srcElement.value); }",
                       disabled=disabled,
-                      class="tag-name",
-                     )
+                      class="tag-name")
     colorpicker = DOM.input(type="color",
                             value=color[],
-                            onchange=js"""
-                            JSServe.update_obs($(color), this.value);
-                            """,
+                            onchange=js"(evt)=>{ $color.notify(evt.srcElement.value); }",
                             disabled=disabled,
-                            class="h-8 w-8 rounded shadow tag-color",
-                           )
+                            class="h-8 w-8 rounded shadow tag-color")
     checkbox = DOM.input(type="checkbox",
                          checked=active,
-                         onchange=js"""
-                         JSServe.update_obs($(active), this.checked);
-                         """,
+                         onchange=js"(evt)=>{ $active.notify(evt.srcElement.checked); }",
                          class="tag-active")
-    setcallbacks!(ti.controller, tag, checkbox, label, colorpicker)
-    notify(active)
-    r(session,
-      DOM.tr(DOM.td(checkbox),
-             DOM.td(label),
-             DOM.td(colorpicker),
-            ))
+    return DOM.tr(DOM.td(checkbox),
+                  DOM.td(label),
+                  DOM.td(colorpicker))
+end
+function Bonito.jsrender(session::Session, ti::IndividualTagView)
+    node = lowerdom(session, ti)
+    tag = ti.tag
+    selector = dom_selector(ti) * " .tag-name"
+    events = tagevents(ti.controller)
+    on(events.renamefailed) do (name, failure)
+        name == tag.name[] || return
+        @info "Reverting to \"$(name)\" after failure: \"$(failure)\""
+        evaljs(session, js"document.querySelector($selector).value = $name;")
+    end
+    notify(tag.active)
+    return node
 end
 
 function addtag! end
 
-mutable struct AddTagButton
-    click::Trigger
-    attributes::Dict{Symbol, Any}
-end
-
-function addtagbutton(controller; kwargs...)
-    button = simpletrigger(AddTagButton,
-                           with_attributes(kwargs;
-                                           class=css_button,
-                                           style="width: 100%; height: 2rem; padding: 0px",
-                                          ))
-    on(button.click) do _
+function addtagbutton(controller)
+    button = NyxWidgets.Button(" + ";
+                               title="New tag",
+                               class=css_button,
+                               style="width: 100%; height: 2rem; padding: 0px")
+    on(button) do _
         addtag!(controller)
     end
     return button
 end
 
-function JSServe.jsrender(session::Session, b::AddTagButton)
-    r(session, DOMtrigger(" + ", b.click, b.attributes))
-end
-
-mutable struct TagFilter
+struct TagFilter
     controller::TagController
     views::Vector{IndividualTagView}
-    addtagbutton::AddTagButton
-    domtable::Union{Nothing, DOM.Hyperscript.Node{DOM.Hyperscript.HTMLSVG}}
+    addtagbutton::NyxWidgets.Button
     attributes
+    dom_id
 end
 
 function TagFilter(controller::TagController; kwargs...)
     TagFilter(controller,
               IndividualTagView[],
               addtagbutton(controller),
-              nothing,
               kwargs,
-             )
+              dom_id())
 end
 
 function tagfilter(controller; manualtag=nothing)
@@ -755,19 +541,18 @@ function tagfilter(controller; manualtag=nothing)
     return tc.view
 end
 
-function JSServe.jsrender(session::Session, tf::TagFilter)
-    setsession!(tf.controller, session)
+function lowerdom(tf::TagFilter)
     tags = newview!(gethub(tf.controller)[:taghooks])
     for tag in reverse(tags)
-        push!(tf.views,
-              tagview(tf.controller,
-                      tag))
+        push!(tf.views, tagview(tf.controller, tag))
     end
-    tf.domtable = r(session, scrollable(tf.views...))
-    r(session,
-      DOM.div(tf.domtable,
-              tf.addtagbutton;
-              with_attributes(tf.attributes; class="panel")...))
+    return DOM.div(scrollable(tf.views...),
+                   tf.addtagbutton;
+                   with_attributes(tf.attributes; class="panel")...)
+end
+function Bonito.jsrender(session::Session, tf::TagFilter)
+    setsession!(tf.controller, session)
+    return lowerdom(session, tf)
 end
 
 function addtag!(parent::TagController)
@@ -782,19 +567,31 @@ function addtag!(parent::TagController)
     #
     # model ordering: highest priority is first, lowest is last
     view = parent.view
-    childview = tagview(parent,
-                        newview!(tag))
+    childview = tagview(parent, newview!(tag))
     addtag!(view, childview)
     notify(taglut)
 end
 
 function addtag!(view::TagFilter, newtag::IndividualTagView)
     session = getsession(view.controller)
-    js_dom = r(session, newtag)
-    html_dom = "<table><tbody>" * replace(string(js_dom), "'" => "\\'") * "</tbody></table>"
-    evaljs(session, js"LarvaTagger.insertNewTag($(view.domtable), $html_dom)")
+    tag = newtag.tag
+    id0 = dom_id(session)
+    @assert id0 isa Int
+    tagactive = dom_selector(id0+3)
+    tagname = dom_selector(id0+5)
+    tagcolor = dom_selector(id0+7)
+    jsdom = r(session, newtag)
+    htmldom = "<table><tbody>" * replace(string(jsdom), "'" => "\\'") * "</tbody></table>"
+    domtable = dom_selector(view)
+    evaljs(session, js"""
+    let domtable = document.querySelector($domtable).firstElementChild;
+    LarvaTagger.insertNewTag(domtable, $htmldom);
+    document.querySelector($tagactive).addEventListener('change', (evt)=>{ $(tag.active).notify(evt.srcElement.checked); });
+    document.querySelector($tagname).addEventListener('change', (evt)=>{ $(tag.name).notify(evt.srcElement.value); });
+    document.querySelector($tagcolor).addEventListener('change', (evt)=>{ $(tag.color).notify(evt.srcElement.value); });
+    """)
     push!(view.views, newtag)
-    return js_dom
+    return jsdom
 end
 
 struct TagSelector
@@ -803,6 +600,8 @@ struct TagSelector
     selected::Observable{Vector{Tuple{String, Observable{Bool}}}}
     selectable::Bool
     showtoggle::Bool
+    fromjs::Union{Nothing, Observable{String}}
+    dom_id
 end
 
 function TagSelector(controller::TagController,
@@ -821,11 +620,27 @@ function TagSelector(controller::TagController,
     if !isnothing(multiple)
         multitag[] = multiple
     end
+    if selectable
+        fromjs = Observable("")
+        on(fromjs) do actuatedtag
+            for (tag, selected) in selectedtags[]
+                if tag == actuatedtag
+                    toggletag!(controller, tag, selected, selectedtags, selectable)
+                    flag_active_larva_as_edited(controller)
+                    break
+                end
+            end
+        end
+    else
+        fromjs = nothing
+    end
     ctrl = TagSelector(controller,
                        tagnames,
                        selectedtags,
                        selectable,
-                       isnothing(multiple))
+                       isnothing(multiple),
+                       fromjs,
+                       dom_id())
     refresh_view = Observable(false)
     on(tagnames) do names
         for i in length(registered_observables)+1:length(names)
@@ -888,7 +703,7 @@ function refresh_selected_tags(controller, id, timestep, taglut, selectedtags, s
     end
     isempty(jscode) && return
     evaljs(getsession(controller),
-           JSServe.JSCode([JSServe.JSString(join(jscode, "\n"))]))
+           Bonito.JSCode([Bonito.JSString(join(jscode, "\n"))]))
 end
 
 function refresh_selected_tag(controller, id, timestep, taglut, selectedtags, selectable)
@@ -922,7 +737,7 @@ function refresh_selected_tag(controller,
     end
     isempty(jscode) && return
     evaljs(getsession(controller),
-           JSServe.JSCode([JSServe.JSString(join(jscode, "\n"))]))
+           Bonito.JSCode([Bonito.JSString(join(jscode, "\n"))]))
 end
 
 function toggletag!(controller, tagname, selected, selectedtags, selectable)
@@ -957,62 +772,58 @@ function toggletag!(controller, tagname, selected, selectedtags, selectable)
     notify(larva.usertags)
 end
 
-function to_dom(selectedtags, selectable)
+function lowertags(selectedtags, selectable)
+    toggle = selectable ? js"LarvaTagger.toggleTagAtPointer" : nothing
     DOM.select(DOM.option(tagname;
                           id=tagname,
                           value=tagname,
                           selected=selected[],
-                          onmousedown=selectable ? js"LarvaTagger.toggleTagAtPointer(event)" : nothing,
-                         ) for (tagname, selected) in selectedtags;
+                          onmousedown=toggle)
+               for (tagname, selected) in selectedtags;
                multiple=true,
-               size=1,
-              )
+               size=1)
 end
 
-function JSServe.jsrender(session::Session, ts::TagSelector)
-    setsession!(ts.controller, session)
-    if ts.selectable
-        controller = ts.controller
-        selectedtags = ts.selected
-        fromjs = Observable("")
-        on(fromjs) do actuatedtag
-            for (tag, selected) in selectedtags[]
-                if tag == actuatedtag
-                    toggletag!(controller, tag, selected, selectedtags, ts.selectable)
-                    flag_active_larva_as_edited(controller)
-                    break
-                end
-            end
-        end
-        evaljs(session, js"LarvaTagger.setTagSelector($fromjs)")
-    end
-    taglist = r(session, DOM.div(to_dom(ts.selected[], ts.selectable); id="tag-selector"))
-    on(ts.selected) do selected
-        js_dom = JSServe.jsrender(session, to_dom(selected, ts.selectable))
-        html_dom = replace(string(js_dom), "'" => "\\'")
-        evaljs(session,
-               js"""
-               const select = $(taglist);
-               select.innerHTML = $html_dom;
-               """)
-    end
+function lowerdom(ts::TagSelector)
+    taglist = DOM.div(lowertags(ts.selected[], ts.selectable); id="tag-selector")
     elements = [taglist]
     if ts.showtoggle
         multitag = filterevents(ts.controller).allow_multiple_tags
+        toggle_multitag = js"(evt)=>{ $multitag.notify(evt.srcElement.checked); }"
         checkbox = DOM.div(DOM.input(type="checkbox",
                                      checked=multitag,
-                                     onchange=js"""
-                                     JSServe.update_obs($(multitag), this.checked);
-                                     """,
+                                     onchange=toggle_multitag,
                                      class="m-1"),
                            DOM.label("Multiple tags per time step");
                            class="flex flex-row")
         push!(elements, checkbox)
     end
-    JSServe.jsrender(session,
-                     DOM.div(elements...;
-                             class="flex flex-col",
-                             onmouseenter=js"LarvaTagger.focusOnTimeSlider()"))
+    return DOM.div(elements...;
+                   class="flex flex-col",
+                   onmouseenter=js"LarvaTagger.focusOnTimeSlider")
+end
+function Bonito.jsrender(session::Session, ts::TagSelector)
+    setsession!(ts.controller, session)
+    node = lowerdom(session, ts)
+    if ts.selectable
+        evaljs(session, js"LarvaTagger.setTagSelector($(ts.fromjs))")
+    end
+    selectedtags = ts.selected
+    prevtags = Ref(empty(selectedtags[]))
+    on(session, selectedtags) do selected
+        selected == prevtags[] && return
+        prevtags[] = selected
+        jsdom = r(session, lowertags(selected, ts.selectable))
+        htmldom = replace(string(jsdom), "'" => "\\'")
+        evaljs(session, js"""
+        document.getElementById('tag-selector').innerHTML = $htmldom;
+        let tags = document.querySelectorAll('#tag-selector option');
+        for (let i = 0; i < tags.length; i++) {
+            tags[i].addEventListener('mousedown', LarvaTagger.toggleTagAtPointer);
+        }
+        """)
+    end
+    return node
 end
 
 function tagselector(controller;
@@ -1025,9 +836,9 @@ end
 
 mutable struct TrackViewer
     plot::TrackPlot
-    player::Player
+    player::PlayerView
     tagselector::TagSelector
-    dom
+    dom_id
 end
 
 function trackviewer(controller;
@@ -1036,102 +847,84 @@ function trackviewer(controller;
         kwargs...
     )
     model = getlarvae(controller)
+    isempty(model) && return nothing
     plot = trackplot(model, controller; editabletags=editabletags, kwargs...)
     play = player(controller)
     sele = tagselector(controller; editabletags=editabletags, multipletags=multipletags)
-    TrackViewer(plot, play, sele, nothing)
+    TrackViewer(plot, play, sele, dom_id())
 end
 
-function JSServe.jsrender(session::Session, tv::TrackViewer)
-    dom = DOM.div(r(session, tv.plot),
-                  r(session, tv.player),
-                  r(session, tv.tagselector);
-                  class="flex flex-col",
-                  style="display: none;",
-                  id="trackviewer",
-                  onmouseenter=js"LarvaTagger.focusOnTimeSlider(this)",
-                 )
-    tv.dom = dom
-    r(session, dom)
+function lowerdom(tv::TrackViewer)
+    focus = js"(evt)=>{ LarvaTagger.focusOnTimeSlider(evt.srcElement); }"
+    return DOM.div(tv.plot,
+                   tv.player,
+                   tv.tagselector;
+                   class="flex flex-col",
+                   style="display: none;",
+                   id="trackviewer",
+                   onmouseenter=focus)
 end
+Bonito.jsrender(session::Session, tv::TrackViewer) = lowerdom(session, tv)
 
 struct MetadataEditor
     controller
     max_entries::Int
+    dom_id
 end
 
-MetadataEditor(controller) = MetadataEditor(controller, 10)
+MetadataEditor(controller) = MetadataEditor(controller, 10, dom_id())
 
 metadataeditor(controller) = MetadataEditor(controller)
 
-function JSServe.jsrender(session::Session, me::MetadataEditor)
+function lowerdom(me::MetadataEditor)
     table = getmetadatatable(me.controller, me.max_entries)
-    dom = DOM.div(DOM.div(DOM.input(value=fieldname,
-                                    type="text",
-                                    class="headerfield",
-                                    onchange=js"""
-                                    JSServe.update_obs($(fieldname), this.value);
-                                    """),
-                          DOM.input(value=fieldvalue,
-                                    type="text",
-                                    class="valuefield",
-                                    onchange=js"""
-                                    JSServe.update_obs($(fieldvalue), this.value);
-                                    """);
-                          class="flex flex-row")
-                  for (fieldname, fieldvalue) in pairs(table);
-                  id="metadata-panel",
-                  class="flex flex-col panel")
-    r(session, dom)
+    return DOM.div(DOM.div(DOM.input(value=fieldname,
+                                     type="text",
+                                     class="headerfield",
+                                     onchange=js"""(evt)=>{
+                                        $fieldname.notify(evt.srcElement.value);
+                                     }"""),
+                           DOM.input(value=fieldvalue,
+                                     type="text",
+                                     class="valuefield",
+                                     onchange=js"""(evt)=>{
+                                        $fieldvalue.notify(evt.srcElement.value);
+                                     }""");
+                           class="flex flex-row")
+                   for (fieldname, fieldvalue) in pairs(table);
+                   id="metadata-panel",
+                   class="flex flex-col panel")
 end
+Bonito.jsrender(session::Session, me::MetadataEditor) = lowerdom(session, me)
 
 struct FileMenu
     controller::ControllerHub
-    menuselection::Observable{String}
-    attributes
+    browser::FileBrowser
 end
 
 function filemenu(controller; kwargs...)
-    selection = Observable("")
-    menu = FileMenu(gethub(controller),
-                    selection,
-                    with_attributes(kwargs; size=5))
     wd = getworkingdir(controller)
-    on(selection) do file
-        if '/' in file
-            @logmsg SecurityAlert "'/' character found in file name" file client=identifyclient(controller)
-        elseif !isempty(file)
-            cwd(wd, file)
-        end
+    dir = joinpath(wd.root, wd.path[])
+    browser = FileBrowser(dir; root=wd.root, upload_button=true)
+    on(FileBrowsers.selectedfile(browser)) do file
+        tryopenfile(controller, file; reload=true)
     end
-    return menu
-end
-
-function JSServe.jsrender(session::Session, fm::FileMenu)
-    wdcontent = getwdcontent(fm.controller)
-    inputfile = getinputfile(fm.controller)
-    select = r(session,
-               DOM.select(multiple=true;
-                          with_attributes(fm.attributes; class="directory-content")...))
-    onjs(session, wdcontent, js"(directory_entries) =>
-         LarvaTagger.updateSelectOptions($select, directory_entries, $(fm.menuselection))")
-    wd = map(getpath(fm.controller)) do path
-        @assert !isempty(path)
-        if isempty(path) || path == "."
-            "."
-        else
-            parts = splitpath(path)
-            length(parts) == 1 ? "./" * parts[1] : ".../" * parts[end]
-        end
+    workingdir = FileBrowsers.workingdir(browser)
+    on(workingdir) do _
+        dir = workingdir[]
+        # maintain the deprecated WorkingDirectory as it may still be in use elsewhere
+        wd.path[] = dir
+        wd.content[] = [d for (_, d) in browser.model.content[]]
+    end
+    on(FilePickers.uploadedfile(browser)) do fileinfo
+        filepath = joinpath(workingdir[], fileinfo["name"])
+        saveinputfile(filepath, fileinfo["content"])
     end
-    dom = DOM.div(DOM.input(value=wd,
-                            type="text",
-                            readonly=true),
-                  select;
-                  id="filemenu-panel",
-                  class="flex flex-col panel")
-    notify(wdcontent) # populate the select element
-    r(session, dom)
+    return FileMenu(gethub(controller), browser)
+end
+
+function Bonito.jsrender(session::Session, fm::FileMenu)
+    r(session, DOM.div(fm.browser; class="panel"))
 end
 
 function globalrefresh(::Type{Session}, controller)
@@ -1161,41 +954,54 @@ end
 
 struct BackendMenu
     backends::Backends
-    send2backend::Observable
+    send2backend::NyxWidgets.Button
+    dom_id
 end
 
 function backendmenu(controller, location)
     backends = getbackends(controller, location)
-    send2backend = Observable(false)
+    disabled = map(isempty, backends.model_instances)
+    send2backend = NyxWidgets.Button("Autotag"; disabled=disabled)
     on(send2backend) do _
         predict(backends, controller[:input][])
     end
-    BackendMenu(backends, send2backend)
+    BackendMenu(backends, send2backend, dom_id())
 end
 
-function JSServe.jsrender(session::Session, bs::BackendMenu)
+function lowerdom(bs::BackendMenu)
     models = bs.backends.model_instances
+    model_instance = bs.backends.model_instance
+    update_model_instance = js"(evt)=>{ $model_instance.notify(evt.srcElement.value); }"
     select_dom = DOM.select(DOM.option(model; value=model) for model in models[];
-                         onchange=js"JSServe.update_obs($(bs.backends.model_instance), this.value)",
-                         class=css_button)
-    onjs(session, models, js"(options) => LarvaTagger.updateSelectOptions($select_dom, options)")
-    disabled = map(isempty, models)
-    dom = DOM.div(DOM.label("Backend"),
-                  DOM.select(DOM.option(backend; value=backend) for backend in bs.backends.backends;
-                             onchange=js"JSServe.update_obs($(bs.backends.active_backend), this.value)",
-                             class=css_button),
-                  DOM.label("Model instance"),
-                  select_dom,
-                  DOMtrigger("Autotag", bs.send2backend; disabled=disabled);
-                  class="flex flex-col panel")
-    r(session, dom)
+                            onchange=update_model_instance,
+                            class=css_button)
+    active_backend = bs.backends.active_backend
+    update_active_backend = js"(evt)=>{ $active_backend.notify(evt.srcElement.value); }"
+    return DOM.div(DOM.label("Backend"),
+                   DOM.select(DOM.option(backend; value=backend)
+                              for backend in bs.backends.backends;
+                              onchange=update_active_backend,
+                              class=css_button),
+                   DOM.label("Model instance"),
+                   select_dom,
+                   bs.send2backend;
+                   class="flex flex-col panel")
+end
+function Bonito.jsrender(session::Session, bs::BackendMenu)
+    node = lowerdom(session, bs)
+    onjs(session, bs.backends.model_instances, js"""(options) => {
+         select = document.querySelector($(dom_selector(bs)) + ' > select');
+         LarvaTagger.updateSelectOptions(select, options);
+    }""")
+    return node
 end
 
 struct LoadAnimation
     visible::Observable{Bool}
+    dom_id
 end
 
-LoadAnimation() = LoadAnimation(Observable(true))
+LoadAnimation() = LoadAnimation(Observable(true), dom_id())
 
 function loadanimation(controller)
     la = LoadAnimation()
@@ -1203,12 +1009,13 @@ function loadanimation(controller)
     return la
 end
 
-function JSServe.jsrender(session::Session, la::LoadAnimation)
-    dom = DOM.div(DOM.div();
-                  id="load-animation",
-                  class="la-ball-clip-rotate")
-    onjs(session, la.visible, js"(b) => $(dom).style.display = (b ? 'block' : 'none')")
-    r(session, dom)
+function lowerdom(::LoadAnimation)
+    DOM.div(DOM.div(); id="load-animation", class="la-ball-clip-rotate")
+end
+function Bonito.jsrender(session::Session, la::LoadAnimation)
+    node = lowerdom(session, la)
+    onjs(session, la.visible, js"(b)=>{ $node.style.display = (b ? 'block' : 'none'); }")
+    return node
 end
 
 struct TwoOptionDialog
@@ -1219,6 +1026,7 @@ struct TwoOptionDialog
     button2::Observable{String}
     visible::Observable{Bool}
     attributes
+    dom_id
 end
 
 TwoOptionDialog(; kwargs...) = TwoOptionDialog(Observable(true),
@@ -1227,7 +1035,8 @@ TwoOptionDialog(; kwargs...) = TwoOptionDialog(Observable(true),
                                                Observable(""),
                                                Observable(""),
                                                Observable(false),
-                                               with_attributes(kwargs; style="display:none"))
+                                               with_attributes(kwargs; style="display:none"),
+                                               dom_id())
 
 twooptiondialog(controller) = get!(gethub(controller), :twooptiondialog, TwoOptionDialog())
 
@@ -1251,28 +1060,27 @@ function twooptiondialog(controller, answer, title, message, button1, button2)
     end
 end
 
-function JSServe.jsrender(session::Session, tod::TwoOptionDialog)
-    dom = DOM.div(DOM.div(tod.title[]; id="two-option-dialog-title"),
-                  DOM.div(tod.prompt[]; id="two-option-dialog-prompt"),
-                  DOM.div(
-                  DOM.button(tod.button1[];
-                             type="button",
-                             id="two-option-dialog-button1",
-                             class="button",
-                             onclick=js"""
-                             JSServe.update_obs($(tod.answer), true);
-                             """),
-                  DOM.button(tod.button2[];
-                             type="button",
-                             id="two-option-dialog-button2",
-                             class="button",
-                             onclick=js"""
-                             JSServe.update_obs($(tod.answer), false);
-                             """);
-                  id="two-option-dialog-buttons");
-                  id="two-option-dialog",
-                  tod.attributes...)
-    onjs(session, tod.visible, js"(b) => $(dom).style.display = (b ? 'block' : 'none')")
-    r(session, dom)
+function lowerdom(tod::TwoOptionDialog)
+    DOM.div(DOM.div(tod.title[]; id="two-option-dialog-title"),
+            DOM.div(tod.prompt[]; id="two-option-dialog-prompt"),
+            DOM.div(
+                    DOM.button(tod.button1[];
+                               type="button",
+                               id="two-option-dialog-button1",
+                               class="button",
+                               onclick=js"()=>{ $(tod.answer).notify(true); }"),
+                    DOM.button(tod.button2[];
+                               type="button",
+                               id="two-option-dialog-button2",
+                               class="button",
+                               onclick=js"()=>{ $(tod.answer).notify(false); }");
+                    id="two-option-dialog-buttons");
+            id="two-option-dialog",
+            tod.attributes...)
+end
+function Bonito.jsrender(session::Session, tod::TwoOptionDialog)
+    node = lowerdom(session, tod)
+    onjs(session, tod.visible, js"(b)=>{ $node.style.display = (b ? 'block' : 'none'); }")
+    return node
 end