From 45552528f8999664a1c08753ae70ad1ca600fe6d Mon Sep 17 00:00:00 2001
From: nish3451 <257724087+nish3451@users.noreply.github.com>
Date: Mon, 10 Aug 2026 21:18:33 +0530
Subject: [PATCH] fix(public): give both appraisal intake fields persistent
labels and kill the two "The Tiny Studio" document titles
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The homepage and /audit intake forms labelled their fields only with
placeholder text, which disappears the moment a buyer starts typing and is
not a persistent programmatic label; both served pages now carry a stable
aria-label on the website and email inputs. /pricing and /brief-requested
still branded their document titles "The Tiny Studio" — the spaced name the
site's own identity copy disavows — while every other page said TinyStudio;
both titles now match the brand (and the pricing title matches its own
og:title). Two deterministic guards in check-site.mjs make both regressions
impossible: every one of the six served appraisal titles must name TinyStudio
and never "The Tiny Studio", and every intake website/email input must carry a
non-empty aria-label. The retired /agent-desk page is excluded deliberately:
its title frames itself as retired and it is noindex.
verify: npm run check
---
public/audit.html | 4 +--
public/brief-requested.html | 2 +-
public/index.html | 4 +--
public/pricing.html | 2 +-
scripts/check-site.mjs | 57 +++++++++++++++++++++++++++++++++++++
5 files changed, 63 insertions(+), 6 deletions(-)
diff --git a/public/audit.html b/public/audit.html
index 87556a55..9057274e 100644
--- a/public/audit.html
+++ b/public/audit.html
@@ -77,8 +77,8 @@
We read your page as a customer with intent reads it.
Four passes, by hand, on the surfaces that decide whether someone who arrived ready to buy stays that way. You get a short, plain document — each fault named, in order of what it costs you, with the fix beside each one. No deck. No dashboard.
Thirty seconds to ask. Findings inside five working days. Yours to keep either way.
diff --git a/public/brief-requested.html b/public/brief-requested.html
index a40ec132..70409ce0 100644
--- a/public/brief-requested.html
+++ b/public/brief-requested.html
@@ -3,7 +3,7 @@
-Request received — The Tiny Studio
+Request received — TinyStudio
diff --git a/public/index.html b/public/index.html
index ae3544c6..5a9ff9aa 100644
--- a/public/index.html
+++ b/public/index.html
@@ -71,8 +71,8 @@
Most of them leave before they ever get in touch.
We read the one page your revenue depends on the way a customer with intent reads it, and show you the exact points at which they go.
Thirty seconds to ask. Findings inside five working days. No call at any point.
diff --git a/public/pricing.html b/public/pricing.html
index 454be253..571fe1b5 100644
--- a/public/pricing.html
+++ b/public/pricing.html
@@ -4,7 +4,7 @@
-Pricing & terms — The Tiny Studio
+TinyStudio — Pricing & terms
diff --git a/scripts/check-site.mjs b/scripts/check-site.mjs
index 2cb3c4c2..81c44b3f 100644
--- a/scripts/check-site.mjs
+++ b/scripts/check-site.mjs
@@ -1492,6 +1492,63 @@ for (const [pageName, pageHtml, expected] of canonicalPages) {
}
}
+// ---- Document titles (brand consistency) ----------------------------------
+// Two served pages still branded themselves "The Tiny Studio" — the spaced
+// name the site's own identity copy disavows (it collides with "The Tiny
+// Studio LA" and other unrelated businesses) — while every other title said
+// "TinyStudio": /pricing served "Pricing & terms — The Tiny Studio" and
+// /brief-requested served "Request received — The Tiny Studio", both
+// byte-identical on origin/main. Title tags are a first-order SERP signal, so
+// every one of the six served appraisal pages must now name the brand in its
+// document title and must never return the spaced "The Tiny Studio" form.
+// The retired /agent-desk surface is deliberately excluded: its title frames
+// itself as retired and it is noindex.
+const titlePages = [
+ ["homepage", siteHome],
+ ["audit page", siteAudit],
+ ["desk page", read("public/agents.html")],
+ ["pricing page", read("public/pricing.html")],
+ ["specimen page", read("public/specimen.html")],
+ ["brief-requested page", read("public/brief-requested.html")]
+];
+
+for (const [pageName, pageHtml] of titlePages) {
+ const title = pageHtml.match(/([^<]*)<\/title>/i)?.[1] ?? "";
+ if (!title) {
+ failures.push(`Document title must exist on ${pageName}.`);
+ continue;
+ }
+ if (!title.includes("TinyStudio")) {
+ failures.push(`Document title on ${pageName} must name TinyStudio (found ${JSON.stringify(title)}).`);
+ }
+ if (title.includes("The Tiny Studio")) {
+ failures.push(`Document title on ${pageName} must not use the spaced "The Tiny Studio" form (found ${JSON.stringify(title)}).`);
+ }
+}
+
+// ---- Intake field labels (activation) -------------------------------------
+// Both appraisal intake forms (homepage and /audit) labelled their fields
+// only with placeholder text, which disappears the moment a buyer starts
+// typing and is not a persistent programmatic label. Each intake input must
+// carry a non-empty aria-label so the field keeps its name for assistive
+// tech and for the browser's own validation announcements, no matter what
+// the field contains.
+const intakePages = [
+ ["homepage", siteHome],
+ ["audit page", siteAudit]
+];
+
+for (const [pageName, pageHtml] of intakePages) {
+ for (const input of pageHtml.matchAll(/]*>/gi)) {
+ const tag = input[0];
+ if (!/\bname="(?:website|email)"/.test(tag)) continue;
+ const aria = tag.match(/\baria-label="([^"]*)"/)?.[1] ?? "";
+ if (!aria.trim()) {
+ failures.push(`Intake input on ${pageName} must carry a persistent programmatic aria-label (placeholder-only labels disappear as buyers type): ${tag}`);
+ }
+ }
+}
+
for (const migration of ["migrations/0002_agent_runs.sql", "migrations/0003_agent_usage_limits.sql"]) { if (!existsSync(new URL(`../${migration}`, import.meta.url))) {
failures.push(`Missing migration: ${migration}`);
continue;