{"slug":"naturaltextilewallpaper","total":27,"limit":100,"offset":0,"since":null,"commits":[{"hash":"4d090cb","date":"2026-07-03 09:57:42 -0700","author":"Steve Abrams","subject":"corner-nav: move login to its own hamburger in upper-left (panel slides from left)","body":""},{"hash":"9148909","date":"2026-06-21 17:04:14 -0700","author":"Steve","subject":"deploy: scaffold .deploy.conf for banner deploy","body":""},{"hash":"6e82e1f","date":"2026-06-21 16:48:56 -0700","author":"Steve","subject":"banner: wire universal promo-banner require above express.static (officer-approved; deploy gated)","body":""},{"hash":"d479737","date":"2026-06-11 09:30:15 -0700","author":"Steve Abrams","subject":"Recompute aesthetic from tags via classifyAesthetic (was degenerate 'all' snapshot)","body":""},{"hash":"13c1284","date":"2026-06-04 08:09:49 -0700","author":"Steve","subject":"seo: add per-page canonical + Open Graph + Twitter card (headHTML threads canonical+ogImage; index collapses facets to material per DTD-B, detail uses redacted product slug)","body":""},{"hash":"2f5369f","date":"2026-06-03 20:45:27 -0700","author":"Steve","subject":"Scrub residual vendor names from products.json (title/vendor/tags); preserve sku/handle","body":""},{"hash":"2895d20","date":"2026-06-03 14:30:24 -0700","author":"Steve Abrams","subject":"Vendor-neutral image proxy via _shared/image-proxy: hero+detail render + /api/products+/api/sliders emit /img/<token>, server streams CDN bytes. Kills image_url vendor-name leak (279 Wolf Gordon products). Proven local: 0 image/cdn leaks, /img 200. (handle/sku concatenated-vendor TEXT leak is separate/pre-existing.)","body":""},{"hash":"c2bd7bc","date":"2026-05-31 21:03:09 -0700","author":"Steve Abrams","subject":"render: route buy CTA + main-store anchor + card sample through /buy/:slug + close denylist gap (cowtan/designtex/knoll/brewster/york/fabricut/stout/hygge/designerwallcoverings); kill vendor-handle DOM leak (mirrors fleet a85eb02)","body":""},{"hash":"f63b951","date":"2026-05-31 16:32:35 -0700","author":"Steve Abrams","subject":"Add per-site favicon (kills /favicon.ico 404)","body":""},{"hash":"e170ae6","date":"2026-05-30 21:40:03 -0700","author":"Steve","subject":"scrub residual 3rd-party vendor names from products.json source (per sku-redact denylist; sku/handle + DW house brands preserved)","body":""},{"hash":"b6f6b8c","date":"2026-05-30 09:51:32 -0700","author":"Steve Abrams","subject":"fix: strip bare '-arte' (Arte International abbrev) from product slugs + sort denylist longest-first so 'arte international' strips before 'arte' (no 'international' residue). Closes residual leak from 2026-05-30 verify sweep.","body":""},{"hash":"1f0773a","date":"2026-05-29 22:19:31 -0700","author":"Steve Abrams","subject":"fix: strip vendor tokens from product slug (redactVendorSlug) + dual-key route resolve — closes standalone slug vendor leak","body":""},{"hash":"97397c5","date":"2026-05-29 15:12:56 -0700","author":"Steve Abrams","subject":"feat(theme): add ?theme=N toggle to headHTML() — mirrors dw-domain-fleet productPage() canonical (commit 34fbe0e). Single block injected after <body> opening (closes the headHTML template-literal cleanly). Uses 'data-page-theme' attribute + 'wallco-page-theme' localStorage key — distinct from this site's existing data-theme dark/light system (ntw_theme key) so no collision. Hidden until ?theme=1|2 or localStorage opt-in.","body":""},{"hash":"8f891e8","date":"2026-05-28 07:26:13 -0700","author":"Steve","subject":"scrub vendor name leaks from products.json source (1309 products: title/vendor/tags cleaned per sku-redact denylist)","body":""},{"hash":"0134964","date":"2026-05-26 09:45:03 -0700","author":"Steve","subject":"redact vendor names from customer-facing /api (close fleet leak)","body":""},{"hash":"1fcdeed","date":"2026-05-25 21:21:32 -0700","author":"Steve Abrams","subject":"replace p.vendor render on detail page with 'Designer Wallcoverings' brand string","body":""},{"hash":"d2fa5f3","date":"2026-05-25 20:37:37 -0700","author":"Steve Abrams","subject":"retag aesthetic from PG: 4166 rows updated","body":"before: <null>=4166\nafter:  all=30, architectural=65, commercial=3940, textured=49, vinyl=82\n\nBackfills correct multi-bucket aesthetic tagging from dw_unified.microsite_products\n(DTD verdict B, 2026-05-25 fleet retag run).\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"151be03","date":"2026-05-19 23:55:11 -0700","author":"Steve Abrams","subject":"make server.js self-contained — gate _shared/admin-catalog behind MICROSITE_ADMIN_ENABLED env flag (kamatera deploy unblock)","body":""},{"hash":"b4fde9f","date":"2026-05-19 22:10:25 -0700","author":"Steve Abrams","subject":"add ns-contact modal (CSS + HTML + JS) — info@naturaltextilewallpaper.com","body":"Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"3418104","date":"2026-05-19 22:10:25 -0700","author":"Steve Abrams","subject":"add corner-nav.js — universal top-right nav per _shared/corner-nav.js","body":"Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"9fe6fb9","date":"2026-05-19 11:33:44 -0700","author":"Steve Abrams","subject":"Phase 2: mobile-ready + tap-to-reveal","body":""},{"hash":"393ab0e","date":"2026-05-19 11:07:43 -0700","author":"Steve Abrams","subject":"stage2: wire naturaltextilewallpaper onto shared catalog (hand-tailored)","body":""},{"hash":"d25f742","date":"2026-05-19 07:52:38 -0700","author":"Steve Abrams","subject":"Untrack server.js.pre-sort-skill snapshot; broaden .gitignore","body":"Removed the committed editor snapshot from version control and\nextended .gitignore to cover *.bak, *.bak.*, *.pre-*, *.orig and\nbackup tilde files so future snapshots never get committed. A static\nmiddleware guard already 404s any .bak/.pre-/.orig/.swp/.tmp request\nso these files never serve from the public root.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"f812ade","date":"2026-05-19 07:52:12 -0700","author":"Steve Abrams","subject":"Hydrate saved sort from localStorage before first grid load","body":"The sort <select> persisted its value but never re-applied it on a\nfresh visit, so the catalog always loaded in 'newest' order regardless\nof the user's last choice. hydrateSort() now checks localStorage and\nissues a window.location.replace with the saved sort BEFORE the grid's\ninitial loadPage() runs, gating the IntersectionObserver setup on it.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"10d0c18","date":"2026-05-13 08:57:57 -0700","author":"Steve","subject":"snapshot: 1 file(s) changed, +1 new","body":""},{"hash":"2b86746","date":"2026-05-10 10:39:49 -0700","author":"Steve","subject":"sort-skill canonical: add light-dark/dark-light/wheel + helpers","body":""},{"hash":"f249379","date":"2026-05-07 15:00:26 -0700","author":"Steve Abrams","subject":"initial scaffold: NTW site — 4166 natural-fiber products, Loro Piana aesthetic, dark/light toggle, material filter pills, infinite scroll","body":""}]}