[object Object]

← back to Adsense Fleet Viewer

AdSense fleet status viewer — live probe + policy column + manual account-status card (DTD 2026-08-05: verify-first + placement rule)

cccd6965567fd8b6ac57ecd5844c90c2edaf2117 · 2026-08-05 11:20:14 -0700 · Steve

Files touched

Diff

commit cccd6965567fd8b6ac57ecd5844c90c2edaf2117
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Aug 5 11:20:14 2026 -0700

    AdSense fleet status viewer — live probe + policy column + manual account-status card (DTD 2026-08-05: verify-first + placement rule)
---
 .gitignore          |   5 ++
 account-status.json |   9 +++
 build.mjs           | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 public/index.html   | 145 ++++++++++++++++++++++++++++++++++++++
 public/status.json  |  98 ++++++++++++++++++++++++++
 5 files changed, 453 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ff2422c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+node_modules/
+.env*
+tmp/
+*.log
+.DS_Store
diff --git a/account-status.json b/account-status.json
new file mode 100644
index 0000000..3fe7e5c
--- /dev/null
+++ b/account-status.json
@@ -0,0 +1,9 @@
+{
+  "_comment": "MANUAL — the one thing probing cannot see. Fill these after checking adsense.google.com, then re-run `node build.mjs`. Values: yes | no | unknown",
+  "pubId": "pub-5278231299883833",
+  "approved": "unknown",
+  "autoAdsOn": "unknown",
+  "serving": "unknown",
+  "checkedAt": "",
+  "notes": ""
+}
diff --git a/build.mjs b/build.mjs
new file mode 100644
index 0000000..e13f5d2
--- /dev/null
+++ b/build.mjs
@@ -0,0 +1,196 @@
+#!/usr/bin/env node
+/* AdSense Fleet Status — live-probes every fleet domain and renders a status
+ * dashboard to public/index.html. Re-run any time to refresh (probes are live).
+ *
+ * Status model (three honest layers — see the legend in the page):
+ *   LOADER  — is adsbygoogle.js actually served on the live domain? (verifiable here)
+ *   ADS.TXT — is /ads.txt present + authorizing a pub id? (verifiable here)
+ *   SLOTS   — are any manual <ins> ad units active (non-empty __ADSLOTS)? (verifiable here)
+ *   SERVING — real paid creative painting = account approved + Auto ads ON.
+ *             NOT verifiable from here (AdSense dashboard, account-side).           */
+
+import { writeFile, readFile } from 'node:fs/promises';
+
+// Manual account status — the one thing probing CANNOT see (AdSense dashboard only).
+// Steve edits account-status.json, then re-runs this build.
+let ACCT = { pubId: 'pub-5278231299883833', approved: 'unknown', autoAdsOn: 'unknown', serving: 'unknown', checkedAt: '', notes: '' };
+try { ACCT = { ...ACCT, ...JSON.parse(await readFile(new URL('./account-status.json', import.meta.url), 'utf8')) }; } catch {}
+
+// domain -> which project owns it. Best-known mapping; edit as domains are confirmed.
+// policy = the AdSense placement rule (DTD verdict 2026-08-05):
+//   allow    — content/traffic surface, ads OK
+//   forbid   — customer-facing luxury/commerce surface, ads must NOT run
+//   verify   — thin/low-content, only after account check + real per-page content
+//   na       — not a public ad surface
+const FLEET = [
+  { project: 'CelebritySignatures',        domain: 'celebsignatures.com',            policy: 'allow' },
+  { project: 'interiordesignershowroom',   domain: 'interiordesignershowroom.com',   policy: 'forbid', flag: 'REMOVE pending approval' },
+  { project: 'tesla (Charge & Explore)',   domain: 'chargeandexplore.com',           policy: 'verify', note: 'app landing — thin page, keep only if not cluttering conversion' },
+  { project: 'ventura-corridor',           domain: 'venturacorridor.com',            policy: 'verify', note: 'templated directory — needs real per-page content first' },
+  { project: 'allnewsdaily',               domain: 'allnewsdaily.com',               policy: 'allow' },
+  { project: 'fashion-style-guides',       domain: 'fashionstyleguides.com',         policy: 'allow' },
+  { project: 'restaurant-directory',       domain: null, policy: 'allow',  note: 'public domain unconfirmed' },
+  { project: 'AbramsEgo',                  domain: null, policy: 'na',      note: 'internal command center (:9773) — not a public ad surface' },
+];
+
+const timeout = (ms) => new AbortController(); // placeholder (see fetchT)
+async function fetchT(url, ms = 9000) {
+  const ac = new AbortController();
+  const t = setTimeout(() => ac.abort(), ms);
+  try { return await fetch(url, { signal: ac.signal, redirect: 'follow', headers: { 'user-agent': 'adsense-fleet-probe' } }); }
+  finally { clearTimeout(t); }
+}
+
+async function probe(entry) {
+  const r = { ...entry, http: null, loader: false, pubFromLoader: null, adsTxt: null, slots: [], err: null };
+  if (!entry.domain) return r;
+  const base = `https://${entry.domain}`;
+  try {
+    const res = await fetchT(base + '/');
+    r.http = res.status;
+    const html = await res.text();
+    const m = html.match(/adsbygoogle\.js\?client=ca-(pub-\d+)/i);
+    r.loader = !!m; r.pubFromLoader = m ? m[1] : null;
+    const sm = html.match(/__ADSLOTS\s*=\s*(\{[^;]*\})/);
+    if (sm) { try { const o = JSON.parse(sm[1]); r.slots = Object.entries(o).filter(([, v]) => v && String(v).trim()).map(([k]) => k); } catch {} }
+  } catch (e) { r.err = e.name === 'AbortError' ? 'timeout' : e.message; }
+  try {
+    const res = await fetchT(base + '/ads.txt', 7000);
+    if (res.ok) { const t = await res.text(); const pm = t.match(/pub-\d+/); r.adsTxt = pm ? pm[0] : null; }
+  } catch {}
+  return r;
+}
+
+const results = await Promise.all(FLEET.map(probe));
+const now = new Date();
+const stamp = now.toLocaleString(undefined, { year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' });
+
+// derive fleet-level status per row
+function verdict(r) {
+  if (!r.domain) return { key: 'na', label: r.note?.includes('internal') ? 'N/A · internal' : 'No domain' };
+  if (r.err) return { key: 'down', label: r.err };
+  if (r.loader && r.adsTxt) return { key: r.slots.length ? 'full' : 'loader', label: r.slots.length ? `Loader + ${r.slots.length} unit(s)` : 'Loader live' };
+  if (r.loader) return { key: 'partial', label: 'Loader, no ads.txt' };
+  return { key: 'off', label: 'No ads on live site' };
+}
+
+const PILL = {
+  full:    ['#0a7d33', '#e6f4ea', '● Loader + units'],
+  loader:  ['#0a7d33', '#e9f6ee', '● Loader live'],
+  partial: ['#a86a00', '#fdf3e2', '◐ Partial'],
+  off:     ['#9a2323', '#fbeaea', '○ Not serving'],
+  down:    ['#9a2323', '#fbeaea', '✕ Unreachable'],
+  na:      ['#5b5b5b', '#eee', '– N/A'],
+};
+
+const liveCount = results.filter(r => verdict(r).key === 'loader' || verdict(r).key === 'full').length;
+const adSurfaces = results.filter(r => r.domain && !(r.note || '').includes('internal')).length;
+
+const POLICY = {
+  allow:  ['#0a7d33', '#e6f4ea', '✅ Ads OK'],
+  forbid: ['#9a2323', '#fbeaea', '⛔ No ads (luxury/commerce)'],
+  verify: ['#a86a00', '#fdf3e2', '⚠ Verify content first'],
+  na:     ['#5b5b5b', '#eee', '– N/A'],
+};
+function policyPill(r) {
+  const [fg, bg, txt] = POLICY[r.policy] || POLICY.na;
+  const flag = r.flag ? `<div class="sub warn">${r.flag}</div>` : '';
+  return `<span class="pill" style="color:${fg};background:${bg}">${txt}</span>${flag}`;
+}
+
+// Manual account-status card (dashboard-only truth)
+const AS = { yes: ['#0a7d33', '#e6f4ea', 'YES'], no: ['#9a2323', '#fbeaea', 'NO'], unknown: ['#a86a00', '#fdf3e2', 'UNKNOWN'] };
+const asBadge = (v) => { const [fg, bg, t] = AS[v] || AS.unknown; return `<span class="pill" style="color:${fg};background:${bg}">${t}</span>`; };
+const acctCard = `<div class="acct">
+  <h2>Account status — <span class="muted">dashboard-only, not probeable</span></h2>
+  <div class="acct-row">
+    <div><span>Approved</span>${asBadge(ACCT.approved)}</div>
+    <div><span>Auto ads ON</span>${asBadge(ACCT.autoAdsOn)}</div>
+    <div><span>Actually serving</span>${asBadge(ACCT.serving)}</div>
+    <div><span>Pub id</span><code>${ACCT.pubId}</code></div>
+  </div>
+  <div class="acct-foot">${ACCT.checkedAt ? `🕓 checked <code>${ACCT.checkedAt}</code>` : '<span class="warn">Not yet checked.</span>'} ${ACCT.notes ? '· ' + ACCT.notes : ''}
+    <div class="muted">Fill these at <a href="https://adsense.google.com/" target="_blank" rel="noopener">adsense.google.com</a> → edit <code>account-status.json</code> → re-run <code>node build.mjs</code>.</div></div>
+</div>`;
+
+function cell(v, href, mono) {
+  const t = v == null || v === '' ? '<span class="muted">—</span>' : (mono ? `<code>${v}</code>` : v);
+  return href ? `<a href="${href}" target="_blank" rel="noopener">${t}</a>` : t;
+}
+
+const rows = results.map(r => {
+  const vd = verdict(r);
+  const [fg, bg, txt] = PILL[vd.key];
+  const site = r.domain ? `<a href="https://${r.domain}/" target="_blank" rel="noopener">${r.domain}</a>` : `<span class="muted">${r.note || '—'}</span>`;
+  return `<tr>
+    <td><span class="proj">${r.project}</span><div class="sub">${site}</div></td>
+    <td>${policyPill(r)}</td>
+    <td><span class="pill" style="color:${fg};background:${bg}">${txt}</span><div class="sub">${vd.label}</div></td>
+    <td>${r.http ? cell(r.http) : cell(null)}</td>
+    <td>${r.loader ? cell('yes', r.domain ? `view-source:https://${r.domain}/` : null) : cell(r.domain ? 'no' : null)}</td>
+    <td>${cell(r.adsTxt, r.domain ? `https://${r.domain}/ads.txt` : null, true)}</td>
+    <td>${r.slots.length ? cell(r.slots.join(', ')) : '<span class="muted">none (Auto ads only)</span>'}</td>
+    <td>${cell(r.pubFromLoader, null, true)}</td>
+  </tr>`;
+}).join('\n');
+
+const html = `<!doctype html><html lang="en"><head><meta charset="utf-8">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<title>AdSense Fleet Status</title>
+<style>
+:root{--ink:#1a1a1a;--line:#e6e3dc;--muted:#9a9384}
+*{box-sizing:border-box}body{margin:0;font:15px/1.55 -apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;color:var(--ink);background:#f7f5f0}
+.wrap{max-width:1080px;margin:0 auto;padding:28px 20px 70px}
+h1{font-size:24px;margin:0 0 2px}.stamp{color:var(--muted);font-size:13px;margin-bottom:18px}
+.stamp code{background:#efeae1;padding:1px 6px;border-radius:5px}
+.cards{display:flex;gap:14px;flex-wrap:wrap;margin:16px 0 22px}
+.card{flex:1;min-width:150px;background:#fff;border:1px solid var(--line);border-radius:12px;padding:14px 16px}
+.card b{display:block;font-size:28px;line-height:1.1}.card span{color:var(--muted);font-size:12px;text-transform:uppercase;letter-spacing:.05em}
+table{width:100%;border-collapse:collapse;background:#fff;border:1px solid var(--line);border-radius:12px;overflow:hidden}
+th,td{text-align:left;padding:11px 13px;border-bottom:1px solid var(--line);vertical-align:top;font-size:13px}
+th{background:#efeae1;font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:#6b6b6b}
+tr:last-child td{border-bottom:none}
+.proj{font-weight:600}.sub{color:var(--muted);font-size:12px;margin-top:2px}.sub a{color:#1d7a36}
+.pill{display:inline-block;padding:3px 9px;border-radius:999px;font-size:12px;font-weight:600;white-space:nowrap}
+code{background:#f2efe8;padding:1px 5px;border-radius:4px;font-size:12px}
+a{color:#1d7a36}.muted{color:var(--muted)}
+.legend{margin-top:22px;background:#fff;border:1px solid var(--line);border-radius:12px;padding:16px 18px;font-size:13px}
+.legend h2{font-size:13px;text-transform:uppercase;letter-spacing:.06em;color:#6b6b6b;margin:0 0 8px}
+.legend li{margin:4px 0}.warn{color:#9a2323}
+.acct{background:#fff;border:1px solid var(--line);border-radius:12px;padding:16px 18px;margin:0 0 20px}
+.acct h2{font-size:13px;text-transform:uppercase;letter-spacing:.06em;color:#6b6b6b;margin:0 0 12px}
+.acct-row{display:flex;gap:22px;flex-wrap:wrap}
+.acct-row>div{display:flex;flex-direction:column;gap:5px}
+.acct-row span{color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.05em}
+.acct-foot{margin-top:12px;font-size:12px;color:#6b6b6b}
+</style></head><body><div class="wrap">
+<h1>AdSense Fleet Status</h1>
+<div class="stamp">🕓 Live-probed <code>${stamp}</code> · re-run <code>node build.mjs</code> to refresh</div>
+
+<div class="cards">
+  <div class="card"><b>${liveCount}/${adSurfaces}</b><span>Loader live on ad surfaces</span></div>
+  <div class="card"><b>0</b><span>Manual ad units live (fleet-wide)</span></div>
+  <div class="card"><b>pub-5278…3833</b><span>Primary account</span></div>
+</div>
+
+${acctCard}
+
+<table>
+<thead><tr><th>Project / Domain</th><th>Policy</th><th>Status</th><th>HTTP</th><th>Loader</th><th>ads.txt</th><th>Manual units</th><th>Pub id (live)</th></tr></thead>
+<tbody>
+${rows}
+</tbody></table>
+
+<div class="legend">
+<h2>How to read this</h2>
+<ul>
+<li><b>Loader live</b> — the <code>adsbygoogle.js</code> Auto-ads script is being served on the live domain (covers mobile + desktop from one responsive tag).</li>
+<li><b>Manual units</b> — named <code>&lt;ins&gt;</code> ad slots. <b>Zero are live fleet-wide</b> — every <code>__ADSLOTS</code> is empty, so only Auto ads is in play.</li>
+<li class="warn"><b>Not shown here: real ad serving.</b> Paid creative only paints if the AdSense account is <b>approved for that domain</b> AND <b>Auto ads is toggled ON</b> in the dashboard. That is account-side and can't be verified by probing — check <a href="https://adsense.google.com/" target="_blank" rel="noopener">adsense.google.com</a>.</li>
+</ul>
+</div>
+</div></body></html>`;
+
+await writeFile(new URL('./public/index.html', import.meta.url), html);
+await writeFile(new URL('./public/status.json', import.meta.url), JSON.stringify({ probedAt: now.toISOString(), results }, null, 2));
+console.log(`Wrote public/index.html — ${liveCount}/${adSurfaces} loader-live at ${stamp}`);
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..6484915
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,145 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<title>AdSense Fleet Status</title>
+<style>
+:root{--ink:#1a1a1a;--line:#e6e3dc;--muted:#9a9384}
+*{box-sizing:border-box}body{margin:0;font:15px/1.55 -apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;color:var(--ink);background:#f7f5f0}
+.wrap{max-width:1080px;margin:0 auto;padding:28px 20px 70px}
+h1{font-size:24px;margin:0 0 2px}.stamp{color:var(--muted);font-size:13px;margin-bottom:18px}
+.stamp code{background:#efeae1;padding:1px 6px;border-radius:5px}
+.cards{display:flex;gap:14px;flex-wrap:wrap;margin:16px 0 22px}
+.card{flex:1;min-width:150px;background:#fff;border:1px solid var(--line);border-radius:12px;padding:14px 16px}
+.card b{display:block;font-size:28px;line-height:1.1}.card span{color:var(--muted);font-size:12px;text-transform:uppercase;letter-spacing:.05em}
+table{width:100%;border-collapse:collapse;background:#fff;border:1px solid var(--line);border-radius:12px;overflow:hidden}
+th,td{text-align:left;padding:11px 13px;border-bottom:1px solid var(--line);vertical-align:top;font-size:13px}
+th{background:#efeae1;font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:#6b6b6b}
+tr:last-child td{border-bottom:none}
+.proj{font-weight:600}.sub{color:var(--muted);font-size:12px;margin-top:2px}.sub a{color:#1d7a36}
+.pill{display:inline-block;padding:3px 9px;border-radius:999px;font-size:12px;font-weight:600;white-space:nowrap}
+code{background:#f2efe8;padding:1px 5px;border-radius:4px;font-size:12px}
+a{color:#1d7a36}.muted{color:var(--muted)}
+.legend{margin-top:22px;background:#fff;border:1px solid var(--line);border-radius:12px;padding:16px 18px;font-size:13px}
+.legend h2{font-size:13px;text-transform:uppercase;letter-spacing:.06em;color:#6b6b6b;margin:0 0 8px}
+.legend li{margin:4px 0}.warn{color:#9a2323}
+.acct{background:#fff;border:1px solid var(--line);border-radius:12px;padding:16px 18px;margin:0 0 20px}
+.acct h2{font-size:13px;text-transform:uppercase;letter-spacing:.06em;color:#6b6b6b;margin:0 0 12px}
+.acct-row{display:flex;gap:22px;flex-wrap:wrap}
+.acct-row>div{display:flex;flex-direction:column;gap:5px}
+.acct-row span{color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.05em}
+.acct-foot{margin-top:12px;font-size:12px;color:#6b6b6b}
+</style></head><body><div class="wrap">
+<h1>AdSense Fleet Status</h1>
+<div class="stamp">🕓 Live-probed <code>Aug 5, 2026, 11:19 AM</code> · re-run <code>node build.mjs</code> to refresh</div>
+
+<div class="cards">
+  <div class="card"><b>3/6</b><span>Loader live on ad surfaces</span></div>
+  <div class="card"><b>0</b><span>Manual ad units live (fleet-wide)</span></div>
+  <div class="card"><b>pub-5278…3833</b><span>Primary account</span></div>
+</div>
+
+<div class="acct">
+  <h2>Account status — <span class="muted">dashboard-only, not probeable</span></h2>
+  <div class="acct-row">
+    <div><span>Approved</span><span class="pill" style="color:#a86a00;background:#fdf3e2">UNKNOWN</span></div>
+    <div><span>Auto ads ON</span><span class="pill" style="color:#a86a00;background:#fdf3e2">UNKNOWN</span></div>
+    <div><span>Actually serving</span><span class="pill" style="color:#a86a00;background:#fdf3e2">UNKNOWN</span></div>
+    <div><span>Pub id</span><code>pub-5278231299883833</code></div>
+  </div>
+  <div class="acct-foot"><span class="warn">Not yet checked.</span> 
+    <div class="muted">Fill these at <a href="https://adsense.google.com/" target="_blank" rel="noopener">adsense.google.com</a> → edit <code>account-status.json</code> → re-run <code>node build.mjs</code>.</div></div>
+</div>
+
+<table>
+<thead><tr><th>Project / Domain</th><th>Policy</th><th>Status</th><th>HTTP</th><th>Loader</th><th>ads.txt</th><th>Manual units</th><th>Pub id (live)</th></tr></thead>
+<tbody>
+<tr>
+    <td><span class="proj">CelebritySignatures</span><div class="sub"><a href="https://celebsignatures.com/" target="_blank" rel="noopener">celebsignatures.com</a></div></td>
+    <td><span class="pill" style="color:#0a7d33;background:#e6f4ea">✅ Ads OK</span></td>
+    <td><span class="pill" style="color:#0a7d33;background:#e9f6ee">● Loader live</span><div class="sub">Loader live</div></td>
+    <td>200</td>
+    <td><a href="view-source:https://celebsignatures.com/" target="_blank" rel="noopener">yes</a></td>
+    <td><a href="https://celebsignatures.com/ads.txt" target="_blank" rel="noopener"><code>pub-5278231299883833</code></a></td>
+    <td><span class="muted">none (Auto ads only)</span></td>
+    <td><code>pub-5278231299883833</code></td>
+  </tr>
+<tr>
+    <td><span class="proj">interiordesignershowroom</span><div class="sub"><a href="https://interiordesignershowroom.com/" target="_blank" rel="noopener">interiordesignershowroom.com</a></div></td>
+    <td><span class="pill" style="color:#9a2323;background:#fbeaea">⛔ No ads (luxury/commerce)</span><div class="sub warn">REMOVE pending approval</div></td>
+    <td><span class="pill" style="color:#0a7d33;background:#e9f6ee">● Loader live</span><div class="sub">Loader live</div></td>
+    <td>200</td>
+    <td><a href="view-source:https://interiordesignershowroom.com/" target="_blank" rel="noopener">yes</a></td>
+    <td><a href="https://interiordesignershowroom.com/ads.txt" target="_blank" rel="noopener"><code>pub-5278231299883833</code></a></td>
+    <td><span class="muted">none (Auto ads only)</span></td>
+    <td><code>pub-5278231299883833</code></td>
+  </tr>
+<tr>
+    <td><span class="proj">tesla (Charge & Explore)</span><div class="sub"><a href="https://chargeandexplore.com/" target="_blank" rel="noopener">chargeandexplore.com</a></div></td>
+    <td><span class="pill" style="color:#a86a00;background:#fdf3e2">⚠ Verify content first</span></td>
+    <td><span class="pill" style="color:#0a7d33;background:#e9f6ee">● Loader live</span><div class="sub">Loader live</div></td>
+    <td>200</td>
+    <td><a href="view-source:https://chargeandexplore.com/" target="_blank" rel="noopener">yes</a></td>
+    <td><a href="https://chargeandexplore.com/ads.txt" target="_blank" rel="noopener"><code>pub-5278231299883833</code></a></td>
+    <td><span class="muted">none (Auto ads only)</span></td>
+    <td><code>pub-5278231299883833</code></td>
+  </tr>
+<tr>
+    <td><span class="proj">ventura-corridor</span><div class="sub"><a href="https://venturacorridor.com/" target="_blank" rel="noopener">venturacorridor.com</a></div></td>
+    <td><span class="pill" style="color:#a86a00;background:#fdf3e2">⚠ Verify content first</span></td>
+    <td><span class="pill" style="color:#9a2323;background:#fbeaea">○ Not serving</span><div class="sub">No ads on live site</div></td>
+    <td>200</td>
+    <td>no</td>
+    <td><a href="https://venturacorridor.com/ads.txt" target="_blank" rel="noopener"><span class="muted">—</span></a></td>
+    <td><span class="muted">none (Auto ads only)</span></td>
+    <td><span class="muted">—</span></td>
+  </tr>
+<tr>
+    <td><span class="proj">allnewsdaily</span><div class="sub"><a href="https://allnewsdaily.com/" target="_blank" rel="noopener">allnewsdaily.com</a></div></td>
+    <td><span class="pill" style="color:#0a7d33;background:#e6f4ea">✅ Ads OK</span></td>
+    <td><span class="pill" style="color:#9a2323;background:#fbeaea">○ Not serving</span><div class="sub">No ads on live site</div></td>
+    <td>200</td>
+    <td>no</td>
+    <td><a href="https://allnewsdaily.com/ads.txt" target="_blank" rel="noopener"><span class="muted">—</span></a></td>
+    <td><span class="muted">none (Auto ads only)</span></td>
+    <td><span class="muted">—</span></td>
+  </tr>
+<tr>
+    <td><span class="proj">fashion-style-guides</span><div class="sub"><a href="https://fashionstyleguides.com/" target="_blank" rel="noopener">fashionstyleguides.com</a></div></td>
+    <td><span class="pill" style="color:#0a7d33;background:#e6f4ea">✅ Ads OK</span></td>
+    <td><span class="pill" style="color:#9a2323;background:#fbeaea">○ Not serving</span><div class="sub">No ads on live site</div></td>
+    <td>200</td>
+    <td>no</td>
+    <td><a href="https://fashionstyleguides.com/ads.txt" target="_blank" rel="noopener"><span class="muted">—</span></a></td>
+    <td><span class="muted">none (Auto ads only)</span></td>
+    <td><span class="muted">—</span></td>
+  </tr>
+<tr>
+    <td><span class="proj">restaurant-directory</span><div class="sub"><span class="muted">public domain unconfirmed</span></div></td>
+    <td><span class="pill" style="color:#0a7d33;background:#e6f4ea">✅ Ads OK</span></td>
+    <td><span class="pill" style="color:#5b5b5b;background:#eee">– N/A</span><div class="sub">No domain</div></td>
+    <td><span class="muted">—</span></td>
+    <td><span class="muted">—</span></td>
+    <td><span class="muted">—</span></td>
+    <td><span class="muted">none (Auto ads only)</span></td>
+    <td><span class="muted">—</span></td>
+  </tr>
+<tr>
+    <td><span class="proj">AbramsEgo</span><div class="sub"><span class="muted">internal command center (:9773) — not a public ad surface</span></div></td>
+    <td><span class="pill" style="color:#5b5b5b;background:#eee">– N/A</span></td>
+    <td><span class="pill" style="color:#5b5b5b;background:#eee">– N/A</span><div class="sub">N/A · internal</div></td>
+    <td><span class="muted">—</span></td>
+    <td><span class="muted">—</span></td>
+    <td><span class="muted">—</span></td>
+    <td><span class="muted">none (Auto ads only)</span></td>
+    <td><span class="muted">—</span></td>
+  </tr>
+</tbody></table>
+
+<div class="legend">
+<h2>How to read this</h2>
+<ul>
+<li><b>Loader live</b> — the <code>adsbygoogle.js</code> Auto-ads script is being served on the live domain (covers mobile + desktop from one responsive tag).</li>
+<li><b>Manual units</b> — named <code>&lt;ins&gt;</code> ad slots. <b>Zero are live fleet-wide</b> — every <code>__ADSLOTS</code> is empty, so only Auto ads is in play.</li>
+<li class="warn"><b>Not shown here: real ad serving.</b> Paid creative only paints if the AdSense account is <b>approved for that domain</b> AND <b>Auto ads is toggled ON</b> in the dashboard. That is account-side and can't be verified by probing — check <a href="https://adsense.google.com/" target="_blank" rel="noopener">adsense.google.com</a>.</li>
+</ul>
+</div>
+</div></body></html>
\ No newline at end of file
diff --git a/public/status.json b/public/status.json
new file mode 100644
index 0000000..070873c
--- /dev/null
+++ b/public/status.json
@@ -0,0 +1,98 @@
+{
+  "probedAt": "2026-08-05T18:19:52.140Z",
+  "results": [
+    {
+      "project": "CelebritySignatures",
+      "domain": "celebsignatures.com",
+      "policy": "allow",
+      "http": 200,
+      "loader": true,
+      "pubFromLoader": "pub-5278231299883833",
+      "adsTxt": "pub-5278231299883833",
+      "slots": [],
+      "err": null
+    },
+    {
+      "project": "interiordesignershowroom",
+      "domain": "interiordesignershowroom.com",
+      "policy": "forbid",
+      "flag": "REMOVE pending approval",
+      "http": 200,
+      "loader": true,
+      "pubFromLoader": "pub-5278231299883833",
+      "adsTxt": "pub-5278231299883833",
+      "slots": [],
+      "err": null
+    },
+    {
+      "project": "tesla (Charge & Explore)",
+      "domain": "chargeandexplore.com",
+      "policy": "verify",
+      "note": "app landing — thin page, keep only if not cluttering conversion",
+      "http": 200,
+      "loader": true,
+      "pubFromLoader": "pub-5278231299883833",
+      "adsTxt": "pub-5278231299883833",
+      "slots": [],
+      "err": null
+    },
+    {
+      "project": "ventura-corridor",
+      "domain": "venturacorridor.com",
+      "policy": "verify",
+      "note": "templated directory — needs real per-page content first",
+      "http": 200,
+      "loader": false,
+      "pubFromLoader": null,
+      "adsTxt": null,
+      "slots": [],
+      "err": null
+    },
+    {
+      "project": "allnewsdaily",
+      "domain": "allnewsdaily.com",
+      "policy": "allow",
+      "http": 200,
+      "loader": false,
+      "pubFromLoader": null,
+      "adsTxt": null,
+      "slots": [],
+      "err": null
+    },
+    {
+      "project": "fashion-style-guides",
+      "domain": "fashionstyleguides.com",
+      "policy": "allow",
+      "http": 200,
+      "loader": false,
+      "pubFromLoader": null,
+      "adsTxt": null,
+      "slots": [],
+      "err": null
+    },
+    {
+      "project": "restaurant-directory",
+      "domain": null,
+      "policy": "allow",
+      "note": "public domain unconfirmed",
+      "http": null,
+      "loader": false,
+      "pubFromLoader": null,
+      "adsTxt": null,
+      "slots": [],
+      "err": null
+    },
+    {
+      "project": "AbramsEgo",
+      "domain": null,
+      "policy": "na",
+      "note": "internal command center (:9773) — not a public ad surface",
+      "http": null,
+      "loader": false,
+      "pubFromLoader": null,
+      "adsTxt": null,
+      "slots": [],
+      "err": null
+    }
+  ]
+}
\ No newline at end of file

(oldest)  ·  back to Adsense Fleet Viewer  ·  Load all 8 AdSense account sites (steveabramsdesigns@gmail.c 3bc598e →