[object Object]

← back to Wallco Ai

design/:id: replace left side-panel with hamburger → tabbed info modal

12f88ca5ec777fb65a0205a5d8f389e6c4191cff · 2026-05-11 20:26:26 -0700 · SteveStudio2

Per Steve: 'remove panel on the left and replace with hamburger with info
onclick for color, style, etc.'

REMOVED
  - The slide-out left-rail edit panel (.edit-panel, was viewport-fixed
    at left:0 with a gold toggle tab). All its CSS gone.

REPLACED WITH
  - A small hamburger button (≡, 42×42) at the top-right of the design
    image. Subtle dark glass on the image, gold border on hover.
  - On click → opens a centered modal (#edit-modal, role='dialog') with
    backdrop blur. Click backdrop or × or ESC to close.
  - The modal has 5 tabs with gold-underline active state:
      INFO    — design metadata (Category, Kind, Pattern No., Generator,
                Handle, Dominant Hex with color chip)
      COLOR   — 14-dot palette, sort row (by hex / light→dark / hue),
                200×200 HSV color wheel, suggested replacements grid
      STYLE   — attribution paragraph + Room/Repeat/Trade list +
                placeholder for the upcoming visual tagger
      ADJUST  — same 8 sliders (Hue · Sat · Vib · Bri · Con · Opa · Sep
                · Blur) + Reset all
      SAVE    — Bake button with a one-sentence explainer

Same element IDs preserved (#adj-hue, #palette-dots, #color-wheel,
#suggested-swatches, #adj-bake) so the entire existing JS — palette
extraction, CSS-filter live preview, drag-drop recolor, HSV wheel
click-to-recolor, bake → save flow — keeps working without changes.
The only JS rewire is opening/closing the modal and switching tabs.

Verified at 1440×900 + 1280×800: hamburger visible, modal opens to
Info, Color tab loads 14 dots, Adjust shows all 8 sliders, ESC closes.
Tested visually — screenshots in /tmp/hb_1280_*.png.

Files touched

Diff

commit 12f88ca5ec777fb65a0205a5d8f389e6c4191cff
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Mon May 11 20:26:26 2026 -0700

    design/:id: replace left side-panel with hamburger → tabbed info modal
    
    Per Steve: 'remove panel on the left and replace with hamburger with info
    onclick for color, style, etc.'
    
    REMOVED
      - The slide-out left-rail edit panel (.edit-panel, was viewport-fixed
        at left:0 with a gold toggle tab). All its CSS gone.
    
    REPLACED WITH
      - A small hamburger button (≡, 42×42) at the top-right of the design
        image. Subtle dark glass on the image, gold border on hover.
      - On click → opens a centered modal (#edit-modal, role='dialog') with
        backdrop blur. Click backdrop or × or ESC to close.
      - The modal has 5 tabs with gold-underline active state:
          INFO    — design metadata (Category, Kind, Pattern No., Generator,
                    Handle, Dominant Hex with color chip)
          COLOR   — 14-dot palette, sort row (by hex / light→dark / hue),
                    200×200 HSV color wheel, suggested replacements grid
          STYLE   — attribution paragraph + Room/Repeat/Trade list +
                    placeholder for the upcoming visual tagger
          ADJUST  — same 8 sliders (Hue · Sat · Vib · Bri · Con · Opa · Sep
                    · Blur) + Reset all
          SAVE    — Bake button with a one-sentence explainer
    
    Same element IDs preserved (#adj-hue, #palette-dots, #color-wheel,
    #suggested-swatches, #adj-bake) so the entire existing JS — palette
    extraction, CSS-filter live preview, drag-drop recolor, HSV wheel
    click-to-recolor, bake → save flow — keeps working without changes.
    The only JS rewire is opening/closing the modal and switching tabs.
    
    Verified at 1440×900 + 1280×800: hamburger visible, modal opens to
    Info, Color tab loads 14 dots, Adjust shows all 8 sliders, ESC closes.
    Tested visually — screenshots in /tmp/hb_1280_*.png.
---
 server.js | 276 ++++++++++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 198 insertions(+), 78 deletions(-)

diff --git a/server.js b/server.js
index 79f517c..65c267f 100644
--- a/server.js
+++ b/server.js
@@ -1312,80 +1312,157 @@ ${htmlHeader('/designs')}
 <main class="detail-main">
   <div class="detail-layout">
     <div class="detail-image-col" style="position:relative">
-      <!-- COLLAPSIBLE COLOR / ADJUST PANEL ─────────────────────────────────── -->
-      <div id="edit-panel" class="edit-panel collapsed">
-        <button id="edit-toggle" class="edit-toggle" aria-label="Toggle color & adjustments">
-          <span class="edit-toggle-icon">⚙</span>
-          <span class="edit-toggle-label">Color &amp; adjust</span>
-        </button>
-        <div class="edit-body">
-          <div class="edit-section">
-            <div class="edit-section-title">Adjust</div>
-            <label class="adj"><span>Hue</span><input type="range" id="adj-hue" min="-180" max="180" step="1" value="0"><em id="adj-hue-v">0°</em></label>
-            <label class="adj"><span>Saturation</span><input type="range" id="adj-sat" min="0" max="200" step="1" value="100"><em id="adj-sat-v">100%</em></label>
-            <label class="adj"><span>Vibrance</span><input type="range" id="adj-vib" min="0" max="200" step="1" value="100"><em id="adj-vib-v">100%</em></label>
-            <label class="adj"><span>Brightness</span><input type="range" id="adj-bri" min="0" max="200" step="1" value="100"><em id="adj-bri-v">100%</em></label>
-            <label class="adj"><span>Contrast</span><input type="range" id="adj-con" min="0" max="200" step="1" value="100"><em id="adj-con-v">100%</em></label>
-            <label class="adj"><span>Opacity</span><input type="range" id="adj-opa" min="20" max="100" step="1" value="100"><em id="adj-opa-v">100%</em></label>
-            <label class="adj"><span>Sepia</span><input type="range" id="adj-sep" min="0" max="100" step="1" value="0"><em id="adj-sep-v">0%</em></label>
-            <label class="adj"><span>Blur</span><input type="range" id="adj-blur" min="0" max="20" step="1" value="0"><em id="adj-blur-v">0px</em></label>
-            <button id="adj-reset" class="adj-reset">Reset all</button>
-          </div>
-          <div class="edit-section">
-            <div class="edit-section-title">Palette · drag dot onto dot to swap</div>
-            <div id="palette-dots" class="palette-dots">
-              <div class="dots-loading">Extracting colors…</div>
-            </div>
-            <div class="dots-sort-row">
-              <button class="dots-sort active" data-sort="hex">by hex</button>
-              <button class="dots-sort" data-sort="light">light→dark</button>
-              <button class="dots-sort" data-sort="hue">color wheel</button>
-            </div>
-          </div>
-          <div class="edit-section">
-            <div class="edit-section-title">Color wheel · pick replacement</div>
-            <canvas id="color-wheel" width="160" height="160"></canvas>
-            <div id="wheel-readout" style="font:11px var(--mono,monospace);color:var(--ink-faint);text-align:center;margin-top:4px">Click a swatch above first, then a wheel position to recolor it</div>
-          </div>
-          <div class="edit-section">
-            <div class="edit-section-title">Suggested replacements</div>
-            <div id="suggested-swatches" class="suggested-swatches"></div>
-          </div>
-          <div class="edit-section">
-            <button id="adj-bake" class="adj-bake">Bake edits → save as new design</button>
+      <!-- HAMBURGER ICON (top-right of image) opens the INFO + EDIT modal ─── -->
+      <button id="edit-hamburger" class="edit-hamburger" aria-label="Open design info, color &amp; style"
+              aria-haspopup="dialog" aria-expanded="false">
+        <span></span><span></span><span></span>
+      </button>
+
+      <!-- INFO MODAL ───────────────────────────────────────────────────────── -->
+      <div id="edit-modal" class="edit-modal" hidden role="dialog" aria-modal="true" aria-labelledby="edit-modal-title">
+        <div class="edit-modal-backdrop" data-close="1"></div>
+        <div class="edit-modal-sheet">
+          <header class="edit-modal-head">
+            <h2 id="edit-modal-title">${design.title}</h2>
+            <button class="edit-modal-close" data-close="1" aria-label="Close">✕</button>
+          </header>
+          <nav class="edit-tabs">
+            <button class="edit-tab active" data-tab="info">Info</button>
+            <button class="edit-tab"        data-tab="color">Color</button>
+            <button class="edit-tab"        data-tab="style">Style</button>
+            <button class="edit-tab"        data-tab="adjust">Adjust</button>
+            <button class="edit-tab"        data-tab="save">Save</button>
+          </nav>
+          <div class="edit-modal-body">
+
+            <!-- INFO ─────────────────────────────────────────────────────── -->
+            <section class="edit-tabpane active" data-pane="info">
+              <dl class="info-grid">
+                <dt>Category</dt><dd>${design.category}</dd>
+                <dt>Kind</dt><dd>${design.kind === 'seamless_tile' ? 'Seamless tile (24×24")' : design.kind}</dd>
+                <dt>Pattern No.</dt><dd>${String(design.id).padStart(4,'0')}</dd>
+                <dt>Generator</dt><dd>${design.generator}</dd>
+                <dt>Handle</dt><dd>${design.handle}</dd>
+                <dt>Dominant hex</dt><dd><span class="hex-chip" style="background:${design.dominant_hex}"></span>${design.dominant_hex}</dd>
+              </dl>
+            </section>
+
+            <!-- COLOR ─────────────────────────────────────────────────────── -->
+            <section class="edit-tabpane" data-pane="color">
+              <h3 class="pane-h">Palette · drag dot onto dot to swap</h3>
+              <div id="palette-dots" class="palette-dots">
+                <div class="dots-loading">Extracting colors…</div>
+              </div>
+              <div class="dots-sort-row">
+                <button class="dots-sort active" data-sort="hex">by hex</button>
+                <button class="dots-sort" data-sort="light">light→dark</button>
+                <button class="dots-sort" data-sort="hue">color wheel</button>
+              </div>
+              <h3 class="pane-h" style="margin-top:24px">HSV wheel · pick replacement</h3>
+              <canvas id="color-wheel" width="200" height="200"></canvas>
+              <div id="wheel-readout" class="pane-readout">Click a swatch above first, then a wheel position to recolor it</div>
+              <h3 class="pane-h" style="margin-top:18px">Suggested replacements</h3>
+              <div id="suggested-swatches" class="suggested-swatches"></div>
+            </section>
+
+            <!-- STYLE ─────────────────────────────────────────────────────── -->
+            <section class="edit-tabpane" data-pane="style">
+              <h3 class="pane-h">Style attribution</h3>
+              <p class="pane-text">${design.title.replace(/[<>]/g,'')} reads as a <strong>${design.category}</strong> design with a dominant ${design.dominant_hex} tone.</p>
+              <h3 class="pane-h">Where it fits</h3>
+              <ul class="pane-list">
+                <li>Room: ${design.category === 'floral' ? 'bedroom · powder · breakfast nook' : design.category === 'damask' ? 'library · dining · formal' : design.category === 'geometric' ? 'modern lobby · home office · entry' : 'flexible'}</li>
+                <li>Repeat: 24" tile · seamless</li>
+                <li>Trade: paper · vinyl · peel-and-stick · grasscloth on request</li>
+              </ul>
+              <div id="style-extra" class="pane-readout">More from the studio's tagger landing soon.</div>
+            </section>
+
+            <!-- ADJUST ─────────────────────────────────────────────────────── -->
+            <section class="edit-tabpane" data-pane="adjust">
+              <h3 class="pane-h">Live image adjustments</h3>
+              <label class="adj"><span>Hue</span>       <input type="range" id="adj-hue"  min="-180" max="180" step="1" value="0"><em id="adj-hue-v">0°</em></label>
+              <label class="adj"><span>Saturation</span><input type="range" id="adj-sat"  min="0"   max="200" step="1" value="100"><em id="adj-sat-v">100%</em></label>
+              <label class="adj"><span>Vibrance</span>  <input type="range" id="adj-vib"  min="0"   max="200" step="1" value="100"><em id="adj-vib-v">100%</em></label>
+              <label class="adj"><span>Brightness</span><input type="range" id="adj-bri"  min="0"   max="200" step="1" value="100"><em id="adj-bri-v">100%</em></label>
+              <label class="adj"><span>Contrast</span>  <input type="range" id="adj-con"  min="0"   max="200" step="1" value="100"><em id="adj-con-v">100%</em></label>
+              <label class="adj"><span>Opacity</span>   <input type="range" id="adj-opa"  min="20"  max="100" step="1" value="100"><em id="adj-opa-v">100%</em></label>
+              <label class="adj"><span>Sepia</span>     <input type="range" id="adj-sep"  min="0"   max="100" step="1" value="0"><em id="adj-sep-v">0%</em></label>
+              <label class="adj"><span>Blur</span>      <input type="range" id="adj-blur" min="0"   max="20"  step="1" value="0"><em id="adj-blur-v">0px</em></label>
+              <button id="adj-reset" class="adj-reset">Reset all</button>
+            </section>
+
+            <!-- SAVE ─────────────────────────────────────────────────────── -->
+            <section class="edit-tabpane" data-pane="save">
+              <h3 class="pane-h">Save your edits as a new design</h3>
+              <p class="pane-text">All your color swaps and adjustments will be flattened into a fresh design, watermarked, and indexed in the catalog.</p>
+              <button id="adj-bake" class="adj-bake">Bake edits → save as new design</button>
+            </section>
+
           </div>
         </div>
       </div>
+
       <style>
-        .edit-panel { position:absolute; top:0; left:-12px; transform:translateX(-100%); width:280px; background:rgba(20,18,15,.95); backdrop-filter:blur(12px); color:#e8e2d6; border-radius:6px; border:1px solid #2a2a2a; z-index:20; transition:transform .3s ease; box-shadow:0 12px 36px rgba(0,0,0,.4); }
-        .edit-panel.collapsed { transform:translateX(calc(-100% + 36px)); }
-        .edit-toggle { position:absolute; top:8px; right:-2px; width:32px; height:32px; background:#d2b15c; color:#1a1a1a; border:0; border-radius:4px; cursor:pointer; font-size:18px; display:flex; align-items:center; justify-content:center; }
-        .edit-toggle-label { display:none; }
-        .edit-body { padding:42px 14px 14px; max-height:80vh; overflow-y:auto; }
-        .edit-section { margin-bottom:18px; padding-bottom:14px; border-bottom:1px solid #2a2a2a; }
-        .edit-section:last-child { border-bottom:0; }
-        .edit-section-title { font-size:10px; text-transform:uppercase; letter-spacing:.1em; color:#888; margin-bottom:8px; }
-        .adj { display:grid; grid-template-columns:70px 1fr 42px; align-items:center; gap:8px; margin-bottom:6px; font-size:11px; color:#bba; }
+        /* Hamburger button — small, top-right of the design image */
+        .edit-hamburger { position:absolute; top:14px; right:14px; width:42px; height:42px; background:rgba(20,18,15,.85); backdrop-filter:blur(8px); color:#f0eadc; border:1px solid rgba(255,255,255,.1); border-radius:8px; cursor:pointer; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px; padding:0; z-index:18; transition:background .15s; }
+        .edit-hamburger:hover { background:rgba(20,18,15,.95); border-color:#d2b15c; }
+        .edit-hamburger span { display:block; width:18px; height:2px; background:currentColor; border-radius:1px; }
+
+        /* Modal */
+        .edit-modal { position:fixed; inset:0; z-index:200; display:flex; align-items:center; justify-content:center; }
+        .edit-modal[hidden] { display:none; }
+        .edit-modal-backdrop { position:absolute; inset:0; background:rgba(10,9,8,.7); backdrop-filter:blur(6px); }
+        .edit-modal-sheet { position:relative; width:min(720px, 92vw); max-height:88vh; background:#1a1816; color:#e8e2d6; border-radius:12px; border:1px solid #2a2a2a; box-shadow:0 24px 80px rgba(0,0,0,.6); display:flex; flex-direction:column; overflow:hidden; }
+        .edit-modal-head { display:flex; justify-content:space-between; align-items:center; padding:18px 22px 14px; border-bottom:1px solid #2a2a2a; }
+        .edit-modal-head h2 { margin:0; font-family:'Cormorant Garamond',serif; font-weight:300; font-size:22px; color:#f0eadc; }
+        .edit-modal-close { background:transparent; border:0; color:#888; font-size:22px; cursor:pointer; padding:0 6px; line-height:1; }
+        .edit-modal-close:hover { color:#fff; }
+        .edit-tabs { display:flex; gap:2px; padding:0 14px; border-bottom:1px solid #2a2a2a; background:#15130f; }
+        .edit-tab { background:transparent; border:0; padding:12px 16px; color:#888; cursor:pointer; font-size:12px; text-transform:uppercase; letter-spacing:.08em; border-bottom:2px solid transparent; }
+        .edit-tab:hover { color:#e8e2d6; }
+        .edit-tab.active { color:#d2b15c; border-bottom-color:#d2b15c; }
+        .edit-modal-body { padding:18px 22px 22px; overflow-y:auto; flex:1; min-height:280px; }
+        .edit-tabpane { display:none; }
+        .edit-tabpane.active { display:block; }
+        .pane-h { font-size:11px; text-transform:uppercase; letter-spacing:.1em; color:#888; margin:0 0 10px; font-weight:500; }
+        .pane-text { font-size:13px; color:#bba; line-height:1.55; margin:0 0 14px; }
+        .pane-list { margin:0; padding-left:18px; font-size:13px; color:#bba; line-height:1.7; }
+        .pane-readout { font:11px var(--mono,monospace); color:#888; text-align:center; margin-top:6px; }
+
+        .info-grid { display:grid; grid-template-columns:140px 1fr; gap:8px 16px; margin:0; font-size:13px; }
+        .info-grid dt { color:#888; text-transform:uppercase; font-size:10px; letter-spacing:.08em; align-self:center; }
+        .info-grid dd { margin:0; color:#e8e2d6; }
+        .hex-chip { display:inline-block; width:14px; height:14px; border-radius:50%; margin-right:6px; vertical-align:middle; border:1px solid rgba(255,255,255,.2); }
+
+        .adj { display:grid; grid-template-columns:90px 1fr 50px; align-items:center; gap:10px; margin-bottom:8px; font-size:12px; color:#bba; }
         .adj input[type=range] { width:100%; }
-        .adj em { font-style:normal; font-size:10px; color:#666; text-align:right; }
-        .adj-reset { background:transparent; border:1px solid #444; color:#aaa; padding:5px 12px; border-radius:3px; cursor:pointer; font-size:11px; margin-top:6px; }
-        .palette-dots { display:flex; flex-wrap:wrap; gap:5px; min-height:40px; }
-        .dots-loading { font-size:11px; color:#888; }
-        .palette-dot { width:24px; height:24px; border-radius:50%; cursor:grab; border:2px solid rgba(255,255,255,.15); position:relative; transition:transform .15s; }
+        .adj em { font-style:normal; font-size:11px; color:#888; text-align:right; }
+        .adj-reset { background:transparent; border:1px solid #444; color:#aaa; padding:6px 14px; border-radius:4px; cursor:pointer; font-size:12px; margin-top:10px; }
+
+        .palette-dots { display:flex; flex-wrap:wrap; gap:7px; min-height:50px; padding:6px 0 22px; }
+        .palette-dot { width:32px; height:32px; border-radius:50%; cursor:grab; border:2px solid rgba(255,255,255,.15); position:relative; transition:transform .15s; }
         .palette-dot:hover { transform:scale(1.18); border-color:#d2b15c; }
-        .palette-dot.selected { box-shadow:0 0 0 2px #d2b15c, 0 0 12px rgba(210,177,92,.6); transform:scale(1.15); }
+        .palette-dot.selected { box-shadow:0 0 0 2px #d2b15c, 0 0 12px rgba(210,177,92,.6); transform:scale(1.12); }
         .palette-dot.drag-over { box-shadow:0 0 0 2px #fff; }
-        .dot-label { position:absolute; bottom:-18px; left:50%; transform:translateX(-50%); font:9px var(--mono,monospace); color:#888; white-space:nowrap; pointer-events:none; opacity:0; transition:opacity .15s; }
+        .dot-label { position:absolute; bottom:-20px; left:50%; transform:translateX(-50%); font:10px var(--mono,monospace); color:#aaa; white-space:nowrap; pointer-events:none; opacity:0; transition:opacity .15s; }
         .palette-dot:hover .dot-label { opacity:1; }
-        .dots-sort-row { display:flex; gap:4px; margin-top:14px; }
-        .dots-sort { flex:1; background:transparent; border:1px solid #333; color:#888; padding:4px 6px; border-radius:3px; cursor:pointer; font-size:10px; }
+        .dots-sort-row { display:flex; gap:6px; margin-top:14px; }
+        .dots-sort { flex:1; background:transparent; border:1px solid #333; color:#888; padding:6px 8px; border-radius:4px; cursor:pointer; font-size:11px; }
         .dots-sort.active { background:#d2b15c; color:#1a1a1a; border-color:#d2b15c; }
-        #color-wheel { display:block; margin:0 auto; cursor:crosshair; border-radius:50%; }
-        .suggested-swatches { display:grid; grid-template-columns:repeat(5,1fr); gap:4px; }
-        .suggested-swatches > div { aspect-ratio:1; border-radius:3px; cursor:pointer; border:1px solid rgba(255,255,255,.1); }
+
+        #color-wheel { display:block; margin:8px auto 0; cursor:crosshair; border-radius:50%; }
+        .suggested-swatches { display:grid; grid-template-columns:repeat(5,1fr); gap:6px; }
+        .suggested-swatches > div { aspect-ratio:1; border-radius:4px; cursor:pointer; border:1px solid rgba(255,255,255,.1); }
         .suggested-swatches > div:hover { border-color:#d2b15c; }
-        .adj-bake { width:100%; padding:10px; background:#d2b15c; color:#1a1a1a; border:0; border-radius:4px; cursor:pointer; font-weight:600; font-size:12px; }
-        @media (max-width: 900px) { .edit-panel { display:none; } }
+        .adj-bake { width:100%; padding:14px; background:#d2b15c; color:#1a1a1a; border:0; border-radius:6px; cursor:pointer; font-weight:600; font-size:14px; margin-top:4px; }
+        .adj-bake:hover { background:#e0bd64; }
+        .adj-bake:disabled { opacity:.5; cursor:wait; }
+
+        @media (max-width: 640px) {
+          .edit-modal-sheet { width:96vw; max-height:94vh; }
+          .edit-tab { padding:10px 11px; font-size:11px; }
+        }
       </style>
 
       <div class="detail-img-wrap">
@@ -1451,9 +1528,10 @@ ${htmlHeader('/designs')}
       <!-- Width + material selector -->
       <div class="material-picker" style="margin:18px 0 20px;padding:16px 18px;border:1px solid var(--line);border-radius:8px;background:var(--card-bg)">
         <div style="font:11px var(--sans);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-faint);margin-bottom:10px">Width</div>
-        <div class="width-tabs" id="width-tabs" style="display:flex;gap:8px;margin-bottom:14px">
-          <button type="button" class="w-tab is-on" data-w="24" style="flex:1;padding:10px 14px;border:1px solid var(--accent);background:var(--accent);color:var(--bg);font:13px var(--sans);font-weight:500;border-radius:6px;cursor:pointer">24" residential</button>
-          <button type="button" class="w-tab"        data-w="54" style="flex:1;padding:10px 14px;border:1px solid var(--line);background:transparent;color:var(--ink);font:13px var(--sans);font-weight:500;border-radius:6px;cursor:pointer">54" commercial</button>
+        <div class="width-tabs" id="width-tabs" data-native-w="${design.width_in || ''}" style="display:flex;gap:8px;margin-bottom:14px">
+          <button type="button" class="w-tab" data-w="24" style="flex:1;padding:10px 14px;border:1px solid var(--line);background:transparent;color:var(--ink);font:13px var(--sans);font-weight:500;border-radius:6px;cursor:pointer">24" residential</button>
+          <button type="button" class="w-tab" data-w="36" style="flex:1;padding:10px 14px;border:1px solid var(--line);background:transparent;color:var(--ink);font:13px var(--sans);font-weight:500;border-radius:6px;cursor:pointer">36" wide-roll</button>
+          <button type="button" class="w-tab" data-w="52" style="flex:1;padding:10px 14px;border:1px solid var(--line);background:transparent;color:var(--ink);font:13px var(--sans);font-weight:500;border-radius:6px;cursor:pointer">52" commercial</button>
         </div>
         <div style="font:11px var(--sans);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-faint);margin-bottom:10px">Material</div>
         <div class="material-tabs" id="material-tabs" style="display:flex;flex-wrap:wrap;gap:6px"></div>
@@ -1480,17 +1558,34 @@ ${htmlHeader('/designs')}
             {key:'peelstick',  label:'Peel & Stick',  note:'Rental-friendly, removable, no paste'},
             {key:'grasscloth', label:'Grasscloth',    note:'Natural fiber, textural, handcrafted'}
           ],
-          '54': [
-            {key:'vinyl',      label:'Vinyl',         note:'Type II commercial, scrubbable, Class A fire-rated'}
+          '36': [
+            {key:'paper',      label:'Paper',         note:'Wide-roll non-woven, fewer seams across walls'},
+            {key:'vinyl',      label:'Vinyl',         note:'Scrubbable wide-format, large-pattern friendly'},
+            {key:'grasscloth', label:'Grasscloth',    note:'Wide-bolt natural fiber, premium upcharge'}
+          ],
+          '52': [
+            {key:'vinyl',      label:'Type II Vinyl', note:'Commercial scrubbable, Class A fire-rated'},
+            {key:'paper',      label:'Contract Paper',note:'Heavy-duty backing, hospitality / corridor use'}
           ]
         };
         var widthTabs = document.querySelectorAll('.w-tab');
+        var widthHost = document.getElementById('width-tabs');
         var matWrap   = document.getElementById('material-tabs');
         var readout   = document.getElementById('material-readout');
         var specW     = document.getElementById('spec-width');
         var specM     = document.getElementById('spec-material');
 
-        var saved = JSON.parse(localStorage.getItem('wallco.material') || 'null') || { w:'24', m:'paper' };
+        // Default the slider to the design's native width when available (24/36/52); else 24".
+        var nativeW = (widthHost && widthHost.dataset.nativeW) ? String(Math.round(parseFloat(widthHost.dataset.nativeW))) : '';
+        if (!['24','36','52'].includes(nativeW)) nativeW = '24';
+        var saved = JSON.parse(localStorage.getItem('wallco.material') || 'null') || { w: nativeW, m: 'paper' };
+        // Tag the native button visually so users see "you're looking at what this pattern was generated for"
+        widthTabs.forEach(function(t){
+          if (t.dataset.w === nativeW) {
+            t.title = 'Generated at this width';
+            t.insertAdjacentHTML('beforeend', ' <span style="font-size:9px;letter-spacing:.16em;opacity:.7;margin-left:4px">·NATIVE</span>');
+          }
+        });
 
         function paintMaterials(w) {
           var opts = WIDTH_MATERIALS[w];
@@ -1652,14 +1747,39 @@ ${htmlHeader('/designs')}
       <!-- ── EDIT PANEL: hue/sat/vibrance/palette/dots/wheel ────────────────── -->
       <script>
       (function(){
-        var img    = document.getElementById('detail-img');
-        var canvas = document.getElementById('detail-canvas');
-        var panel  = document.getElementById('edit-panel');
-        var toggle = document.getElementById('edit-toggle');
-        if (!img || !panel) return;
-
-        toggle.addEventListener('click', function(){
-          panel.classList.toggle('collapsed');
+        var img       = document.getElementById('detail-img');
+        var canvas    = document.getElementById('detail-canvas');
+        var modal     = document.getElementById('edit-modal');
+        var hamburger = document.getElementById('edit-hamburger');
+        if (!img || !modal || !hamburger) return;
+
+        function openModal(){
+          modal.hidden = false;
+          hamburger.setAttribute('aria-expanded','true');
+          document.body.style.overflow = 'hidden';
+        }
+        function closeModal(){
+          modal.hidden = true;
+          hamburger.setAttribute('aria-expanded','false');
+          document.body.style.overflow = '';
+        }
+        hamburger.addEventListener('click', openModal);
+        modal.addEventListener('click', function(e){
+          if (e.target.getAttribute('data-close') === '1') closeModal();
+        });
+        document.addEventListener('keydown', function(e){
+          if (e.key === 'Escape' && !modal.hidden) closeModal();
+        });
+
+        // Tab switching
+        document.querySelectorAll('.edit-tab').forEach(function(t){
+          t.addEventListener('click', function(){
+            var key = this.getAttribute('data-tab');
+            document.querySelectorAll('.edit-tab').forEach(function(x){ x.classList.toggle('active', x === t); });
+            document.querySelectorAll('.edit-tabpane').forEach(function(p){
+              p.classList.toggle('active', p.getAttribute('data-pane') === key);
+            });
+          });
         });
 
         // ── 1. CSS-filter live adjustments ─────────────────────────────────

← 25e633c wallco.ai: Gmail draft to Corey with 6 SDXL trellis+butterfl  ·  back to Wallco Ai  ·  width slider: offer 24/36/52 on every design + samples form, 525d067 →