diff --git a/.gitignore b/.gitignore
index e1a14e8..32b3671 100644
--- a/.gitignore
+++ b/.gitignore
@@ -61,3 +61,13 @@ gleam@@compile.erl
/.expert
.envrc
.worktrees/
+
+# Nix development environment state
+.nix-mix/
+.nix-hex/
+.nix-postgres/
+.nix-postgres-version
+.nix-services-started
+.direnv/
+# Generated by pre-commit-hooks.nix (symlink into /nix/store)
+.pre-commit-config.yaml
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..32d3c33
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,124 @@
+{
+ "nodes": {
+ "flake-compat": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1767039857,
+ "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
+ "owner": "NixOS",
+ "repo": "flake-compat",
+ "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "repo": "flake-compat",
+ "type": "github"
+ }
+ },
+ "flake-parts": {
+ "inputs": {
+ "nixpkgs-lib": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1769996383,
+ "narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=",
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "rev": "57928607ea566b5db3ad13af0e57e921e6b12381",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "type": "github"
+ }
+ },
+ "gitignore": {
+ "inputs": {
+ "nixpkgs": [
+ "pre-commit-hooks",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1709087332,
+ "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
+ "owner": "hercules-ci",
+ "repo": "gitignore.nix",
+ "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "gitignore.nix",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1784120854,
+ "narHash": "sha256-KesHgItiZPgGX740axSiQLcIQ8D24MDqNpkKYWIek8k=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "753cc8a3a87467296ddd1fa93f0cc3e81120ee46",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "pre-commit-hooks": {
+ "inputs": {
+ "flake-compat": "flake-compat",
+ "gitignore": "gitignore",
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1769939035,
+ "narHash": "sha256-Fok2AmefgVA0+eprw2NDwqKkPGEI5wvR+twiZagBvrg=",
+ "owner": "cachix",
+ "repo": "pre-commit-hooks.nix",
+ "rev": "a8ca480175326551d6c4121498316261cbb5b260",
+ "type": "github"
+ },
+ "original": {
+ "owner": "cachix",
+ "repo": "pre-commit-hooks.nix",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "flake-parts": "flake-parts",
+ "nixpkgs": "nixpkgs",
+ "pre-commit-hooks": "pre-commit-hooks",
+ "systems": "systems"
+ }
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "type": "github"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..a1d0375
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,73 @@
+{
+ description = "aprs.me - Phoenix LiveView APRS packet mapping platform";
+
+ inputs = {
+ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
+
+ flake-parts = {
+ url = "github:hercules-ci/flake-parts";
+ inputs.nixpkgs-lib.follows = "nixpkgs";
+ };
+
+ systems.url = "github:nix-systems/default";
+
+ pre-commit-hooks = {
+ url = "github:cachix/pre-commit-hooks.nix";
+ inputs.nixpkgs.follows = "nixpkgs";
+ };
+ };
+
+ outputs =
+ inputs@{
+ self,
+ nixpkgs,
+ flake-parts,
+ systems,
+ pre-commit-hooks,
+ }:
+ flake-parts.lib.mkFlake { inherit inputs; } {
+ # nixpkgs 26.11 dropped x86_64-darwin support
+ systems = builtins.filter (s: s != "x86_64-darwin") (import systems);
+
+ perSystem =
+ {
+ config,
+ self',
+ inputs',
+ pkgs,
+ system,
+ ...
+ }:
+ {
+ _module.args.pkgs = import nixpkgs {
+ inherit system;
+ overlays = [
+ # Pin specific Elixir and Erlang versions to match .tool-versions
+ # Elixir 1.20.2-otp-29, Erlang 29.0.1
+ (final: prev: {
+ # Use Erlang 29 — no top-level erlang_29 yet, use beam.interpreters
+ erlang = prev.beam.interpreters.erlang_29;
+
+ # Use Elixir 1.20 with Erlang 29 (1.20.2-otp-29)
+ elixir = prev.beam.packages.erlang_29.elixir_1_20;
+
+ # Ensure beam packages use our pinned versions
+ beamPackages = prev.beam.packages.erlang_29.extend (
+ self: super: {
+ elixir = super.elixir_1_20;
+ }
+ );
+ })
+ ];
+ };
+
+ # Development shell with PostgreSQL (PostGIS), LSPs, formatters
+ devShells.default = pkgs.callPackage ./nix/shell.nix {
+ pre-commit-hooks = inputs.pre-commit-hooks.lib.${system};
+ };
+
+ # Nix formatter for flake files
+ formatter = pkgs.nixfmt;
+ };
+ };
+}
diff --git a/lib/aprsme_web/components/layouts/root.html.heex b/lib/aprsme_web/components/layouts/root.html.heex
index c6face7..cf8ebc9 100644
--- a/lib/aprsme_web/components/layouts/root.html.heex
+++ b/lib/aprsme_web/components/layouts/root.html.heex
@@ -35,15 +35,15 @@
{assigns[:page_title] || "Aprs"}
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+