From 85f8a3a072dcfb3c815b010613f04c9b39a392eb Mon Sep 17 00:00:00 2001 From: Vuong Hoang Date: Mon, 28 Sep 2026 15:42:00 -0700 Subject: [PATCH] fix(phasefinal-web): clean up the multi-page site The 2026-09-28 expansion grew the one-page design to nine pages by bolting a nav band under each hero. This makes the cluster one site: - One site header on every page: the wordmark (home link) and a single row of short, consistent labels (Applications, Systems, Hosting, AI, Engagement, Track record, Contact). The nav no longer wraps into two rows at desktop width or four on a phone. - A real

on every page (the title was a

), a skip link, and one footer outside

on every page, home included. - The contact page was unreadable: its section borrowed the home page's dark contact-card id, so its copy was grey on near-black (2.3:1) and its headings measured 1.0:1. It is an ordinary section now. - Section numbers only where there is a sequence (home, engagement). - The spec bar names all four services; it stacks cleanly on a phone. - Contrast: --text-tertiary 4.3:1 -> 4.96:1, --accent-text 4.36:1 -> 4.74:1 on paper. Track-record labels drop tracked capitals. - The grid behind the hero and the contact card is its own layer; the contact card loses its left side-tab stripe. - The stylesheet link is versioned (style.css?v=2026-09-28): CSS is cached for an hour, and the new markup must not meet the old styles. - README: nine pages, the shared structure, the CSS-version rule, and the operator's anti-slop waivers (the hero's volt rule, Space Grotesk). Copy is unchanged apart from nav labels (the brief's legal constraints hold). Still zero JavaScript and zero external requests. Impeccable detector (controls 7/0, static + rendered at 1280 and 390, light and dark): 200 unwaived -> 54, all of them the two waived brand devices; two identical runs. Operator: "ship the site cleanup, keep the rule and the font". --- stacks/phasefinal-web/README.md | 12 +- .../conf/site/contact/index.html | 55 +++--- .../conf/site/engagement/index.html | 51 +++--- stacks/phasefinal-web/conf/site/index.html | 64 ++++--- .../conf/site/privacy/index.html | 53 +++--- .../conf/site/services/ai/index.html | 53 +++--- .../conf/site/services/hosting/index.html | 53 +++--- .../conf/site/services/software/index.html | 51 +++--- .../services/systems-engineering/index.html | 53 +++--- stacks/phasefinal-web/conf/site/style.css | 160 +++++++++++------- .../conf/site/track-record/index.html | 51 +++--- 11 files changed, 378 insertions(+), 278 deletions(-) diff --git a/stacks/phasefinal-web/README.md b/stacks/phasefinal-web/README.md index 2265629..15cad63 100644 --- a/stacks/phasefinal-web/README.md +++ b/stacks/phasefinal-web/README.md @@ -1,6 +1,6 @@ # phasefinal-web -The Phase Final, Inc. corporate site — a single static page served by nginx on +The Phase Final, Inc. corporate site — nine static pages served by nginx on **ana-docker**, fronted by Traefik at `www.phasefinal.com`. Built from the design brief at `docs/design/` (boothed 2026-08-29); the markup @@ -57,3 +57,13 @@ The copy is governed by hard legal and voice constraints — client naming rules the never-"military"/"weapons" phrasing rule, no named individuals. **Read the design brief before editing any text.** Edits are made surgically in `conf/site/index.html`; there is no CMS and there will not be one. + +## Structure and design rules (cleanup, 2026-09-28) + +- Every page carries the same site header (wordmark + nav), a real `

`, and the same footer outside `
`. Change the nav in all nine files at once: the labels are Applications, Systems, Hosting, AI, Engagement, Track record, Contact (+ Privacy in the footer). +- **The stylesheet link carries a version** (`/style.css?v=`). CSS is cached for an hour (`nginx.conf`), so markup that changes with the CSS must bump it, or new pages meet old styles at the edge. +- **Anti-slop waivers, operator ruling 2026-09-28:** + - The hero's full-bleed 3px volt rule is a brand device. The Impeccable detector's `side-tab` rule flags it; that is waived. + - Space Grotesk is the brand display face. `overused-font` is waived. + - Everything else must stay clean: 0 other unwaived rows at 1280 and 390, light and dark. + diff --git a/stacks/phasefinal-web/conf/site/contact/index.html b/stacks/phasefinal-web/conf/site/contact/index.html index 9b99862..d534727 100644 --- a/stacks/phasefinal-web/conf/site/contact/index.html +++ b/stacks/phasefinal-web/conf/site/contact/index.html @@ -3,40 +3,45 @@ + Contact — Phase Final, Inc. - + + + + - +
-
- -
-

01Reach us

+
+

Reach us

Email is the front door: inquiry@phasefinal.com. A sentence about the problem and the date it needs to exist by is a complete first message.

    @@ -48,20 +53,20 @@
-
+
+ + - -
diff --git a/stacks/phasefinal-web/conf/site/engagement/index.html b/stacks/phasefinal-web/conf/site/engagement/index.html index 26597ac..940e78e 100644 --- a/stacks/phasefinal-web/conf/site/engagement/index.html +++ b/stacks/phasefinal-web/conf/site/engagement/index.html @@ -3,37 +3,42 @@ + How We Engage — Phase Final, Inc. - + + + + - - -
+

01Engagement models

@@ -64,20 +69,20 @@
-
+
+ + - -
diff --git a/stacks/phasefinal-web/conf/site/index.html b/stacks/phasefinal-web/conf/site/index.html index 73a98a7..fc831e2 100644 --- a/stacks/phasefinal-web/conf/site/index.html +++ b/stacks/phasefinal-web/conf/site/index.html @@ -3,43 +3,49 @@ + Phase Final, Inc. — Systems Engineering Consultancy - - + + + + +
- Phase Final, Inc.
-

A systems engineering consultancy.

+

A systems engineering consultancy.

Phase Final, Inc. has a long track record in hardware, firmware, and software engineering across an unusually wide range of industries: consumer electronics, financial services, health services, government, film and video game production, and industrial automation.

+ Applications Systems engineering - Infrastructure hosting + Hosting AI services Fixed-bid or retainer

- - -
+

01What we do

@@ -109,16 +115,26 @@
-
+

04Contact

inquiry@phasefinal.com

Tell us the spec and the deadline. We'll tell you what it takes to reach the final phase.

-
- Phase Final, Inc. -
-
+ + diff --git a/stacks/phasefinal-web/conf/site/privacy/index.html b/stacks/phasefinal-web/conf/site/privacy/index.html index 84c98a3..7dd1311 100644 --- a/stacks/phasefinal-web/conf/site/privacy/index.html +++ b/stacks/phasefinal-web/conf/site/privacy/index.html @@ -3,40 +3,45 @@ + Privacy — Phase Final, Inc. - + + + + - - -
+
-

01Details

+

Details

  • What this site collects

    Nothing from your browser. Fonts and images are served from this site itself. No advertising or analytics scripts are loaded, so no cross-site profiling occurs on our side.

  • @@ -48,20 +53,20 @@
-
+
+ + - -
diff --git a/stacks/phasefinal-web/conf/site/services/ai/index.html b/stacks/phasefinal-web/conf/site/services/ai/index.html index a46de50..aecb815 100644 --- a/stacks/phasefinal-web/conf/site/services/ai/index.html +++ b/stacks/phasefinal-web/conf/site/services/ai/index.html @@ -3,40 +3,45 @@ + AI Services & Engineering — Phase Final, Inc. - + + + + - - -
+
-

01What we do

+

What we do

A model call is easy. An AI system — with latency budgets, evaluation suites, fallbacks, cost ceilings, and an upgrade story — is engineering. That part is no different from any other discipline the firm practices.

    @@ -61,20 +66,20 @@
-
+
+ + - -
diff --git a/stacks/phasefinal-web/conf/site/services/hosting/index.html b/stacks/phasefinal-web/conf/site/services/hosting/index.html index 2c92324..e6ecf6f 100644 --- a/stacks/phasefinal-web/conf/site/services/hosting/index.html +++ b/stacks/phasefinal-web/conf/site/services/hosting/index.html @@ -3,40 +3,45 @@ + Infrastructure Hosting — Phase Final, Inc. - + + + + - - -
+
-

01What we operate

+

What we operate

The firm runs its own multi-site production infrastructure — colocated servers, virtualization, private networking, backup, and monitoring — and has for years. That is not a brochure claim; it is the same infrastructure that runs the tools this site's owners depend on daily.

    @@ -61,20 +66,20 @@
-
+
+ + - -
diff --git a/stacks/phasefinal-web/conf/site/services/software/index.html b/stacks/phasefinal-web/conf/site/services/software/index.html index 46871a6..6429832 100644 --- a/stacks/phasefinal-web/conf/site/services/software/index.html +++ b/stacks/phasefinal-web/conf/site/services/software/index.html @@ -3,40 +3,45 @@ + Custom Application Development — Phase Final, Inc. - + + + + - - -
+
-

01Scope

+

Scope

An application is never just the app. It is the client binary, the API behind it, the data model, the release train, and the operational story for what happens at 2 a.m. when something breaks. We take the whole problem.

    @@ -65,20 +70,20 @@
-
+
+ + - -
diff --git a/stacks/phasefinal-web/conf/site/services/systems-engineering/index.html b/stacks/phasefinal-web/conf/site/services/systems-engineering/index.html index 6311f64..1a47a96 100644 --- a/stacks/phasefinal-web/conf/site/services/systems-engineering/index.html +++ b/stacks/phasefinal-web/conf/site/services/systems-engineering/index.html @@ -3,40 +3,45 @@ + Systems Engineering — Phase Final, Inc. - + + + + - - -
+
-

01Method

+

Method

Most engineering failures between disciplines happen in the seams: firmware blames the app, the app blames the API, the API blames the infrastructure. Systems engineering is the discipline of refusing those seams — one accountable owner across the stack.

    @@ -61,20 +66,20 @@
-
+
+ + - -
diff --git a/stacks/phasefinal-web/conf/site/style.css b/stacks/phasefinal-web/conf/site/style.css index fbcf54e..1af1209 100644 --- a/stacks/phasefinal-web/conf/site/style.css +++ b/stacks/phasefinal-web/conf/site/style.css @@ -13,16 +13,17 @@ @font-face { font-family: "IBM Plex Mono"; src: url("fonts/IBMPlexMono-SemiBold.woff2") format("woff2"); font-weight: 600; font-display: swap; } :root { + color-scheme: light dark; --ink: #0f1318; --ink-2: #181d22; --text: #0f1318; --text-secondary: #4d5763; - --text-tertiary: #6b7682; + --text-tertiary: #626d78; /* 4.96:1 on paper (was #6b7682, 4.3:1) */ --paper: #f6f8f9; --border: #c2cad2; --border-subtle: #dde2e7; --accent: #90c73e; - --accent-text: #588021; /* AA on light */ + --accent-text: #527a1e; /* 4.74:1 on paper (was #588021, 4.36:1) */ --volt-200: #d0e8a0; --volt-300: #b8db72; --volt-400: #a3d052; @@ -65,6 +66,52 @@ body { ::selection { background: var(--volt-200); color: #0f1318; } a { color: var(--accent-text); text-decoration: underline; text-underline-offset: 3px; } +:focus-visible { outline: 2px solid var(--volt-500); outline-offset: 3px; } + +/* ---- Skip link: first stop for the keyboard, off-screen until focused ---- */ +.skip { + position: absolute; left: 16px; top: 12px; z-index: 10; + transform: translateY(-200%); + padding: 8px 12px; border-radius: 6px; + background: var(--paper); color: var(--text); + font-family: var(--mono); font-size: 12.5px; +} +.skip:focus-visible { transform: none; } + +/* ---- Site header: the brand and the whole site's nav, one row, every page ---- */ +.site-header { background: var(--ink); color: #f6f8f9; } +.site-header-inner { + max-width: 920px; + margin: 0 auto; + padding: 18px 28px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 14px 32px; + flex-wrap: wrap; +} +.brand { display: block; line-height: 0; } +.brand img { height: 28px; width: auto; display: block; } +.site-nav ul { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-wrap: wrap; + gap: 4px 22px; +} +.site-nav a { + display: inline-block; + padding: 6px 0; + font-family: var(--mono); + font-size: 12px; + letter-spacing: 0.08em; + text-transform: uppercase; + text-decoration: none; + color: var(--slate-300); +} +.site-nav a:hover { color: var(--volt-300); } +.site-nav a[aria-current="page"] { color: #ffffff; box-shadow: inset 0 -2px 0 var(--volt-500); } p { margin: 0; text-wrap: pretty; } /* ---- Hero: ink console surface with engineering grid ---- */ @@ -72,28 +119,37 @@ p { margin: 0; text-wrap: pretty; } background: var(--ink); color: #f6f8f9; position: relative; + isolation: isolate; overflow: hidden; + border-bottom: 3px solid var(--volt-500); +} +/* The engineering grid is its own layer, so the text's background is the ink it + actually sits on (a detector reading the gradient as the background saw 1.1:1). */ +#hero::before, .contact-card::before { + content: ""; + position: absolute; + inset: 0; + z-index: -1; + pointer-events: none; background-image: linear-gradient(rgba(246,248,249,0.05) 1px, transparent 1px), linear-gradient(90deg, rgba(246,248,249,0.05) 1px, transparent 1px); background-size: 48px 48px; - border-bottom: 3px solid var(--volt-500); } .hero-inner { max-width: 920px; margin: 0 auto; - padding: clamp(56px, 9vw, 104px) 28px clamp(56px, 8vw, 96px); + padding: clamp(40px, 7vw, 80px) 28px clamp(52px, 8vw, 88px); display: flex; flex-direction: column; gap: clamp(40px, 6vw, 64px); } -.wordmark { height: clamp(48px, 7.5vw, 72px); width: auto; display: block; align-self: flex-start; } - .hero-copy { max-width: 800px; } .lede { + margin: 0; font-family: var(--display); font-size: clamp(32px, 5.6vw, 54px); line-height: 1.1; @@ -128,6 +184,11 @@ p { margin: 0; text-wrap: pretty; } .spec-bar span { padding: 0 18px; border-left: 1px solid var(--slate-700); } .spec-bar span:first-child { padding-left: 0; border-left: none; } .spec-bar .spec-accent { color: var(--volt-300); } +@media (max-width: 600px) { + /* stacked, a divider would only ever start a line: drop it */ + .spec-bar { flex-direction: column; gap: 6px; } + .spec-bar span { padding: 0; border-left: none; } +} /* ---- Body ---- */ main { @@ -143,7 +204,7 @@ section { flex-wrap: wrap; gap: 32px; } -#services { border-top: none; padding-top: clamp(52px, 7vw, 76px); } +main > section:first-of-type { border-top: none; padding-top: 72px; } h2 { margin: 0; @@ -191,9 +252,7 @@ h2 .num { color: var(--accent-text); margin-right: 12px; } .record + .record { border-top: 1px solid var(--border-subtle); } .record dt { font-family: var(--mono); - font-size: 12.5px; - letter-spacing: 0.05em; - text-transform: uppercase; + font-size: 13px; color: var(--text-tertiary); margin-bottom: 6px; } @@ -201,22 +260,20 @@ h2 .num { color: var(--accent-text); margin-right: 12px; } .record dd { margin: 0; font-size: 17.5px; } .record strong { font-family: var(--display); font-weight: 600; } -/* Contact — ink console panel */ -#contact { +/* Contact — ink console panel (the home page's closing card) */ +.contact-card { display: block; + position: relative; + isolation: isolate; + overflow: hidden; border-top: none; background: var(--ink-2); - background-image: - linear-gradient(rgba(246,248,249,0.05) 1px, transparent 1px), - linear-gradient(90deg, rgba(246,248,249,0.05) 1px, transparent 1px); - background-size: 48px 48px; - border-left: 3px solid var(--volt-500); border-radius: 16px; - padding: clamp(40px, 6vw, 72px); + padding: 64px; color: #f6f8f9; } -#contact h2 { color: var(--slate-400); margin-bottom: 20px; } -#contact h2 .num { color: var(--volt-400); } +.contact-card h2 { color: var(--slate-400); margin-bottom: 20px; } +.contact-card h2 .num { color: var(--volt-400); } .contact-address { font-size: clamp(23px, 4vw, 38px); line-height: 1.25; @@ -237,67 +294,49 @@ h2 .num { color: var(--accent-text); margin-right: 12px; } font-size: 16px; } -/* Footer */ -footer { +/* Footer — the same on every page */ +.site-footer { + max-width: 864px; + width: calc(100% - 56px); + margin: 0 auto; + padding: 28px 0 48px; + border-top: 1px solid var(--border-subtle); display: flex; justify-content: space-between; align-items: baseline; - gap: 16px; + gap: 12px 24px; flex-wrap: wrap; - padding-top: 28px; font-family: var(--mono); font-size: 12.5px; color: var(--text-tertiary); } +.site-footer nav { display: flex; flex-wrap: wrap; gap: 4px 18px; } +.site-footer nav a { color: var(--text-tertiary); text-decoration: none; } +.site-footer nav a:hover { color: var(--accent-text); text-decoration: underline; } +.site-footer nav a[aria-current="page"] { color: var(--text); } @media (max-width: 480px) { main { padding: 0 20px 56px; } - .hero-inner { padding-left: 20px; padding-right: 20px; } + .hero-inner, .site-header-inner { padding-left: 20px; padding-right: 20px; } + .site-footer { width: calc(100% - 40px); } section { padding: 40px 0 48px; } + main > section:first-of-type { padding-top: 48px; } + .contact-card { padding: 36px 28px; } + .site-nav ul { gap: 2px 18px; } } /* ---- Multi-page additions (2026-09-28) ---- */ -/* Top nav: monospace strip under the hero rule, matching the spec-bar voice */ -.site-nav { - background: var(--ink-2); - border-bottom: 1px solid var(--border); -} -.site-nav ul { - max-width: 920px; - margin: 0 auto; - padding: 0 28px; - list-style: none; - display: flex; - flex-wrap: wrap; - gap: 4px 0; -} -.site-nav a { - display: block; - padding: 12px 16px; - font-family: var(--mono); - font-size: 12px; - letter-spacing: 0.09em; - text-transform: uppercase; - text-decoration: none; - color: var(--slate-400); -} -.site-nav a:hover { color: var(--volt-300); } -.site-nav a[aria-current="page"] { - color: #f6f8f9; - box-shadow: inset 0 -2px 0 var(--volt-500); -} - /* Sub-pages get a compact page header instead of the full hero */ .page-header { background: var(--ink); border-bottom: 3px solid var(--volt-500); color: #f6f8f9; } -.page-header .hero-inner { gap: 20px; padding-bottom: clamp(36px, 5vw, 56px); padding-top: clamp(36px, 5vw, 56px); } +.page-header .hero-inner { gap: 20px; padding-bottom: clamp(36px, 5vw, 56px); padding-top: clamp(28px, 4vw, 44px); } .page-header .lede { font-size: clamp(26px, 4.4vw, 40px); } .page-header .sublede { margin-top: 14px; } -.page-header .wordmark { height: clamp(30px, 4.5vw, 40px); } +.page-header .lede { line-height: 1.18; } /* Service detail pages */ .detail-lede { font-size: 18.5px; max-width: 62ch; } @@ -330,9 +369,4 @@ footer { text-transform: uppercase; color: var(--text-tertiary); } -.related a { margin-right: 18px; } - -/* Footer grows a nav echo */ -footer nav { display: flex; flex-wrap: wrap; gap: 4px 18px; } -footer nav a { color: var(--text-tertiary); text-decoration: none; } -footer nav a:hover { color: var(--accent-text); } +.related a { margin-right: 18px; display: inline-block; white-space: nowrap; } diff --git a/stacks/phasefinal-web/conf/site/track-record/index.html b/stacks/phasefinal-web/conf/site/track-record/index.html index d988bc9..8ed36a6 100644 --- a/stacks/phasefinal-web/conf/site/track-record/index.html +++ b/stacks/phasefinal-web/conf/site/track-record/index.html @@ -3,40 +3,45 @@ + Track Record — Phase Final, Inc. - + + + + - - -
+
-

01The record

+

The record

@@ -72,20 +77,20 @@
-
+
+ + - -