[object Object]

← back to Interiordesignershowroom

refine: price-freshness trust chip on cards (price_checked_at) + PNG/apple-touch favicon fallbacks

1ead1ebaab5899b2d26d95a3b4179f15bba63dc5 · 2026-08-01 22:16:24 -0700 · Steve Abrams

Files touched

Diff

commit 1ead1ebaab5899b2d26d95a3b4179f15bba63dc5
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sat Aug 1 22:16:24 2026 -0700

    refine: price-freshness trust chip on cards (price_checked_at) + PNG/apple-touch favicon fallbacks
---
 lib/render.js                   |  18 +++++++++++++++++-
 public/css/site.css             |  21 +++++++++++++++++++++
 public/img/apple-touch-icon.png | Bin 0 -> 4465 bytes
 public/img/favicon-32.png       | Bin 0 -> 811 bytes
 4 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/lib/render.js b/lib/render.js
index a848144..2d76703 100644
--- a/lib/render.js
+++ b/lib/render.js
@@ -16,6 +16,20 @@ const esc = (s = '') => String(s)
 // Callers MUST check for empty string and hide the price element when blank.
 const money = (n) => (n == null ? '' : '$' + Number(n).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }));
 
+// Price-freshness trust signal. Uses price_checked_at (NOT updated_at) — the
+// column that specifically records when we last confirmed the price against the
+// feed — so we never overstate how current a price is. Flags anything older than
+// two weeks as `stale` so a drifted feed price is visible rather than silent.
+function freshness(ts) {
+  if (!ts) return '';
+  const days = Math.floor((Date.now() - new Date(ts).getTime()) / 86400e3);
+  const label = days <= 0 ? 'today' : days === 1 ? 'yesterday'
+    : days < 30 ? `${days}d ago` : `${Math.floor(days / 30)}mo ago`;
+  const stale = days > 14;
+  return `<span class="price-fresh${stale ? ' stale' : ''}" title="Price last verified ${esc(new Date(ts).toISOString())}">`
+    + `Price verified ${label}</span>`;
+}
+
 // FTC 16 CFR Part 255 requires a clear, conspicuous disclosure. This is not optional.
 const DISCLOSURE_SHORT =
   'This showroom is reader-supported. When you buy through links on our site, we may earn an affiliate commission at no extra cost to you.';
@@ -34,7 +48,7 @@ function productCard(p) {
   }
   // card-meta is only rendered when there is a price; empty div suppressed by CSS display:none
   const metaHtml = priceHtml
-    ? `<div class="card-meta">${priceHtml}</div>`
+    ? `<div class="card-meta">${priceHtml}${freshness(p.price_checked_at)}</div>`
     : '';
 
   // Image: use img with explicit width/height for aspect-ratio + graceful broken-img handling
@@ -84,6 +98,8 @@ function layout({ title, description, canonical, jsonld, body, activeNav }) {
 <meta name="twitter:card" content="summary_large_image">
 <meta name="twitter:image" content="${esc(SITE.url)}/img/og.png">
 <link rel="icon" href="/img/favicon.svg" type="image/svg+xml">
+<link rel="icon" type="image/png" sizes="32x32" href="/img/favicon-32.png">
+<link rel="apple-touch-icon" href="/img/apple-touch-icon.png">
 <link rel="preconnect" href="https://fonts.googleapis.com">
 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
 <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@300;400;500&family=DM+Serif+Display&display=swap">
diff --git a/public/css/site.css b/public/css/site.css
index a0419f4..777bb3b 100644
--- a/public/css/site.css
+++ b/public/css/site.css
@@ -442,6 +442,27 @@ h2.ruled::after {
   font-size: .8rem;
   margin-left: 4px;
 }
+/* Price-freshness trust chip: its own subtle line under the price. A small
+   dot reads as "live/verified"; .stale flips it amber when the feed price is
+   older than two weeks so drift is visible, not hidden. */
+.price-fresh {
+  display: block;
+  margin-top: 3px;
+  font-size: .66rem;
+  letter-spacing: .02em;
+  color: var(--muted-lt);
+}
+.price-fresh::before {
+  content: "";
+  display: inline-block;
+  width: 5px; height: 5px;
+  margin-right: 5px;
+  border-radius: 50%;
+  background: #6f9f6f;   /* verified-green dot */
+  vertical-align: middle;
+}
+.price-fresh.stale { color: #9a7b3a; }
+.price-fresh.stale::before { background: #d3a24a; }
 .buy {
   display: inline-block;
   margin-top: 10px;
diff --git a/public/img/apple-touch-icon.png b/public/img/apple-touch-icon.png
new file mode 100644
index 0000000..b47c991
Binary files /dev/null and b/public/img/apple-touch-icon.png differ
diff --git a/public/img/favicon-32.png b/public/img/favicon-32.png
new file mode 100644
index 0000000..d923ebc
Binary files /dev/null and b/public/img/favicon-32.png differ

← 7f7de63 harden /api/render + admin: persistent restart-proof render  ·  back to Interiordesignershowroom  ·  refine: soften freshness chip to 'Price checked' + SEO struc 082c438 →