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 <h1> on every page (the title was a <p>), a skip link, and one footer outside <main> 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".
70 lines
3.5 KiB
Markdown
70 lines
3.5 KiB
Markdown
# phasefinal-web
|
|
|
|
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
|
|
and CSS came from a Claude Design session and are checked in verbatim under
|
|
`conf/site/` apart from the two post-export steps below.
|
|
|
|
## Two steps every fresh export needs
|
|
|
|
The design tool ships the site with **no font binaries** and the `@font-face`
|
|
block **commented out**, deliberately, to honour the brief's zero-external-
|
|
requests rule. Both must be undone on import:
|
|
|
|
1. Drop the four woff2 files into `conf/site/fonts/`
|
|
(Space Grotesk variable, IBM Plex Sans variable, IBM Plex Mono 400 + 600 —
|
|
all SIL OFL, self-hosted, no CDN).
|
|
2. Uncomment the four `@font-face` rules at the top of `conf/site/style.css`.
|
|
|
|
Without these the page silently falls back to system fonts and looks wrong
|
|
rather than broken, which is the failure mode you won't notice.
|
|
|
|
## Routing — and the healthcheck trap that looks like a routing bug
|
|
|
|
Routers come from the compose labels: `phasefinal-web` serves
|
|
`www.phasefinal.com`; `phasefinal-apex` 301s `phasefinal.com` to it.
|
|
|
|
⚠ **Traefik silently skips containers Docker reports as unhealthy.** There is no
|
|
error, no log line, and no router — it looks exactly like a broken provider. The
|
|
first deploy of this stack hit that: the healthcheck used
|
|
`http://localhost/`, which resolves to `::1` in `nginx:alpine`, and nginx listens
|
|
on IPv4 only, so the check got "Connection refused" forever and the container
|
|
never left `unhealthy`. **Use `127.0.0.1`, never `localhost`, in a healthcheck
|
|
for an IPv4-only listener** — and if a correctly-labelled container never appears
|
|
in `/api/http/routers`, check `docker inspect --format '{{.State.Health.Status}}'`
|
|
*before* suspecting Traefik.
|
|
|
|
## Deploy
|
|
|
|
```bash
|
|
scripts/deploy-stack.sh ana-docker phasefinal-web
|
|
ssh infra-ops@10.250.50.70 'cd /opt/docker/compose/phasefinal-web && docker compose up -d'
|
|
```
|
|
|
|
## DNS
|
|
|
|
`www.phasefinal.com` → A `38.120.12.44` (the Anaheim public IP, forwarded to
|
|
ana-docker's Traefik). The apex `phasefinal.com` has **no** record — the site is
|
|
www-only by operator decision. Cloudflare-proxied is the intended end state, for
|
|
edge caching and the automatic email-address obfuscation the contact block
|
|
relies on.
|
|
|
|
## Content constraints
|
|
|
|
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 `<h1>`, and the same footer outside `<main>`. 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=<date>`). 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.
|
|
|