[object Object]

← back to Carmelwallpapers

chore: untrack server.js.pre-sort-skill snapshot, broaden .gitignore

d43943cdf5acc5d0c539ed41252c3248ad93ef94 · 2026-05-19 08:00:12 -0700 · Steve Abrams

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

Files touched

Diff

commit d43943cdf5acc5d0c539ed41252c3248ad93ef94
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue May 19 08:00:12 2026 -0700

    chore: untrack server.js.pre-sort-skill snapshot, broaden .gitignore
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
 .gitignore               |   3 +
 server.js.pre-sort-skill | 477 -----------------------------------------------
 2 files changed, 3 insertions(+), 477 deletions(-)

diff --git a/.gitignore b/.gitignore
index 1924158..e7c3361 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,6 @@ tmp/
 dist/
 build/
 .next/
+*.bak
+*.bak.*
+*.pre-*
diff --git a/server.js.pre-sort-skill b/server.js.pre-sort-skill
deleted file mode 100644
index 05e067f..0000000
--- a/server.js.pre-sort-skill
+++ /dev/null
@@ -1,477 +0,0 @@
-'use strict';
-const express = require('express');
-const fs = require('fs');
-const path = require('path');
-
-const PORT = process.env.PORT || 9832;
-const DATA_PATH = path.join(__dirname, 'data', 'products.json');
-
-// ── Junk filter ──────────────────────────────────────────────────────────────
-const JUNK_TITLE = /(lamp|rug|pillow|throw|tripod|frame|mirror|vase|candle|sculpture|figurine|stool|table|chair|sofa|curtain|bedding|towel|plate|bowl|mug)/i;
-function isJunk(p) {
-  if (!p.image_url) return true;
-  if (!p.product_type || !/wallcovering/i.test(p.product_type)) return true;
-  if (JUNK_TITLE.test(p.title || '')) return true;
-  return false;
-}
-
-// ── Sort helpers ─────────────────────────────────────────────────────────────
-const COLOR_RANK = {
-  black:0,gray:1,grey:1,silver:2,white:3,ivory:4,cream:5,beige:6,brown:7,tan:8,sand:9,stone:10,
-  copper:11,driftwood:12,taupe:13,oatmeal:14,linen:15,parchment:16,wheat:17,
-  red:20,pink:21,coral:22,orange:23,peach:24,salmon:25,
-  yellow:30,gold:31,mustard:32,olive:33,sage:34,
-  green:40,lime:41,mint:42,seafoam:43,teal:50,aqua:51,turquoise:52,cyan:53,
-  blue:60,navy:61,indigo:62,periwinkle:63,
-  purple:70,violet:71,lavender:72,plum:73,
-};
-function colorRank(p) {
-  const tags = (p.tags || []).map(t => String(t).toLowerCase());
-  for (const t of tags) for (const k of Object.keys(COLOR_RANK)) if (t.includes(k)) return COLOR_RANK[k];
-  return 999;
-}
-const STYLE_TAGS = ['grasscloth','linen','raffia','sisal','seagrass','jute','coastal','botanical','woven','natural','organic','bamboo','rattan','texture'];
-function styleKey(p) {
-  const tags = (p.tags || []).map(t => String(t).toLowerCase());
-  for (const s of STYLE_TAGS) if (tags.some(t => t.includes(s))) return s;
-  return 'zzz';
-}
-function sortProducts(list, mode) {
-  if (mode === 'color') return [...list].sort((a,b) => colorRank(a)-colorRank(b) || (a.title||'').localeCompare(b.title||''));
-  if (mode === 'style') return [...list].sort((a,b) => styleKey(a).localeCompare(styleKey(b)) || (a.title||'').localeCompare(b.title||''));
-  if (mode === 'title') return [...list].sort((a,b) => (a.title||'').localeCompare(b.title||''));
-  if (mode === 'vendor') return [...list].sort((a,b) => (a.vendor||'').localeCompare(b.vendor||'') || (a.title||'').localeCompare(b.title||''));
-  if (mode === 'price-asc')  return [...list].sort((a,b) => (Number(a.price)||0)-(Number(b.price)||0));
-  if (mode === 'price-desc') return [...list].sort((a,b) => (Number(b.price)||0)-(Number(a.price)||0));
-  return list; // newest = file order
-}
-
-// ── Load + filter data ───────────────────────────────────────────────────────
-let PRODUCTS = [];
-let DROPPED  = 0;
-let AESTHETICS = {};
-
-function loadData() {
-  try {
-    const raw = JSON.parse(fs.readFileSync(DATA_PATH, 'utf8'));
-    const clean = raw.filter(p => !isJunk(p));
-    DROPPED = raw.length - clean.length;
-    PRODUCTS = clean;
-    AESTHETICS = {};
-    for (const p of PRODUCTS) {
-      AESTHETICS[p.aesthetic] = (AESTHETICS[p.aesthetic] || 0) + 1;
-    }
-    console.log(`Loaded ${PRODUCTS.length} products (dropped ${DROPPED} junk)`);
-  } catch(e) {
-    console.error('Failed to load products.json:', e.message);
-    PRODUCTS = [];
-  }
-}
-loadData();
-
-const app = express();
-app.use(express.static(path.join(__dirname, 'public')));
-app.set('trust proxy', 1);
-
-// ── HTML helpers ─────────────────────────────────────────────────────────────
-const HTML_HEAD = (title, desc) => `<!DOCTYPE html>
-<html lang="en">
-<head>
-<meta charset="utf-8">
-<meta name="viewport" content="width=device-width,initial-scale=1">
-<script>(function(){try{var t=localStorage.getItem('cw_theme')||((window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches)?'dark':'light');document.documentElement.setAttribute('data-theme',t);}catch(e){}})();</script>
-<!-- GA4 -->
-<script async src="https://www.googletagmanager.com/gtag/js?id=G-ENN0QV40F8"></script>
-<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-ENN0QV40F8');</script>
-<title>${title}</title>
-<meta name="description" content="${desc}">
-<meta name="theme-color" content="#f7f3ec" media="(prefers-color-scheme: light)">
-<meta name="theme-color" content="#12100d" media="(prefers-color-scheme: dark)">
-<link rel="preconnect" href="https://fonts.googleapis.com">
-<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
-<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,600;1,300;1,400&family=Inter:wght@300;400;500&display=swap" rel="stylesheet">
-<link rel="stylesheet" href="/css/site.css">
-</head>`;
-
-const THEME_TOGGLE_SCRIPT = `<script>
-(function(){
-  var btn=document.getElementById('theme-toggle');if(!btn)return;
-  function sync(){var t=document.documentElement.getAttribute('data-theme')||'light';btn.setAttribute('aria-pressed',t==='dark'?'true':'false');}
-  sync();
-  btn.addEventListener('click',function(){
-    var cur=document.documentElement.getAttribute('data-theme')||'light';
-    var nxt=cur==='dark'?'light':'dark';
-    document.documentElement.setAttribute('data-theme',nxt);
-    try{localStorage.setItem('cw_theme',nxt);}catch(e){}
-    sync();
-  });
-})();
-</script>`;
-
-const SUN_MOON_SVG = `<button type="button" id="theme-toggle" class="theme-toggle" aria-label="Toggle dark mode" aria-pressed="false" title="Toggle dark / light">
-  <svg class="icon-sun" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></svg>
-  <svg class="icon-moon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12.79A9 9 0 1 1 11.21 3a7 7 0 0 0 9.79 9.79z"/></svg>
-</button>`;
-
-const HEADER_HTML = (q, sort, density) => `
-<header class="site-header">
-  <a href="/" class="brand">CARMEL<small>Wallpapers for the California Coast</small></a>
-  <nav class="site-nav">
-    <a href="/?type=grasscloth">Grasscloth</a>
-    <a href="/?type=linen">Linen</a>
-    <a href="/?type=botanical">Botanical</a>
-    <a href="/?type=coastal">Coastal</a>
-    <a href="/?type=raffia">Raffia</a>
-    <a href="mailto:info@carmelwallpapers.com" class="nav-contact">Contact</a>
-    ${SUN_MOON_SVG}
-  </nav>
-</header>`;
-
-// ── API: products (infinite scroll) ──────────────────────────────────────────
-app.get('/api/products', (req, res) => {
-  const { q, type, sort = 'newest', page = 1, limit = 24 } = req.query;
-  let list = PRODUCTS;
-  if (q) {
-    const tokens = q.toLowerCase().split(/\s+/).filter(Boolean);
-    list = list.filter(p => {
-      const hay = ((p.title||'') + ' ' + (p.vendor||'') + ' ' + (p.tags||[]).join(' ')).toLowerCase();
-      return tokens.every(tok => hay.includes(tok));
-    });
-  }
-  if (type && type !== 'all') {
-    list = list.filter(p => p.aesthetic === type);
-  }
-  list = sortProducts(list, sort);
-  const total = list.length;
-  const pageNum = Math.max(1, parseInt(page)||1);
-  const lim = Math.min(60, parseInt(limit)||24);
-  const start = (pageNum-1)*lim;
-  res.json({ total, page: pageNum, limit: lim, pages: Math.ceil(total/lim), sort, items: list.slice(start, start+lim) });
-});
-
-// ── API: sliders ─────────────────────────────────────────────────────────────
-app.get('/api/sliders', (req, res) => {
-  const RAIL_ORDER = ['grasscloth','linen','botanical','coastal','raffia','natural'];
-  const out = [];
-  for (const a of RAIL_ORDER) {
-    const items = PRODUCTS.filter(p => p.aesthetic === a).slice(0, 12);
-    if (items.length >= 4) out.push({ aesthetic: a, items });
-  }
-  res.json({ rails: out });
-});
-
-// ── Health ────────────────────────────────────────────────────────────────────
-app.get('/health', (req, res) => {
-  res.json({ ok: true, products: PRODUCTS.length, dropped: DROPPED, aesthetics: AESTHETICS });
-});
-
-// ── Product detail ────────────────────────────────────────────────────────────
-app.get('/p/:handle', (req, res) => {
-  const p = PRODUCTS.find(x => x.handle === req.params.handle);
-  if (!p) return res.status(404).send('<h1>Not found</h1>');
-
-  const title = `${p.title} — Carmel Wallpapers | Free Memo Samples, Trade Service`;
-  const desc = `${p.title} by ${p.vendor}. Order a free memo sample. Coastal California's curated wallpaper source — expert trade service, hand-holding from Carmel to Pebble Beach.`;
-
-  const imgs = (p.images||[p.image_url]).filter(Boolean);
-  const imgHtml = imgs.map((src,i) =>
-    `<img src="${src}" alt="${p.title}${i?` view ${i+1}`:''}${i===0?' (main)':''}" loading="${i===0?'eager':'lazy'}" class="detail-img${i===0?' detail-img--main':''}">`
-  ).join('\n');
-
-  const tags = (p.tags||[]).filter(t => !/^\d+$/.test(t));
-
-  res.send(`${HTML_HEAD(title, desc)}
-<body>
-${HEADER_HTML()}
-<main class="detail-wrap">
-  <a href="/" class="back-link">&#8592; Back to collection</a>
-  <div class="detail-grid">
-    <div class="detail-images">${imgHtml}</div>
-    <div class="detail-info">
-      <p class="detail-vendor">${p.vendor}</p>
-      <h1 class="detail-title">${p.title}</h1>
-      <div class="detail-tags">${tags.slice(0,10).map(t=>`<span class="tag">${t}</span>`).join('')}</div>
-      <div class="detail-ctas">
-        <a href="${p.sample_url}" class="btn btn-sample" target="_blank" rel="noopener">Order Memo Sample (free)</a>
-        <a href="${p.product_url}" class="btn btn-full" target="_blank" rel="noopener">View Full Product</a>
-      </div>
-      <div class="detail-service">
-        <h3>Carmel Trade Service</h3>
-        <p>Designer Wallcoverings provides complimentary trade hand-holding for every Carmel and Pebble Beach project — from sampling through installation. <a href="mailto:info@carmelwallpapers.com">Reach us at info@carmelwallpapers.com</a>.</p>
-      </div>
-    </div>
-  </div>
-</main>
-<footer class="site-footer">
-  <div class="footer-inner">
-    <p>Carmel Wallpapers &mdash; a Designer Wallcoverings service</p>
-    <p><a href="mailto:info@carmelwallpapers.com">info@carmelwallpapers.com</a></p>
-    <p class="footer-fine">Serving Carmel-by-the-Sea, Pebble Beach &amp; Big Sur</p>
-  </div>
-</footer>
-<script type="application/ld+json">${JSON.stringify({
-  "@context":"https://schema.org","@type":"Product","name":p.title,"brand":{"@type":"Brand","name":p.vendor},
-  "image":imgs[0]||'',
-  "offers":{"@type":"Offer","priceCurrency":"USD","price":"0","description":"Free memo sample"},
-  "description":desc
-})}</script>
-${THEME_TOGGLE_SCRIPT}
-</body></html>`);
-});
-
-// ── Index (catalog) ──────────────────────────────────────────────────────────
-app.get('/', (req, res) => {
-  const { q='', type='', sort='newest', density='220' } = req.query;
-
-  const pageTitle = q
-    ? `"${q}" — Carmel Wallpapers`
-    : type
-      ? `${type.charAt(0).toUpperCase()+type.slice(1)} Wallpapers — Carmel, Pebble Beach & Big Sur`
-      : 'Carmel Wallpapers — Coastal California Luxury Wallcoverings';
-  const pageDesc = 'Curated grasscloth, linen, raffia and coastal-botanical wallcoverings for Carmel-by-the-Sea, Pebble Beach and Big Sur residences. Free memo samples. Expert trade service from Designer Wallcoverings.';
-
-  // Facet counts for filter chips
-  const aestheticCounts = {};
-  for (const p of PRODUCTS) {
-    aestheticCounts[p.aesthetic] = (aestheticCounts[p.aesthetic]||0)+1;
-  }
-
-  const aestheticChips = ['grasscloth','linen','botanical','coastal','raffia','natural']
-    .filter(a => aestheticCounts[a])
-    .map(a => {
-      const active = type === a ? ' chip--active' : '';
-      const href = type === a ? '/' : `/?type=${a}${q?'&q='+encodeURIComponent(q):''}`;
-      return `<a href="${href}" class="chip${active}">${a.charAt(0).toUpperCase()+a.slice(1)} <span class="chip-count">${aestheticCounts[a]}</span></a>`;
-    }).join('');
-
-  res.send(`${HTML_HEAD(pageTitle, pageDesc)}
-<body>
-${HEADER_HTML(q, sort, density)}
-
-<section class="hero" aria-label="Collection introduction">
-  <div class="hero-text">
-    <h1 class="hero-headline">CARMEL</h1>
-    <p class="hero-sub">Quiet luxury for the California coast.</p>
-    <p class="hero-body">Natural fibers. Coastal botanicals. The restraint of Carmel-by-the-Sea captured in texture and tone — curated from the world's finest wallcovering houses.</p>
-    <a href="#catalog" class="hero-cta">Explore the Collection</a>
-  </div>
-</section>
-
-<section class="sliders-section" id="sliders" aria-label="Browse by aesthetic">
-  <div class="sliders-wrap">
-    <div id="sliders-container"></div>
-  </div>
-</section>
-
-<section class="catalog-section" id="catalog">
-  <div class="catalog-controls">
-    <form class="search-form" method="GET" action="/" role="search">
-      ${type?`<input type="hidden" name="type" value="${type}">`:''}
-      <input type="search" name="q" value="${q.replace(/"/g,'&quot;')}" placeholder="Search ${PRODUCTS.length.toLocaleString()} wallcoverings…" class="search-input" aria-label="Search wallcoverings">
-      <button type="submit" class="search-btn" aria-label="Search">
-        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg>
-      </button>
-    </form>
-    <div class="sort-density">
-      <select id="sortSelect" class="sort-select" aria-label="Sort products">
-        <option value="newest" ${sort==='newest'?'selected':''}>Newest</option>
-        <option value="color" ${sort==='color'?'selected':''}>Color</option>
-        <option value="style" ${sort==='style'?'selected':''}>Style</option>
-        <option value="title" ${sort==='title'?'selected':''}>Title A→Z</option>
-        <option value="vendor" ${sort==='vendor'?'selected':''}>Vendor A→Z</option>
-        <option value="price-asc" ${sort==='price-asc'?'selected':''}>Price ↑</option>
-        <option value="price-desc" ${sort==='price-desc'?'selected':''}>Price ↓</option>
-      </select>
-      <label class="density-label" aria-label="Grid density">
-        <svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><rect x="0" y="0" width="4" height="4"/><rect x="6" y="0" width="4" height="4"/><rect x="12" y="0" width="4" height="4"/><rect x="0" y="6" width="4" height="4"/><rect x="6" y="6" width="4" height="4"/><rect x="12" y="6" width="4" height="4"/></svg>
-        <input type="range" id="densitySlider" min="140" max="320" step="20" value="${density}" aria-label="Grid column width">
-      </label>
-    </div>
-  </div>
-  <div class="facet-chips" aria-label="Filter by aesthetic">
-    <a href="/${q?'?q='+encodeURIComponent(q):''}" class="chip${!type?' chip--active':''}">All <span class="chip-count">${PRODUCTS.length}</span></a>
-    ${aestheticChips}
-  </div>
-  <div id="grid" class="product-grid" role="list" style="--cols:${density}px"></div>
-  <div id="sentinel" class="sentinel" aria-hidden="true"></div>
-  <div id="grid-status" class="grid-status"></div>
-</section>
-
-<footer class="site-footer">
-  <div class="footer-inner">
-    <div class="footer-col">
-      <strong>Carmel Wallpapers</strong>
-      <p>A Designer Wallcoverings service for the Carmel Coast.</p>
-      <p><a href="mailto:info@carmelwallpapers.com">info@carmelwallpapers.com</a></p>
-    </div>
-    <div class="footer-col">
-      <strong>Our Service</strong>
-      <ul>
-        <li>Free memo samples on every pattern</li>
-        <li>Trade pricing available</li>
-        <li>Installation guidance</li>
-        <li>Serving Carmel, Pebble Beach &amp; Big Sur</li>
-      </ul>
-    </div>
-    <div class="footer-col">
-      <strong>Aesthetics</strong>
-      <ul>
-        ${['grasscloth','linen','botanical','coastal','raffia','natural'].map(a=>`<li><a href="/?type=${a}">${a.charAt(0).toUpperCase()+a.slice(1)}</a></li>`).join('')}
-      </ul>
-    </div>
-  </div>
-  <p class="footer-legal">© 2026 Carmel Wallpapers · Curated by Designer Wallcoverings · <a href="mailto:info@carmelwallpapers.com">info@carmelwallpapers.com</a></p>
-</footer>
-
-<script type="application/ld+json">${JSON.stringify({
-  "@context":"https://schema.org",
-  "@type":"LocalBusiness",
-  "name":"Carmel Wallpapers",
-  "description":pageDesc,
-  "url":"https://carmelwallpapers.com",
-  "email":"info@carmelwallpapers.com",
-  "areaServed":["Carmel-by-the-Sea","Pebble Beach","Big Sur","Monterey County"],
-  "sameAs":["https://designerwallcoverings.com"]
-})}</script>
-
-<script>
-// ── State ────────────────────────────────────────────────────────────────────
-const STATE = {
-  q: ${JSON.stringify(q)},
-  type: ${JSON.stringify(type)},
-  sort: ${JSON.stringify(sort)},
-  page: 1,
-  pages: 1,
-  total: 0,
-  loading: false,
-  exhausted: false
-};
-
-// ── Persist sort + density ───────────────────────────────────────────────────
-const sortSel = document.getElementById('sortSelect');
-const densSlider = document.getElementById('densitySlider');
-const grid = document.getElementById('grid');
-
-const savedSort = localStorage.getItem('cw_sort');
-const savedDensity = localStorage.getItem('cw_density');
-if(savedSort && !${JSON.stringify(!!req.query.sort)}) { sortSel.value = savedSort; STATE.sort = savedSort; }
-if(savedDensity) { densSlider.value = savedDensity; grid.style.setProperty('--cols', savedDensity+'px'); }
-
-sortSel.addEventListener('change', () => {
-  STATE.sort = sortSel.value;
-  localStorage.setItem('cw_sort', STATE.sort);
-  const url = new URL(location.href);
-  url.searchParams.set('sort', STATE.sort);
-  location.href = url.toString();
-});
-densSlider.addEventListener('input', () => {
-  const v = densSlider.value;
-  grid.style.setProperty('--cols', v+'px');
-  localStorage.setItem('cw_density', v);
-});
-
-// ── Card renderer ────────────────────────────────────────────────────────────
-function renderCard(p) {
-  const div = document.createElement('div');
-  div.className = 'product-card';
-  div.setAttribute('role','listitem');
-  div.innerHTML = \`
-    <a href="/p/\${p.handle}" class="card-link" aria-label="\${p.title.replace(/"/g,'&quot;')}">
-      <div class="card-img-wrap">
-        <img src="\${p.image_url}" alt="\${p.title.replace(/"/g,'&quot;')}" loading="lazy" class="card-img">
-      </div>
-      <div class="card-body">
-        <p class="card-vendor">\${p.vendor}</p>
-        <p class="card-title">\${p.title}</p>
-        <p class="card-aesthetic">\${p.aesthetic}</p>
-      </div>
-    </a>
-    <a href="\${p.sample_url}" class="card-sample" target="_blank" rel="noopener" aria-label="Order free memo sample of \${p.title.replace(/"/g,'&quot;')}">Order Memo Sample (free)</a>
-  \`;
-  return div;
-}
-
-// ── Load page ────────────────────────────────────────────────────────────────
-async function loadPage() {
-  if (STATE.loading || STATE.exhausted) return;
-  STATE.loading = true;
-  const status = document.getElementById('grid-status');
-  status.textContent = 'Loading…';
-  const params = new URLSearchParams({ sort: STATE.sort, page: STATE.page, limit: 24 });
-  if (STATE.q) params.set('q', STATE.q);
-  if (STATE.type) params.set('type', STATE.type);
-  try {
-    const res = await fetch('/api/products?' + params);
-    const data = await res.json();
-    STATE.pages = data.pages;
-    STATE.total = data.total;
-    if (STATE.page === 1) {
-      status.textContent = \`\${data.total.toLocaleString()} wallcoverings\`;
-    }
-    for (const p of data.items) grid.appendChild(renderCard(p));
-    if (STATE.page >= data.pages) {
-      STATE.exhausted = true;
-      status.textContent = \`— end of archive · \${data.total.toLocaleString()} patterns —\`;
-    } else {
-      STATE.page++;
-      status.textContent = \`\${data.total.toLocaleString()} wallcoverings · \${STATE.page-1} of \${data.pages} pages loaded\`;
-    }
-  } catch(e) {
-    status.textContent = 'Error loading products.';
-    console.error(e);
-  }
-  STATE.loading = false;
-}
-
-// ── Infinite scroll ──────────────────────────────────────────────────────────
-const sentinel = document.getElementById('sentinel');
-const observer = new IntersectionObserver((entries) => {
-  if (entries[0].isIntersecting) loadPage();
-}, { rootMargin: '600px 0px' });
-observer.observe(sentinel);
-
-// ── Grid sliders ─────────────────────────────────────────────────────────────
-async function loadSliders() {
-  try {
-    const res = await fetch('/api/sliders');
-    const data = await res.json();
-    const container = document.getElementById('sliders-container');
-    if (!data.rails || !data.rails.length) { container.closest('.sliders-section').style.display='none'; return; }
-    for (const rail of data.rails) {
-      const label = rail.aesthetic.charAt(0).toUpperCase() + rail.aesthetic.slice(1);
-      const trackId = 'track-' + rail.aesthetic;
-      const section = document.createElement('div');
-      section.className = 'rail';
-      section.innerHTML = \`
-        <div class="rail-header">
-          <h2 class="rail-title"><a href="/?type=\${rail.aesthetic}" class="rail-link">\${label}</a></h2>
-          <div class="rail-controls">
-            <button class="rail-btn rail-prev" aria-label="Scroll \${label} left">&#8592;</button>
-            <button class="rail-btn rail-next" aria-label="Scroll \${label} right">&#8594;</button>
-          </div>
-        </div>
-        <div class="rail-track" id="\${trackId}">
-          \${rail.items.map(p=>\`
-            <a href="/p/\${p.handle}" class="rail-card" aria-label="\${(p.title||'').replace(/"/g,'&quot;')}">
-              <div class="rail-img-wrap"><img src="\${p.image_url}" alt="\${(p.title||'').replace(/"/g,'&quot;')}" loading="lazy" class="rail-img"></div>
-              <p class="rail-card-title">\${p.title}</p>
-              <p class="rail-card-vendor">\${p.vendor}</p>
-            </a>
-          \`).join('')}
-        </div>
-      \`;
-      const track = section.querySelector('.rail-track');
-      section.querySelector('.rail-prev').addEventListener('click', () => track.scrollBy({ left: -track.clientWidth*.85, behavior:'smooth' }));
-      section.querySelector('.rail-next').addEventListener('click', () => track.scrollBy({ left: track.clientWidth*.85, behavior:'smooth' }));
-      container.appendChild(section);
-    }
-  } catch(e) { console.error('Sliders error:', e); }
-}
-
-loadSliders();
-</script>
-${THEME_TOGGLE_SCRIPT}
-</body></html>`);
-});
-
-app.listen(PORT, () => console.log(`carmelwallpapers listening on :${PORT}`));

← 1797e17 snapshot: 2 file(s) changed, +2 new  ·  back to Carmelwallpapers  ·  fix: 404-guard so .bak/.pre- snapshot paths never serve from 78e1c9d →