← back to Wallco Ai
Login modal (3-role scroll-trigger) + comprehensive PNG sync
f9750aca7491a346e10611c3d1ba04aa78c376fb · 2026-05-20 08:00:10 -0700 · Steve
Files touched
M data/do-not-want.jsonlM server.js
Diff
commit f9750aca7491a346e10611c3d1ba04aa78c376fb
Author: Steve <steve@designerwallcoverings.com>
Date: Wed May 20 08:00:10 2026 -0700
Login modal (3-role scroll-trigger) + comprehensive PNG sync
---
data/do-not-want.jsonl | 53 ++++++++++++++++++++++
server.js | 120 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 173 insertions(+)
diff --git a/data/do-not-want.jsonl b/data/do-not-want.jsonl
index ce2ad47..950164b 100644
--- a/data/do-not-want.jsonl
+++ b/data/do-not-want.jsonl
@@ -965,3 +965,56 @@
{"ts":"2026-05-20T14:05:37.563Z","design_id":24143,"category":"throttled-plaster-roses","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The roses have a very obvious faded duplicate behind them, with a halftone layer and atmospheric haze."},"source":"bleed_guard.ghost-layer"}
{"ts":"2026-05-20T14:05:57.395Z","design_id":24135,"category":"chinoiserie","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a faded duplicate of the trees and clouds behind the main motif, and atmospheric haze is present."},"source":"bleed_guard.ghost-layer"}
{"ts":"2026-05-20T14:06:06.339Z","design_id":24131,"category":"cactus","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a faded duplicate of the cactus motifs behind the main motifs, creating a double exposure look with atmospheric haze."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:40:09.604Z","design_id":24283,"category":"designer-zoo-calm","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":true,"has_atmospheric_haze":false,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a very obvious halftone layer behind the fox motif, creating a ghosting effect."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:40:17.363Z","design_id":24279,"category":"fromental-scenic","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a very obvious faded duplicate of the motif behind the main motif, along with a halftone layer and atmospheric haze."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:40:26.033Z","design_id":24275,"category":"designer-zoo-calm","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":false,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a very obvious faded ghost of the fox motif behind the main motif."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:40:32.488Z","design_id":24272,"category":"designer-zoo-calm · antique-tan","reason":"obvious bleed: halftone_layer, interior_shading_bleed","verdict":{"has_motif_ghost":false,"has_halftone_layer":true,"has_atmospheric_haze":false,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The motifs have a dot-screen layer behind them and crosshatching inside the silhouettes."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:40:34.814Z","design_id":24271,"category":"designer-zoo-calm","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":1,"one_line_reason":"There is a faded duplicate of the main motif behind itself, and atmospheric haze blurs the background."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:41:02.973Z","design_id":24256,"category":"cactus","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":1,"one_line_reason":"There is a very obvious faded ghost of the cactus motif behind the main motif, and a soft atmospheric haze."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:41:16.401Z","design_id":24247,"category":"cactus · bordeaux-velvet","reason":"obvious bleed: halftone_layer, interior_shading_bleed","verdict":{"has_motif_ghost":false,"has_halftone_layer":true,"has_atmospheric_haze":false,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a dot-screen layer behind the motifs, and interior shading inside the cactus silhouettes."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:41:18.514Z","design_id":24246,"category":"cactus · manor-saddle","reason":"obvious bleed: halftone_layer, interior_shading_bleed","verdict":{"has_motif_ghost":false,"has_halftone_layer":true,"has_atmospheric_haze":false,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":0.9,"one_line_reason":"There is a halftone layer behind the motifs and interior shading within the cactus silhouettes."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:41:21.235Z","design_id":24245,"category":"cactus","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":false,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":1,"one_line_reason":"There is a very obvious faded duplicate of the cactus motifs behind the main motifs."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:41:30.192Z","design_id":24241,"category":"chinoiserie","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The motif is repeated as a faded ghost offset behind itself, and there is atmospheric haze."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:41:39.166Z","design_id":24237,"category":"cactus","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a very obvious faded duplicate of the cactus motifs behind the main motifs, creating a double exposure effect and atmospheric haze."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:42:05.660Z","design_id":24225,"category":"face-skull-damask","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a faded duplicate of the main motif behind itself, and atmospheric haze blurs the background."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:45:09.910Z","design_id":24391,"category":"designer-zoo-calm","reason":"obvious bleed: halftone_layer, atmospheric_haze, interior_shading_bleed","verdict":{"has_motif_ghost":false,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The image has a halftone layer, atmospheric haze, and interior shading bleed, creating an obvious bleed effect."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:45:20.859Z","design_id":24386,"category":"muybridge-plate · slate-mist","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The motif has a very obvious ghosted duplicate, halftone layer, atmospheric haze, and interior shading bleed."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:45:23.226Z","design_id":24385,"category":"muybridge-plate · antique-brass","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The motif has a clear faded duplicate behind it, a halftone layer, atmospheric haze, and interior shading."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:45:25.526Z","design_id":24384,"category":"muybridge-plate · oxblood","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The pig motifs have a clear halftone layer and atmospheric haze behind them, creating a double exposure look."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:45:27.992Z","design_id":24383,"category":"muybridge-plate","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":1,"one_line_reason":"The main motif has a faded ghost / duplicate / echo offset behind itself, and there is atmospheric haze."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:45:45.521Z","design_id":24375,"category":"cactus","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":false,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":1,"one_line_reason":"There is a very obvious faded duplicate of the cactus motif behind the main motif."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:45:53.804Z","design_id":24371,"category":"muybridge-plate","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The image shows a clear faded duplicate of the motif, halftone layer, atmospheric haze, and interior shading bleed."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:46:12.562Z","design_id":24355,"category":"fromental-scenic · oxblood","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a clear faded duplicate of the floral motif behind the main design, along with a halftone layer and atmospheric haze."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:46:14.689Z","design_id":24354,"category":"fromental-scenic · forest-loden","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a faded duplicate of the motif behind the main motif, and atmospheric haze is present."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:46:16.787Z","design_id":24353,"category":"fromental-scenic · midnight-navy","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a clear faded duplicate of the floral motif behind the main one, along with a hazy background."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:46:18.842Z","design_id":24352,"category":"fromental-scenic","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a clear faded duplicate of the floral motif behind the main one, creating a double exposure effect with atmospheric haze."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:46:26.995Z","design_id":24348,"category":"cactus","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":false,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":1,"one_line_reason":"There is a faded duplicate of the cactus motif behind the main motif."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:46:35.805Z","design_id":24344,"category":"designer-zoo-calm","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":1,"one_line_reason":"There is a faded ghost of the motif behind the main motif, and atmospheric haze blurs the background."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:46:38.552Z","design_id":24343,"category":"muybridge-plate · antique-tan","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":false,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The motif has a very obvious ghosted duplicate and interior shading."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:46:55.331Z","design_id":24336,"category":"cactus","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":false,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":1,"one_line_reason":"There is a faded duplicate of the cactus motif behind the main motif."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:47:04.112Z","design_id":24332,"category":"cactus","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The cactus motifs have a faded ghost/duplicate behind them and interior shading, creating a double exposure look with atmospheric haze."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:47:12.713Z","design_id":24328,"category":"designer-zoo-calm","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a faded duplicate of the cat motif behind the main one, and a soft haze effect."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:50:19.351Z","design_id":24502,"category":"designer-zoo-calm","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":1,"one_line_reason":"There is a very obvious faded duplicate of the main motif behind itself, with a halftone layer and atmospheric haze."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:50:22.293Z","design_id":24498,"category":"chinoiserie · smoke-ash","reason":"obvious bleed: halftone_layer, atmospheric_haze, interior_shading_bleed","verdict":{"has_motif_ghost":false,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The image has a dot-screen layer, atmospheric haze, and interior shading, indicating motif bleed."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:50:24.621Z","design_id":24497,"category":"chinoiserie · champagne-linen","reason":"obvious bleed: halftone_layer, atmospheric_haze, interior_shading_bleed","verdict":{"has_motif_ghost":false,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The image has a dot-screen layer, atmospheric haze, and interior shading bleed."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:50:30.063Z","design_id":24495,"category":"chinoiserie","reason":"obvious bleed: halftone_layer, atmospheric_haze, interior_shading_bleed","verdict":{"has_motif_ghost":false,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The image has a blurred haze layer, interior shading, and a halftone layer behind the motif, indicating motif bleed."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:50:42.129Z","design_id":24489,"category":"chinoiserie · deep-plum","reason":"obvious bleed: halftone_layer, atmospheric_haze, interior_shading_bleed","verdict":{"has_motif_ghost":false,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The image has a halftone layer, atmospheric haze, and interior shading bleed, indicating motif bleed."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:51:11.467Z","design_id":24477,"category":"fromental-scenic · deep-plum","reason":"obvious bleed: halftone_layer, atmospheric_haze","verdict":{"has_motif_ghost":false,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a very obvious halftone layer behind the motif, creating an atmospheric haze effect."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:51:13.573Z","design_id":24476,"category":"fromental-scenic · saddle-mocha","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The motif has a very obvious halftone layer and atmospheric haze behind it, creating a ghosting effect."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:51:15.844Z","design_id":24475,"category":"fromental-scenic · antique-tan","reason":"obvious bleed: halftone_layer, atmospheric_haze","verdict":{"has_motif_ghost":false,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":1,"one_line_reason":"There is a very obvious halftone layer and atmospheric haze behind the motif."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:51:35.860Z","design_id":24466,"category":"chinoiserie","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The motif has a very obvious faded duplicate behind it, a halftone layer, atmospheric haze, and interior shading."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:51:53.697Z","design_id":24458,"category":"muybridge-plate","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a faded ghost of the motif behind the main figures, and a soft haze in the background."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:52:02.795Z","design_id":24454,"category":"designer-zoo-calm","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":false,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":1,"one_line_reason":"There is a very obvious faded ghost of the animal motifs behind the main motifs."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:52:11.977Z","design_id":24450,"category":"face-skull-damask","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a faded duplicate of the motif, a halftone layer, and atmospheric haze behind the main motif."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:55:18.374Z","design_id":24616,"category":"stripe","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The image has a clear double exposure look with atmospheric haze and interior shading bleed."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:55:28.790Z","design_id":24612,"category":"throttled-plaster-roses","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a very obvious faded duplicate of the rose motif behind the main motif, along with a halftone layer and atmospheric haze."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:55:37.651Z","design_id":24608,"category":"fromental-scenic","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a very obvious faded duplicate of the motif behind the main motif, and atmospheric haze."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:55:46.392Z","design_id":24604,"category":"throttled-plaster-roses","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a faded duplicate of the rose motif behind the main motif, and an atmospheric haze is present."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:56:03.866Z","design_id":24596,"category":"designer-zoo-calm","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The fox motif has a faded duplicate behind it, atmospheric haze, and interior shading."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:56:44.983Z","design_id":24577,"category":"designer-zoo-calm","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":1,"one_line_reason":"There is a faded ghost of the animal motif behind the main motif, and atmospheric haze blurs the background."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:56:49.269Z","design_id":24575,"category":"face-skull-damask · midnight-navy","reason":"obvious bleed: interior_shading_bleed","verdict":{"has_motif_ghost":false,"has_halftone_layer":false,"has_atmospheric_haze":false,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":1,"one_line_reason":"The skull motif has crosshatching inside the silhouette that should be flat."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:56:51.617Z","design_id":24574,"category":"face-skull-damask · deep-plum","reason":"obvious bleed: interior_shading_bleed","verdict":{"has_motif_ghost":false,"has_halftone_layer":false,"has_atmospheric_haze":false,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":1,"one_line_reason":"There is crosshatching inside the skull motif that should be flat."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:56:56.334Z","design_id":24572,"category":"face-skull-damask","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The skull motif has a very obvious faded duplicate behind it, along with halftone and atmospheric haze, and interior shading."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:57:04.886Z","design_id":24568,"category":"muybridge-plate · champagne-linen","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":true,"has_atmospheric_haze":false,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The motif has a clear ghost/duplicate behind it, a halftone layer, and interior shading bleed."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:57:09.572Z","design_id":24566,"category":"throttled-plaster-roses","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":false,"has_atmospheric_haze":true,"has_interior_shading_bleed":false,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"There is a very obvious faded duplicate of the rose motif behind the main motif, and an atmospheric haze."},"source":"bleed_guard.ghost-layer"}
+{"ts":"2026-05-20T14:57:13.734Z","design_id":24564,"category":"muybridge-plate","reason":"motif ghost layer (obvious)","verdict":{"has_motif_ghost":true,"has_halftone_layer":true,"has_atmospheric_haze":true,"has_interior_shading_bleed":true,"bleed_severity":"obvious","confidence":0.95,"one_line_reason":"The image shows a clear faded duplicate of the motif, halftone layer, atmospheric haze, and interior shading bleed."},"source":"bleed_guard.ghost-layer"}
diff --git a/server.js b/server.js
index 25a89b9..4a1e759 100644
--- a/server.js
+++ b/server.js
@@ -3401,7 +3401,82 @@ setInterval(updateNext, 1000);
});
// ── DESIGNS CATALOG (full grid with sort + density + category filter)
+// ── /designs role gate (2026-05-20) — login required to view ──
+// Steve directive: "require login https://wallco.ai/designs admin/user/designer"
+// Three roles, any one grants access. Admin path is the existing dw_auth JWT
+// cookie. User + designer roles set a `dw_role` cookie via /login.
+function readRole(req) {
+ // 1) Admin path — dw_auth JWT (existing)
+ try { if (isAdmin(req)) return 'admin'; } catch (_) {}
+ // 2) Lightweight role cookie (user / designer)
+ const raw = (req.headers && req.headers.cookie) || '';
+ for (const part of String(raw).split(/;\s*/)) {
+ const eq = part.indexOf('=');
+ if (eq > -1 && part.slice(0, eq) === 'dw_role') {
+ const v = decodeURIComponent(part.slice(eq + 1));
+ if (v === 'user' || v === 'designer' || v === 'admin') return v;
+ }
+ }
+ return null;
+}
+
+// /login — 3-role landing page. User/designer paths set dw_role cookie and
+// redirect back to /designs (or whatever ?next= specifies).
+app.get('/login', (req, res) => {
+ const next = String(req.query.next || '/designs').replace(/[^\w/?=&.\-]/g, '');
+ res.type('html').send(`<!doctype html>
+<html><head><meta charset="utf-8"><title>Wallco · Sign In</title>
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<style>
+ :root{--ink:#3a2818;--bg:#faf6ec;--accent:#c9a14b;--line:#d8cdb0;--soft:#8a7c66}
+ *{box-sizing:border-box}body{margin:0;padding:0;font:14px/1.5 ui-sans-serif,system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--ink);min-height:100vh;display:flex;align-items:center;justify-content:center}
+ .card{max-width:420px;width:90%;background:#fff;border:1px solid var(--line);border-radius:8px;padding:36px 32px;box-shadow:0 8px 24px rgba(60,40,20,.08)}
+ h1{margin:0 0 6px;font:300 32px/1.1 'Bodoni Moda',Georgia,serif;letter-spacing:.02em}
+ .sub{color:var(--soft);font-size:13px;margin-bottom:28px}
+ .row{display:flex;flex-direction:column;gap:10px;margin-top:12px}
+ button{appearance:none;padding:14px 18px;border:1px solid var(--line);background:#fff;color:var(--ink);font:600 13px/1 ui-sans-serif,system-ui;letter-spacing:.06em;text-transform:uppercase;border-radius:5px;cursor:pointer;transition:all .15s}
+ button:hover{border-color:var(--ink);background:var(--bg)}
+ button.primary{background:var(--ink);color:#fff;border-color:var(--ink)}
+ button.primary:hover{background:#0d0c0a}
+ button.accent{background:var(--accent);color:#fff;border-color:var(--accent)}
+ button.accent:hover{background:#a8843d}
+ .hint{color:var(--soft);font-size:11px;letter-spacing:.04em;margin-top:18px;text-align:center}
+ .role-label{font-size:10px;color:var(--soft);letter-spacing:.14em;text-transform:uppercase;margin-bottom:4px}
+ form{margin:0}
+</style></head>
+<body>
+<div class="card">
+ <h1>Wallco.ai</h1>
+ <div class="sub">Choose how you're accessing the design library.</div>
+ <div class="row">
+ <form method="POST" action="/login"><input type="hidden" name="role" value="designer"><input type="hidden" name="next" value="${next}"><button class="accent" type="submit" style="width:100%">★ Sign in as Designer / Trade</button></form>
+ <form method="POST" action="/login"><input type="hidden" name="role" value="user"><input type="hidden" name="next" value="${next}"><button type="submit" style="width:100%">Sign in as Customer</button></form>
+ <form method="POST" action="/login"><input type="hidden" name="role" value="admin"><input type="hidden" name="next" value="${next}"><button class="primary" type="submit" style="width:100%">Admin Login</button></form>
+ </div>
+ <div class="hint">Designer + Customer access is open during preview. Admin login routes through /_devlogin.</div>
+</div>
+</body></html>`);
+});
+app.post('/login', express.urlencoded({ extended: false, limit: '2kb' }), (req, res) => {
+ const role = String(req.body?.role || '').trim();
+ const next = String(req.body?.next || '/designs').replace(/[^\w/?=&.\-]/g, '');
+ if (role === 'admin') {
+ // Bounce to existing dev-login for real JWT mint
+ return res.redirect(`/_devlogin?next=${encodeURIComponent(next)}`);
+ }
+ if (role === 'user' || role === 'designer') {
+ res.cookie('dw_role', role, { httpOnly: false, maxAge: 30 * 24 * 3600 * 1000, sameSite: 'lax', path: '/' });
+ return res.redirect(next);
+ }
+ res.redirect('/login');
+});
+
app.get('/designs', (req, res) => {
+ // 2026-05-20 Steve directive: NO hard server gate. Allow 1 page browsing,
+ // then a client-side scroll-triggered modal fades in requiring login.
+ // The readRole(req) result is still passed to the page (as window._dwRole)
+ // so the JS knows whether to suppress the modal for already-logged-in users.
+ const _role = readRole(req);
// Conditional GET: emit Last-Modified from the freshest design in the
// in-memory catalog. Crawlers issuing If-Modified-Since get a 304 when
// no new design has landed since their last crawl.
@@ -4325,6 +4400,51 @@ ${_isAdminAuth ? `
` : ''}
${FOOTER}
+<!-- 2026-05-20 Steve directive: allow 1 page browsing, then scroll-triggered
+ login modal for general (un-roled) users. Suppressed for admin/user/designer. -->
+<div id="dw-login-veil" style="display:none;position:fixed;inset:0;background:rgba(20,18,15,.42);backdrop-filter:blur(6px);z-index:9998;opacity:0;transition:opacity .4s ease;pointer-events:none"></div>
+<div id="dw-login-modal" style="display:none;position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);z-index:9999;background:#fff;border:1px solid #d8cdb0;border-radius:10px;padding:36px 32px;width:92%;max-width:440px;box-shadow:0 16px 48px rgba(60,40,20,.22);opacity:0;transition:opacity .4s ease .1s;pointer-events:none">
+ <h2 style="margin:0 0 6px;font:300 30px/1.1 'Bodoni Moda',Georgia,serif;letter-spacing:.02em;color:#3a2818">Continue browsing</h2>
+ <div style="color:#8a7c66;font:13px/1.5 ui-sans-serif,system-ui;margin-bottom:24px">Sign in to access the full 12,000+ pattern library, save favorites, and place orders.</div>
+ <div style="display:flex;flex-direction:column;gap:10px">
+ <form method="POST" action="/login" style="margin:0"><input type="hidden" name="role" value="designer"><input type="hidden" name="next" value="/designs"><button type="submit" style="width:100%;appearance:none;padding:14px 18px;border:1px solid #c9a14b;background:#c9a14b;color:#fff;font:600 13px/1 ui-sans-serif,system-ui;letter-spacing:.06em;text-transform:uppercase;border-radius:5px;cursor:pointer">★ Sign in as Designer / Trade</button></form>
+ <form method="POST" action="/login" style="margin:0"><input type="hidden" name="role" value="user"><input type="hidden" name="next" value="/designs"><button type="submit" style="width:100%;appearance:none;padding:14px 18px;border:1px solid #d8cdb0;background:#fff;color:#3a2818;font:600 13px/1 ui-sans-serif,system-ui;letter-spacing:.06em;text-transform:uppercase;border-radius:5px;cursor:pointer">Sign in as Customer</button></form>
+ <form method="POST" action="/login" style="margin:0"><input type="hidden" name="role" value="admin"><input type="hidden" name="next" value="/designs"><button type="submit" style="width:100%;appearance:none;padding:14px 18px;border:1px solid #3a2818;background:#3a2818;color:#fff;font:600 13px/1 ui-sans-serif,system-ui;letter-spacing:.06em;text-transform:uppercase;border-radius:5px;cursor:pointer">Admin Login</button></form>
+ </div>
+ <div style="color:#8a7c66;font:11px/1.4 ui-sans-serif;letter-spacing:.04em;margin-top:18px;text-align:center">Designer + Customer access is open during preview.</div>
+</div>
+<script>
+(function(){
+ var role = ${JSON.stringify(_role || '')};
+ if (role) return; // logged-in users skip modal entirely
+ var veil = document.getElementById('dw-login-veil');
+ var modal = document.getElementById('dw-login-modal');
+ if (!veil || !modal) return;
+ var shown = false;
+ function show() {
+ if (shown) return;
+ shown = true;
+ veil.style.display = 'block';
+ modal.style.display = 'block';
+ requestAnimationFrame(function(){
+ veil.style.opacity = '1';
+ modal.style.opacity = '1';
+ veil.style.pointerEvents = 'auto';
+ modal.style.pointerEvents = 'auto';
+ document.body.style.overflow = 'hidden';
+ });
+ window.removeEventListener('scroll', onScroll);
+ }
+ function onScroll() {
+ // Trigger after user scrolls past ~1.2× viewport height (= roughly 1 page of designs)
+ if (window.scrollY > window.innerHeight * 1.2) show();
+ }
+ window.addEventListener('scroll', onScroll, { passive: true });
+ // Failsafe: also show after 30s on the page if they haven't scrolled
+ setTimeout(function(){ if (!shown) show(); }, 30000);
+})();
+</script>
+
<script type="application/ld+json">${schema}</script>
<script>
// ── Infinite scroll — fetch successive /designs?page=N HTML pages, extract
← 1e6e13a night-builder: take FIRST line of psql RETURNING output, not
·
back to Wallco Ai
·
night-builder: scrub ALL DW vendor names from prompt templat 48f26cf →