From 17a6fabcc203ed4dc90e674222e41c281a2f9bcb Mon Sep 17 00:00:00 2001 From: nexxo Date: Wed, 29 Jul 2026 14:30:44 +0200 Subject: [PATCH] One wordmark, one typeface, and the address the server actually issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported as "logo and font are different everywhere" — maintenance page, customer panel, console, homepage. They were. Counting them up: the site header set the name in ink at -0.02em, the sidebar split it into "Clu" plus an orange "Pilot", the sign-in plate set it white at a third size, the customer two-factor page set it in MONO with no mark at all, the error pages and the placeholder each inlined their own — and the maintenance screen had no wordmark whatsoever. Nine surfaces, seven ideas of the brand. There is one now: resources/views/components/ui/brand.blade.php. Mark plus wordmark in one piece, in ink, with an optional muted "Cloud". No orange half — that made the accent a permanent fixture instead of something used sparingly. The four places that cannot use a Blade component (the two self-contained pages, the error layout, the mail layout) inline it and are held to the same shape by a test. The typeface. The console loads IBM Plex through Vite; the 503 page has no stylesheet at all, so the maintenance screen — the one page shown when somebody is already worried — was rendering in the system sans. It declares the fonts itself now. And the bold weight was missing from public/fonts entirely: the placeholder and the error pages both asked for ibm-plex-sans-latin-700-normal.woff2, which was not there. A @font-face pointing at nothing fails silently, which is why nobody noticed. The error pages carried the same two faults the placeholder shipped with: an empty gradient tile where the mark belongs, and `gap` on an inline-flex row spacing "Clu", "Pilot" and "Cloud" apart as three separate words. And the price sheet promised "Adresse auf clupilot.cloud" — a domain the company does not own — while provisioning was issuing addresses under whatever CLUPILOT_DNS_ZONE says. It is assembled from that setting now, the same reason the prices are read from the catalogue rather than typed into the page. welcome.blade.php removed: Laravel's leftover, reachable from no route. Co-Authored-By: Claude Opus 5 --- VERSION | 2 +- app/Http/Controllers/LandingController.php | 34 ++++++- .../ibm-plex-sans-latin-700-normal.woff2 | Bin 0 -> 22832 bytes resources/views/coming-soon.blade.php | 6 +- .../views/components/auth/brand.blade.php | 5 +- .../views/components/layouts/site.blade.php | 10 +- .../views/components/mail/layout.blade.php | 2 +- .../views/components/shell/nav.blade.php | 5 +- resources/views/components/ui/brand.blade.php | 37 ++++++++ resources/views/errors/layout.blade.php | 22 ++++- resources/views/livewire/auth/login.blade.php | 5 +- .../livewire/auth/operator-login.blade.php | 5 +- .../operator-two-factor-challenge.blade.php | 5 +- .../views/livewire/auth/register.blade.php | 5 +- .../auth/two-factor-challenge.blade.php | 4 +- .../views/partials/updating-panel.blade.php | 20 +++- resources/views/welcome.blade.php | 21 ----- tests/Feature/SiteDesignSystemTest.php | 89 ++++++++++++++++++ 18 files changed, 207 insertions(+), 70 deletions(-) create mode 100644 public/fonts/ibm-plex-sans-latin-700-normal.woff2 create mode 100644 resources/views/components/ui/brand.blade.php delete mode 100644 resources/views/welcome.blade.php diff --git a/VERSION b/VERSION index d4c4950..0c00f61 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.3.9 +1.3.10 diff --git a/app/Http/Controllers/LandingController.php b/app/Http/Controllers/LandingController.php index c244996..f6359f0 100644 --- a/app/Http/Controllers/LandingController.php +++ b/app/Http/Controllers/LandingController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\Services\Billing\PlanCatalogue; +use App\Support\ProvisioningSettings; use Illuminate\Contracts\View\View; use Illuminate\Support\Facades\Log; use Throwable; @@ -38,7 +39,12 @@ class LandingController extends Controller 'managed_updates' => 'Updates & Wartung', 'daily_backups' => 'Tägliche Sicherung', 'monitoring' => 'Überwachung rund um die Uhr', - 'subdomain' => 'Adresse auf clupilot.cloud', + // Filled in from the configured zone by label(), because a + // hard-coded domain here is the same two-sources-of-truth mistake + // as a hard-coded price — and it shipped naming clupilot.cloud, a + // domain the company does not own, while provisioning was issuing + // addresses under whatever CLUPILOT_DNS_ZONE said. + 'subdomain' => 'Adresse auf :zone', 'custom_domain' => 'Eigene Domain', 'office' => 'Office im Browser', 'branding' => 'Ihr Logo & Ihre Farben', @@ -73,9 +79,13 @@ class LandingController extends Controller { $offered = array_merge(...array_column($plans, 'features')) ?: []; + // Through label(), like the plans themselves. Compared against the raw + // constant this stopped matching the moment one label gained a + // placeholder, and the row for it would simply have vanished from the + // comparison table — with nothing anywhere saying why. return array_values(array_filter( - self::FEATURES, - fn (string $label) => in_array($label, $offered, true), + array_map(fn (string $key) => $this->label($key), array_keys(self::FEATURES)), + fn (?string $label) => $label !== null && in_array($label, $offered, true), )); } @@ -114,6 +124,22 @@ class LandingController extends Controller return $plans; } + /** + * One feature, in the words a customer uses. + * + * The address the platform hands out is a setting, not a constant — a + * server can be installed against any zone — so the sentence naming it is + * assembled rather than written down twice. + */ + private function label(string $key): ?string + { + $label = self::FEATURES[$key] ?? null; + + return $label === null + ? null + : str_replace(':zone', ProvisioningSettings::dnsZone(), $label); + } + /** Currencies we have a symbol for; anything else prints its ISO code. */ private const SYMBOLS = ['EUR' => '€', 'CHF' => 'CHF', 'USD' => '$', 'GBP' => '£']; @@ -156,7 +182,7 @@ class LandingController extends Controller // the catalogue without a translation would otherwise appear on the // public page as "premium_sla". return array_values(array_filter(array_map( - fn (string $key) => self::FEATURES[$key] ?? null, + fn (string $key) => $this->label($key), $keys, ))); } diff --git a/public/fonts/ibm-plex-sans-latin-700-normal.woff2 b/public/fonts/ibm-plex-sans-latin-700-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..953f94fa178e8ca69a8894c2c9d1325b3a15c150 GIT binary patch literal 22832 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