admin: paint the dev server's chrome in ink blue
A working copy passed for the deployment till you read the footer's fine print;
now a run with no release wears ink blue, its header and footer filled with the tint.

Assisted-by: Claude Opus 5 via Claude Code
change tzowzmvtwyvrmmnvpkmvxzqtxklwnvrn
commit 883676f036eef2b27aa22bd3880cfd3418833b76
author Alpha Chen <alpha@kejadlen.dev>
date
parent psoptzrs
diff --git a/docs/DESIGN.md b/docs/DESIGN.md
index c850540..bbc83f4 100644
--- a/docs/DESIGN.md
+++ b/docs/DESIGN.md
@@ -41,6 +41,8 @@ What that inheritance actually means:
   `_components/<name>.md` rather than guessing from a sibling.
 - **One accent, held constant.** Pro-tacts sets `--gl-color-accent` once and
   never shows two accents in a view. `-ink` and `-soft` re-derive themselves.
+  The one swap is by run, not by view: a dev server repoints it to ink blue on
+  every screen (see the footer, below).
 - `--gl-color-success` and `--gl-color-danger` are **fixed** — they mean
   confirmed and destructive, never decoration, never emphasis.
 - Surface contrast does the elevation work: a bounded surface reads by
@@ -92,7 +94,9 @@ Under every screen sits the footer, the header's counterpart, holding what no
 search can turn up because none of it is a record: the version answering,
 whether debug logging is recording, and a link to device setup. A run with no
 release behind it says "dev server" in the version's place, since a working
-copy dressed as the deployment is the confusion the line exists to end. Debug
+copy dressed as the deployment is the confusion the line exists to end. The
+same run fills the header and footer with its accent, so the difference shows
+before a word of either is read. Debug
 logging appears only while it is on, written `+debug` — a flag on the build,
 not a sentence beside it — and a quiet log has nothing to say.
 
diff --git a/lib/pro_tacts/admin/layout.rb b/lib/pro_tacts/admin/layout.rb
index 2907189..8b1900e 100644
--- a/lib/pro_tacts/admin/layout.rb
+++ b/lib/pro_tacts/admin/layout.rb
@@ -41,7 +41,11 @@ module ProTacts
 
       def view_template
         doctype
-        html(lang: "en") do
+        # A run with no release behind it, the footer's "dev server",
+        # marks the root, and admin.css repaints the accent and the
+        # chrome for it: a working copy should not pass for the
+        # deployment at a glance (docs/DESIGN.md).
+        html(lang: "en", **(ProTacts.config.version ? {} : {data: {server: "dev"}})) do
           head do
             meta(charset: "utf-8")
             meta(name: "viewport", content: "width=device-width, initial-scale=1")
diff --git a/public/admin.css b/public/admin.css
index de3fdc8..7bdc604 100644
--- a/public/admin.css
+++ b/public/admin.css
@@ -369,6 +369,31 @@ textarea:focus {
   margin-left: auto;
 }
 
+/* A run with no release behind it (Layout marks the root; the footer
+   names it "dev server") wears Gloss's ink blue instead of the default
+   accent, and the chrome bars fill with it, so a working copy cannot
+   pass for the deployment at a glance. The whole root repoints rather
+   than the bars alone, so a view still shows one accent. Clay was
+   passed over for sitting too near danger. The fill is the accent
+   under accent-fg, the pairing Gloss's primary button uses;
+   accent-soft would be a pale band in dark mode, where tokens.css
+   never derives it again. */
+:root[data-server="dev"] {
+  --gl-color-accent: var(--gl-accent-ink-blue);
+}
+:root[data-server="dev"] :is(.admin-header, .admin-footer) {
+  background: var(--gl-color-accent);
+  border-color: var(--gl-color-accent);
+}
+:root[data-server="dev"] :is(.admin-header a, .admin-footer .type-label) {
+  color: var(--gl-color-accent-fg);
+}
+/* The one focus ring is drawn in the accent, which vanishes against a
+   bar filled with it. */
+:root[data-server="dev"] :is(.admin-header, .admin-footer) :focus-visible {
+  outline-color: var(--gl-color-accent-fg);
+}
+
 /* The viewport-tall column the footer's `margin-top: auto` measures
    against. Gloss's own body rule sets type and surface and leaves
    layout to the page, which until there was a footer meant nothing at
diff --git a/test/pro_tacts/admin/test_footer.rb b/test/pro_tacts/admin/test_footer.rb
index 91fea5d..11bc16e 100644
--- a/test/pro_tacts/admin/test_footer.rb
+++ b/test/pro_tacts/admin/test_footer.rb
@@ -71,4 +71,16 @@ class AdminFooterTest < Minitest::Test
 
     refute_includes last_response.body, "+debug"
   end
+
+  def test_an_unversioned_run_marks_the_page_for_the_dev_theme
+    with_env({})
+
+    assert_includes last_response.body, %(<html lang="en" data-server="dev">)
+  end
+
+  def test_a_release_leaves_the_page_unmarked
+    with_env("VERSION" => "20260904-1822-a1b2c3d")
+
+    refute_includes last_response.body, "data-server"
+  end
 end