[object Object]

← back to Big Red

feat(big-red/tick1): per-mode launcher ring color (next: shopify TTL cache)

56b06d56f5a1a9026a7beb27465bce0fc12aff6e · 2026-05-12 00:47:59 -0700 · Steve Abrams

Closed-state launcher ring now reflects mode:
- retail   → red glow (unchanged default)
- wholesale → trade-blue (#5fa8ff) ring + blue tag 'TRADE · BIG RED'
- admin    → operator-amber (#f0a93a) ring + amber tag 'OPERATOR · BIG RED'

Live (open) state still goes green across all modes — preserves the
red→green active-chat metaphor while letting the closed launcher
telegraph which persona Big Red will load when clicked.

CSS attribute selectors keyed on data-mode set on the launcher element.
Three new keyframe animations (blue/amber/green pulses) match the
existing red rhythm. Tag color shadow follows mode on hover.

YOLO tick 1 — next tick (T+30m): Shopify lookup TTL cache.

Files touched

Diff

commit 56b06d56f5a1a9026a7beb27465bce0fc12aff6e
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue May 12 00:47:59 2026 -0700

    feat(big-red/tick1): per-mode launcher ring color (next: shopify TTL cache)
    
    Closed-state launcher ring now reflects mode:
    - retail   → red glow (unchanged default)
    - wholesale → trade-blue (#5fa8ff) ring + blue tag 'TRADE · BIG RED'
    - admin    → operator-amber (#f0a93a) ring + amber tag 'OPERATOR · BIG RED'
    
    Live (open) state still goes green across all modes — preserves the
    red→green active-chat metaphor while letting the closed launcher
    telegraph which persona Big Red will load when clicked.
    
    CSS attribute selectors keyed on data-mode set on the launcher element.
    Three new keyframe animations (blue/amber/green pulses) match the
    existing red rhythm. Tag color shadow follows mode on hover.
    
    YOLO tick 1 — next tick (T+30m): Shopify lookup TTL cache.
---
 public/widget.js | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 81 insertions(+), 2 deletions(-)

diff --git a/public/widget.js b/public/widget.js
index 11291e1..1cd9b49 100644
--- a/public/widget.js
+++ b/public/widget.js
@@ -201,6 +201,74 @@
       .bigred-tag { right: 80px; bottom: 28px; font-size: 9px; }
       .bigred-panel { left: 8px; right: 8px; bottom: 8px; width: auto; height: min(560px, calc(100vh - 40px)); }
     }
+
+    /* Per-mode launcher ring — closed state only. Live (open) state stays
+       green across all modes to signal "actively chatting" universally.
+       Wholesale = trade-blue, admin = operator-amber, retail = default red. */
+    .bigred-launcher[data-mode="wholesale"] {
+      box-shadow:
+        0 0 0 3px #5fa8ff,
+        0 0 0 5px rgba(95,168,255,.18),
+        0 8px 24px rgba(95,168,255,.5),
+        0 2px 10px rgba(0,0,0,.4);
+      animation: bigred-pulse-blue 2.8s ease-in-out infinite;
+    }
+    .bigred-launcher[data-mode="wholesale"]::after {
+      background: radial-gradient(circle at 35% 30%, rgba(140,190,255,.18) 0%, rgba(46,96,180,.40) 65%, rgba(20,50,120,.52) 100%);
+    }
+    .bigred-launcher[data-mode="admin"] {
+      box-shadow:
+        0 0 0 3px #f0a93a,
+        0 0 0 5px rgba(240,169,58,.22),
+        0 8px 24px rgba(240,169,58,.5),
+        0 2px 10px rgba(0,0,0,.4);
+      animation: bigred-pulse-amber 2.2s ease-in-out infinite;
+    }
+    .bigred-launcher[data-mode="admin"]::after {
+      background: radial-gradient(circle at 35% 30%, rgba(255,220,140,.20) 0%, rgba(180,120,30,.40) 65%, rgba(90,55,15,.55) 100%);
+    }
+    /* Live state always wins — green ring overrides mode ring while open. */
+    .bigred-launcher.live[data-mode] {
+      box-shadow:
+        0 0 0 3px #2fbf4a,
+        0 0 0 5px rgba(47,191,74,.20),
+        0 8px 28px rgba(47,191,74,.55),
+        0 2px 10px rgba(0,0,0,.4);
+      animation: bigred-pulse-green 1.8s ease-in-out infinite;
+    }
+    @keyframes bigred-pulse-blue {
+      0%, 100% { box-shadow:
+        0 0 0 3px #5fa8ff,
+        0 0 0 5px rgba(95,168,255,.18),
+        0 8px 24px rgba(95,168,255,.5),
+        0 2px 10px rgba(0,0,0,.4); }
+      50% { box-shadow:
+        0 0 0 3px #8cc3ff,
+        0 0 0 7px rgba(140,195,255,.24),
+        0 8px 32px rgba(140,195,255,.72),
+        0 2px 12px rgba(0,0,0,.45); }
+    }
+    @keyframes bigred-pulse-amber {
+      0%, 100% { box-shadow:
+        0 0 0 3px #f0a93a,
+        0 0 0 5px rgba(240,169,58,.22),
+        0 8px 24px rgba(240,169,58,.5),
+        0 2px 10px rgba(0,0,0,.4); }
+      50% { box-shadow:
+        0 0 0 3px #ffcc60,
+        0 0 0 7px rgba(255,204,96,.28),
+        0 8px 32px rgba(255,204,96,.72),
+        0 2px 12px rgba(0,0,0,.45); }
+    }
+    /* Tag color follows mode in closed state */
+    .bigred-launcher[data-mode="wholesale"]:hover + .bigred-tag {
+      color: #8cc3ff;
+      text-shadow: 0 0 12px rgba(140,195,255,.55), 0 0 2px rgba(0,0,0,.6);
+    }
+    .bigred-launcher[data-mode="admin"]:hover + .bigred-tag {
+      color: #ffcc60;
+      text-shadow: 0 0 12px rgba(255,204,96,.55), 0 0 2px rgba(0,0,0,.6);
+    }
   `;
 
   const style = document.createElement('style');
@@ -211,7 +279,14 @@
   const launcher = document.createElement('button');
   launcher.type = 'button';
   launcher.className = 'bigred-launcher';
-  launcher.setAttribute('aria-label', 'Open Big Red voice chat');
+  // data-mode drives the closed-state ring color via CSS attribute selectors
+  // (retail = default red, wholesale = blue, admin = amber).
+  launcher.setAttribute('data-mode', mode);
+  launcher.setAttribute('aria-label', mode === 'admin'
+    ? 'Open Big Red (admin operator mode)'
+    : mode === 'wholesale'
+      ? 'Open Big Red (trade chat)'
+      : 'Open Big Red voice chat');
   // Avatar image — falls back to Steve's stored avatar at /avatar/steve.jpg.
   // Add a query bust so updated frames refresh on next page load.
   const avatarImg = document.createElement('img');
@@ -229,7 +304,11 @@
 
   const tag = document.createElement('div');
   tag.className = 'bigred-tag';
-  tag.textContent = 'TALK TO BIG RED';
+  tag.textContent = mode === 'admin'
+    ? 'OPERATOR · BIG RED'
+    : mode === 'wholesale'
+      ? 'TRADE · BIG RED'
+      : 'TALK TO BIG RED';
 
   const panel = document.createElement('div');
   panel.className = 'bigred-panel';

← bd46b8f feat(big-red/tick0): chat-only /embed.html (panel iframe rep  ·  back to Big Red  ·  feat(big-red/tick2): Shopify lookup 60s TTL cache (next: SKU 8379170 →