diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000000000000000000000000000000000000..ab996e15dcc31e21bffc58cb941cf19c4760a814
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,16 @@
+Dockerfile
+.dockerignore
+.git
+.gitignore
+__pycache__/
+*.pyc
+*.pyo
+*.pyd
+venv/
+.venv/
+env/
+*.env
+*.db
+*.sqlite3
+instance/
+experiments/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..1287b3caf432336f776902fa5db21ac879ae33e0
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,55 @@
+stages:
+  - test
+  # - build_and_deploy
+
+variables:
+  UV_VERSION: 0.6
+  PYTHON_VERSION: 3.12
+  BASE_LAYER: bookworm-slim
+  # GitLab CI creates a separate mountpoint for the build directory,
+  # so we need to copy instead of using hard links.
+  UV_LINK_MODE: copy
+
+# Only run the pipeline for tag pushes matching the pattern "v*-pre"
+workflow:
+  rules:
+    - if: $CI_COMMIT_TAG =~ /^v.*-pre/
+
+# Run tests...
+test:
+  stage: test
+  # image: python:3.12
+  # script:
+  #   - pip install pip --upgrade
+  #   - pip install -r requirements.txt
+  #   - python -m pytest . -v
+  image: ghcr.io/astral-sh/uv:$UV_VERSION-python$PYTHON_VERSION-$BASE_LAYER
+  script:
+    - uv sync
+    - uv run pytest . -v
+    - uv cache prune --ci
+  only:
+    - tags
+#
+#
+# # Build and deploy the Docker container directly on the server
+# build_and_deploy:
+#   stage: build_and_deploy
+#   script:
+#     - echo "Starting build and deployment of $APP_NAME version $CI_COMMIT_TAG"
+#     # Build the Docker image locally
+#     - docker build -t $APP_NAME:$CI_COMMIT_TAG .
+#     # Stop and remove existing container if it exists
+#     - |
+#       if docker ps -a | grep -q $APP_NAME; then
+#         docker stop $APP_NAME
+#         docker rm $APP_NAME
+#       fi
+#     # Start the new container
+#     - docker run -d --name $APP_NAME -p $HOST_PORT:$CONTAINER_PORT --restart unless-stopped $APP_NAME:$CI_COMMIT_TAG
+#     - echo "Build and deployment complete!"
+#   only:
+#     - tags
+#   # This ensures the job runs on the specific runner on your server
+#   tags:
+#     - deployment-server
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5f99247d9d21b9b09089c91ebdfca8aa122ac176..03a3804b70734d907a551537d42d021b329861b1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,7 +15,7 @@ Types of changes
 
 ## TODOs
 
-- set log level via config
+- disable the "show trajectories" checkbox if no data
 - add sources for BB tagging and ACN background
 - investigate if data caching is necessary (load-from-thredds)?
 - scatter-plot: plot as line if sufficient data is available?
@@ -26,6 +26,7 @@ Types of changes
 
 ## Backlog
 
+- containerize & auto-deploy
 - use a polars dataframe or [fireducks](https://fireducks-dev.github.io/) for better performance - if that turns out to be a bottleneck. (!) Keep this in mind when using pandas-specific functions.
 - add tests
 - add pre-commit
@@ -35,6 +36,18 @@ Types of changes
 
 ## [Unreleased]
 
+### Added
+
+- trajectory data info
+- dockerfile and dockerignore
+
+### Changed
+
+- select a secondary parameter by default
+- same bg color for both plots
+- show map attribution on top of map instead of hiding it below timeseries plot
+- put 'show trajectories' button below map plot
+
 ## v0.0.18, 2025-03-31
 
 ### Fixed
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..84a6d16e1ab6a1eac99196da40bac1732d8000ae
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,42 @@
+# ###
+# generated with Gemini 2.5 pro/experimental
+# ###
+#
+# Use an official Python runtime as a parent image
+# Choose a specific version for reproducibility, -slim versions are smaller
+FROM python:3.12-slim
+
+# Set environment variables
+# Prevents Python from writing pyc files to disc (optional)
+ENV PYTHONDONTWRITEBYTECODE=1
+# Ensures Python output is sent straight to terminal (useful for logs)
+ENV PYTHONUNBUFFERED=1
+
+# Set the working directory in the container
+WORKDIR /app
+
+# Copy the requirements file into the container at /app
+COPY requirements.txt .
+
+# Install any needed packages specified in requirements.txt
+# --no-cache-dir reduces image size
+# --upgrade pip ensures you have the latest pip
+# RUN pip install --no-cache-dir --upgrade pip && \
+#     pip install --no-cache-dir -r requirements.txt
+
+# uv variant:
+RUN pip install --no-cache-dir uv
+RUN uv pip install --no-cache -r requirements.txt --system
+
+# Copy the rest of the application code into the container at /app
+# Assumes your Dockerfile is in the root of your project directory
+COPY . .
+
+# Make port 19000 available to the world outside this container
+EXPOSE 19000
+
+# Define the command to run your app using Gunicorn
+# Binds Gunicorn to 0.0.0.0 so it's accessible from outside the container
+# 'app:server' assumes your Dash app instance is 'app' in 'app.py'
+# Gunicorn needs the underlying Flask server instance, typically 'app.server'
+CMD ["gunicorn", "-b", ":19000", "main:server", "-w", "2"]
diff --git a/assets/custom.css b/assets/custom.css
index 5d16dcf101f4c7c8c9c08d3feb8f3f08b165c6e0..bb16e74aab7b03354fb5ec076b260322dbce01ec 100644
--- a/assets/custom.css
+++ b/assets/custom.css
@@ -1,124 +1,160 @@
 :root {
-    --neon-pink: #ff71ce;
-    --neon-blue: #01cdfe;
-    --neon-green: #05ff81;
-    --neon-purple: #b967ff;
-    --neon-yellow: #fdfb96;
-    --grey-bg: #404040;
-    --grey-bg-bright: #d4d4d4;
-    --dark-bg: #1a1a1a;
-    --darker-bg: #121212;
-    --grid-color: rgba(33, 33, 99, 0.8);
+  --neon-pink: #ff71ce;
+  --neon-blue: #01cdfe;
+  --neon-green: #05ff81;
+  --neon-purple: #b967ff;
+  --neon-yellow: #fdfb96;
+  --grey-bg: #404040;
+  --grey-bg-bright: #d4d4d4;
+  --dark-bg: #1a1a1a;
+  --darker-bg: #121212;
+  --grid-color: rgba(33, 33, 99, 0.8);
 }
 
 body {
-    background-color: var(--dark-bg);
-    background-image:
-        linear-gradient(0deg, var(--grid-color) 1px, transparent 1px),
-        linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
-    background-size: 20px 20px;
-    color: white;
-    font-family: 'VT323', 'Courier New', monospace;
-    margin-top: 70px;
-    padding: 15px;
+  background-color: var(--dark-bg);
+  background-image: linear-gradient(
+      0deg,
+      var(--grid-color) 1px,
+      transparent 1px
+    ),
+    linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
+  background-size: 20px 20px;
+  color: white;
+  font-family: "VT323", "Courier New", monospace;
+  margin-top: 70px;
+  padding: 15px;
 }
 
 .title-bar {
-    position: fixed;
-    top: 0;
-    left: 0;
-    width: 100%;
-    background-color: var(--grey-bg);
-    padding: 10px 20px;
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    border-bottom: 1px solid var(--grid-color);
-    z-index: 1000;
-    box-sizing: border-box;
-    height: 60px;
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  background-color: var(--grey-bg);
+  padding: 10px 20px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  border-bottom: 1px solid var(--grid-color);
+  z-index: 1000;
+  box-sizing: border-box;
+  height: 60px;
 }
 
 .title-text {
-    color: white;
-    font-size: 1.4rem;
-    font-weight: bold;
-    white-space: nowrap;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    max-width: 70%;
+  color: white;
+  font-size: 1.4rem;
+  font-weight: bold;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  max-width: 70%;
 }
 
 .logo-container {
-    display: flex;
-    align-items: center;
+  display: flex;
+  align-items: center;
 }
 
 .header-logo {
-    height: 40px;
-    background-color: var(--grey-bg-bright);
-    border-radius: 4px;
+  height: 40px;
+  background-color: var(--grey-bg-bright);
+  border-radius: 4px;
 }
 
 /* Logo wrapper to create the background box */
 .logo-wrapper {
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    background-color: var(--grey-bg-bright);
-    border-radius: 0.4rem;
-    padding: 0.3rem 0.7rem 0.3rem 0.7rem;
-    margin-left: 0.5rem;
-    border: 1px solid var(--grid-color);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-color: var(--grey-bg-bright);
+  border-radius: 0.4rem;
+  padding: 0.3rem 0.7rem 0.3rem 0.7rem;
+  margin-left: 0.5rem;
+  border: 1px solid var(--grid-color);
 }
 
 /* Responsive adjustments */
 @media (max-width: 600px) {
-    .logo-wrapper {
-        padding: 0.3rem;
-    }
+  .logo-wrapper {
+    padding: 0.3rem;
+  }
 
-    .header-logo {
-        height: 2rem;
-    }
+  .header-logo {
+    height: 2rem;
+  }
 }
 
 /* ----- drop boxes ----- */
 .Select-control {
-    background-color: var(--dark-bg);
+  background-color: var(--dark-bg);
 }
 
 .Select-control:hover {
-    background-color: var(--grey-bg);
+  background-color: var(--grey-bg);
 }
 
 .Select-value-label {
-    color: white !important;
+  color: white !important;
 }
 
 .Select-menu-outer {
-    background-color: var(--dark-bg);
-    color: white;
+  background-color: var(--dark-bg);
+  color: white;
 }
 
+/* The following styles ensure the attribution tag floats above the map and should be removed if the issue is fixed in a future plotly release. */
+.maplibregl-ctrl-bottom-right {
+  bottom: 5px;
+  left: 10px;
+}
+
+.maplibregl-ctrl-bottom-left,
+.maplibregl-ctrl-bottom-right,
+.maplibregl-ctrl-top-left,
+.maplibregl-ctrl-top-right {
+  pointer-events: none;
+  position: absolute;
+  z-index: 2;
+}
+
+.maplibregl-map {
+  font:
+    12px/20px Helvetica Neue,
+    Arial,
+    Helvetica,
+    sans-serif;
+  font-family:
+    Helvetica Neue,
+    Arial,
+    Helvetica,
+    sans-serif;
+  color: var(--grey-bg);
+}
+
+/*This is necessary to remove spacing below the canvas.*/
+.maplibregl-canvas {
+  display: block;
+}
 
 .footer {
-    position: fixed;
-    bottom: 0;
-    left: 0;
-    width: 100%;
-    background-color: var(--dark-bg);
-    padding: 10px 20px;
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    border-top: 1px solid var(--grid-color);
-    z-index: 1000;
-    box-sizing: border-box;
-    font-size: 0.9rem;
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  background-color: var(--dark-bg);
+  padding: 10px 20px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  border-top: 1px solid var(--grid-color);
+  z-index: 1000;
+  box-sizing: border-box;
+  font-size: 0.9rem;
 }
 
 /* Add some bottom margin to the main content to prevent overlap with footer */
 .main-content {
-    margin-bottom: 20px;
+  margin-bottom: 20px;
 }
diff --git a/pyproject.toml b/pyproject.toml
index a39da93213033b51d8c9f43304a9df20d9a01d71..3d11354b19e768c27f9e0bc77a39a97098ae51a2 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,8 @@
 [project]
 name = "caribic-dash"
-version = "0.0.18"
+version = "0.0.19"
 description = "IRISCC dashboard with CARIBIC data"
+authors = [{ name = "Florian Obersteiner", email = "f.obersteiner@kit.edu" }]
 readme = "README.md"
 requires-python = ">=3.12"
 classifiers = [
diff --git a/requirements.txt b/requirements.txt
index 3944ec763444c9efd6edb63791b188d3b174f490..ae8e7bd430f3a2504a22ced29bb43765d698292c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -36,7 +36,7 @@ markupsafe==3.0.2
     # via
     #   jinja2
     #   werkzeug
-narwhals==1.32.0
+narwhals==1.33.0
     # via plotly
 nest-asyncio==1.6.0
     # via dash
@@ -63,7 +63,7 @@ plotly==6.0.1
     # via
     #   caribic-dash (pyproject.toml)
     #   dash
-protobuf==6.30.1
+protobuf==6.30.2
     # via siphon
 python-dateutil==2.9.0.post0
     # via pandas
@@ -99,7 +99,7 @@ werkzeug==3.0.6
     # via
     #   dash
     #   flask
-xarray==2025.3.0
+xarray==2025.3.1
     # via caribic-dash (pyproject.toml)
 zipp==3.21.0
     # via importlib-metadata
diff --git a/src/callbacks.py b/src/callbacks.py
index e0779e99857a5833752e83e74e1fdfe0f9dcdfb3..69024eb7bc7c0f4230921f1191f04876827307e9 100644
--- a/src/callbacks.py
+++ b/src/callbacks.py
@@ -68,8 +68,6 @@ def register_map_plot(
             # try to find the trajectory file for selected flight
             if trajs := trajectory_data.get(selected_flight):
                 # make plots if trajectory file found
-                print(trajs["latitude"].dtype.byteorder)
-                print(trajs["longitude"].dtype.byteorder)
                 for i in range(trajs.trajectory.size):
                     try:
                         fig_map.add_trace(
@@ -145,6 +143,7 @@ def register_map_plot(
             },
             margin={"l": 0, "r": 10, "t": 0, "b": 0},
             showlegend=False,
+            paper_bgcolor="#fbf8f3",
         )
 
         return [fig_map, show_trajdata_warn]
@@ -220,7 +219,7 @@ def register_timeseries_plot(app: Dash, timeseries_data: pd.DataFrame, var_info:
             )
 
         fig_ts.update_layout(
-            paper_bgcolor="#e8e8e8",
+            paper_bgcolor="#fbf8f3",
             margin={"l": 45, "r": 10, "t": 20, "b": 50},
             legend={
                 "x": 0.01,
diff --git a/src/layout.py b/src/layout.py
index b76dca336c37d4bdb89ac25be19a8f9d77796fd2..ffa7f039b77858048e6af22ed1499868ef6c81f8 100644
--- a/src/layout.py
+++ b/src/layout.py
@@ -9,7 +9,7 @@ from dash import dcc, html
 from .config import DotDict
 
 
-def create(df: pd.DataFrame, config: DotDict) -> list:
+def create(df: pd.DataFrame, config: DotDict) -> list[html.Header]:
     """
     Create the layout for the dashboard.
 
@@ -40,6 +40,8 @@ def create(df: pd.DataFrame, config: DotDict) -> list:
         if vo["label"] == "ACN":
             var_sel_idx = idx
 
+    var2_sel_idx = 3
+
     return [
         html.Header(
             [
@@ -171,7 +173,7 @@ def create(df: pd.DataFrame, config: DotDict) -> list:
                                 dcc.Dropdown(
                                     id="second-variable-dropdown",
                                     options=([{"value": "None", "label": "(None)"}] + var_opts),  # type: ignore
-                                    value="None",  # type: ignore
+                                    value=var_opts[var2_sel_idx]["value"],  # type: ignore
                                     clearable=False,
                                     searchable=True,
                                     style={
@@ -200,26 +202,6 @@ def create(df: pd.DataFrame, config: DotDict) -> list:
                                 "vertical-align": "middle",
                             },
                         ),
-                        html.Div(
-                            [
-                                html.Label(
-                                    "Show trajectories",
-                                    style={"margin-right": "10px", "vertical-align": "middle"},
-                                ),
-                                dcc.Checklist(
-                                    id="trajectories-checkbox",
-                                    options=[{"label": "", "value": 1}],
-                                    inline=True,
-                                    style={"display": "inline-block", "vertical-align": "middle"},
-                                ),
-                            ],
-                            style={
-                                "width": "25%",
-                                "display": "inline-block",
-                                "text-align": "right",
-                                "vertical-align": "middle",
-                            },
-                        ),
                     ],
                     style={
                         "display": "flex",
@@ -247,6 +229,44 @@ def create(df: pd.DataFrame, config: DotDict) -> list:
                         )
                     ),
                 ),
+                html.Div(
+                    children=[
+                        html.Div(
+                            html.Label(
+                                "Trajectories: KNMI Trajectory Model TRAJKS, driven by ECMWF re-analysis data",
+                                id="trajectory-info",
+                                style={"margin-left": "15px", "font-size": "12px"},
+                            )
+                        ),
+                        html.Div(
+                            [
+                                html.Label(
+                                    "Show trajectories",
+                                    style={"margin-right": "10px"},
+                                ),
+                                dcc.Checklist(
+                                    id="trajectories-checkbox",
+                                    options=[{"label": "", "value": 1}],
+                                    inline=True,
+                                    style={"display": "inline-block", "vertical-align": "right"},
+                                ),
+                            ],
+                            style={
+                                "text-align": "right",
+                                "vertical-align": "right",
+                            },
+                        ),
+                    ],
+                    style={
+                        "display": "flex",
+                        "justify-content": "space-between",
+                        "align-items": "center",
+                        "padding-top": "5px",
+                        "padding-bottom": "10px",
+                        "padding-left": "5px",
+                        "padding-right": "15px",
+                    },
+                ),
                 dcc.Loading(
                     id="loading-ts",
                     type="circle",  # Options: "graph", "cube", "circle", "dot", or "default"
@@ -263,10 +283,17 @@ def create(df: pd.DataFrame, config: DotDict) -> list:
                         ),
                     ),
                 ),
-                html.Label(
-                    "BB flagging: BB influence if ACN > (145 ppt + 3*ACN_prc)",
-                    id="flagging-info",
-                    style={"margin-left": "15px", "font-size": "12px"},
+                html.Div(
+                    children=[
+                        html.Div(
+                            html.Label(
+                                "BB flagging: BB influence if ACN > (145 ppt + 3*ACN_prc)",
+                                id="flagging-info",
+                                style={"margin-left": "15px", "font-size": "12px"},
+                            )
+                        ),
+                    ],
+                    style={"padding-top": "5px", "padding-bottom": "10px"},
                 ),
             ]
         ),
@@ -304,7 +331,7 @@ def create(df: pd.DataFrame, config: DotDict) -> list:
                         html.A(
                             [
                                 html.I(className="fab fa-gitlab", style={"marginRight": "5px"}),
-                                f"src ({config.VERSION})",
+                                f"src {config.VERSION}",
                             ],
                             href=config.CODEURL,
                             target="_blank",
diff --git a/uv.lock b/uv.lock
index a1b94f181effeb420306e01f8a305085dae17839..aebf8af8a967201619bd5222105b9e6b835a5c0c 100644
--- a/uv.lock
+++ b/uv.lock
@@ -80,7 +80,7 @@ wheels = [
 
 [[package]]
 name = "caribic-dash"
-version = "0.0.18"
+version = "0.0.19"
 source = { virtual = "." }
 dependencies = [
     { name = "dash" },
@@ -392,11 +392,11 @@ wheels = [
 
 [[package]]
 name = "narwhals"
-version = "1.32.0"
+version = "1.33.0"
 source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/c1/e5/aa97891440bf6bc4239e9b918d89fea58eb87dff1d8d14a69b45ef677e66/narwhals-1.32.0.tar.gz", hash = "sha256:bd0aa41434737adb4b26f8593f3559abc7d938730ece010fe727b58bc363580d", size = 258915 }
+sdist = { url = "https://files.pythonhosted.org/packages/85/fd/484aa8bb557f97a1781f38c78b79f795a2fa320e4165c4230f679937d1e8/narwhals-1.33.0.tar.gz", hash = "sha256:6233d2457debf4b5fe4a1da54530c6fe2d84326f4a8e3bca35bbbff580a347cb", size = 262554 }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/e9/96/79a6168dc7a5098066e097c01a45d01608c8df6552dfb92a2676ce623186/narwhals-1.32.0-py3-none-any.whl", hash = "sha256:8bdbf3f76155887412eea04b0b06303856ac1aa3d9e8bda5b5e54612855fa560", size = 320073 },
+    { url = "https://files.pythonhosted.org/packages/41/c1/e9bc6b67c774e7c1f939c91ea535f18f7644fedc61b20d6baa861ad52b34/narwhals-1.33.0-py3-none-any.whl", hash = "sha256:f653319112fd121a1f1c18a40cf70dada773cdacfd53e62c2aa0afae43c17129", size = 322750 },
 ]
 
 [[package]]