[object Object]

← back to Adsense Command Post

adsense command post: grid renderer + synced shots/data (TK-11322)

c2f837ff5db45e940a5f1c3ae21321257b3cacfb · 2026-09-09 16:34:55 -0700 · Steve Abrams

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit c2f837ff5db45e940a5f1c3ae21321257b3cacfb
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 9 16:34:55 2026 -0700

    adsense command post: grid renderer + synced shots/data (TK-11322)
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 data/audit-latest.json                    | 107 ++++++++++++++++++++-
 data/shots/1800wallcoverings.com.png      | Bin 0 -> 1278490 bytes
 data/shots/asseeninhotels.com.png         | Bin 0 -> 817498 bytes
 data/shots/asseeninla.com.png             | Bin 0 -> 1276046 bytes
 data/shots/asseeninmovies.com.png         | Bin 0 -> 262811 bytes
 data/shots/asseeninshowrooms.com.png      | Bin 0 -> 1283570 bytes
 data/shots/barwallpaper.com.png           | Bin 0 -> 1060050 bytes
 data/shots/blankstocklining.com.png       | Bin 0 -> 778924 bytes
 data/shots/carmelwallpaper.com.png        | Bin 0 -> 1583928 bytes
 data/shots/classawallcovering.com.png     | Bin 0 -> 1061227 bytes
 data/shots/commercialsalesreps.com.png    | Bin 0 -> 614519 bytes
 data/shots/commercialwallcovering.com.png | Bin 0 -> 614827 bytes
 data/shots/designermagnetics.com.png      | Bin 0 -> 1066114 bytes
 data/shots/etciemurals.com.png            | Bin 0 -> 1354102 bytes
 data/shots/fabricfridays.com.png          | Bin 0 -> 1278244 bytes
 data/shots/fireratedwallcovering.com.png  | Bin 0 -> 980608 bytes
 data/shots/handmadewallcovering.com.png   | Bin 0 -> 1583339 bytes
 data/shots/hospitalitysalesagency.com.png | Bin 0 -> 825937 bytes
 data/shots/montereywallpaper.com.png      | Bin 0 -> 1582853 bytes
 data/shots/naturalwalltextures.com.png    | Bin 0 -> 1584714 bytes
 data/shots/printmurals.com.png            | Bin 0 -> 1354006 bytes
 data/shots/restaurantfabrics.com.png      | Bin 0 -> 1276256 bytes
 data/shots/restaurantmurals.com.png       | Bin 0 -> 1356057 bytes
 data/shots/roomsettings.com.png           | Bin 0 -> 1278258 bytes
 data/shots/sheltermagazines.com.png       | Bin 0 -> 1277168 bytes
 data/shots/specifywallpaper.com.png       | Bin 0 -> 613605 bytes
 data/shots/thehotelwallpaper.com.png      | Bin 0 -> 816998 bytes
 data/shots/traditionalwhimsy.com.png      | Bin 0 -> 974069 bytes
 data/shots/unitedstateswallpaper.com.png  | Bin 0 -> 1280070 bytes
 data/shots/wallcovering.net.png           | Bin 0 -> 1275586 bytes
 data/shots/wallpaperexports.com.png       | Bin 0 -> 1279052 bytes
 data/shots/wallpaperfromthe80s.com.png    | Bin 0 -> 1032262 bytes
 data/shots/wallpaperny.com.png            | Bin 0 -> 1279465 bytes
 data/shots/wallpaperpurchasing.com.png    | Bin 0 -> 613122 bytes
 data/shots/wallpaperweekly.com.png        | Bin 0 -> 1278305 bytes
 data/shots/wc01wallcoverings.com.png      | Bin 0 -> 611804 bytes
 server.mjs                                | 154 +++++++++++++++++++-----------
 37 files changed, 204 insertions(+), 57 deletions(-)

diff --git a/data/audit-latest.json b/data/audit-latest.json
index 60c0bb1..7ca7477 100644
--- a/data/audit-latest.json
+++ b/data/audit-latest.json
@@ -1,11 +1,14 @@
 {
-  "ts": "2026-09-09T23:28:01.087Z",
+  "ts": "2026-09-09T23:31:41.522Z",
   "publisher": "pub-5278231299883833",
   "count": 35,
   "ready": 35,
   "results": [
     {
       "domain": "1800wallcoverings.com",
+      "name": "1-800 Wallcoverings",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/morris-acorn-morris-and-co-ochre-fabric-236827-image01.jpg?v=1788956952",
+      "shot": "/shots/1800wallcoverings.com.png",
       "words": 644,
       "passed": 12,
       "total": 12,
@@ -29,6 +32,9 @@
     },
     {
       "domain": "asseeninhotels.com",
+      "name": "As Seen In Hotels",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/Screenshot2026-08-24at11.27.02AM_3a1177ad-f0bb-42cc-b784-c27e73d8c85d.png?v=1787596059",
+      "shot": "/shots/asseeninhotels.com.png",
       "words": 610,
       "passed": 12,
       "total": 12,
@@ -52,6 +58,9 @@
     },
     {
       "domain": "asseeninla.com",
+      "name": "As Seen In LA",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/morris-acorn-morris-and-co-ochre-fabric-236827-image01.jpg?v=1788956952",
+      "shot": "/shots/asseeninla.com.png",
       "words": 645,
       "passed": 12,
       "total": 12,
@@ -75,6 +84,9 @@
     },
     {
       "domain": "asseeninmovies.com",
+      "name": "As Seen In Movies",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/0065257_vogue-black-and-metallic-gold_650.jpg?v=1776906916",
+      "shot": "/shots/asseeninmovies.com.png",
       "words": 630,
       "passed": 12,
       "total": 12,
@@ -98,6 +110,9 @@
     },
     {
       "domain": "asseeninshowrooms.com",
+      "name": "As Seen In Showrooms",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/morris-acorn-morris-and-co-ochre-fabric-236827-image01.jpg?v=1788956952",
+      "shot": "/shots/asseeninshowrooms.com.png",
       "words": 619,
       "passed": 12,
       "total": 12,
@@ -121,6 +136,9 @@
     },
     {
       "domain": "barwallpaper.com",
+      "name": "Bar Wallpaper",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/VV240_circles_SCARLET_1031x_2c1424d9-e2e8-456a-beb8-cd6ac0df55d9.webp?v=1781638431",
+      "shot": "/shots/barwallpaper.com.png",
       "words": 620,
       "passed": 12,
       "total": 12,
@@ -144,6 +162,9 @@
     },
     {
       "domain": "blankstocklining.com",
+      "name": "Blankstock Lining",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/8502GR.jpg?v=1785089689",
+      "shot": "/shots/blankstocklining.com.png",
       "words": 637,
       "passed": 12,
       "total": 12,
@@ -167,6 +188,9 @@
     },
     {
       "domain": "carmelwallpaper.com",
+      "name": "Carmel Wallpaper",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/dwnat-100153_2570b5ca-f3a2-41ab-a8a9-09d8a0a11bad.jpg?v=1788903844",
+      "shot": "/shots/carmelwallpaper.com.png",
       "words": 578,
       "passed": 12,
       "total": 12,
@@ -190,6 +214,9 @@
     },
     {
       "domain": "classawallcovering.com",
+      "name": "Class A Wallcovering",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/VV240_circles_SCARLET_1031x_2c1424d9-e2e8-456a-beb8-cd6ac0df55d9.webp?v=1781638431",
+      "shot": "/shots/classawallcovering.com.png",
       "words": 633,
       "passed": 12,
       "total": 12,
@@ -213,6 +240,9 @@
     },
     {
       "domain": "commercialsalesreps.com",
+      "name": "Commercial Sales Reps",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/7e15d64102f5502083f5aa8db2f95189.jpg?v=1572310604",
+      "shot": "/shots/commercialsalesreps.com.png",
       "words": 628,
       "passed": 12,
       "total": 12,
@@ -236,6 +266,9 @@
     },
     {
       "domain": "commercialwallcovering.com",
+      "name": "Commercial Wallcovering",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/7e15d64102f5502083f5aa8db2f95189.jpg?v=1572310604",
+      "shot": "/shots/commercialwallcovering.com.png",
       "words": 595,
       "passed": 12,
       "total": 12,
@@ -259,6 +292,9 @@
     },
     {
       "domain": "designermagnetics.com",
+      "name": "Designer Magnetics",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/ENV1242.jpg?v=1785091068",
+      "shot": "/shots/designermagnetics.com.png",
       "words": 650,
       "passed": 12,
       "total": 12,
@@ -282,6 +318,9 @@
     },
     {
       "domain": "etciemurals.com",
+      "name": "Et Cie Murals",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/R21147_interior1.jpg?v=1785862226",
+      "shot": "/shots/etciemurals.com.png",
       "words": 715,
       "passed": 12,
       "total": 12,
@@ -305,6 +344,9 @@
     },
     {
       "domain": "fabricfridays.com",
+      "name": "Fabric Fridays",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/morris-acorn-morris-and-co-ochre-fabric-236827-image01.jpg?v=1788956952",
+      "shot": "/shots/fabricfridays.com.png",
       "words": 719,
       "passed": 12,
       "total": 12,
@@ -328,6 +370,9 @@
     },
     {
       "domain": "fireratedwallcovering.com",
+      "name": "Fire-Rated Wallcovering",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/c8020e929ff4b8b73c7f99f0f1e91df5.jpg?v=1787151495",
+      "shot": "/shots/fireratedwallcovering.com.png",
       "words": 682,
       "passed": 12,
       "total": 12,
@@ -351,6 +396,9 @@
     },
     {
       "domain": "handmadewallcovering.com",
+      "name": "Hand-Made Wallcovering",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/dwnat-100153_2570b5ca-f3a2-41ab-a8a9-09d8a0a11bad.jpg?v=1788903844",
+      "shot": "/shots/handmadewallcovering.com.png",
       "words": 698,
       "passed": 12,
       "total": 12,
@@ -374,6 +422,9 @@
     },
     {
       "domain": "hospitalitysalesagency.com",
+      "name": "Hospitality Sales Agency",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/Screenshot2026-08-24at11.27.02AM_3a1177ad-f0bb-42cc-b784-c27e73d8c85d.png?v=1787596059",
+      "shot": "/shots/hospitalitysalesagency.com.png",
       "words": 732,
       "passed": 12,
       "total": 12,
@@ -397,6 +448,9 @@
     },
     {
       "domain": "montereywallpaper.com",
+      "name": "Monterey Wallpaper",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/dwnat-100153_2570b5ca-f3a2-41ab-a8a9-09d8a0a11bad.jpg?v=1788903844",
+      "shot": "/shots/montereywallpaper.com.png",
       "words": 673,
       "passed": 12,
       "total": 12,
@@ -420,6 +474,9 @@
     },
     {
       "domain": "naturalwalltextures.com",
+      "name": "Natural Wall Textures",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/dwnat-100153_2570b5ca-f3a2-41ab-a8a9-09d8a0a11bad.jpg?v=1788903844",
+      "shot": "/shots/naturalwalltextures.com.png",
       "words": 666,
       "passed": 12,
       "total": 12,
@@ -443,6 +500,9 @@
     },
     {
       "domain": "printmurals.com",
+      "name": "Print Murals",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/R21147_interior1.jpg?v=1785862226",
+      "shot": "/shots/printmurals.com.png",
       "words": 702,
       "passed": 12,
       "total": 12,
@@ -466,6 +526,9 @@
     },
     {
       "domain": "restaurantfabrics.com",
+      "name": "Restaurant Fabrics",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/morris-acorn-morris-and-co-ochre-fabric-236827-image01.jpg?v=1788956952",
+      "shot": "/shots/restaurantfabrics.com.png",
       "words": 683,
       "passed": 12,
       "total": 12,
@@ -489,6 +552,9 @@
     },
     {
       "domain": "restaurantmurals.com",
+      "name": "Restaurant Murals",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/R21147_interior1.jpg?v=1785862226",
+      "shot": "/shots/restaurantmurals.com.png",
       "words": 708,
       "passed": 12,
       "total": 12,
@@ -512,6 +578,9 @@
     },
     {
       "domain": "roomsettings.com",
+      "name": "Room Settings",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/morris-acorn-morris-and-co-ochre-fabric-236827-image01.jpg?v=1788956952",
+      "shot": "/shots/roomsettings.com.png",
       "words": 731,
       "passed": 12,
       "total": 12,
@@ -535,6 +604,9 @@
     },
     {
       "domain": "sheltermagazines.com",
+      "name": "Shelter Magazines",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/morris-acorn-morris-and-co-ochre-fabric-236827-image01.jpg?v=1788956952",
+      "shot": "/shots/sheltermagazines.com.png",
       "words": 793,
       "passed": 12,
       "total": 12,
@@ -558,6 +630,9 @@
     },
     {
       "domain": "specifywallpaper.com",
+      "name": "Specify Wallpaper",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/7e15d64102f5502083f5aa8db2f95189.jpg?v=1572310604",
+      "shot": "/shots/specifywallpaper.com.png",
       "words": 782,
       "passed": 12,
       "total": 12,
@@ -581,6 +656,9 @@
     },
     {
       "domain": "thehotelwallpaper.com",
+      "name": "The Hotel Wallpaper",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/Screenshot2026-08-24at11.27.02AM_3a1177ad-f0bb-42cc-b784-c27e73d8c85d.png?v=1787596059",
+      "shot": "/shots/thehotelwallpaper.com.png",
       "words": 755,
       "passed": 12,
       "total": 12,
@@ -604,6 +682,9 @@
     },
     {
       "domain": "traditionalwhimsy.com",
+      "name": "Traditional Whimsy",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/c8020e929ff4b8b73c7f99f0f1e91df5.jpg?v=1787151495",
+      "shot": "/shots/traditionalwhimsy.com.png",
       "words": 779,
       "passed": 12,
       "total": 12,
@@ -627,6 +708,9 @@
     },
     {
       "domain": "unitedstateswallpaper.com",
+      "name": "United States Wallpaper",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/morris-acorn-morris-and-co-ochre-fabric-236827-image01.jpg?v=1788956952",
+      "shot": "/shots/unitedstateswallpaper.com.png",
       "words": 734,
       "passed": 12,
       "total": 12,
@@ -650,6 +734,9 @@
     },
     {
       "domain": "wallcovering.net",
+      "name": "Wallcovering",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/morris-acorn-morris-and-co-ochre-fabric-236827-image01.jpg?v=1788956952",
+      "shot": "/shots/wallcovering.net.png",
       "words": 793,
       "passed": 12,
       "total": 12,
@@ -673,6 +760,9 @@
     },
     {
       "domain": "wallpaperexports.com",
+      "name": "Wallpaper Exports",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/morris-acorn-morris-and-co-ochre-fabric-236827-image01.jpg?v=1788956952",
+      "shot": "/shots/wallpaperexports.com.png",
       "words": 718,
       "passed": 12,
       "total": 12,
@@ -696,6 +786,9 @@
     },
     {
       "domain": "wallpaperfromthe80s.com",
+      "name": "Wallpaper From The 80s",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/VV248_retro_blocks_WHITESILVER_1050x_92d07f40-9743-41a3-abb0-7c689d407c74.jpg?v=1781638402",
+      "shot": "/shots/wallpaperfromthe80s.com.png",
       "words": 662,
       "passed": 12,
       "total": 12,
@@ -719,6 +812,9 @@
     },
     {
       "domain": "wallpaperny.com",
+      "name": "Wallpaper NY",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/morris-acorn-morris-and-co-ochre-fabric-236827-image01.jpg?v=1788956952",
+      "shot": "/shots/wallpaperny.com.png",
       "words": 667,
       "passed": 12,
       "total": 12,
@@ -742,6 +838,9 @@
     },
     {
       "domain": "wallpaperpurchasing.com",
+      "name": "Wallpaper Purchasing",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/7e15d64102f5502083f5aa8db2f95189.jpg?v=1572310604",
+      "shot": "/shots/wallpaperpurchasing.com.png",
       "words": 718,
       "passed": 12,
       "total": 12,
@@ -765,6 +864,9 @@
     },
     {
       "domain": "wallpaperweekly.com",
+      "name": "Wallpaper Weekly",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/morris-acorn-morris-and-co-ochre-fabric-236827-image01.jpg?v=1788956952",
+      "shot": "/shots/wallpaperweekly.com.png",
       "words": 704,
       "passed": 12,
       "total": 12,
@@ -788,6 +890,9 @@
     },
     {
       "domain": "wc01wallcoverings.com",
+      "name": "WC-01 Wallcoverings",
+      "hero": "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/7e15d64102f5502083f5aa8db2f95189.jpg?v=1572310604",
+      "shot": "/shots/wc01wallcoverings.com.png",
       "words": 685,
       "passed": 12,
       "total": 12,
diff --git a/data/shots/1800wallcoverings.com.png b/data/shots/1800wallcoverings.com.png
new file mode 100644
index 0000000..8d568dd
Binary files /dev/null and b/data/shots/1800wallcoverings.com.png differ
diff --git a/data/shots/asseeninhotels.com.png b/data/shots/asseeninhotels.com.png
new file mode 100644
index 0000000..07ad2ef
Binary files /dev/null and b/data/shots/asseeninhotels.com.png differ
diff --git a/data/shots/asseeninla.com.png b/data/shots/asseeninla.com.png
new file mode 100644
index 0000000..ee6a712
Binary files /dev/null and b/data/shots/asseeninla.com.png differ
diff --git a/data/shots/asseeninmovies.com.png b/data/shots/asseeninmovies.com.png
new file mode 100644
index 0000000..128be2c
Binary files /dev/null and b/data/shots/asseeninmovies.com.png differ
diff --git a/data/shots/asseeninshowrooms.com.png b/data/shots/asseeninshowrooms.com.png
new file mode 100644
index 0000000..9e6af26
Binary files /dev/null and b/data/shots/asseeninshowrooms.com.png differ
diff --git a/data/shots/barwallpaper.com.png b/data/shots/barwallpaper.com.png
new file mode 100644
index 0000000..9bb8b67
Binary files /dev/null and b/data/shots/barwallpaper.com.png differ
diff --git a/data/shots/blankstocklining.com.png b/data/shots/blankstocklining.com.png
new file mode 100644
index 0000000..6adb94c
Binary files /dev/null and b/data/shots/blankstocklining.com.png differ
diff --git a/data/shots/carmelwallpaper.com.png b/data/shots/carmelwallpaper.com.png
new file mode 100644
index 0000000..e7162af
Binary files /dev/null and b/data/shots/carmelwallpaper.com.png differ
diff --git a/data/shots/classawallcovering.com.png b/data/shots/classawallcovering.com.png
new file mode 100644
index 0000000..cb4283c
Binary files /dev/null and b/data/shots/classawallcovering.com.png differ
diff --git a/data/shots/commercialsalesreps.com.png b/data/shots/commercialsalesreps.com.png
new file mode 100644
index 0000000..beb727f
Binary files /dev/null and b/data/shots/commercialsalesreps.com.png differ
diff --git a/data/shots/commercialwallcovering.com.png b/data/shots/commercialwallcovering.com.png
new file mode 100644
index 0000000..ab5c0a9
Binary files /dev/null and b/data/shots/commercialwallcovering.com.png differ
diff --git a/data/shots/designermagnetics.com.png b/data/shots/designermagnetics.com.png
new file mode 100644
index 0000000..1f9ce8b
Binary files /dev/null and b/data/shots/designermagnetics.com.png differ
diff --git a/data/shots/etciemurals.com.png b/data/shots/etciemurals.com.png
new file mode 100644
index 0000000..af4dff2
Binary files /dev/null and b/data/shots/etciemurals.com.png differ
diff --git a/data/shots/fabricfridays.com.png b/data/shots/fabricfridays.com.png
new file mode 100644
index 0000000..2e622d2
Binary files /dev/null and b/data/shots/fabricfridays.com.png differ
diff --git a/data/shots/fireratedwallcovering.com.png b/data/shots/fireratedwallcovering.com.png
new file mode 100644
index 0000000..8edb44d
Binary files /dev/null and b/data/shots/fireratedwallcovering.com.png differ
diff --git a/data/shots/handmadewallcovering.com.png b/data/shots/handmadewallcovering.com.png
new file mode 100644
index 0000000..22eaf14
Binary files /dev/null and b/data/shots/handmadewallcovering.com.png differ
diff --git a/data/shots/hospitalitysalesagency.com.png b/data/shots/hospitalitysalesagency.com.png
new file mode 100644
index 0000000..7eddd46
Binary files /dev/null and b/data/shots/hospitalitysalesagency.com.png differ
diff --git a/data/shots/montereywallpaper.com.png b/data/shots/montereywallpaper.com.png
new file mode 100644
index 0000000..05a8633
Binary files /dev/null and b/data/shots/montereywallpaper.com.png differ
diff --git a/data/shots/naturalwalltextures.com.png b/data/shots/naturalwalltextures.com.png
new file mode 100644
index 0000000..d0e7a24
Binary files /dev/null and b/data/shots/naturalwalltextures.com.png differ
diff --git a/data/shots/printmurals.com.png b/data/shots/printmurals.com.png
new file mode 100644
index 0000000..d1081ac
Binary files /dev/null and b/data/shots/printmurals.com.png differ
diff --git a/data/shots/restaurantfabrics.com.png b/data/shots/restaurantfabrics.com.png
new file mode 100644
index 0000000..504f95f
Binary files /dev/null and b/data/shots/restaurantfabrics.com.png differ
diff --git a/data/shots/restaurantmurals.com.png b/data/shots/restaurantmurals.com.png
new file mode 100644
index 0000000..0b45901
Binary files /dev/null and b/data/shots/restaurantmurals.com.png differ
diff --git a/data/shots/roomsettings.com.png b/data/shots/roomsettings.com.png
new file mode 100644
index 0000000..69bb03a
Binary files /dev/null and b/data/shots/roomsettings.com.png differ
diff --git a/data/shots/sheltermagazines.com.png b/data/shots/sheltermagazines.com.png
new file mode 100644
index 0000000..ea15b61
Binary files /dev/null and b/data/shots/sheltermagazines.com.png differ
diff --git a/data/shots/specifywallpaper.com.png b/data/shots/specifywallpaper.com.png
new file mode 100644
index 0000000..81d3a77
Binary files /dev/null and b/data/shots/specifywallpaper.com.png differ
diff --git a/data/shots/thehotelwallpaper.com.png b/data/shots/thehotelwallpaper.com.png
new file mode 100644
index 0000000..eb0eb94
Binary files /dev/null and b/data/shots/thehotelwallpaper.com.png differ
diff --git a/data/shots/traditionalwhimsy.com.png b/data/shots/traditionalwhimsy.com.png
new file mode 100644
index 0000000..f3757c3
Binary files /dev/null and b/data/shots/traditionalwhimsy.com.png differ
diff --git a/data/shots/unitedstateswallpaper.com.png b/data/shots/unitedstateswallpaper.com.png
new file mode 100644
index 0000000..e3ad383
Binary files /dev/null and b/data/shots/unitedstateswallpaper.com.png differ
diff --git a/data/shots/wallcovering.net.png b/data/shots/wallcovering.net.png
new file mode 100644
index 0000000..afdb86c
Binary files /dev/null and b/data/shots/wallcovering.net.png differ
diff --git a/data/shots/wallpaperexports.com.png b/data/shots/wallpaperexports.com.png
new file mode 100644
index 0000000..e5326c7
Binary files /dev/null and b/data/shots/wallpaperexports.com.png differ
diff --git a/data/shots/wallpaperfromthe80s.com.png b/data/shots/wallpaperfromthe80s.com.png
new file mode 100644
index 0000000..b53ba75
Binary files /dev/null and b/data/shots/wallpaperfromthe80s.com.png differ
diff --git a/data/shots/wallpaperny.com.png b/data/shots/wallpaperny.com.png
new file mode 100644
index 0000000..5cdfef2
Binary files /dev/null and b/data/shots/wallpaperny.com.png differ
diff --git a/data/shots/wallpaperpurchasing.com.png b/data/shots/wallpaperpurchasing.com.png
new file mode 100644
index 0000000..26116f3
Binary files /dev/null and b/data/shots/wallpaperpurchasing.com.png differ
diff --git a/data/shots/wallpaperweekly.com.png b/data/shots/wallpaperweekly.com.png
new file mode 100644
index 0000000..8e775a9
Binary files /dev/null and b/data/shots/wallpaperweekly.com.png differ
diff --git a/data/shots/wc01wallcoverings.com.png b/data/shots/wc01wallcoverings.com.png
new file mode 100644
index 0000000..6660e74
Binary files /dev/null and b/data/shots/wc01wallcoverings.com.png differ
diff --git a/server.mjs b/server.mjs
index 5c7ac18..1d56a4c 100644
--- a/server.mjs
+++ b/server.mjs
@@ -1,7 +1,8 @@
 #!/usr/bin/env node
-// AdSense Manager dashboard — Basic-auth page showing, per monetize domain:
-// approval status (live console), readiness audit %, ads.txt state, and blockers.
-// Reads data/audit-latest.json + data/status-latest.json. Port: env PORT or 9971.
+// AdSense Manager command post — Basic-auth GRID of all monetize sites (thumbnail +
+// approval status + readiness + ads.txt), plus a table view. Sort + density slider
+// (localStorage-persisted). Reads data/audit-latest.json + data/status-latest.json,
+// serves screenshots from data/shots/. Port: env PORT or 9971.
 import http from 'http';
 import fs from 'fs';
 import path from 'path';
@@ -9,75 +10,111 @@ import { fileURLToPath } from 'url';
 import { execFile } from 'child_process';
 
 const __dirname = path.dirname(fileURLToPath(import.meta.url));
-const SKILL = __dirname; // command post: reads ./data synced from Mac2
+const SKILL = __dirname; // command post: ./data synced from Mac2
+const DATA = path.join(SKILL, 'data');
+const SHOTS = path.join(DATA, 'shots');
 const PORT = process.env.PORT || 9971;
 const USER = 'admin', PASS = 'DW2024!';
-const read = f => { try { return JSON.parse(fs.readFileSync(path.join(SKILL, 'data', f), 'utf8')); } catch { return null; } };
+const read = f => { try { return JSON.parse(fs.readFileSync(path.join(DATA, f), 'utf8')); } catch { return null; } };
 const esc = s => String(s == null ? '' : s).replace(/[&<>"]/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]));
 
-function page() {
-  const audit = read('audit-latest.json') || { results: [], ready: 0, count: 0, ts: '' };
+function data() {
+  const audit = read('audit-latest.json') || { results: [], ready: 0, ts: '' };
   const status = read('status-latest.json') || { rows: [], tally: {}, ts: '', source: '?' };
   const sMap = Object.fromEntries((status.rows || []).map(r => [r.domain, r]));
-  const rows = (audit.results || []).map(a => ({ ...a, ...(sMap[a.domain] || {}) }))
-    .sort((a, b) => (a.approval === 'Ready' ? -1 : 0) - (b.approval === 'Ready' ? -1 : 0) || a.readiness - b.readiness);
-  const badge = s => {
-    const m = { 'Ready': ['#16794a', '#e6f2ea'], 'Getting ready': ['#a9761a', '#f6eeda'], 'Requires review': ['#5b6470', '#eef0f3'], 'Needs attention': ['#b0413a', '#f7e7e5'] };
-    const [c, b] = m[s] || ['#8a8f98', '#eef0f2'];
-    return `<span class="pill" style="color:${c};background:${b}">${esc(s || '—')}</span>`;
-  };
-  const tr = r => `<tr>
-    <td class="dom">${esc(r.domain)}</td>
-    <td>${badge(r.approval)}</td>
-    <td class="num ${r.ready ? 'ok' : 'warn'}">${r.readiness}%</td>
-    <td class="ads ${/(Authorized)/i.test(r.ads_txt) ? 'ok' : 'muted'}">${esc(r.ads_txt || '—')}</td>
-    <td class="blk">${r.blockers && r.blockers.length ? esc(r.blockers.join('; ')) : '<span class="ok">all checks pass</span>'}</td></tr>`;
+  const rows = (audit.results || []).map(a => ({ ...a, approval: (sMap[a.domain] || {}).approval || 'UNKNOWN', ads_txt: (sMap[a.domain] || {}).ads_txt || '—' }));
+  return { rows, audit, status };
+}
+
+function page() {
+  const { rows, audit, status } = data();
   const tally = status.tally || {};
+  const shotExists = d => { try { return fs.existsSync(path.join(SHOTS, d + '.png')); } catch { return false; } };
+  const cards = rows.map(r => {
+    const thumb = shotExists(r.domain) ? `/shots/${encodeURIComponent(r.domain)}.png` : (r.hero || '');
+    return { ...r, thumb };
+  });
+  const DATAJSON = JSON.stringify(cards.map(r => ({ domain: r.domain, name: r.name, thumb: r.thumb, approval: r.approval, ads_txt: r.ads_txt, readiness: r.readiness, ready: r.ready, blockers: r.blockers || [] })));
   const tile = (n, l, cls = '') => `<div class="tile ${cls}"><div class="n">${n}</div><div class="l">${l}</div></div>`;
   return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
-<title>AdSense Manager</title><style>
-:root{--bg:#f6f5f2;--panel:#fff;--ink:#1b1917;--muted:#6b6560;--rule:#e6e2db;--accent:#2f7a4f;--mono:ui-monospace,Menlo,monospace}
+<title>AdSense Command Post</title><style>
+:root{--bg:#f5f4f1;--panel:#fff;--ink:#1b1917;--muted:#6b6560;--rule:#e5e1da;--accent:#2f7a4f;--mono:ui-monospace,Menlo,monospace;--cols:5}
 *{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.5 -apple-system,'Segoe UI',Roboto,sans-serif}
-.wrap{max-width:1150px;margin:0 auto;padding:28px 22px 70px}
-h1{font-size:23px;margin:0 0 3px}.sub{color:var(--muted);font-size:13px;margin-bottom:18px}
-.tiles{display:grid;grid-template-columns:repeat(5,1fr);gap:11px;margin-bottom:18px}
+.wrap{max-width:1320px;margin:0 auto;padding:26px 20px 80px}
+h1{font-size:23px;margin:0 0 3px}.sub{color:var(--muted);font-size:13px;margin-bottom:16px}
+.tiles{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin-bottom:16px}
 @media(max-width:720px){.tiles{grid-template-columns:repeat(2,1fr)}}
-.tile{background:var(--panel);border:1px solid var(--rule);border-radius:9px;padding:13px 15px}
-.tile .n{font-size:25px;font-weight:600}.tile .l{font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);margin-top:6px}
-.tile.ready .n{color:#16794a}
-.bar{display:flex;gap:10px;align-items:center;margin-bottom:12px}
-button{font:inherit;background:var(--accent);color:#fff;border:0;border-radius:7px;padding:8px 15px;cursor:pointer}
-button:disabled{opacity:.5}
-table{width:100%;border-collapse:collapse;background:var(--panel);border:1px solid var(--rule);border-radius:10px;overflow:hidden;font-size:13px}
-th{text-align:left;font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);padding:10px 13px;border-bottom:1px solid var(--rule)}
-td{padding:9px 13px;border-bottom:1px solid #f0ede8;vertical-align:top}
-tr:last-child td{border-bottom:0}
-.dom{font-family:var(--mono);font-weight:600;white-space:nowrap}
-.pill{font-size:11px;font-weight:600;padding:2px 9px;border-radius:5px;white-space:nowrap}
-.num{font-weight:600;font-variant-numeric:tabular-nums}.num.ok{color:#16794a}.num.warn{color:#b0413a}
-.ads.ok{color:#16794a}.ads.muted{color:var(--muted)}.blk{color:var(--muted);max-width:420px}
-.ok{color:#16794a}.foot{color:var(--muted);font-size:12px;margin-top:16px;line-height:1.7}
+.tile{background:var(--panel);border:1px solid var(--rule);border-radius:9px;padding:12px 14px}
+.tile .n{font-size:24px;font-weight:600}.tile .l{font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);margin-top:5px}
+.tile.ready .n{color:#16794a}.tile.rev .n{color:#5b6470}
+.ctl{display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-bottom:16px}
+.ctl label{font-size:12px;color:var(--muted);text-transform:uppercase;letter-spacing:.05em}
+select,button{font:inherit;border:1px solid var(--rule);background:var(--panel);color:var(--ink);border-radius:7px;padding:8px 11px;cursor:pointer}
+button.pri{background:var(--accent);color:#fff;border-color:var(--accent)}
+input[type=range]{accent-color:var(--accent)}
+.grid{display:grid;grid-template-columns:repeat(var(--cols),1fr);gap:16px}
+.card{background:var(--panel);border:1px solid var(--rule);border-radius:11px;overflow:hidden;display:flex;flex-direction:column;transition:transform .15s,box-shadow .15s}
+.card:hover{transform:translateY(-3px);box-shadow:0 12px 28px rgba(0,0,0,.12)}
+.thumb{aspect-ratio:1.4/1;background:#e9e6e0 center/cover no-repeat;position:relative;border-bottom:1px solid var(--rule)}
+.thumb .badge{position:absolute;top:8px;left:8px;font-size:10.5px;font-weight:700;padding:3px 8px;border-radius:5px;color:#fff;letter-spacing:.02em}
+.thumb .rd{position:absolute;top:8px;right:8px;font-size:10.5px;font-weight:700;padding:3px 7px;border-radius:5px;background:rgba(0,0,0,.62);color:#fff;font-variant-numeric:tabular-nums}
+.card .body{padding:10px 12px 12px}
+.card .nm{font-weight:600;font-size:13.5px;line-height:1.2}
+.card .dm{font-family:var(--mono);font-size:11.5px;color:var(--muted);margin-top:2px;word-break:break-all}
+.card .meta{display:flex;gap:8px;align-items:center;margin-top:8px;font-size:11px;color:var(--muted)}
+.card a{color:var(--accent);text-decoration:none}
+.noshot{display:flex;align-items:center;justify-content:center;height:100%;color:var(--muted);font-size:12px}
+.foot{color:var(--muted);font-size:12px;margin-top:22px;line-height:1.7}
 </style></head><body><div class="wrap">
-<h1>AdSense Manager</h1>
-<div class="sub">Publisher pub-5278231299883833 · ${(audit.results||[]).length} monetize domains · audit ${esc((audit.ts||'').slice(0,16))}Z · status ${esc(status.source)} ${esc((status.ts||'').slice(11,16))}</div>
+<h1>AdSense Command Post</h1>
+<div class="sub">pub-5278231299883833 · ${audit.results ? audit.results.length : 0} monetize sites · audit ${esc((audit.ts||'').slice(0,16))}Z · console ${esc((status.ts||'').slice(11,16))}</div>
 <div class="tiles">
-  ${tile((audit.results||[]).length, 'Sites')}
+  ${tile(audit.results ? audit.results.length : 0, 'Sites')}
   ${tile(audit.ready ?? 0, 'Audit 100%', 'ready')}
   ${tile(tally['Ready'] || 0, 'Approved', 'ready')}
-  ${tile(tally['Requires review'] || 0, 'In review')}
-  ${tile((tally['Needs attention'] || 0), 'Needs attention')}
+  ${tile(tally['Requires review'] || 0, 'In review', 'rev')}
+  ${tile(tally['Needs attention'] || 0, 'Needs attention')}
+</div>
+<div class="ctl">
+  <label>Sort</label>
+  <select id="sort"><option value="approval">Approval</option><option value="readiness">Readiness</option><option value="domain">Domain A→Z</option><option value="ads">ads.txt</option></select>
+  <label>Density</label><input type="range" id="dens" min="2" max="7" value="5">
+  <span style="flex:1"></span>
+  <button class="pri" onclick="rc(this)">Re-check</button><span class="sub" id="msg"></span>
 </div>
-<div class="bar"><button onclick="rc(this)">Re-check now</button><span class="sub" id="msg"></span></div>
-<table><thead><tr><th>Domain</th><th>AdSense status</th><th>Readiness</th><th>ads.txt (Google)</th><th>Blockers</th></tr></thead>
-<tbody>${rows.map(tr).join('')}</tbody></table>
-<p class="foot"><b>Readiness</b> = our own audit vs the AdSense approval checklist (content, required pages, ads.txt, code, no for-sale banner). 100% = nothing on our side blocks approval.
-<b>AdSense status</b> = live from your console; "Requires review" is normal until Google finishes its review. ads.txt "Not found" clears once Google re-crawls (it's live).
-Re-check re-runs the audit + reads the console.</p>
+<div class="grid" id="grid"></div>
+<p class="foot"><b>Grid</b> shows every monetize site with a live screenshot (ads render in the shot once a site is AdSense-approved). Badge = AdSense status, corner = our readiness audit. "Requires review" is normal until Google finishes review; ads.txt "Not found" clears when Google re-crawls (it's live). Sort + density persist. Re-check re-runs the audit + reads the console.</p>
 </div>
 <script>
-async function rc(b){b.disabled=true;document.getElementById('msg').textContent='re-checking…';
- try{const r=await fetch('/api/refresh',{method:'POST'});await r.json();location.reload();}catch(e){document.getElementById('msg').textContent='error';b.disabled=false;}}
-setTimeout(()=>location.reload(),120000);
+const DATA=${DATAJSON};
+const BADGE={'Ready':'#16794a','Getting ready':'#a9761a','Requires review':'#5b6470','Needs attention':'#b0413a','UNKNOWN':'#8a8f98'};
+const g=document.getElementById('grid'),sortSel=document.getElementById('sort'),dens=document.getElementById('dens');
+try{const s=localStorage.getItem('acp_sort');if(s)sortSel.value=s;const d=localStorage.getItem('acp_dens');if(d)dens.value=d;}catch(e){}
+document.documentElement.style.setProperty('--cols',dens.value);
+const order={'Ready':0,'Needs attention':1,'Getting ready':2,'Requires review':3,'UNKNOWN':4};
+function render(){
+  const k=sortSel.value;
+  let r=DATA.slice();
+  r.sort((a,b)=> k==='approval'?(order[a.approval]-order[b.approval]||a.domain.localeCompare(b.domain))
+    : k==='readiness'?(a.readiness-b.readiness||a.domain.localeCompare(b.domain))
+    : k==='ads'?String(a.ads_txt).localeCompare(String(b.ads_txt))
+    : a.domain.localeCompare(b.domain));
+  g.innerHTML=r.map(x=>{
+    const th=x.thumb?('background-image:url("'+x.thumb+'")'):'';
+    const badge=BADGE[x.approval]||'#8a8f98';
+    return '<div class="card"><a href="https://'+x.domain+'" target="_blank" rel="noopener">'
+      +'<div class="thumb" style="'+th+'">'+(x.thumb?'':'<div class=noshot>no preview</div>')
+      +'<span class="badge" style="background:'+badge+'">'+x.approval+'</span>'
+      +'<span class="rd">'+x.readiness+'%</span></div></a>'
+      +'<div class="body"><div class="nm">'+x.name+'</div><div class="dm">'+x.domain+'</div>'
+      +'<div class="meta">ads.txt: '+x.ads_txt+' · <a href="https://'+x.domain+'" target="_blank" rel="noopener">open ↗</a></div></div></div>';
+  }).join('');
+}
+sortSel.onchange=()=>{try{localStorage.setItem('acp_sort',sortSel.value)}catch(e){}render()};
+dens.oninput=()=>{document.documentElement.style.setProperty('--cols',dens.value);try{localStorage.setItem('acp_dens',dens.value)}catch(e){}};
+async function rc(b){b.disabled=true;document.getElementById('msg').textContent='re-checking…';try{await fetch('/api/refresh',{method:'POST'});location.reload();}catch(e){b.disabled=false;document.getElementById('msg').textContent='err';}}
+setTimeout(()=>location.reload(),180000);
+render();
 </script></body></html>`;
 }
 
@@ -85,6 +122,11 @@ http.createServer((req, res) => {
   const auth = (req.headers.authorization || '').split(' ')[1] || '';
   const [u, p] = Buffer.from(auth, 'base64').toString().split(':');
   if (u !== USER || p !== PASS) { res.writeHead(401, { 'WWW-Authenticate': 'Basic realm="adsense"' }); return res.end('Auth required'); }
-  if (req.method === 'POST' && req.url === '/api/refresh') { res.writeHead(200,{'content-type':'application/json'}); return res.end('{"ok":true,"note":"data refreshed by the Mac2 poller every 6h"}'); }
+  if (req.url.startsWith('/shots/')) {
+    const f = path.join(SHOTS, path.basename(decodeURIComponent(req.url.slice(7))));
+    if (f.endsWith('.png') && fs.existsSync(f)) { res.writeHead(200, { 'content-type': 'image/png', 'cache-control': 'public,max-age=300' }); return fs.createReadStream(f).pipe(res); }
+    res.writeHead(404); return res.end();
+  }
+  if (req.method === 'POST' && req.url === '/api/refresh'){res.writeHead(200,{'content-type':'application/json'});return res.end('{"ok":true,"note":"refreshed by Mac2 poller"}');}
   res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' }); res.end(page());
-}).listen(PORT, () => console.log(`AdSense Manager dashboard on http://127.0.0.1:${PORT} (admin/DW2024!)`));
+}).listen(PORT, () => console.log(`AdSense Command Post on http://127.0.0.1:${PORT} (admin/DW2024!)`));

← bf278cf adsense command post: dashboard app reading synced audit/sta  ·  back to Adsense Command Post  ·  adsense command post: Kamatera remote-deploy script (pm2+ngi be41f3f →