diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5205190f1..ec5381fa7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,15 @@
## [Unreleased]
+## [1.27.19] — 2026-07-07 — Vault page polish
+
+### Changed
+
+- The Documents page now uses the standard content width like every other page instead of a wider container.
+- The standing dashed drop-zone is gone: the header Upload button and page-wide drag-and-drop already cover uploading, so the zone no longer takes a band of space on every visit (the quota bar still appears above 80% usage).
+- Search shares one row with the type tags on desktop instead of sitting on its own line above them.
+- In a document's detail view, Delete moved to the bottom-left as a quiet text button — the bottom-right slot reads as the primary action, so a destructive control no longer sits there; Download keeps the trailing edge.
+
## [1.27.18] — 2026-07-07 — Late doses no longer jump the evening slot
### Fixed
diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml
index abae07dfc..60486b9ce 100644
--- a/docs/api/openapi.yaml
+++ b/docs/api/openapi.yaml
@@ -1,7 +1,7 @@
openapi: 3.1.0
info:
title: HealthLog API
- version: 1.27.18
+ version: 1.27.19
description: >-
Self-hosted personal-health-tracking PWA — public API surface for the iOS native client and external ingest.
diff --git a/messages/de.json b/messages/de.json
index c7ec2d512..4f004de08 100644
--- a/messages/de.json
+++ b/messages/de.json
@@ -8073,8 +8073,6 @@
"listLabel": "Dokumenten-Zeitleiste"
},
"upload": {
- "zoneTitle": "Dateien hier ablegen oder vom Gerät auswählen",
- "zoneHint": "PDF, Fotos und Office-Dateien bis je {maxSize}",
"browse": "Dateien auswählen",
"quotaAlmostFull": "Speicher fast voll",
"quotaUsed": "{used} von {quota} belegt",
diff --git a/messages/en.json b/messages/en.json
index 9f3a0a5a2..ccd34b44c 100644
--- a/messages/en.json
+++ b/messages/en.json
@@ -8073,8 +8073,6 @@
"listLabel": "Documents timeline"
},
"upload": {
- "zoneTitle": "Drop files here, or choose from your device",
- "zoneHint": "PDF, photos, and office files up to {maxSize} each",
"browse": "Choose files",
"quotaAlmostFull": "Storage almost full",
"quotaUsed": "{used} of {quota} used",
diff --git a/messages/es.json b/messages/es.json
index 68948531b..a640f86eb 100644
--- a/messages/es.json
+++ b/messages/es.json
@@ -8073,8 +8073,6 @@
"listLabel": "Cronología de documentos"
},
"upload": {
- "zoneTitle": "Suelta archivos aquí o elige desde tu dispositivo",
- "zoneHint": "PDF, fotos y archivos de Office de hasta {maxSize} cada uno",
"browse": "Elegir archivos",
"quotaAlmostFull": "Almacenamiento casi lleno",
"quotaUsed": "{used} de {quota} usados",
diff --git a/messages/fr.json b/messages/fr.json
index 46914f47c..87256c3e2 100644
--- a/messages/fr.json
+++ b/messages/fr.json
@@ -8073,8 +8073,6 @@
"listLabel": "Chronologie des documents"
},
"upload": {
- "zoneTitle": "Déposez des fichiers ici ou choisissez depuis votre appareil",
- "zoneHint": "PDF, photos et fichiers Office jusqu'à {maxSize} chacun",
"browse": "Choisir des fichiers",
"quotaAlmostFull": "Stockage presque plein",
"quotaUsed": "{used} sur {quota} utilisés",
diff --git a/messages/it.json b/messages/it.json
index 7d13b2fa0..43ae6aefa 100644
--- a/messages/it.json
+++ b/messages/it.json
@@ -8073,8 +8073,6 @@
"listLabel": "Cronologia dei documenti"
},
"upload": {
- "zoneTitle": "Trascina i file qui o scegli dal dispositivo",
- "zoneHint": "PDF, foto e file Office fino a {maxSize} ciascuno",
"browse": "Scegli file",
"quotaAlmostFull": "Spazio quasi pieno",
"quotaUsed": "{used} di {quota} usati",
diff --git a/messages/pl.json b/messages/pl.json
index e51e0c00d..27cfe24be 100644
--- a/messages/pl.json
+++ b/messages/pl.json
@@ -8073,8 +8073,6 @@
"listLabel": "Oś czasu dokumentów"
},
"upload": {
- "zoneTitle": "Upuść pliki tutaj lub wybierz z urządzenia",
- "zoneHint": "PDF, zdjęcia i pliki Office do {maxSize} każdy",
"browse": "Wybierz pliki",
"quotaAlmostFull": "Miejsce prawie pełne",
"quotaUsed": "Użyto {used} z {quota}",
diff --git a/package.json b/package.json
index 20a8a40fa..c754ad4ad 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "healthlog",
- "version": "1.27.18",
+ "version": "1.27.19",
"description": "Self-hosted personal-health-tracking PWA with Withings integration, AI insights, and doctor-report PDF export.",
"license": "PolyForm-Noncommercial-1.0.0",
"homepage": "https://healthlog.dev",
diff --git a/public/sw.js b/public/sw.js
index d7bb11bb1..53fe8ffb5 100644
--- a/public/sw.js
+++ b/public/sw.js
@@ -36,7 +36,7 @@ try {
// v1.4.38.4 → v1.4.42. Do not hand-edit; bump `package.json` and rebuild.
const CACHE_VERSION =
(typeof self !== "undefined" && self.__APP_VERSION__) ||
- /* @sw-version-fallback */ "v1.27.18";
+ /* @sw-version-fallback */ "v1.27.19";
const STATIC_CACHE = `healthlog-static-${CACHE_VERSION}`;
const PAGE_CACHE = `healthlog-pages-${CACHE_VERSION}`;
// v1.18.6 — read-only data cache for a curated allowlist of safe GET `/api/*`
diff --git a/src/components/documents/document-detail-sheet.tsx b/src/components/documents/document-detail-sheet.tsx
index 5c82a3e6a..1c89bb981 100644
--- a/src/components/documents/document-detail-sheet.tsx
+++ b/src/components/documents/document-detail-sheet.tsx
@@ -285,22 +285,27 @@ export function DocumentDetailSheet({
contentWidth="3xl"
footer={
doc ? (
- <>
-
+ // Delete sits bottom-LEFT, quieted to a text button: the
+ // bottom-right slot reads as the primary/confirm action, and a
+ // destructive control there is a footgun. Download — the safe,
+ // expected action — keeps the trailing edge.
+
+ {/* Search shares one row with the tag chips on desktop — it wraps to
+ its own line only on phone widths where a chip scroller plus an
+ input can't share a row. The chip scroller flexes to fill the
+ rest; the clear button pins to the trailing edge. */}
+
);
}
diff --git a/src/components/documents/upload-zone.tsx b/src/components/documents/upload-zone.tsx
index 91c4ba1e8..c94863234 100644
--- a/src/components/documents/upload-zone.tsx
+++ b/src/components/documents/upload-zone.tsx
@@ -1,23 +1,23 @@
"use client";
/**
- * The vault's upload entry: a dashed drop-target card with a file button.
+ * The vault's upload plumbing: the hidden file input the page header's
+ * Upload button opens, plus a quota bar that appears only above 80 %
+ * usage. There is no standing drop-target card — it took a full band of
+ * vertical space on every visit for an affordance the header button and
+ * the page-wide drag-and-drop overlay already cover. Dropping a file
+ * anywhere on the page is caught by that overlay; the header button opens
+ * this input.
+ *
* The `accept` list comes verbatim from the usage endpoint (HEIC is
* deliberately absent so the iOS picker transcodes camera photos to JPEG),
* `multiple` is on, and there is NO `capture` attribute — mobile Safari
- * then offers camera AND library. Dropping files onto the card feeds the
- * same queue as the picker.
- *
- * The quota bar renders only above 80 % usage — a calm surface until
- * storage actually becomes a topic.
+ * then offers camera AND library.
*/
-import { UploadCloud } from "lucide-react";
-import { useRef, useState, type RefObject } from "react";
+import { type RefObject } from "react";
-import { Button } from "@/components/ui/button";
import { Progress } from "@/components/ui/progress";
import { useTranslations } from "@/lib/i18n/context";
-import { cn } from "@/lib/utils";
import type { DocumentUsageDto } from "@/lib/validations/inbound-documents";
import { formatBytes } from "./vault-utils";
@@ -36,8 +36,6 @@ export function UploadZone({
inputRef: RefObject;
}) {
const { t, locale } = useTranslations();
- const [dragOver, setDragOver] = useState(false);
- const dragDepth = useRef(0);
const accept = usage?.acceptedExtensions.join(",");
const usedFraction =
@@ -49,71 +47,20 @@ export function UploadZone({
};
return (
-
diff --git a/src/components/layout/auth-shell.tsx b/src/components/layout/auth-shell.tsx
index c2db1f0bd..23fb7b3a7 100644
--- a/src/components/layout/auth-shell.tsx
+++ b/src/components/layout/auth-shell.tsx
@@ -36,13 +36,6 @@ const PUBLIC_PATHS = [
"/c/",
];
-// Routes that opt into the wide content container (`max-w-screen-2xl`
-// instead of the standard `max-w-screen-xl`). The shell stays the single
-// owner of the page frame — a wide page never re-implements paddings or
-// margins; it is listed here instead. Currently: the Dokumente vault,
-// whose multi-column document timeline earns the extra width.
-const WIDE_CONTENT_PATHS = ["/documents"];
-
export function AuthShell({
children,
demoMode = false,
@@ -76,9 +69,6 @@ export function AuthShell({
pathname.startsWith("/about") ||
pathname.startsWith("/c/");
const isAdminPage = pathname.startsWith("/admin");
- const isWidePage = WIDE_CONTENT_PATHS.some(
- (p) => pathname === p || pathname.startsWith(`${p}/`),
- );
const isOnboardingPage = pathname === "/onboarding";
const showUnlockNotifier = isAuthenticated && !isPublicPage && !!user?.id;
@@ -343,13 +333,7 @@ export function AuthShell({
the desktop bottom-6 anchor), so the last line of content
can always scroll out from under the floating button.
*/}
-