← back to Losangelesfabrics Tmp

index.html

3797 lines

<!DOCTYPE html>
<html lang="en">
<head>
<!-- Google Analytics 4 (Los Angeles Fabrics) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-3R2Q55DMKL"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'G-3R2Q55DMKL');
</script>

<!-- Anti-flash theme: read saved theme before paint so the page never flickers light → dark -->
<script>
  (function() {
    try {
      var t = localStorage.getItem('laf_theme');
      if (!t) t = 'dark';
      document.documentElement.setAttribute('data-theme', t);
    } catch (e) {
      document.documentElement.setAttribute('data-theme', 'dark');
    }
  })();
</script>

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Los Angeles Fabrics — Commercial Fabric &amp; Drapery</title>
    <meta name="description" content="Los Angeles Fabrics — commercial-grade fabrics and drapery for designers, architects, and contract installations. Free memo samples; trade service direct from the mill.">
    <!-- TODO 2026-05-07: replace favicon with a real LA Fabrics asset; current path is a Novasuede leftover -->
    <link rel="icon" type="image/jpeg" href="/images/favicon.jpg">
    <link rel="shortcut icon" type="image/jpeg" href="/images/favicon.jpg">

    <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,500;1,300;1,400;1,500&display=swap" rel="stylesheet">

    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
            line-height: 1.6;
            color: #f5f1e8;
            background: #0a0a0a;
        }

        header {
            background: rgba(10, 10, 10, 0.78);
            backdrop-filter: blur(14px);
            -webkit-backdrop-filter: blur(14px);
            box-shadow: 0 1px 0 rgba(245, 241, 232, 0.08);
            position: fixed;
            width: 100%;
            top: 0;
            z-index: 1000;
        }
        header .logo h1 { color: #f5f1e8 !important; font-weight: 300; letter-spacing: 0.18em; font-size: 1.05rem; }
        header nav a, header .nav-container a { color: rgba(245, 241, 232, 0.85) !important; }
        header nav a:hover, header .nav-container a:hover { color: #fff !important; }

        .nav-container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 1rem 2rem;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .logo h1 {
            color: #DDAB1E;
            font-size: 1.8rem;
        }

        .logo h1::before {
            content: '';
            display: inline-block;
            width: 28px;
            height: 28px;
            background-image: url('https://losangelesfabrics.com/images/colors/losangelesfabrics-ruby.jpg');
            background-size: cover;
            background-position: center;
            border-radius: 50%;
            margin-right: 10px;
            vertical-align: middle;
            box-shadow: 0 2px 4px rgba(0,0,0,0.2);
        }

        .nav-menu {
            list-style: none;
            display: flex;
            gap: 2rem;
        }

        .nav-menu a {
            color: #333;
            text-decoration: none;
            font-weight: 500;
            transition: color 0.3s;
        }

        .nav-menu a:hover {
            color: #DDAB1E;
        }

        .mobile-toggle {
            display: none;
            flex-direction: column;
            background: none;
            border: none;
            cursor: pointer;
        }

        .mobile-toggle span {
            width: 25px;
            height: 3px;
            background: #333;
            margin: 3px 0;
            transition: 0.3s;
        }

        .hero {
            margin-top: 0;
            padding: 0;
            background: #000;
            text-align: center;
            position: relative;
            min-height: 100vh;
            overflow: hidden;
            isolation: isolate;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        /* Rotating background layers — full-bleed crossfade through 5 dark/jewel suede tones */
        .hero-bg {
            position: absolute;
            inset: 0;
            background-size: cover;
            background-position: center;
            background-repeat: no-repeat;
            opacity: 0;
            transition: opacity 2200ms cubic-bezier(0.4, 0, 0.2, 1);
            z-index: 0;
            will-change: opacity;
        }
        .hero-bg.on { opacity: 1; }
        /* Subtle vignette overlay to deepen the edges and hold the wordmark */
        .hero::after {
            content: '';
            position: absolute;
            inset: 0;
            background: radial-gradient(ellipse at center, rgba(0,0,0,0.0) 35%, rgba(0,0,0,0.55) 100%);
            z-index: 1;
            pointer-events: none;
        }
        .hero .hero-content {
            position: relative;
            z-index: 2;
            padding: 0 24px;
        }
        @media (max-width: 768px) {
            .hero { min-height: 88vh; }
        }

        /* No white veil — the suede tones already provide the right backdrop on black */
        .hero::before { content: none; }

        .hero-content {
            position: relative;
            z-index: 2;
            max-width: 920px;
            margin: 0 auto;
            text-align: center;
        }

        /* Drapery removed — hidden if any leftover markup tries to render it */
        .curtain { display: none !important; }

        /* Simple Gucci-style wordmark — wide-tracked light caps in white */
        .ns-wordmark-link {
            display: inline-block;
            text-decoration: none;
            cursor: pointer;
        }
        .ns-wordmark {
            font-family: -apple-system, "Helvetica Neue", "Inter", Arial, sans-serif;
            font-weight: 200;
            font-size: clamp(36px, 6.5vw, 96px);
            letter-spacing: 0.32em;
            line-height: 1;
            color: #f5f1e8;
            margin: 0 auto 2rem;
            text-shadow: 0 4px 22px rgba(0, 0, 0, 0.55);
            text-transform: uppercase;
            transition: color 280ms ease;
        }
        .ns-wordmark sup {
            font-size: 0.32em;
            font-weight: 300;
            letter-spacing: 0;
            position: relative;
            top: -1.2em;
            margin-left: 0.08em;
        }
        .ns-wordmark-link:hover .ns-wordmark { color: #fff; }
        @media (max-width: 768px) {
            .ns-wordmark { letter-spacing: 0.22em; margin-bottom: 1.5rem; }
        }

        .hero h2 {
            font-family: 'Cormorant Garamond', 'Didot', 'Bodoni 72', Georgia, serif;
            font-style: italic;
            font-weight: 300;
            font-size: clamp(20px, 2.4vw, 32px);
            line-height: 1.35;
            margin: 0 auto 1.6rem;
            max-width: 640px;
            color: #f5f1e8;
            letter-spacing: 0.01em;
            text-shadow: 0 2px 16px rgba(0, 0, 0, 0.6);
        }

        .hero p {
            font-size: 13px;
            letter-spacing: 0.22em;
            text-transform: uppercase;
            font-weight: 300;
            margin: 0 auto 2.25rem;
            max-width: 580px;
            color: rgba(245, 241, 232, 0.78);
            text-shadow: 0 1px 8px rgba(0, 0, 0, 0.6);
        }
        .hero .authorized-dealer { margin-top: 2.5rem; }
        .hero .authorized-dealer p {
            font-size: 9.5px;
            letter-spacing: 0.42em;
            color: rgba(245, 241, 232, 0.55);
            margin: 0;
        }

        /* Gucci-style hairline button — white border on black, no fill */
        .sample-btn {
            background: transparent;
            color: #f5f1e8;
            border: 1px solid rgba(245, 241, 232, 0.85);
            padding: 14px 38px;
            font-family: -apple-system, "SF Pro Text", "Helvetica Neue", Helvetica, sans-serif;
            font-size: 11px;
            font-weight: 400;
            letter-spacing: 0.32em;
            text-transform: uppercase;
            border-radius: 0;
            cursor: pointer;
            transition: background 280ms ease, color 280ms ease, border-color 280ms ease;
        }
        .sample-btn:hover {
            background: #f5f1e8;
            color: #0a0a0a;
            border-color: #f5f1e8;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 2rem;
        }

        .colors-section {
            padding: 4rem 0;
            background: #0a0a0a;
            color: #f5f1e8;
        }

        .colors-section h2 {
            text-align: center;
            font-size: 2.5rem;
            margin-bottom: 1rem;
            color: #f5f1e8;
        }

        .section-subtitle {
            text-align: center;
            color: rgba(245, 241, 232, 0.6);
            margin-bottom: 3rem;
        }

        .colors-grid {
            display: grid;
            grid-template-columns: repeat(var(--cols, 6), minmax(0, 1fr));
            gap: 1.5rem;
        }
        @media (max-width: 720px) { .colors-grid { grid-template-columns: repeat(2, 1fr) !important; } }
        @media (min-width: 721px) and (max-width: 1024px) { .colors-grid { grid-template-columns: repeat(3, 1fr) !important; } }
        /* Sort + density toolbar (CLAUDE.md "every grid needs sort + density" rule) */
        .ns-toolbar { display: inline-flex; gap: 18px; align-items: center; margin-left: auto; flex-wrap: wrap; }
        .ns-ctrl { display: inline-flex; gap: 8px; align-items: center; font-family: ui-sans-serif, system-ui, sans-serif; font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: #666; }
        .ns-ctrl select { background: #fff; color: #222; border: 1px solid #ccc; padding: 5px 8px; font: inherit; font-size: 12px; text-transform: none; letter-spacing: 0; border-radius: 3px; cursor: pointer; }
        .ns-ctrl select:focus { outline: none; border-color: #DDAB1E; }
        .ns-ctrl input[type="range"] { width: 90px; accent-color: #DDAB1E; cursor: pointer; }
        .ns-ctrl #nsColsVal { font-family: ui-monospace, "SF Mono", Menlo, monospace; color: #DDAB1E; min-width: 14px; text-align: right; }

        .color-item {
            cursor: pointer;
            transition: transform 0.3s;
            text-align: center;
            position: relative;
            background: #0a0a0a;
            border-radius: 8px;
            overflow: hidden;
        }

        .color-item:hover {
            transform: translateY(-5px);
        }

        /* Photo-only until hover — chip-actions + name/code overlay reveal on hover */
        .color-item .color-info {
            position: absolute;
            left: 0; right: 0; bottom: 0;
            margin: 0;
            padding: 0.85rem 0.85rem 0.7rem;
            background: linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0.85) 60%);
            opacity: 0;
            transform: translateY(8px);
            transition: opacity 220ms ease, transform 220ms ease;
            text-align: left;
            pointer-events: none;
            color: #fff;
        }
        .color-item:hover .color-info,
        .color-item:focus-within .color-info {
            opacity: 1;
            transform: translateY(0);
            pointer-events: auto;
        }
        .color-item .color-name { color: #fff; }
        .color-item .color-code { color: rgba(255,255,255,0.72); }
        .color-item .chip-actions { margin-top: 0.55rem; gap: 0.45rem; }

        .color-item .new-badge {
            position: absolute;
            top: 6px;
            right: 6px;
            background: #8b6914;
            color: #fff;
            font-size: 0.65rem;
            font-weight: 700;
            letter-spacing: 0.05em;
            padding: 2px 8px;
            border-radius: 10px;
            box-shadow: 0 1px 4px rgba(0,0,0,0.2);
            z-index: 2;
        }

        .color-swatch {
            width: 100%;
            height: 120px;
            border-radius: 8px;
            overflow: hidden;
            box-shadow: 0 2px 8px rgba(0,0,0,0.15);
            background: #f0f0f0;
        }

        .color-swatch img {
            width: 100%;
            height: 100%;
            object-fit: cover;
        }

        .color-info {
            margin-top: 0.5rem;
        }

        .color-name {
            font-weight: 600;
            color: #333;
        }

        .color-code {
            font-size: 0.85rem;
            color: #999;
        }

        .chip-actions {
            display: flex;
            gap: 6px;
            margin-top: 6px;
            justify-content: center;
            flex-wrap: wrap;
        }
        .drapery-btn, .sample-btn-chip {
            padding: 5px 10px;
            font-size: 0.75rem;
            font-weight: 500;
            color: #fff;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            transition: background 0.2s, transform 0.05s;
        }
        .drapery-btn { background: #DDAB1E; }
        .drapery-btn:hover { background: #b3870e; }
        .sample-btn-chip { background: #5a5a5a; }
        .sample-btn-chip:hover { background: #2a2a2a; }
        .drapery-btn:active, .sample-btn-chip:active { transform: scale(0.96); }

        /* When the page is showing a drapery swag, get OUT of the way so the
           recolored swag shows full strength. No white fade, no blur, no filter.
           Sections become fully transparent; chips/text get a small dark plate
           so they remain readable on top of saturated colors. */
        body.has-drapery .colors-section,
        body.has-drapery .specifications-section,
        body.has-drapery .features-section,
        body.has-drapery .installation-section,
        body.has-drapery footer {
            background: transparent !important;
        }
        /* Kill the white veil over the hero so the recolored swag reads true. */
        body.has-drapery .hero::before { background: transparent !important; }
        /* Header stays opaque-ish so nav remains legible. */
        body.has-drapery header {
            background: rgba(20, 20, 20, 0.55) !important;
            backdrop-filter: blur(6px);
            -webkit-backdrop-filter: blur(6px);
        }
        body.has-drapery header .nav-menu a,
        body.has-drapery header .logo h1 { color: #fff !important; }
        /* Chip swatches and section cards keep their own backgrounds, just give
           text on the swag a soft dark backplate for legibility. */
        body.has-drapery .colors-section h2,
        body.has-drapery .colors-section .section-subtitle,
        body.has-drapery .specifications-section h2,
        body.has-drapery .features-section h2,
        body.has-drapery .installation-section h2,
        body.has-drapery .installation-section .section-subtitle,
        body.has-drapery .hero h2,
        body.has-drapery .hero p,
        body.has-drapery .hero .authorized-dealer p {
            color: #fff !important;
            text-shadow: 0 2px 12px rgba(0,0,0,0.85), 0 1px 4px rgba(0,0,0,0.95) !important;
        }
        body.has-drapery .authorized-dealer {
            background: rgba(0,0,0,0.55) !important;
            border-color: #DDAB1E !important;
            backdrop-filter: blur(2px);
            -webkit-backdrop-filter: blur(2px);
        }
        body.has-drapery .color-item { background: rgba(255,255,255,0.94); border-radius: 8px; }

        /* Contact info bar — pinned to the top, above the main nav. */
        .contact-bar {
            position: fixed;
            top: 0; left: 0; right: 0;
            z-index: 200;
            background: linear-gradient(90deg, #1a1a1a 0%, #2a2a2a 100%);
            color: #f5e7c0;
            font-size: 13px;
            padding: 6px 0;
            border-bottom: 2px solid #DDAB1E;
        }
        .contact-bar-inner {
            max-width: 1280px;
            margin: 0 auto;
            padding: 0 16px;
            display: flex;
            align-items: center;
            gap: 22px;
            flex-wrap: wrap;
            justify-content: center;
        }
        .cb-link {
            color: #f5e7c0;
            text-decoration: none;
            display: inline-flex;
            align-items: center;
            gap: 6px;
            transition: color 0.15s;
        }
        .cb-link:hover { color: #DDAB1E; }
        .cb-static { color: #aaa; cursor: default; }
        .cb-ico { font-size: 14px; }
        .cb-cta {
            background: #DDAB1E;
            color: #1a1a1a;
            border: none;
            padding: 5px 14px;
            border-radius: 4px;
            font-size: 12px;
            font-weight: 700;
            cursor: pointer;
            margin-left: auto;
            transition: background 0.15s, transform 0.05s;
        }
        .cb-cta:hover { background: #b3870e; color: #fff; }
        .cb-cta:active { transform: scale(0.97); }
        /* Push the existing fixed header down so it sits below the contact bar. */
        header { top: 36px !important; }
        body { padding-top: 36px; }
        @media (max-width: 720px) {
            .contact-bar { font-size: 11px; padding: 5px 0; }
            .contact-bar-inner { gap: 12px; }
            .cb-static { display: none; }
            header { top: 30px !important; }
            body { padding-top: 30px; }
        }
        @media (max-width: 480px) {
            .cb-text { display: none; } /* icons-only on tiny screens */
            .cb-cta { padding: 4px 8px; font-size: 11px; }
        }

        /* Fixed left + right drapery panels — visible at full strength for the
           entire scroll of the page, not just the hero. Each is the recolored
           swag image cropped to show only its respective edge. */
        .has-drapery-side {
            position: fixed;
            top: 0;
            bottom: 0;
            width: 12vw;
            min-width: 90px;
            max-width: 220px;
            z-index: 50;
            pointer-events: none;
            background-size: auto 100vh;
            background-repeat: no-repeat;
            display: none; /* shown only when body.has-drapery */
            box-shadow: 0 0 30px rgba(0,0,0,0.25);
        }
        .has-drapery-side.left  { left: 0;  background-position: left center; }
        .has-drapery-side.right { right: 0; background-position: right center; }
        body.has-drapery .has-drapery-side { display: block; }
        @media (max-width: 600px) {
            body.has-drapery .has-drapery-side { display: none; } /* mobile content too narrow */
        }

        /* Standalone "New Arrivals 2026" section that sits above the main collection */
        .new-arrivals-section {
            padding: 3rem 0 1.5rem;
            background: #0a0a0a;
            color: #f5f1e8;
        }
        .new-arrivals-header {
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 14px;
            margin-bottom: 6px;
            flex-wrap: wrap;
        }
        .new-arrivals-section h2 {
            color: #DDAB1E !important;
            font-size: 2rem;
            margin: 0;
        }
        .new-arrivals-pill {
            background: #DDAB1E;
            color: #1a1a1a;
            font-weight: 700;
            font-size: 0.85rem;
            padding: 4px 12px;
            border-radius: 999px;
            text-transform: uppercase;
            letter-spacing: 0.05em;
        }
        .new-arrivals-section .section-subtitle {
            color: #d8c79a;
            text-align: center;
            margin-bottom: 1.75rem;
        }
        .new-arrivals-section .colors-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
            gap: 1rem;
        }
        .new-arrivals-section .color-item {
            background: rgba(255, 255, 255, 0.96);
            color: #1a1a1a;
        }

        .color-filters {
            display: flex;
            gap: 8px;
            align-items: center;
            margin: 10px 0 18px;
            flex-wrap: wrap;
        }
        .color-filter-btn {
            padding: 6px 14px;
            font-size: 0.85rem;
            font-weight: 500;
            color: #555;
            background: #fff;
            border: 1px solid #ccc;
            border-radius: 999px;
            cursor: pointer;
            transition: all 0.15s;
        }
        .color-filter-btn:hover { border-color: #DDAB1E; color: #1a1a1a; }
        .color-filter-btn.active { background: #DDAB1E; border-color: #DDAB1E; color: #fff; }
        .color-count { color: #888; font-size: 0.85rem; margin-left: auto; }

        /* Color-family dot row — circular swatches that filter the grid by hue bucket. */
        .color-dots {
            display: flex;
            gap: 10px;
            align-items: center;
            margin: 6px 0 14px;
            flex-wrap: wrap;
        }
        .color-dot {
            width: 28px;
            height: 28px;
            border-radius: 50%;
            border: 2px solid rgba(0,0,0,0.12);
            padding: 0;
            cursor: pointer;
            position: relative;
            transition: transform 0.12s ease, box-shadow 0.12s ease, border-color 0.12s ease;
            box-shadow: 0 1px 3px rgba(0,0,0,0.15);
        }
        .color-dot:hover { transform: scale(1.12); border-color: rgba(0,0,0,0.45); }
        .color-dot.active {
            transform: scale(1.18);
            border-color: #1a1a1a;
            box-shadow: 0 0 0 2px #fff, 0 0 0 4px #DDAB1E;
        }
        .color-dot[data-family="all"] {
            width: auto; height: 28px; border-radius: 16px; padding: 0 12px;
            font-size: 0.75rem; font-weight: 600; color: #fff; letter-spacing: 0.04em;
        }
        .color-dot[data-family="all"] .dot-label { color: #fff; text-shadow: 0 1px 2px rgba(0,0,0,0.5); }

        .color-toolbar-row { display: flex; gap: 8px; align-items: center; margin: 6px 0 14px; flex-wrap: wrap; }

        /* Modal Styles */
        .modal {
            display: none;
            position: fixed;
            z-index: 2000;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,0.5);
        }

        .modal-content {
            background: white;
            margin: 50px auto;
            padding: 2rem;
            width: 90%;
            max-width: 500px;
            border-radius: 10px;
            position: relative;
            max-height: 90vh;
            overflow-y: auto;
        }

        .close {
            position: absolute;
            right: 1.5rem;
            top: 1.5rem;
            font-size: 2rem;
            cursor: pointer;
            color: #999;
        }

        .close:hover {
            color: #333;
        }

        .modal h2 {
            margin-bottom: 1.5rem;
            color: #333;
        }

        .color-preview {
            text-align: center;
            padding: 0 0 1.25rem;
            background: transparent;
            border-radius: 0;
            margin-bottom: 1.5rem;
            border-bottom: 1px solid rgba(0,0,0,0.08);
        }

        .color-preview img {
            display: block;
            width: 100%;
            max-width: 460px;
            height: 280px;
            border-radius: 4px;
            object-fit: cover;
            margin: 0 auto;
            box-shadow: 0 8px 24px rgba(0,0,0,0.18);
            transition: opacity 320ms ease;
            background: #1a1a1a; /* shows while image loads */
        }
        .color-preview img.loading { opacity: 0.35; }

        .color-preview span#colorPreviewName {
            display: block;
            margin-top: 0.9rem;
            font-family: -apple-system, "Helvetica Neue", Arial, sans-serif;
            font-size: 11px;
            font-weight: 400;
            letter-spacing: 0.32em;
            text-transform: uppercase;
            color: #1a1a1a;
        }
        .color-preview .preview-hex {
            display: block;
            font-family: ui-monospace, "SF Mono", Menlo, monospace;
            font-size: 11px;
            color: #888;
            margin-top: 4px;
            letter-spacing: 0.04em;
            text-transform: lowercase;
        }

        /* Multi-select sample request — pick + Add chips list */
        .color-pick-row {
            display: flex;
            gap: 10px;
            align-items: stretch;
        }
        .color-pick-row select {
            flex: 1;
            min-width: 0;
        }
        .add-to-request-btn {
            white-space: nowrap;
            background: #1a1a1a;
            color: #f5f1e8;
            border: 1px solid #1a1a1a;
            padding: 0 18px;
            font-family: -apple-system, "Helvetica Neue", Arial, sans-serif;
            font-size: 11px;
            font-weight: 500;
            letter-spacing: 0.18em;
            text-transform: uppercase;
            border-radius: 4px;
            cursor: pointer;
            transition: background 220ms ease, color 220ms ease;
        }
        .add-to-request-btn:hover { background: #f5f1e8; color: #1a1a1a; }
        .add-to-request-btn:disabled {
            background: #ddd;
            color: #999;
            border-color: #ddd;
            cursor: not-allowed;
        }

        .request-list {
            margin-top: 1rem;
            padding: 14px 16px;
            background: rgba(184, 153, 104, 0.06);
            border: 1px solid rgba(0, 0, 0, 0.08);
            border-radius: 4px;
        }
        .request-list-header {
            display: flex;
            justify-content: space-between;
            align-items: baseline;
            font-size: 9px;
            letter-spacing: 0.32em;
            text-transform: uppercase;
            color: #666;
            margin-bottom: 10px;
            font-weight: 500;
        }
        .request-list-header .request-count {
            font-family: ui-monospace, "SF Mono", Menlo, monospace;
            color: #1a1a1a;
            font-weight: 600;
            letter-spacing: 0.05em;
        }
        .request-chips {
            display: flex;
            flex-wrap: wrap;
            gap: 6px;
        }
        .request-chip {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            padding: 6px 10px 6px 8px;
            background: #fff;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 12px;
            color: #1a1a1a;
            line-height: 1;
        }
        .request-chip .chip-swatch {
            display: inline-block;
            width: 14px;
            height: 14px;
            border-radius: 2px;
            border: 1px solid rgba(0,0,0,0.1);
            flex: none;
        }
        .request-chip button {
            background: transparent;
            border: none;
            color: #999;
            cursor: pointer;
            font-size: 16px;
            line-height: 1;
            padding: 0 0 0 4px;
            margin: 0;
        }
        .request-chip button:hover { color: #1a1a1a; }
        .request-list.empty { display: none; }

        .form-group {
            margin-bottom: 1.5rem;
        }

        .form-group label {
            display: block;
            margin-bottom: 0.5rem;
            font-weight: 600;
            color: #333;
        }

        .form-group input,
        .form-group select,
        .form-group textarea {
            width: 100%;
            padding: 0.75rem;
            border: 1px solid #ddd;
            border-radius: 5px;
            font-size: 1rem;
            font-family: inherit;
        }

        .form-group textarea {
            min-height: 120px;
            resize: vertical;
        }

        .submit-btn {
            width: 100%;
            background: #DDAB1E;
            color: white;
            border: none;
            padding: 1rem;
            font-size: 1.1rem;
            border-radius: 5px;
            cursor: pointer;
            font-weight: 600;
        }

        .submit-btn:hover {
            background: #c4981a;
        }

        /* Authorized Dealer Badge */
        .authorized-dealer {
            margin-top: 2rem;
            padding: 1rem;
            background: rgba(0, 0, 0, 0.55);
            border-radius: 8px;
            border: 2px solid #DDAB1E;
            backdrop-filter: blur(2px);
            -webkit-backdrop-filter: blur(2px);
        }

        .authorized-dealer p {
            color: #ffffff;
            font-weight: 600;
            margin: 0;
            text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
        }

        /* Specifications Section */
        .specifications-section {
            padding: 4rem 0;
            background: #0a0a0a;
            color: #f5f1e8;
        }
        .specifications-section h2, .specifications-section .section-subtitle, .specifications-section p, .specifications-section li, .specifications-section td, .specifications-section th { color: #f5f1e8; }
        .specifications-section .section-subtitle { color: rgba(245, 241, 232, 0.6); }

        .specifications-section h2 {
            text-align: center;
            font-size: 2.5rem;
            margin-bottom: 3rem;
            color: #222;
        }

        .specs-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
            gap: 1.5rem;
            max-width: 900px;
            margin: 0 auto;
        }

        .spec-item {
            display: flex;
            justify-content: space-between;
            padding: 1rem;
            background: white;
            border-radius: 8px;
            border: 1px solid #e0e0e0;
        }

        .spec-label {
            font-weight: 600;
            color: #666;
        }

        .spec-value {
            color: #333;
            text-align: right;
        }

        /* Features Section */
        .features-section {
            padding: 4rem 0;
            background: #0a0a0a;
            color: #f5f1e8;
        }
        .features-section h2, .features-section h3, .features-section p, .features-section li, .features-section .section-subtitle { color: #f5f1e8; }
        .features-section .section-subtitle { color: rgba(245, 241, 232, 0.6); }

        .features-section h2 {
            text-align: center;
            font-size: 2.5rem;
            margin-bottom: 3rem;
            color: #222;
        }

        .features-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 2rem;
        }

        .feature-card {
            padding: 2rem;
            background: #f8f8f8;
            border-radius: 10px;
            text-align: center;
            transition: transform 0.3s;
        }

        .feature-card:hover {
            transform: translateY(-5px);
            box-shadow: 0 5px 20px rgba(0,0,0,0.1);
        }

        .feature-card h3 {
            margin-bottom: 1rem;
            color: #333;
            font-size: 1.3rem;
        }

        .feature-card p {
            color: #666;
            line-height: 1.6;
        }

        /* Installation Section */
        .installation-section {
            padding: 4rem 0;
            background: #0a0a0a;
            color: #f5f1e8;
        }
        .installation-section h2, .installation-section h3, .installation-section p, .installation-section li, .installation-section .section-subtitle { color: #f5f1e8; }
        .installation-section .section-subtitle { color: rgba(245, 241, 232, 0.6); }

        .installation-section h2 {
            text-align: center;
            font-size: 2.5rem;
            margin-bottom: 1rem;
            color: #f5f1e8;
        }

        .info-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
            gap: 2rem;
            margin-bottom: 3rem;
        }

        .info-card-link {
            text-decoration: none;
            display: block;
            transition: transform 0.3s;
        }

        .info-card-link:hover {
            transform: translateY(-5px);
        }

        .info-card {
            background: white;
            padding: 2rem;
            border-radius: 10px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            transition: box-shadow 0.3s, border-color 0.3s;
            border: 2px solid transparent;
            height: 100%;
            cursor: pointer;
        }

        .info-card:hover {
            box-shadow: 0 5px 20px rgba(0,0,0,0.15);
            border-color: #DDAB1E;
        }

        .info-card h3 {
            margin-bottom: 1rem;
            color: #333;
            font-size: 1.2rem;
        }

        .info-card p {
            color: #666;
            line-height: 1.6;
            margin-bottom: 1rem;
        }

        .card-arrow {
            color: #DDAB1E;
            font-weight: 600;
            display: inline-block;
            transition: margin-left 0.3s;
        }

        .info-card:hover .card-arrow {
            margin-left: 5px;
        }

        .care-highlight {
            background: white;
            padding: 2.5rem;
            border-radius: 10px;
            max-width: 800px;
            margin: 0 auto;
            text-align: center;
            border: 2px solid #DDAB1E;
        }

        .care-highlight h3 {
            color: #333;
            margin-bottom: 1rem;
            font-size: 1.5rem;
        }

        .care-highlight p {
            color: #666;
            line-height: 1.8;
        }

        /* Footer */
        footer {
            background: #0a0a0a;
            color: #f5f1e8;
            padding: 3rem 0 1rem;
            border-top: 1px solid rgba(245, 241, 232, 0.08);
        }

        .footer-content {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
            gap: 2rem;
            margin-bottom: 2rem;
        }

        .footer-section h3 {
            margin-bottom: 1rem;
            color: #DDAB1E;
        }

        .footer-section p {
            opacity: 0.9;
            margin-bottom: 0.5rem;
            line-height: 1.6;
        }

        .footer-section ul {
            list-style: none;
        }

        .footer-section ul li {
            margin-bottom: 0.5rem;
        }

        .footer-section ul li a {
            color: white;
            text-decoration: none;
            opacity: 0.9;
            transition: opacity 0.3s;
        }

        .footer-section ul li a:hover {
            opacity: 1;
            color: #DDAB1E;
        }

        .authorized {
            background: rgba(221, 171, 30, 0.2);
            padding: 0.5rem;
            border-radius: 5px;
            font-size: 0.9rem;
        }

        .footer-sample-btn {
            background: #DDAB1E;
            color: white;
            border: none;
            padding: 0.75rem 1.5rem;
            font-size: 1rem;
            border-radius: 5px;
            cursor: pointer;
            margin-top: 1rem;
            transition: background 0.3s;
        }

        .footer-sample-btn:hover {
            background: #c4981a;
        }

        .footer-bottom {
            text-align: center;
            padding-top: 2rem;
            border-top: 1px solid rgba(255,255,255,0.1);
            opacity: 0.7;
        }

        /* Mobile Styles */
        @media (max-width: 768px) {
            .mobile-toggle {
                display: flex;
            }

            .nav-menu {
                display: none;
                position: absolute;
                top: 100%;
                left: 0;
                width: 100%;
                background: white;
                flex-direction: column;
                padding: 1rem;
                box-shadow: 0 2px 5px rgba(0,0,0,0.1);
            }

            .nav-menu.active {
                display: flex;
            }

            .hero h2 {
                font-size: 2rem;
            }

            .colors-grid {
                grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
                gap: 1rem;
            }

            .color-swatch {
                height: 80px;
            }

            .modal-content {
                margin: 20px;
                width: calc(100% - 40px);
            }

            .color-preview img {
                width: 150px;
                height: 150px;
            }

            .specs-grid {
                grid-template-columns: 1fr;
            }

            .features-grid {
                grid-template-columns: 1fr;
            }

            .info-grid {
                grid-template-columns: 1fr;
            }

            .footer-content {
                grid-template-columns: 1fr;
                text-align: center;
            }

            .authorized-dealer {
                margin-top: 1.5rem;
                padding: 0.75rem;
            }

            .care-highlight {
                padding: 1.5rem;
            }

            .specifications-section,
            .features-section,
            .installation-section {
                padding: 2rem 0;
            }

            .specifications-section h2,
            .features-section h2,
            .installation-section h2 {
                font-size: 2rem;
            }
        }

        /* ─── Unified dark-theme overrides — keeps every section on #0a0a0a ─── */
        .color-search input,
        #colorSearch {
            background: rgba(245, 241, 232, 0.04) !important;
            border: 1px solid rgba(245, 241, 232, 0.18) !important;
            color: #f5f1e8 !important;
            padding: 12px 16px;
            font-family: -apple-system, "Helvetica Neue", Arial, sans-serif;
            font-size: 13px;
            letter-spacing: 0.04em;
            border-radius: 4px;
            width: 100%;
            max-width: 460px;
        }
        .color-search input::placeholder, #colorSearch::placeholder { color: rgba(245, 241, 232, 0.4); }
        .color-search input:focus, #colorSearch:focus { outline: none; border-color: rgba(245, 241, 232, 0.45) !important; }
        .color-search { display: flex; justify-content: center; margin-bottom: 1.4rem; }

        .color-filter-btn {
            background: transparent;
            color: rgba(245, 241, 232, 0.75);
            border: 1px solid rgba(245, 241, 232, 0.22);
            padding: 9px 18px;
            font-size: 11px;
            letter-spacing: 0.18em;
            text-transform: uppercase;
            border-radius: 4px;
            cursor: pointer;
            font-family: -apple-system, "Helvetica Neue", Arial, sans-serif;
            font-weight: 400;
            transition: all 220ms ease;
        }
        .color-filter-btn:hover { border-color: rgba(245, 241, 232, 0.7); color: #fff; background: transparent; }
        .color-filter-btn.active {
            background: #f5f1e8 !important;
            border-color: #f5f1e8 !important;
            color: #1a1a1a !important;
        }
        .color-filters {
            display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
            justify-content: center;
            padding: 0 24px;
            margin-bottom: 1.5rem;
        }
        .color-count { color: rgba(245, 241, 232, 0.55); font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; margin-left: 8px; }

        .ns-ctrl { color: rgba(245, 241, 232, 0.55); font-size: 10px; letter-spacing: 0.22em; }
        .ns-ctrl select {
            background: rgba(245, 241, 232, 0.06) !important;
            color: #f5f1e8 !important;
            border: 1px solid rgba(245, 241, 232, 0.18) !important;
        }
        .ns-ctrl select:focus { border-color: rgba(245, 241, 232, 0.45) !important; }
        .ns-ctrl input[type="range"] { accent-color: #f5f1e8; }
        .ns-ctrl #nsColsVal { color: #f5f1e8 !important; }

        /* Footer dark-theme touch-up */
        footer .footer-section h3 { color: #f5f1e8 !important; font-weight: 400; letter-spacing: 0.2em; text-transform: uppercase; font-size: 11px; }
        footer .footer-section p, footer .footer-section a, footer ul a { color: rgba(245, 241, 232, 0.65) !important; text-decoration: none; }
        footer .footer-section a:hover, footer ul a:hover { color: #fff !important; }
        footer .footer-bottom { color: rgba(245, 241, 232, 0.4); border-top: 1px solid rgba(245, 241, 232, 0.08); margin-top: 2rem; padding-top: 1.5rem; }

        /* New-arrivals heading on the unified bg — keep gold accent but trim the gradient feel */
        .new-arrivals-section h2 { color: #f5f1e8 !important; }
        .new-arrivals-section .new-arrivals-pill { background: #f5f1e8; color: #0a0a0a; }
        .new-arrivals-section .section-subtitle { color: rgba(245, 241, 232, 0.6); }

        /* Modal still light-on-dark by design — keep its own contrast island */
        .modal-content { background: #fff; color: #1a1a1a; }

        /* Internal cards in sections — flip from white islands to dark cards on dark bg */
        .spec-row {
            background: rgba(245, 241, 232, 0.04) !important;
            color: #f5f1e8 !important;
            border: 1px solid rgba(245, 241, 232, 0.10);
        }
        .feature-card {
            background: rgba(245, 241, 232, 0.04) !important;
            color: #f5f1e8 !important;
            border: 1px solid rgba(245, 241, 232, 0.10);
        }
        .feature-card h3, .feature-card p { color: #f5f1e8 !important; }
        .info-card {
            background: rgba(245, 241, 232, 0.04) !important;
            color: #f5f1e8 !important;
            border: 1px solid rgba(245, 241, 232, 0.10);
        }
        .info-card h3, .info-card p, .info-card li { color: #f5f1e8 !important; }

        /* ──────────────────────────────────────────────────────────────────
           NOVASUEDE-UX UPGRADE 2026-05-08
           Right-side hamburger · Dark/Light theme · Live product grid w/ tabs
           Sort + density slider · Product detail modal · Footer parity
           ────────────────────────────────────────────────────────────────── */

        /* CSS theme variables — flipped by [data-theme="light"] */
        :root {
            --bg: #0a0a0a;
            --bg-alt: #111;
            --surface: rgba(245, 241, 232, 0.04);
            --surface-hover: rgba(245, 241, 232, 0.08);
            --surface-strong: rgba(245, 241, 232, 0.10);
            --text: #f5f1e8;
            --text-mute: rgba(245, 241, 232, 0.65);
            --text-faint: rgba(245, 241, 232, 0.45);
            --line: rgba(245, 241, 232, 0.10);
            --line-strong: rgba(245, 241, 232, 0.22);
            --accent: #DDAB1E;
            --accent-soft: #b3870e;
            --header-bg: rgba(10, 10, 10, 0.78);
        }
        html[data-theme="light"] {
            --bg: #f5f1e8;
            --bg-alt: #fff;
            --surface: rgba(26, 26, 26, 0.04);
            --surface-hover: rgba(26, 26, 26, 0.08);
            --surface-strong: rgba(26, 26, 26, 0.12);
            --text: #1a1a1a;
            --text-mute: rgba(26, 26, 26, 0.70);
            --text-faint: rgba(26, 26, 26, 0.45);
            --line: rgba(26, 26, 26, 0.12);
            --line-strong: rgba(26, 26, 26, 0.28);
            --header-bg: rgba(245, 241, 232, 0.85);
        }

        /* Hide the cross-fleet DW nav widget on this page — our LA Fabrics
           hamburger lives in the same top-right slot and opens an
           LA-Fabrics-specific side panel. */
        #dw-fn-trigger, #dw-fn-overlay, #dw-fn-drawer { display: none !important; }

        /* ─── Right-side hamburger button (fixed top-right) ─── */
        .laf-hamburger {
            position: fixed;
            top: 14px;
            right: 16px;
            z-index: 1500;
            width: 46px;
            height: 46px;
            background: rgba(10, 10, 10, 0.78);
            border: 1px solid rgba(245, 241, 232, 0.30);
            border-radius: 50%;
            cursor: pointer;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            gap: 5px;
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            transition: background 220ms ease, transform 220ms ease;
        }
        .laf-hamburger:hover { background: rgba(221, 171, 30, 0.20); transform: scale(1.05); }
        .laf-hamburger:focus { outline: 2px solid var(--accent); outline-offset: 2px; }
        .laf-hamburger span {
            display: block;
            width: 20px;
            height: 2px;
            background: #f5f1e8;
            border-radius: 2px;
            transition: transform 250ms ease, opacity 200ms ease;
        }
        .laf-hamburger.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
        .laf-hamburger.open span:nth-child(2) { opacity: 0; }
        .laf-hamburger.open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

        /* Dark/light toggle — pinned just below the hamburger */
        .laf-theme-toggle {
            position: fixed;
            top: 68px;
            right: 16px;
            z-index: 1500;
            width: 38px;
            height: 38px;
            background: rgba(10, 10, 10, 0.78);
            border: 1px solid rgba(245, 241, 232, 0.30);
            border-radius: 50%;
            cursor: pointer;
            color: #f5f1e8;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            font-size: 15px;
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            transition: background 220ms ease, transform 220ms ease;
        }
        .laf-theme-toggle:hover { background: rgba(221, 171, 30, 0.20); transform: scale(1.05); }

        /* ─── Side-panel (slides in from the right) ─── */
        .laf-side-panel {
            position: fixed;
            top: 0;
            right: 0;
            bottom: 0;
            width: 320px;
            max-width: 86vw;
            background: linear-gradient(180deg, #0d0d0d 0%, #1a1a1a 100%);
            color: #f5f1e8;
            box-shadow: -10px 0 40px rgba(0, 0, 0, 0.55);
            transform: translateX(100%);
            transition: transform 320ms cubic-bezier(0.4, 0, 0.2, 1);
            z-index: 1400;
            overflow-y: auto;
            padding: 5rem 1.75rem 2rem;
        }
        .laf-side-panel.open { transform: translateX(0); }
        .laf-side-overlay {
            position: fixed;
            inset: 0;
            background: rgba(0, 0, 0, 0.55);
            opacity: 0;
            pointer-events: none;
            transition: opacity 320ms ease;
            z-index: 1300;
        }
        .laf-side-overlay.open { opacity: 1; pointer-events: auto; }
        .laf-side-panel h3 {
            font-family: -apple-system, "Helvetica Neue", Arial, sans-serif;
            font-weight: 300;
            font-size: 11px;
            letter-spacing: 0.32em;
            text-transform: uppercase;
            color: rgba(245, 241, 232, 0.55);
            margin-bottom: 1rem;
            padding-bottom: 0.6rem;
            border-bottom: 1px solid rgba(245, 241, 232, 0.10);
        }
        .laf-side-panel ul {
            list-style: none;
            padding: 0;
            margin: 0 0 2rem;
        }
        .laf-side-panel ul li { margin-bottom: 0.25rem; }
        .laf-side-panel ul li a {
            display: block;
            padding: 0.85rem 0.5rem;
            color: #f5f1e8;
            text-decoration: none;
            font-size: 15px;
            font-weight: 300;
            letter-spacing: 0.04em;
            border-bottom: 1px solid rgba(245, 241, 232, 0.06);
            transition: color 200ms ease, background 200ms ease, padding-left 200ms ease;
        }
        .laf-side-panel ul li a:hover {
            color: #DDAB1E;
            background: rgba(221, 171, 30, 0.06);
            padding-left: 1rem;
        }
        .laf-side-panel .side-mail {
            display: block;
            margin-top: 1.25rem;
            padding: 0.75rem 1rem;
            background: rgba(221, 171, 30, 0.10);
            border: 1px solid rgba(221, 171, 30, 0.40);
            color: #DDAB1E;
            text-align: center;
            text-decoration: none;
            border-radius: 4px;
            font-size: 13px;
            letter-spacing: 0.06em;
            transition: background 200ms ease;
        }
        .laf-side-panel .side-mail:hover {
            background: rgba(221, 171, 30, 0.20);
            color: #fff;
        }
        .laf-side-panel .side-fineprint {
            margin-top: 1.5rem;
            font-size: 11px;
            color: rgba(245, 241, 232, 0.45);
            line-height: 1.5;
        }

        @media (max-width: 720px) {
            .laf-side-panel { width: 88vw; }
        }

        /* ─── Live product grid section ─── */
        .laf-products-section {
            padding: 4rem 0 5rem;
            background: var(--bg);
            color: var(--text);
        }
        .laf-products-section h2 {
            text-align: center;
            font-size: clamp(28px, 4vw, 44px);
            font-family: -apple-system, "Helvetica Neue", Arial, sans-serif;
            font-weight: 200;
            letter-spacing: 0.12em;
            text-transform: uppercase;
            color: var(--text);
            margin-bottom: 0.5rem;
        }
        .laf-products-section .section-subtitle {
            text-align: center;
            color: var(--text-mute);
            font-family: 'Cormorant Garamond', Georgia, serif;
            font-style: italic;
            font-size: 18px;
            margin-bottom: 2.5rem;
        }

        /* Tab strip — horizontal, scrollable on mobile */
        .laf-tabs {
            display: flex;
            gap: 0.4rem;
            justify-content: center;
            flex-wrap: wrap;
            margin-bottom: 1.5rem;
            padding: 0 0.5rem;
        }
        .laf-tab {
            background: transparent;
            color: var(--text-mute);
            border: 1px solid var(--line);
            padding: 0.55rem 1.15rem;
            font-family: -apple-system, "Helvetica Neue", Arial, sans-serif;
            font-size: 11px;
            font-weight: 400;
            letter-spacing: 0.18em;
            text-transform: uppercase;
            cursor: pointer;
            border-radius: 999px;
            transition: background 200ms ease, color 200ms ease, border-color 200ms ease;
            white-space: nowrap;
        }
        .laf-tab:hover {
            color: var(--text);
            border-color: var(--line-strong);
        }
        .laf-tab.active {
            background: var(--accent);
            color: #1a1a1a;
            border-color: var(--accent);
        }
        .laf-tab .tab-count {
            margin-left: 0.5rem;
            font-family: ui-monospace, "SF Mono", Menlo, monospace;
            font-size: 10px;
            opacity: 0.75;
            letter-spacing: 0;
        }

        /* Toolbar: search + sort + density */
        .laf-toolbar {
            display: flex;
            gap: 0.75rem;
            align-items: center;
            justify-content: center;
            flex-wrap: wrap;
            margin-bottom: 1.75rem;
            padding: 0 1rem;
        }
        .laf-toolbar .toolbar-search {
            flex: 1 1 280px;
            max-width: 360px;
            min-width: 200px;
            background: var(--surface);
            color: var(--text);
            border: 1px solid var(--line);
            padding: 0.6rem 0.9rem;
            font-size: 14px;
            font-family: inherit;
            border-radius: 999px;
            transition: border-color 200ms ease, background 200ms ease;
        }
        .laf-toolbar .toolbar-search::placeholder { color: var(--text-faint); }
        .laf-toolbar .toolbar-search:focus {
            outline: none;
            border-color: var(--accent);
            background: var(--surface-hover);
        }
        .laf-ctrl {
            display: inline-flex;
            gap: 0.5rem;
            align-items: center;
            font-size: 10px;
            letter-spacing: 0.18em;
            text-transform: uppercase;
            color: var(--text-faint);
            font-family: -apple-system, Arial, sans-serif;
        }
        .laf-ctrl select {
            background: var(--surface);
            color: var(--text);
            border: 1px solid var(--line);
            padding: 0.45rem 0.7rem;
            font-size: 12px;
            text-transform: none;
            letter-spacing: 0;
            border-radius: 4px;
            cursor: pointer;
            font-family: inherit;
        }
        .laf-ctrl select:focus { outline: none; border-color: var(--accent); }
        .laf-ctrl input[type="range"] { width: 100px; accent-color: var(--accent); cursor: pointer; }
        .laf-ctrl .density-val {
            min-width: 1.5em;
            font-family: ui-monospace, "SF Mono", Menlo, monospace;
            color: var(--accent);
            text-align: right;
            font-size: 12px;
            letter-spacing: 0;
        }
        .laf-result-count {
            font-family: ui-monospace, "SF Mono", Menlo, monospace;
            font-size: 11px;
            color: var(--text-faint);
            letter-spacing: 0.04em;
        }

        /* Product grid + cards */
        .laf-product-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(var(--card-min, 240px), 1fr));
            gap: 1.25rem;
            padding: 0 1rem;
        }
        @media (max-width: 720px) {
            .laf-product-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)) !important; gap: 0.75rem; }
        }
        .laf-card {
            background: var(--surface);
            border: 1px solid var(--line);
            border-radius: 8px;
            overflow: hidden;
            cursor: pointer;
            transition: transform 280ms ease, border-color 280ms ease, box-shadow 280ms ease;
            display: flex;
            flex-direction: column;
            position: relative;
        }
        .laf-card:hover {
            transform: translateY(-3px);
            border-color: var(--accent);
            box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
        }
        .laf-card-image {
            width: 100%;
            aspect-ratio: 4 / 3;
            background: #1a1a1a;
            position: relative;
            overflow: hidden;
        }
        .laf-card-image img {
            width: 100%;
            height: 100%;
            object-fit: cover;
            transition: transform 600ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 320ms ease;
            opacity: 0;
        }
        .laf-card-image img.loaded { opacity: 1; }
        .laf-card:hover .laf-card-image img { transform: scale(1.04); }
        .laf-card-body {
            padding: 0.85rem 0.95rem 0.95rem;
            display: flex;
            flex-direction: column;
            gap: 0.25rem;
            flex: 1;
        }
        .laf-card-title {
            font-family: -apple-system, "Helvetica Neue", Arial, sans-serif;
            font-size: 13px;
            font-weight: 400;
            color: var(--text);
            letter-spacing: 0.02em;
            line-height: 1.3;
            display: -webkit-box;
            -webkit-line-clamp: 2;
            -webkit-box-orient: vertical;
            overflow: hidden;
        }
        .laf-card-meta {
            font-size: 10px;
            letter-spacing: 0.18em;
            text-transform: uppercase;
            color: var(--text-faint);
            margin-top: 0.25rem;
        }
        .laf-card-color {
            font-family: 'Cormorant Garamond', Georgia, serif;
            font-style: italic;
            font-size: 14px;
            color: var(--accent);
        }
        .laf-card-price {
            font-family: ui-monospace, "SF Mono", Menlo, monospace;
            font-size: 11px;
            color: var(--text-mute);
            margin-top: 0.4rem;
            letter-spacing: 0.04em;
        }
        .laf-card-badge {
            position: absolute;
            top: 8px;
            left: 8px;
            background: rgba(0, 0, 0, 0.65);
            color: #f5f1e8;
            font-size: 9px;
            font-weight: 500;
            letter-spacing: 0.20em;
            padding: 3px 8px;
            border-radius: 999px;
            text-transform: uppercase;
            z-index: 2;
        }
        .laf-load-more {
            display: block;
            margin: 2rem auto 0;
            padding: 0.85rem 2.4rem;
            background: transparent;
            color: var(--text);
            border: 1px solid var(--line-strong);
            font-family: -apple-system, Arial, sans-serif;
            font-size: 11px;
            letter-spacing: 0.32em;
            text-transform: uppercase;
            border-radius: 999px;
            cursor: pointer;
            transition: background 220ms ease, color 220ms ease, border-color 220ms ease;
        }
        .laf-load-more:hover {
            background: var(--text);
            color: var(--bg);
            border-color: var(--text);
        }
        .laf-grid-empty {
            text-align: center;
            padding: 3rem 1rem;
            color: var(--text-faint);
            font-family: 'Cormorant Garamond', Georgia, serif;
            font-style: italic;
            font-size: 18px;
        }

        /* Product detail modal — opens on card click */
        .laf-product-modal {
            display: none;
            position: fixed;
            inset: 0;
            z-index: 2200;
            background: rgba(0, 0, 0, 0.80);
            backdrop-filter: blur(6px);
            -webkit-backdrop-filter: blur(6px);
            overflow-y: auto;
            padding: 2rem 1rem;
        }
        .laf-product-modal.open { display: block; }
        .laf-product-modal-inner {
            background: var(--bg-alt);
            color: var(--text);
            max-width: 980px;
            margin: 0 auto;
            border-radius: 12px;
            overflow: hidden;
            display: grid;
            grid-template-columns: 1.1fr 1fr;
            min-height: 480px;
            border: 1px solid var(--line);
        }
        @media (max-width: 720px) {
            .laf-product-modal-inner { grid-template-columns: 1fr; }
        }
        .laf-product-modal-close {
            position: absolute;
            top: 1.4rem;
            right: 1.4rem;
            width: 36px;
            height: 36px;
            background: rgba(0,0,0,0.7);
            color: #f5f1e8;
            border: 1px solid rgba(245, 241, 232, 0.30);
            border-radius: 50%;
            cursor: pointer;
            font-size: 18px;
            line-height: 1;
            z-index: 2300;
        }
        .laf-product-modal-close:hover { background: var(--accent); color: #1a1a1a; }
        .laf-product-images {
            background: #0a0a0a;
            display: flex;
            flex-direction: column;
        }
        .laf-product-hero {
            width: 100%;
            aspect-ratio: 1 / 1;
            background: #1a1a1a;
        }
        .laf-product-hero img {
            width: 100%;
            height: 100%;
            object-fit: cover;
            display: block;
        }
        .laf-product-thumbs {
            display: flex;
            gap: 0.4rem;
            padding: 0.6rem;
            overflow-x: auto;
        }
        .laf-product-thumbs img {
            width: 64px;
            height: 64px;
            object-fit: cover;
            cursor: pointer;
            border: 1px solid var(--line);
            border-radius: 4px;
            transition: border-color 200ms ease, transform 200ms ease;
        }
        .laf-product-thumbs img:hover,
        .laf-product-thumbs img.active {
            border-color: var(--accent);
            transform: scale(1.04);
        }
        .laf-product-info {
            padding: 2rem 1.75rem;
            display: flex;
            flex-direction: column;
            gap: 0.85rem;
            overflow-y: auto;
        }
        .laf-product-info .pi-eyebrow {
            font-family: -apple-system, Arial, sans-serif;
            font-size: 10px;
            letter-spacing: 0.32em;
            text-transform: uppercase;
            color: var(--text-faint);
        }
        .laf-product-info h2 {
            font-family: -apple-system, "Helvetica Neue", Arial, sans-serif;
            font-weight: 200;
            font-size: clamp(22px, 2.6vw, 30px);
            color: var(--text);
            letter-spacing: 0.04em;
            margin: 0;
            line-height: 1.2;
        }
        .laf-product-info .pi-color {
            font-family: 'Cormorant Garamond', Georgia, serif;
            font-style: italic;
            font-size: 22px;
            color: var(--accent);
        }
        .laf-product-info .pi-price {
            font-family: ui-monospace, "SF Mono", Menlo, monospace;
            font-size: 13px;
            color: var(--text-mute);
            letter-spacing: 0.06em;
        }
        .laf-product-info .pi-desc {
            color: var(--text-mute);
            line-height: 1.7;
            font-size: 14px;
            margin-top: 0.5rem;
        }
        .laf-product-info .pi-desc p,
        .laf-product-info .pi-desc div { margin-bottom: 0.6rem; }
        .laf-product-info .pi-specs {
            margin-top: 0.75rem;
            border-top: 1px solid var(--line);
            padding-top: 0.85rem;
            display: grid;
            grid-template-columns: 1fr;
            gap: 0.45rem;
        }
        .laf-product-info .pi-spec-row {
            display: flex;
            justify-content: space-between;
            gap: 1rem;
            font-size: 12px;
            padding: 0.4rem 0;
            border-bottom: 1px solid var(--line);
        }
        .laf-product-info .pi-spec-label {
            font-size: 10px;
            letter-spacing: 0.18em;
            text-transform: uppercase;
            color: var(--text-faint);
        }
        .laf-product-info .pi-spec-value {
            color: var(--text);
            font-family: 'Cormorant Garamond', Georgia, serif;
            font-style: italic;
            font-size: 14px;
            text-align: right;
        }
        .laf-product-info .pi-cta {
            margin-top: 1rem;
            display: flex;
            gap: 0.6rem;
            flex-wrap: wrap;
        }
        .laf-product-info .pi-cta button {
            background: var(--accent);
            color: #1a1a1a;
            border: 1px solid var(--accent);
            padding: 0.85rem 1.6rem;
            font-family: -apple-system, Arial, sans-serif;
            font-size: 11px;
            font-weight: 500;
            letter-spacing: 0.22em;
            text-transform: uppercase;
            border-radius: 0;
            cursor: pointer;
            transition: background 200ms ease, color 200ms ease;
        }
        .laf-product-info .pi-cta button:hover {
            background: var(--text);
            color: var(--bg);
            border-color: var(--text);
        }
        .laf-product-info .pi-cta .pi-secondary {
            background: transparent;
            color: var(--text);
            border-color: var(--line-strong);
        }
        .laf-product-info .pi-cta .pi-secondary:hover {
            background: var(--text);
            color: var(--bg);
        }
        .laf-product-info .pi-tags {
            margin-top: 0.85rem;
            display: flex;
            flex-wrap: wrap;
            gap: 0.35rem;
        }
        .laf-product-info .pi-tag {
            background: var(--surface);
            border: 1px solid var(--line);
            padding: 0.25rem 0.7rem;
            font-size: 10px;
            letter-spacing: 0.10em;
            text-transform: uppercase;
            color: var(--text-mute);
            border-radius: 999px;
        }

        /* Footer enhancements (info@ mailto + DW office address + sister-site) */
        .laf-footer-extra {
            display: flex;
            flex-direction: column;
            gap: 0.45rem;
            margin-top: 0.9rem;
            font-size: 12px;
            color: rgba(245, 241, 232, 0.65);
        }
        .laf-footer-extra a { color: rgba(245, 241, 232, 0.85); text-decoration: none; }
        .laf-footer-extra a:hover { color: #DDAB1E; }
        .laf-footer-address {
            font-style: italic;
            font-family: 'Cormorant Garamond', Georgia, serif;
            font-size: 14px;
            color: rgba(245, 241, 232, 0.72);
        }

    </style>

    <!-- Canonical UX: canonical + social meta + JSON-LD (added 2026-05-08) -->
    <link rel="canonical" href="https://losangelesfabrics.com/">
    <meta property="og:type" content="website">
    <meta property="og:site_name" content="Los Angeles Fabrics">
    <meta property="og:title" content="Los Angeles Fabrics — Commercial Fabric & Drapery">
    <meta property="og:description" content="Los Angeles Fabrics — commercial-grade fabrics and drapery for designers, architects, and contract installations. Free memo samples; trade service direct from the mill.">
    <meta property="og:url" content="https://losangelesfabrics.com/">
    <meta property="og:image" content="https://losangelesfabrics.com/images/favicon.jpg">
    <meta name="twitter:card" content="summary_large_image">
    <meta name="twitter:title" content="Los Angeles Fabrics — Commercial Fabric & Drapery">
    <meta name="twitter:description" content="Los Angeles Fabrics — commercial-grade fabrics and drapery for designers, architects, and contract installations. Free memo samples; trade service direct from the mill.">
    <meta name="twitter:image" content="https://losangelesfabrics.com/images/favicon.jpg">

    <script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Organization",
  "name": "Los Angeles Fabrics",
  "alternateName": "Los Angeles Fabrics™",
  "url": "https://losangelesfabrics.com/",
  "logo": "https://losangelesfabrics.com/images/favicon.jpg",
  "email": "info@losangelesfabrics.com",
  "parentOrganization": {
    "@type": "Organization",
    "name": "Designer Wallcoverings",
    "url": "https://designerwallcoverings.com/"
  },
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "15442 Ventura Bl #102",
    "addressLocality": "Sherman Oaks",
    "addressRegion": "CA",
    "postalCode": "91403",
    "addressCountry": "US"
  }
}
    </script>
    <script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "WebSite",
  "name": "Los Angeles Fabrics",
  "url": "https://losangelesfabrics.com/",
  "publisher": {
    "@type": "Organization",
    "name": "Designer Wallcoverings"
  },
  "inLanguage": "en-US"
}
    </script>
    <script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Los Angeles Fabrics™ Commercial Fabric & Drapery",
  "description": "Commercial-grade specialty fabrics and draperies for high-traffic offices, hotels, healthcare, and high-end residential. Free memo samples; trade-direct pricing.",
  "brand": {
    "@type": "Brand",
    "name": "Los Angeles Fabrics"
  },
  "manufacturer": {
    "@type": "Organization",
    "name": "Designer Wallcoverings"
  },
  "category": "Commercial Fabric, Drapery",
  "url": "https://losangelesfabrics.com/",
  "image": "https://losangelesfabrics.com/images/favicon.jpg"
}
    </script>
</head>
<body>
    <!-- Top contact-bar removed 2026-05-07 per Steve -->

    <!-- Right-side hamburger (always visible, fixed top-right) -->
    <button class="laf-hamburger" id="lafHamburger" aria-label="Open menu" aria-expanded="false" aria-controls="lafSidePanel" onclick="lafToggleSide()">
        <span></span>
        <span></span>
        <span></span>
    </button>
    <button class="laf-theme-toggle" id="lafThemeToggle" aria-label="Toggle dark / light theme" onclick="lafToggleTheme()" title="Toggle dark / light">
        <span id="lafThemeIcon">☾</span>
    </button>

    <!-- Slide-in side panel (right edge) -->
    <div class="laf-side-overlay" id="lafSideOverlay" onclick="lafCloseSide()"></div>
    <aside class="laf-side-panel" id="lafSidePanel" aria-hidden="true">
        <h3>Navigation</h3>
        <ul>
            <li><a href="#home" onclick="lafCloseSide()">Home</a></li>
            <li><a href="#products" onclick="lafCloseSide()">Live Catalog</a></li>
            <li><a href="#colors" onclick="lafCloseSide()">Color Collection</a></li>
            <li><a href="#specifications" onclick="lafCloseSide()">Specifications</a></li>
            <li><a href="#features" onclick="lafCloseSide()">Features</a></li>
            <li><a href="#installation" onclick="lafCloseSide()">Installation &amp; Care</a></li>
        </ul>

        <h3>Resources</h3>
        <ul>
            <li><a href="faq.html">FAQ</a></li>
            <li><a href="fire-ratings.html">Fire Ratings</a></li>
            <li><a href="installation.html">Installation Guide</a></li>
            <li><a href="cleaning.html">Cleaning &amp; Care</a></li>
            <li><a href="cutting.html">Cutting Guidelines</a></li>
        </ul>

        <h3>Contact</h3>
        <ul>
            <li><a href="#" onclick="lafCloseSide();openModal();return false;">Request a Sample</a></li>
        </ul>
        <a class="side-mail" href="mailto:info@losangelesfabrics.com">info@losangelesfabrics.com</a>

        <p class="side-fineprint">
            Los Angeles Fabrics is a Designer Wallcoverings sub-brand.
            Trade-direct commercial fabric &amp; drapery.<br>
            Sister site: <a href="https://designerwallcoverings.com" style="color:#DDAB1E">designerwallcoverings.com</a>
        </p>
    </aside>


    <header>
        <nav class="navbar">
            <div class="nav-container">
                <div class="logo">
                    <h1>Los Angeles Fabrics™</h1>
                </div>
                <button class="mobile-toggle" id="mobileToggle" onclick="toggleMobileMenu()">
                    <span></span>
                    <span></span>
                    <span></span>
                </button>
                <ul class="nav-menu" id="navMenu">
                    <li><a href="#home">Home</a></li>
                    <li><a href="#colors">Colors</a></li>
                    <li><a href="#specifications">Specifications</a></li>
                    <li><a href="#features">Features</a></li>
                    <li><a href="fire-ratings.html">Fire Ratings</a></li>
                </ul>
            </div>
        </nav>
    </header>

    <section id="home" class="hero">
        <!-- Rotating suede — 5 dark/jewel tones, gucci-house aesthetic. -->
        <div class="hero-bg on" style="background-image:url('images/colors/losangelesfabrics-black.jpg')"></div>
        <div class="hero-bg" style="background-image:url('images/colors/losangelesfabrics-carbon.jpg')"></div>
        <div class="hero-bg" style="background-image:url('images/colors/losangelesfabrics-aubergine.jpg')"></div>
        <div class="hero-bg" style="background-image:url('images/colors/losangelesfabrics-navy.jpg')"></div>
        <div class="hero-bg" style="background-image:url('images/colors/losangelesfabrics-ruby.jpg')"></div>
        <div class="hero-content">
            <a href="#colors" class="ns-wordmark-link" onclick="event.preventDefault();document.getElementById(&quot;colors&quot;).scrollIntoView({behavior:&quot;smooth&quot;});" aria-label="Enter Los Angeles Fabrics">
                <h1 class="ns-wordmark">LOSANGELESFABRICS<sup>&trade;</sup></h1>
            </a>
            <h2>Commercial Fabric &amp; Drapery, Trade-Direct</h2>
            <p>Los Angeles Fabrics is the commercial-grade specialty of the Designer Wallcoverings family — fabrics and draperies specified for high-traffic offices, hotels, healthcare, and high-end residential. Free memo samples ship same-day; trade pricing applies to every order.</p>
            <button class="sample-btn" onclick="openModal()">Request Sample</button>
            <div class="authorized-dealer">
                <p>DesignerWallcoverings.com is an authorized dealer of Los Angeles Fabrics™ products</p>
            </div>
        </div>
    </section>

    <!-- New Arrivals section removed 2026-05-18 per Steve -->

    <!-- ───────────────────────────────────────────────────────────────
         Live Catalog: 1,634 DWPR Commercial Fabric SKUs from Shopify.
         Tabs filter by use (Drapery, Upholstery, Sheer, Outdoor, Fire Rated)
         and by named Collection (Le Cirque, Monument, Hollywood Babylon, …).
         Sort + density slider + search above the grid (per CLAUDE.md
         standing rule). Cards open a Novasuede-style detail modal.
         ─────────────────────────────────────────────────────────────── -->
    <section id="products" class="laf-products-section">
        <div class="container">
            <h2>Commercial Fabric — Live Catalog</h2>
            <p class="section-subtitle">Every active Los Angeles Fabrics SKU, mill-direct. Browse, filter, and request memo samples.</p>

            <div class="laf-tabs" id="lafTabs" role="tablist" aria-label="Filter products by category">
                <!-- tabs injected by JS so counts stay live -->
            </div>

            <div class="laf-toolbar">
                <input type="search" id="lafSearch" class="toolbar-search" placeholder="Search by pattern, color, SKU, tag…" aria-label="Search products">
                <label class="laf-ctrl">Sort
                    <select id="lafSort" aria-label="Sort products">
                        <option value="newest">Newest</option>
                        <option value="color">Color</option>
                        <option value="style">Style</option>
                        <option value="sku-asc">SKU A→Z</option>
                        <option value="title-asc">Title A→Z</option>
                        <option value="price-asc">Price ↑</option>
                        <option value="price-desc">Price ↓</option>
                    </select>
                </label>
                <label class="laf-ctrl">Density
                    <input type="range" id="lafDensity" min="160" max="360" step="20" value="240" aria-label="Card size (density)">
                    <span class="density-val" id="lafDensityVal">240</span>
                </label>
                <span class="laf-result-count" id="lafResultCount">loading…</span>
            </div>

            <div class="laf-product-grid" id="lafProductGrid" aria-live="polite">
                <!-- cards injected by JS -->
            </div>
            <div class="laf-grid-empty" id="lafGridEmpty" style="display:none">
                No fabrics match your filters. Try clearing the search or switching tabs.
            </div>
            <button class="laf-load-more" id="lafLoadMore" type="button" style="display:none">Load more</button>
        </div>
    </section>

    <!-- Product detail modal (Novasuede-style). Opened on card click. -->
    <div class="laf-product-modal" id="lafProductModal" role="dialog" aria-modal="true" aria-labelledby="lafProductTitle" onclick="if(event.target===this)lafCloseProduct()">
        <button class="laf-product-modal-close" type="button" aria-label="Close" onclick="lafCloseProduct()">&times;</button>
        <div class="laf-product-modal-inner">
            <div class="laf-product-images">
                <div class="laf-product-hero">
                    <img id="lafProductHeroImg" src="" alt="">
                </div>
                <div class="laf-product-thumbs" id="lafProductThumbs"></div>
            </div>
            <div class="laf-product-info">
                <span class="pi-eyebrow" id="lafProductEyebrow">Los Angeles Fabrics</span>
                <h2 id="lafProductTitle"></h2>
                <span class="pi-color" id="lafProductColor"></span>
                <span class="pi-price" id="lafProductPrice"></span>
                <div class="pi-desc" id="lafProductDesc"></div>
                <div class="pi-specs" id="lafProductSpecs"></div>
                <div class="pi-cta">
                    <button type="button" onclick="lafRequestSampleFromProduct()">Request Sample</button>
                    <button type="button" class="pi-secondary" id="lafProductShopifyLink" onclick="lafOpenShopifyLink()">View Full Listing</button>
                </div>
                <div class="pi-tags" id="lafProductTags"></div>
            </div>
        </div>
    </div>

    <section id="colors" class="colors-section">
        <div class="container">
            <h2>Our Color Collection</h2>
            <p class="section-subtitle">Choose from our collection of 118 premium colors</p>
            <div class="color-search">
                <input type="text" id="colorSearch" placeholder="Search colors by name or code..." onkeyup="filterColors()">
            </div>
            <div class="color-filters">
                <button type="button" class="color-filter-btn active" data-filter="all" onclick="setColorFilter('all')">All</button>
                <button type="button" class="color-filter-btn" data-filter="new" onclick="setColorFilter('new')">NEW (2026)</button>
                <button type="button" class="color-filter-btn" data-filter="legacy" onclick="setColorFilter('legacy')">Classic</button>
                <span id="color-count" class="color-count"></span>
            </div>
            <div class="color-dots" role="group" aria-label="Filter by color family">
                <button type="button" class="color-dot active" data-family="all"     style="background:linear-gradient(135deg,#fff 0%,#999 50%,#1a1a1a 100%)" onclick="setColorFamily('all')"     title="All colors"><span class="dot-label">All</span></button>
                <button type="button" class="color-dot"        data-family="red"     style="background:#c0392b" onclick="setColorFamily('red')"     title="Reds"></button>
                <button type="button" class="color-dot"        data-family="orange"  style="background:#e67e22" onclick="setColorFamily('orange')"  title="Oranges"></button>
                <button type="button" class="color-dot"        data-family="yellow"  style="background:#e6c419" onclick="setColorFamily('yellow')"  title="Yellows"></button>
                <button type="button" class="color-dot"        data-family="green"   style="background:#27ae60" onclick="setColorFamily('green')"   title="Greens"></button>
                <button type="button" class="color-dot"        data-family="teal"    style="background:#16a3a3" onclick="setColorFamily('teal')"    title="Teals"></button>
                <button type="button" class="color-dot"        data-family="blue"    style="background:#2c5fa8" onclick="setColorFamily('blue')"    title="Blues"></button>
                <button type="button" class="color-dot"        data-family="purple"  style="background:#7d3c98" onclick="setColorFamily('purple')"  title="Purples"></button>
                <button type="button" class="color-dot"        data-family="pink"    style="background:#e8a3b8" onclick="setColorFamily('pink')"    title="Pinks"></button>
                <button type="button" class="color-dot"        data-family="brown"   style="background:#8b5a2b" onclick="setColorFamily('brown')"   title="Browns"></button>
                <button type="button" class="color-dot"        data-family="neutral" style="background:#b8b3a8" onclick="setColorFamily('neutral')" title="Neutrals"></button>
                <button type="button" class="color-dot"        data-family="black"   style="background:#1a1a1a" onclick="setColorFamily('black')"   title="Blacks"></button>
                <button type="button" class="color-dot"        data-family="white"   style="background:#f5f1e8;border-color:#bbb" onclick="setColorFamily('white')" title="Whites &amp; Cream"></button>
            </div>
            <div class="color-toolbar-row">
                <span class="ns-toolbar">
                    <label class="ns-ctrl">Sort
                        <select id="nsSort" onchange="setSort(this.value)">
                            <option value="color-wheel">Color · wheel (hue)</option>
                            <option value="color-light">Color · light → dark</option>
                            <option value="color-dark">Color · dark → light</option>
                            <option value="name-asc">Name A→Z</option>
                            <option value="name-desc">Name Z→A</option>
                            <option value="code-asc">Code A→Z</option>
                            <option value="new-first">NEW first</option>
                            <option value="random">Random</option>
                        </select>
                    </label>
                    <label class="ns-ctrl">Cols
                        <input type="range" id="nsCols" min="4" max="12" step="1" value="6" oninput="setCols(this.value)" />
                        <span id="nsColsVal">6</span>
                    </label>
                </span>
            </div>
            <div class="colors-grid" id="colorsGrid">
                <!-- Colors will be populated here -->
            </div>
        </div>
    </section>

    <section id="specifications" class="specifications-section">
        <div class="container">
            <h2>Product Specifications</h2>
            <div class="specs-grid">
                <div class="spec-item">
                    <span class="spec-label">Composition</span>
                    <span class="spec-value">100% Nylon Fiber Matrix</span>
                </div>
                <div class="spec-item">
                    <span class="spec-label">Width</span>
                    <span class="spec-value">53-54"</span>
                </div>
                <div class="spec-item">
                    <span class="spec-label">Weight</span>
                    <span class="spec-value">8.85 oz/yd</span>
                </div>
                <div class="spec-item">
                    <span class="spec-label">Type</span>
                    <span class="spec-value">Commercial Fabric</span>
                </div>
                <div class="spec-item">
                    <span class="spec-label">Abrasion</span>
                    <span class="spec-value">100,000 Double Rubs Wyzenbeek</span>
                </div>
                <div class="spec-item">
                    <span class="spec-label">Flammability</span>
                    <span class="spec-value">Cal 117, UFAC Class I, NFPA 260</span>
                </div>
                <div class="spec-item">
                    <span class="spec-label">Common Uses</span>
                    <span class="spec-value">Upholstery, Wallcovering, Panel</span>
                </div>
                <div class="spec-item">
                    <span class="spec-label">Colors Available</span>
                    <span class="spec-value">118 Premium Colors</span>
                </div>
            </div>
        </div>
    </section>

    <section id="features" class="features-section">
        <div class="container">
            <h2>Features of Los Angeles Fabrics™</h2>
            <div class="features-grid">
                <div class="feature-card">
                    <h3>Mill-Direct</h3>
                    <p>Specified by mills we've worked with directly for over a decade</p>
                </div>
                <div class="feature-card">
                    <h3>Contract-Spec</h3>
                    <p>Designed for high-traffic commercial installations</p>
                </div>
                <div class="feature-card">
                    <h3>Free Memo Samples</h3>
                    <p>Same-day shipping; CFA available on request for contract projects</p>
                </div>
                <div class="feature-card">
                    <h3>Custom Colorways</h3>
                    <p>Custom color options on request; small minimums apply</p>
                </div>
                <div class="feature-card">
                    <h3>🔧 Custom Embossing</h3>
                    <p>Available for unique design requirements</p>
                </div>
                <div class="feature-card">
                    <h3>🌿 No Plasticizers</h3>
                    <p>No off-gassing for healthier environments</p>
                </div>
            </div>
        </div>
    </section>

    <section id="installation" class="installation-section">
        <div class="container">
            <h2>Installation & Care</h2>
            <p class="section-subtitle">Professional guidance for perfect results</p>

            <div class="info-grid">
                <a href="installation.html" class="info-card-link">
                    <div class="info-card">
                        <h3>📐 Wallcovering Installation</h3>
                        <p>Professional installation techniques for seamless application of Los Angeles Fabrics™ wallcovering.</p>
                        <span class="card-arrow">View Guide →</span>
                    </div>
                </a>

                <a href="cutting.html" class="info-card-link">
                    <div class="info-card">
                        <h3>✂️ Cutting Guidelines</h3>
                        <p>Proper cutting techniques to ensure precise measurements and clean edges.</p>
                        <span class="card-arrow">View Guide →</span>
                    </div>
                </a>

                <a href="cleaning.html" class="info-card-link">
                    <div class="info-card">
                        <h3>🧹 Cleaning & Maintenance</h3>
                        <p>Easy-to-follow cleaning instructions to maintain the beauty of your Los Angeles Fabrics™.</p>
                        <span class="card-arrow">View Chart →</span>
                    </div>
                </a>

                <a href="faq.html" class="info-card-link">
                    <div class="info-card">
                        <h3>❓ Common Questions</h3>
                        <p>Answers to frequently asked questions about Los Angeles Fabrics™ care and maintenance.</p>
                        <span class="card-arrow">View FAQ →</span>
                    </div>
                </a>

                <a href="fire-ratings.html" class="info-card-link">
                    <div class="info-card">
                        <h3>🔥 Fire Ratings</h3>
                        <p>Comprehensive fire safety information and flammability test results for Los Angeles Fabrics™.</p>
                        <span class="card-arrow">View Details →</span>
                    </div>
                </a>
            </div>

            <div class="care-highlight">
                <h3>Care Codes Documented Per-Product</h3>
                <p>Every Los Angeles Fabrics product carries a standard cleaning code on its spec panel — W (water-based), S (solvent), WS (either), or X (vacuum only). Routine maintenance, spot cleaning, and contract-installation protocols are documented on the <a href="/cleaning.html">care &amp; cleaning</a> page.</p>
            </div>
        </div>
    </section>

    <footer>
        <div class="container">
            <div class="footer-content">
                <div class="footer-section">
                    <h3>Los Angeles Fabrics</h3>
                    <p>Commercial fabric and drapery, trade-direct.</p>
                    <div class="laf-footer-extra">
                        <span class="laf-footer-address">Designer Wallcoverings<br>15442 Ventura Bl #102<br>Sherman Oaks, CA 91403</span>
                    </div>
                </div>
                <div class="footer-section">
                    <h3>Contact</h3>
                    <p>Email: <a href="mailto:info@losangelesfabrics.com">info@losangelesfabrics.com</a></p>
                    <button class="footer-sample-btn" onclick="openModal()">Request Sample</button>
                </div>
                <div class="footer-section">
                    <h3>Quick Links</h3>
                    <ul>
                        <li><a href="#products">Live Catalog</a></li>
                        <li><a href="#colors">Colors</a></li>
                        <li><a href="#specifications">Specifications</a></li>
                        <li><a href="#features">Features</a></li>
                        <li><a href="fire-ratings.html">Fire Ratings</a></li>
                        <li><a href="#installation">Installation</a></li>
                    </ul>
                </div>
            </div>
            <div class="footer-bottom">
                <p>&copy; 2026 Los Angeles Fabrics.com. All rights reserved. &middot; Sister site of <a href="https://designerwallcoverings.com" style="color:#DDAB1E;text-decoration:none">designerwallcoverings.com</a></p>
            </div>
        </div>
    </footer>

    <!-- Contact Modal -->
    <div id="contactModal" class="modal">
        <div class="modal-content">
            <span class="close" onclick="closeModal()">&times;</span>
            <h2>Request a Sample</h2>
            <div class="color-preview">
                <img id="colorPreviewImage" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='460' height='280'%3E%3Crect width='460' height='280' fill='%231a1a1a'/%3E%3Ctext x='50%25' y='50%25' fill='%23666' font-family='Helvetica' font-size='12' letter-spacing='0.32em' text-anchor='middle' dy='.3em'%3ESELECT A COLOR BELOW%3C/text%3E%3C/svg%3E" alt="Color Preview">
                <span id="colorPreviewName">Select a color below</span>
                <span class="preview-hex" id="colorPreviewHex"></span>
                <div class="form-group">
                    <label for="selectedColor">Browse colors</label>
                    <div class="color-pick-row">
                        <select id="selectedColor" name="selectedColor" onchange="updatePreview()">
                            <option value="">Choose a color…</option>
                        </select>
                        <button type="button" class="add-to-request-btn" onclick="addToRequest()" title="Add the currently-selected color to your sample request">+ Add to request</button>
                    </div>
                </div>

                <div class="request-list" id="requestList" style="display:none">
                    <div class="request-list-header">
                        <span>Your sample request</span>
                        <span class="request-count" id="requestCount">0</span>
                    </div>
                    <div class="request-chips" id="requestChips"></div>
                </div>
                <!-- hidden — submitted with the form. comma-separated list of all picked SKU codes. -->
                <input type="hidden" name="requested_colors" id="requestedColorsHidden" value="">
            </div>
            <form id="contactForm" onsubmit="handleSubmit(event)" method="POST">
                <div class="form-group">
                    <label>Full Name*
                        <input type="text" name="name" required>
                    </label>
                </div>
                <div class="form-group">
                    <label>Your email*
                        <input type="email" name="email" required>
                    </label>
                </div>
                <div class="form-group">
                    <label>Company
                        <input type="text" name="company">
                    </label>
                </div>
                <div class="form-group">
                    <label>Shipping Address*
                        <input type="text" name="address" required>
                    </label>
                </div>
                <div class="form-group">
                    <label>City*
                        <input type="text" name="city" required>
                    </label>
                </div>
                <div class="form-group">
                    <label>State*
                        <input type="text" name="state" required maxlength="2" placeholder="e.g. CA">
                    </label>
                </div>
                <div class="form-group">
                    <label>ZIP Code*
                        <input type="text" name="zip" required pattern="[0-9]{5}(-[0-9]{4})?" placeholder="12345">
                    </label>
                </div>
                <div class="form-group">
                    <label>Selected Color
                        <input type="text" name="color" id="selectedColorInput" readonly>
                    </label>
                </div>
                <div class="form-group">
                    <label>Your message / Project Details*
                        <textarea name="message" required rows="5" style="width: 100%; resize: vertical;" placeholder="Please describe your project needs..."></textarea>
                    </label>
                </div>
                <button type="submit" class="submit-btn">Send Sample Request</button>
            </form>
        </div>
    </div>

    <script>
        // Complete Los Angeles Fabrics Colors Data
        // NEW (2026-04) colors at top, alphabetically. Legacy list follows below.
        var colors = [
            // --- NEW: latest-drop colors, alphabetical ---
            {name: "Amber", code: "T3Q8W", isNew: true},
            {name: "Blank Slate", code: "T3L0W", isNew: true},
            {name: "Bliss", code: "T3Q7W", isNew: true},
            {name: "Blush Rose", code: "T3RFW", isNew: true},
            {name: "Cement", code: "T3LAW", isNew: true},
            {name: "Colonial Blue", code: "T3C9W", isNew: true},
            {name: "Dusty Ranch", code: "T3FAW", isNew: true},
            {name: "Dusty Teal", code: "T3NGW", isNew: true},
            {name: "Fire", code: "T3QAW", isNew: true},
            {name: "Flagstone", code: "T3LKW", isNew: true},
            {name: "Henna", code: "T3G3W", isNew: true},
            {name: "Kiva", code: "T3ECW", isNew: true},
            {name: "Lipstick", code: "T3M4W", isNew: true},
            {name: "Noir", code: "T3A2W", isNew: true},
            {name: "Opal", code: "T3PQW", isNew: true},
            {name: "Pastel Blue", code: "T3N0W", isNew: true},
            {name: "Pastel Pink", code: "T3R6W", isNew: true},
            {name: "Persimmon", code: "T3M8W", isNew: true},
            {name: "Pink Lace", code: "T3RCW", isNew: true},
            {name: "Port Blue", code: "T3NJW", isNew: true},
            {name: "Raisin", code: "T3PRW", isNew: true},
            {name: "Rosewater", code: "T3RSW", isNew: true},
            {name: "Royal Blue", code: "T3CCW", isNew: true},
            {name: "Sedona", code: "T3Q9W", isNew: true},
            {name: "Shale", code: "T3KDW", isNew: true},
            {name: "Snow", code: "T3B3W", isNew: true},
            {name: "Spruce", code: "T3PYW", isNew: true},
            {name: "Terracotta", code: "T3FCW", isNew: true},
            {name: "Thistle", code: "T3PMW", isNew: true},
            {name: "Tusk", code: "T3DJW", isNew: true},
            {name: "Umber", code: "T3G4W", isNew: true},
            {name: "Verdigris", code: "T3NHW", isNew: true},
            {name: "Vermilion", code: "T3MBW", isNew: true},
            {name: "Wheat", code: "T3FDW", isNew: true},
            {name: "Winter White", code: "T3B2W", isNew: true},
            {name: "Woodland", code: "T3H6W", isNew: true},
            // --- Legacy catalog, alphabetical ---
            {name: "Acorn", code: "T3H5W"},
            {name: "Adobe", code: "T3K6W"},
            {name: "African Violet", code: "T3SAW"},
            {name: "Apricot", code: "T3Q4W"},
            {name: "Arrow Wood", code: "T3F8W"},
            {name: "Aubergine", code: "T3SCW"},
            {name: "Baltic", code: "T3L6W"},
            {name: "Beige", code: "T3E8W"},
            {name: "Biscuit", code: "T3DHW"},
            {name: "Black", code: "T3BLW"},
            {name: "Bluestone", code: "T3N7W"},
            {name: "Blush Pink", code: "T3C7W"},
            {name: "Bone", code: "T3DSW"},
            {name: "Brown", code: "T3F6W"},
            {name: "Buff", code: "T3D1W"},
            {name: "Buttercup", code: "T3DEW"},
            {name: "Cane", code: "T3D3W"},
            {name: "Carbon", code: "T3C6W"},
            {name: "Cashew", code: "T3D2W"},
            {name: "Cashmere", code: "T3E6W"},
            {name: "Celadon", code: "T3P1W"},
            {name: "Celery", code: "T3PFW"},
            {name: "Champagne", code: "T3D4W"},
            {name: "China Blue", code: "T3NBW"},
            {name: "Chrome Grey", code: "T3PJW"},
            {name: "Cinder", code: "T3LCW"},
            {name: "Cloud", code: "T3L4W"},
            {name: "Cocoa", code: "T315W"},
            {name: "Coral", code: "T3Q3W"},
            {name: "Cottage Grey", code: "T3LGW"},
            {name: "Cream", code: "T3D9W"},
            {name: "Cypress", code: "T3PHW"},
            {name: "Dark Sand", code: "T3EAW"},
            {name: "Dew", code: "T3F3W"},
            {name: "Dove", code: "T3E2W"},
            {name: "Dune", code: "T3F4W"},
            {name: "Dusty Rose", code: "T3R2W"},
            {name: "Ecru", code: "T3K4W"},
            {name: "Fawn", code: "T3E4W"},
            {name: "French Blue", code: "T3NCW"},
            {name: "Gainsborough", code: "T3LLW"},
            {name: "Garnet", code: "T3J6W"},
            {name: "Gelato", code: "T3D7W"},
            {name: "Gold", code: "T3J9W"},
            {name: "Goldstone", code: "T3F1W"},
            {name: "Granite", code: "T3LOW"},
            {name: "Grape", code: "TES8W"},
            {name: "Gray", code: "T3L3W"},
            {name: "Green", code: "T3P5W"},
            {name: "Green Marble", code: "T3P4W"},
            {name: "Greystone", code: "T3LNW"},
            {name: "Harvest", code: "T3E9W"},
            {name: "Honey", code: "T3DDW"},
            {name: "Hyacinth", code: "T3S2W"},
            {name: "Iceberg", code: "T3LHW"},
            {name: "Indigo", code: "T3C3W"},
            {name: "Ivory", code: "T3E3W"},
            {name: "Jade", code: "T3P6W"},
            {name: "Java", code: "T3H4W"},
            {name: "Latte", code: "T3E3W"},
            {name: "Lavender", code: "T3S3W"},
            {name: "Light Amethyst", code: "T3S6W"},
            {name: "Light Camel", code: "T3E5W"},
            {name: "Light Grey", code: "T3L7W"},
            {name: "Lilac", code: "T3RAW"},
            {name: "Lime", code: "T3PEW"},
            {name: "Linen", code: "T3D6W"},
            {name: "Madison Grey", code: "T3L1W"},
            {name: "Maize", code: "T3O3W"},
            {name: "Marine Blue", code: "T3N1W"},
            {name: "Maroon", code: "T3JAW"},
            {name: "Mauve", code: "T3S1W"},
            {name: "Meadow", code: "T3PIW"},
            {name: "Melon", code: "T3Q6W"},
            {name: "Milk", code: "T3DQW"},
            {name: "Mint", code: "T3PAW"},
            {name: "Mint Julip", code: "T3P5W"},
            {name: "Mist", code: "T3DDW"},
            {name: "Misty Dew", code: "T3PLW"},
            {name: "Mocha", code: "T3H7W"},
            {name: "Moss", code: "T3L3W"},
            {name: "Mushroom", code: "T3K1W"},
            {name: "Navy", code: "T3NAW"},
            {name: "Newport", code: "T3C2W"},
            {name: "Nutmeg", code: "T3F9W"},
            {name: "Ocean", code: "T3POW"},
            {name: "Olive", code: "T3PEW"},
            {name: "Orange", code: "T3Q2W"},
            {name: "Oyster", code: "T3EBW"},
            {name: "Pale Green", code: "T3P8W"},
            {name: "Passion Blue", code: "T3NAW"},
            {name: "Peach", code: "T3Q2W"},
            {name: "Pebble", code: "T3K2W"},
            {name: "Peppermint", code: "T3R4W"},
            {name: "Peridot", code: "T3P7W"},
            {name: "Periwinkle", code: "T3N6W"},
            {name: "Pistachio", code: "T3P6W"},
            {name: "Plum Pleasure", code: "T3S5W"},
            {name: "Porcelain", code: "T3DAW"},
            {name: "Powder Grey", code: "T3E5W"},
            {name: "Prairie", code: "T3KCW"},
            {name: "Putty", code: "T3K3W"},
            {name: "Raffia", code: "T3D5W"},
            {name: "Red", code: "T3R8W"},
            {name: "Rose", code: "T3R4W"},
            {name: "Royal Purple", code: "T3S4W"},
            {name: "Ruby", code: "T3R6W"},
            {name: "Sable", code: "T3F7W"},
            {name: "Safari", code: "T3E7W"},
            {name: "Saffron", code: "T3O2W"},
            {name: "Sage", code: "T3P3W"},
            {name: "Salt", code: "T3DBW"},
            {name: "Sand", code: "T3D8W"},
            {name: "Sandalwood", code: "T3F5W"},
            {name: "Scarlet Wine", code: "T3R5W"},
            {name: "Sea Blue", code: "T3N9W"},
            {name: "Sea Grass", code: "T3P2W"},
            {name: "Sea Breeze", code: "T3N8W"},
            {name: "Seafoam", code: "T3N3W"},
            {name: "Sienna", code: "T3Q7W"},
            {name: "Silver", code: "T3LEW"},
            {name: "Sky Blue", code: "T3N5W"},
            {name: "Smoke", code: "T3L5W"},
            {name: "Soft Clay", code: "T3K7W"},
            {name: "Solar Yellow", code: "T3O1W"},
            {name: "Spice", code: "T3K1W"},
            {name: "Steel", code: "T3LBW"},
            {name: "Stone Grey", code: "T3L2W"},
            {name: "Stone Taupe", code: "T3K8W"},
            {name: "Storm", code: "T3LHW"},
            {name: "Straw", code: "T3DCW"},
            {name: "Strong White", code: "T3DAW"},
            {name: "Sugar Plum", code: "T3S7W"},
            {name: "Sweet Lilac", code: "T3S1W"},
            {name: "Tan", code: "T3E1W"},
            {name: "Tarragon", code: "T3PGW"},
            {name: "Taupe", code: "T3K9W"},
            {name: "Thyme", code: "T3PDW"},
            {name: "Trench", code: "T3F2W"},
            {name: "Vanilla", code: "T3D5W"},
            {name: "White", code: "T3WHW"},
        ];

        // Mobile menu toggle
        function toggleMobileMenu() {
            var menu = document.getElementById('navMenu');
            if (menu.className.indexOf('active') === -1) {
                menu.className = 'nav-menu active';
            } else {
                menu.className = 'nav-menu';
            }
        }

        // Modal functions
        function openModal() {
            document.getElementById('contactModal').style.display = 'block';
            populateDropdown();
        }

        function closeModal() {
            document.getElementById('contactModal').style.display = 'none';
        }

        // Populate color dropdown
        function populateDropdown() {
            var select = document.getElementById('selectedColor');
            if (select.options.length > 1) return;

            for (var i = 0; i < colors.length; i++) {
                var option = document.createElement('option');
                option.value = 'Los Angeles Fabrics-' + colors[i].name;
                option.text = 'Los Angeles Fabrics-' + colors[i].name + ' - ' + colors[i].code;
                select.appendChild(option);
            }
        }

        // ─── Multi-color sample-request basket ────────────────────────────
        // Users can pick a color → "+ Add to request" → builds a chip list.
        // Submits the full list as a comma-separated value in `requested_colors`.
        var requestedColors = []; // array of "Los Angeles Fabrics-Acorn" strings

        function addToRequest() {
            var select = document.getElementById('selectedColor');
            if (!select || !select.value) return;
            var v = select.value;
            if (requestedColors.indexOf(v) !== -1) return; // already added
            requestedColors.push(v);
            renderRequestList();
        }
        function removeFromRequest(v) {
            requestedColors = requestedColors.filter(function(x){ return x !== v; });
            renderRequestList();
        }
        function renderRequestList() {
            var list = document.getElementById('requestList');
            var chips = document.getElementById('requestChips');
            var count = document.getElementById('requestCount');
            var hidden = document.getElementById('requestedColorsHidden');
            if (!list || !chips) return;
            if (requestedColors.length === 0) {
                list.style.display = 'none';
                if (hidden) hidden.value = '';
                return;
            }
            list.style.display = 'block';
            count.textContent = requestedColors.length + (requestedColors.length === 1 ? ' color' : ' colors');
            chips.innerHTML = '';
            requestedColors.forEach(function(v) {
                var chip = document.createElement('span');
                chip.className = 'request-chip';
                var label = v.replace('Los Angeles Fabrics-', '');
                // Lookup hex for swatch dot
                var hex = '#cccccc';
                for (var i = 0; i < colors.length; i++) {
                    if (colors[i].name.toLowerCase() === label.toLowerCase() && colors[i].hex) { hex = colors[i].hex; break; }
                }
                chip.innerHTML = '<span class="chip-swatch" style="background:' + hex + '"></span>' + label.replace(/[<>"]/g,'');
                var btn = document.createElement('button');
                btn.type = 'button';
                btn.setAttribute('aria-label', 'Remove ' + label);
                btn.innerHTML = '&times;';
                btn.onclick = function(){ removeFromRequest(v); };
                chip.appendChild(btn);
                chips.appendChild(chip);
            });
            if (hidden) hidden.value = requestedColors.join(', ');
        }

        // Update preview when color selected
        // Loads the FULL product photo for the picked SKU and shows its true
        // center-pixel hex underneath. Forces the image visible (display:block,
        // opacity:1) so the photo always appears.
        function updatePreview() {
            var select = document.getElementById('selectedColor');
            var preview = document.getElementById('colorPreviewName');
            var img = document.getElementById('colorPreviewImage');
            var hexEl = document.getElementById('colorPreviewHex');
            var colorInput = document.getElementById('selectedColorInput');

            if (!select || !select.value) return;

            preview.textContent = select.value;
            if (colorInput) colorInput.value = select.value;

            var colorName = select.value.replace('Los Angeles Fabrics-', '');
            var fileName = colorName.toLowerCase().replace(/ /g, '-');
            var src = 'images/colors/losangelesfabrics-' + fileName + '.jpg';

            // Surface the real center-pixel hex from the colors object (hydrated
            // from /colors-data.json) so the customer sees the canonical color value.
            var hex = '';
            for (var i = 0; i < colors.length; i++) {
                if (colors[i].name.toLowerCase() === colorName.toLowerCase() && colors[i].hex) {
                    hex = colors[i].hex;
                    break;
                }
            }
            if (hexEl) hexEl.textContent = hex || '';

            // Force visible + fade-in via .loading class
            img.style.display = 'block';
            img.classList.add('loading');
            img.onload = function() { img.classList.remove('loading'); };
            img.onerror = function() {
                // If the photo 404s, fall back to a flat swatch using the real hex
                var fallbackHex = (hex || '#cccccc').replace('#', '');
                img.src = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="460" height="280"%3E%3Crect width="460" height="280" fill="%23' + fallbackHex + '"/%3E%3C/svg%3E';
                img.classList.remove('loading');
            };
            img.src = src;
        }

        // Get color hex
        function getColorHex(name) {
            var colorMap = {
                'Black': '000000', 'White': 'FFFFFF', 'Gray': '808080',
                'Red': 'CC0000', 'Blue': '0066CC', 'Green': '008800',
                'Brown': '654321', 'Beige': 'F5DEB3', 'Cream': 'FFFDD0',
                'Navy': '000080', 'Gold': 'FFD700', 'Silver': 'C0C0C0'
            };
            return colorMap[name] || 'CCCCCC';
        }


        // ─── SORT + DENSITY (CLAUDE.md "every grid needs sort + density" rule) ───
        // Hardcoded best-guess hex per color name. Tunable — fine-tune by sampling actual swatch
        // images later. Sort-by-luminance uses these for light→dark / dark→light ordering.
        var NAME_HEX = {
          "Acorn":"#7e5a3c","Adobe":"#a6694b","African Violet":"#8a4f88","Amber":"#d4a04a","Apricot":"#f4b78a","Arrow Wood":"#7a5a3a","Aubergine":"#523040","Baltic":"#5b6a7a",
          "Beige":"#e6d8be","Biscuit":"#dec79a","Black":"#1a1a1a","Blank Slate":"#bfb8a8","Bliss":"#f3e3d0","Bluestone":"#5d7686","Blush Pink":"#f1c2c0","Blush Rose":"#e6a9af",
          "Bone":"#ede4d3","Brown":"#5b3a25","Buff":"#d6b78a","Buttercup":"#f5d97a","Cane":"#c8a172","Carbon":"#3e3e3e","Cashew":"#bfa17a","Cashmere":"#d8c2a3","Celadon":"#a6c4ae",
          "Celery":"#bdc88a","Cement":"#a39a8b","Champagne":"#ead9b8","China Blue":"#4f6f8c","Chrome Grey":"#9aa1a4","Cinder":"#3a3633","Cloud":"#dce0e2","Cocoa":"#5b3a2c",
          "Colonial Blue":"#4d6b89","Coral":"#f08070","Cottage Grey":"#a3a39a","Cream":"#f3e8d0","Cypress":"#4f5f48","Dark Sand":"#a89070","Dew":"#cfe0d6","Dove":"#bfb6ad",
          "Dune":"#bfa37c","Dusty Ranch":"#b08960","Dusty Rose":"#c69594","Dusty Teal":"#5e8a8a","Ecru":"#dccfb6","Fawn":"#a98c6b","Fire":"#a8281c","Flagstone":"#6a6e6e",
          "French Blue":"#5d7da3","Gainsborough":"#9aa3a5","Garnet":"#7a1a26","Gelato":"#f1d4cf","Gold":"#d4a017","Goldstone":"#a07735","Granite":"#5e5e5e","Grape":"#5a3a6a",
          "Gray":"#888888","Green":"#3a6e3a","Green Marble":"#5d7d52","Greystone":"#6a6a66","Harvest":"#a07033","Henna":"#8c422a","Honey":"#d09a3a","Hyacinth":"#7a6da8",
          "Ivory":"#efe6cf","Jade":"#3f8870","Java":"#3e2a1f","Kiva":"#8a5a3e","Latte":"#bfa07a","Lavender":"#aea3cc","Light Amethyst":"#bfa5cf","Light Camel":"#cfae84",
          "Light Grey":"#cfcfcf","Lilac":"#c5a8d2","Lime":"#bcd34a","Linen":"#e6dac3","Lipstick":"#b22247","Madison Grey":"#7a7a7a","Maize":"#e0c870","Marine Blue":"#26486b",
          "Maroon":"#5a1f23","Mauve":"#a98090","Meadow":"#789e63","Melon":"#f0a075","Milk":"#f3ece2","Mint":"#a7d6c0","Mint Julip":"#b8d4b3","Mist":"#d6dada","Misty Dew":"#b8c5be",
          "Mocha":"#6e4830","Moss":"#5b6e3a","Mushroom":"#9c8b73","Navy":"#1f2c54","Newport":"#3a5a78","Noir":"#1a1a1a","Nutmeg":"#7e4a2e","Ocean":"#2a5a7a","Olive":"#5e6b32",
          "Opal":"#b6c4cc","Orange":"#e07b1a","Oyster":"#bfae90","Pale Green":"#bcd2a3","Passion Blue":"#2a3f6e","Pastel Blue":"#b3cfe5","Pastel Pink":"#f3c8cf","Peach":"#f0b78a",
          "Pebble":"#9c948a","Peppermint":"#b3d6c4","Peridot":"#9eb050","Periwinkle":"#8090c8","Persimmon":"#c95a2c","Pink Lace":"#f1c5cd","Pistachio":"#b4cc99",
          "Plum Pleasure":"#5e3a4a","Porcelain":"#f0eadd","Port Blue":"#26405e","Powder Grey":"#c2c4c5","Prairie":"#a07b4f","Putty":"#bca887","Raffia":"#d6c4a3","Raisin":"#3e2a35",
          "Red":"#a82424","Rose":"#d57a87","Rosewater":"#f0c8c8","Royal Blue":"#1a3a8a","Royal Purple":"#4a2466","Ruby":"#8a1a36","Sable":"#3e2a1f","Safari":"#9a7e52",
          "Saffron":"#e0a020","Sage":"#9aa888","Salt":"#efe8de","Sand":"#d4c08a","Sandalwood":"#b08a5a","Scarlet Wine":"#6e1a26","Sea Blue":"#3a6a8a","Sea Breeze":"#b3cfd9",
          "Sea Grass":"#6e9a73","Seafoam":"#a3d3c4","Sedona":"#a05a3a","Shale":"#5a5e5e","Sienna":"#8e4a2a","Silver":"#c0c0c0","Sky Blue":"#9ec3da","Smoke":"#7a7a7a",
          "Snow":"#f5f5f5","Soft Clay":"#b4937c","Solar Yellow":"#f0c020","Spice":"#9c4a26","Spruce":"#3a5a3a","Steel":"#5e6e7a","Stone Grey":"#8a8a86","Stone Taupe":"#9c8a73",
          "Storm":"#5a5e62","Straw":"#d4c07a","Strong White":"#efe9da","Sugar Plum":"#4a2a3e","Sweet Lilac":"#bfa3c2","Tan":"#bfa074","Tarragon":"#6e7e3e","Taupe":"#8e7c5e",
          "Terracotta":"#b35a3a","Thistle":"#a89ab8","Thyme":"#5c6b3e","Trench":"#3e3a30","Tusk":"#dccfb0","Umber":"#5e3a26","Vanilla":"#f0e8d0","Verdigris":"#5a8a7a",
          "Vermilion":"#d23a1f","Wheat":"#dec38a","White":"#ffffff","Winter White":"#f0e8d0","Woodland":"#3e4a2a"
        };
        function nsHex(c) { return c.hex || NAME_HEX[c.name] || '#cccccc'; }
        function nsLuma(h) {
          var s = (h || '#cccccc').replace('#',''); if (s.length === 3) s = s.split('').map(function(x){return x+x;}).join('');
          var r = parseInt(s.slice(0,2),16), g = parseInt(s.slice(2,4),16), b = parseInt(s.slice(4,6),16);
          return 0.299*r + 0.587*g + 0.114*b;
        }
        // Convert hex → HSL for color-wheel (hue) sort. Desaturated colors
        // (s < 12%) fall to a "neutrals" bucket sorted by lightness so grays
        // don't shuffle randomly inside the rainbow.
        function nsHsl(h) {
          var s = (h || '#cccccc').replace('#',''); if (s.length === 3) s = s.split('').map(function(x){return x+x;}).join('');
          var r = parseInt(s.slice(0,2),16)/255, g = parseInt(s.slice(2,4),16)/255, b = parseInt(s.slice(4,6),16)/255;
          var max = Math.max(r,g,b), min = Math.min(r,g,b), L = (max+min)/2;
          var H = 0, S = 0;
          if (max !== min) {
            var d = max - min;
            S = L > 0.5 ? d / (2 - max - min) : d / (max + min);
            if (max === r) H = (g - b) / d + (g < b ? 6 : 0);
            else if (max === g) H = (b - r) / d + 2;
            else H = (r - g) / d + 4;
            H *= 60;
          }
          return [H, S * 100, L * 100];
        }
        function nsWheelKey(c) {
          var hsl = nsHsl(nsHex(c));
          // Neutrals (s < 12%) go to the END, sorted dark→light. Saturated
          // colors come first by hue (red 0° → orange → yellow → green → blue → purple → 360°).
          if (hsl[1] < 12) return 1000 + hsl[2];
          return hsl[0]; // 0..360 hue
        }
        var NS_SORTERS = {
          'color-wheel': function(a,b){ return nsWheelKey(a) - nsWheelKey(b); },
          'color-light': function(a,b){ return nsLuma(nsHex(b)) - nsLuma(nsHex(a)); },
          'color-dark':  function(a,b){ return nsLuma(nsHex(a)) - nsLuma(nsHex(b)); },
          'name-asc':    function(a,b){ return a.name.localeCompare(b.name); },
          'name-desc':   function(a,b){ return b.name.localeCompare(a.name); },
          'code-asc':    function(a,b){ return (a.code||'').localeCompare(b.code||''); },
          'new-first':   function(a,b){ return ((b.isNew?1:0) - (a.isNew?1:0)) || a.name.localeCompare(b.name); },
          'random':      function(){ return Math.random() - 0.5; },
        };
        var NS_PREFS = (function(){ try { return JSON.parse(localStorage.getItem('ns-prefs')||'{}'); } catch(e){ return {}; } })();
        function nsSavePrefs(){ try { localStorage.setItem('ns-prefs', JSON.stringify(NS_PREFS)); } catch(e){} }
        function setSort(mode){ NS_PREFS.sort = mode; nsSavePrefs(); loadColors(); }
        function setCols(n){
          NS_PREFS.cols = parseInt(n,10) || 6;
          nsSavePrefs();
          var grids = document.querySelectorAll('.colors-grid');
          for (var i=0;i<grids.length;i++) grids[i].style.setProperty('--cols', String(NS_PREFS.cols));
          var v = document.getElementById('nsColsVal'); if (v) v.textContent = NS_PREFS.cols;
        }
        function nsApplyInitial(){
          // Default first-visit sort = color-light (per Steve 2026-05-07).
          var sort = NS_PREFS.sort || 'color-light';
          var cols = NS_PREFS.cols || 6;
          var sel = document.getElementById('nsSort'); if (sel) sel.value = sort;
          var rng = document.getElementById('nsCols'); if (rng) rng.value = cols;
          var v = document.getElementById('nsColsVal'); if (v) v.textContent = cols;
          var grids = document.querySelectorAll('.colors-grid');
          for (var i=0;i<grids.length;i++) grids[i].style.setProperty('--cols', String(cols));
        }

        // Load colors grid
        // Render a chip into the given grid element.
        function renderChip(grid, color) {
            var item = document.createElement('div');
            item.className = 'color-item';
            if (color.hex) item.setAttribute('data-family-hex', String(color.hex).replace('#',''));
            if (color.isNew) {
                var badge = document.createElement('div');
                badge.className = 'new-badge';
                badge.textContent = 'NEW';
                item.appendChild(badge);
            }
            var swatch = document.createElement('div');
            swatch.className = 'color-swatch';
            var img = document.createElement('img');
            var fileName = color.name.toLowerCase().replace(/ /g, '-');
            img.alt = color.name;
            img.onerror = function() {
                if (this && this.style) this.style.display = 'none';
                if (this && this.parentElement) this.parentElement.style.background = '#' + getColorHex(this.alt);
            };
            swatch.appendChild(img);
            // src AFTER append — so onerror always finds the parent and the parent's swatch context
            img.src = '/images/colors/losangelesfabrics-' + fileName + '.jpg';
            var info = document.createElement('div');
            info.className = 'color-info';
            var nameDiv = document.createElement('div');
            nameDiv.className = 'color-name'; nameDiv.textContent = color.name;
            var codeDiv = document.createElement('div');
            codeDiv.className = 'color-code'; codeDiv.textContent = color.code;
            info.appendChild(nameDiv); info.appendChild(codeDiv);
            item.appendChild(swatch); item.appendChild(info);
            var btnRow = document.createElement('div');
            btnRow.className = 'chip-actions';
            var drapeBtn = document.createElement('button');
            drapeBtn.className = 'drapery-btn'; drapeBtn.type = 'button'; drapeBtn.textContent = 'See in Room';
            (function(n, im) { drapeBtn.onclick = function(e) { e.stopPropagation(); applyDraperyColor(n, im); }; })(color.name, img);
            btnRow.appendChild(drapeBtn);
            var sampleBtn = document.createElement('button');
            sampleBtn.className = 'sample-btn-chip'; sampleBtn.type = 'button'; sampleBtn.textContent = 'Sample';
            (function(n) { sampleBtn.onclick = function(e) { e.stopPropagation(); openModal(); setTimeout(function() { document.getElementById('selectedColor').value = 'Los Angeles Fabrics-' + n; updatePreview(); addToRequest(); }, 100); }; })(color.name);
            btnRow.appendChild(sampleBtn);
            info.appendChild(btnRow);
            (function(n) { item.onclick = function() { openModal(); setTimeout(function() { document.getElementById('selectedColor').value = 'Los Angeles Fabrics-' + n; updatePreview(); }, 100); }; })(color.name);
            grid.appendChild(item);
        }

        function loadColors() {
            nsApplyInitial();
            var grid = document.getElementById('colorsGrid');
            var newGrid = document.getElementById('newArrivalsGrid');
            // Sorted COPY of colors based on current pref
            var sortMode = (NS_PREFS && NS_PREFS.sort) || 'color-light';
            var cmp = NS_SORTERS[sortMode] || NS_SORTERS['color-light'];
            var sorted = colors.slice().sort(cmp);
            // New arrivals section — only isNew chips, in current sort order
            if (newGrid) {
                newGrid.innerHTML = '';
                for (var j = 0; j < sorted.length; j++) if (sorted[j].isNew) renderChip(newGrid, sorted[j]);
            }
            // Main collection — all colors, sorted
            if (!grid) return;
            grid.innerHTML = '';
            for (var i = 0; i < sorted.length; i++) renderChip(grid, sorted[i]);
            return;
            // Below: legacy inline renderer (kept for diff-readability — never executes)
            for (var i = 0; i < colors.length; i++) {
                var item = document.createElement('div');
                item.className = 'color-item';

                if (colors[i].isNew) {
                    var badge = document.createElement('div');
                    badge.className = 'new-badge';
                    badge.textContent = 'NEW';
                    item.appendChild(badge);
                }

                var swatch = document.createElement('div');
                swatch.className = 'color-swatch';

                var img = document.createElement('img');
                var fileName = colors[i].name.toLowerCase().replace(/ /g, '-');
                img.src = '/images/colors/losangelesfabrics-' + fileName + '.jpg';
                img.alt = colors[i].name;
                img.onerror = function(e) {
                    this.style.display = 'none';
                    this.parentElement.style.background = '#' + getColorHex(this.alt);
                };

                swatch.appendChild(img);

                var info = document.createElement('div');
                info.className = 'color-info';

                var nameDiv = document.createElement('div');
                nameDiv.className = 'color-name';
                nameDiv.textContent = colors[i].name;

                var codeDiv = document.createElement('div');
                codeDiv.className = 'color-code';
                codeDiv.textContent = colors[i].code;

                info.appendChild(nameDiv);
                info.appendChild(codeDiv);

                item.appendChild(swatch);
                item.appendChild(info);

                // Two action buttons per chip: Drapery (recolor whole page background)
                // and Sample (open the contact modal pre-filled with this color).
                var btnRow = document.createElement('div');
                btnRow.className = 'chip-actions';

                var drapeBtn = document.createElement('button');
                drapeBtn.className = 'drapery-btn';
                drapeBtn.type = 'button';
                drapeBtn.textContent = '🪟 Drapery';
                (function(colorName, swatchImg) {
                    drapeBtn.onclick = function(e) {
                        e.stopPropagation();
                        applyDraperyColor(colorName, swatchImg);
                    };
                })(colors[i].name, img);
                btnRow.appendChild(drapeBtn);

                var sampleBtn = document.createElement('button');
                sampleBtn.className = 'sample-btn-chip';
                sampleBtn.type = 'button';
                sampleBtn.textContent = '📦 Sample';
                (function(colorName) {
                    sampleBtn.onclick = function(e) {
                        e.stopPropagation();
                        openModal();
                        setTimeout(function() {
                            document.getElementById('selectedColor').value = 'Los Angeles Fabrics-' + colorName;
                            updatePreview();
                        }, 100);
                    };
                })(colors[i].name);
                btnRow.appendChild(sampleBtn);

                info.appendChild(btnRow);

                (function(colorName) {
                    item.onclick = function() {
                        openModal();
                        setTimeout(function() {
                            document.getElementById('selectedColor').value = 'Los Angeles Fabrics-' + colorName;
                            updatePreview();
                        }, 100);
                    };
                })(colors[i].name);

                grid.appendChild(item);
            }
        }

        // Sample the average color of the chip's swatch image so we don't need a
        // hand-maintained hex table for all 118 colors. Falls back to the small
        // colorMap in getColorHex() if the swatch hasn't loaded.
        function sampleAverageColor(imgEl, colorName) {
            try {
                if (!imgEl || !imgEl.complete || !imgEl.naturalWidth) {
                    return getColorHex(colorName);
                }
                var c = document.createElement('canvas');
                c.width = 16; c.height = 16;
                var ctx = c.getContext('2d');
                ctx.drawImage(imgEl, 0, 0, 16, 16);
                var d = ctx.getImageData(0, 0, 16, 16).data;
                var r=0,g=0,b=0,n=0;
                for (var i=0; i<d.length; i+=4) { r+=d[i]; g+=d[i+1]; b+=d[i+2]; n++; }
                r = Math.round(r/n); g = Math.round(g/n); b = Math.round(b/n);
                return ((1<<24)+(r<<16)+(g<<8)+b).toString(16).slice(1).toUpperCase();
            } catch(e) {
                return getColorHex(colorName);
            }
        }

        // Apply the drapery: every color has a pre-rendered swag at
        // /images/drapery-swags/<slug>.jpg (Reinhard-style luminance-preserving
        // recolor of the hero curtain composite — generated by render-drapery-swags.js).
        // Falls back to live in-browser canvas recolor if the pre-rendered file
        // 404s (rare — only happens for color names that fail the slug round-trip).
        function applyDraperyColor(colorName, swatchImg) {
            var slug = String(colorName).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
            // v6 — whole frame in one color (sides + center recolored together)
            var url = '/images/drapery-swags/' + slug + '.jpg?v=6';
            var hex = sampleAverageColor(swatchImg, colorName);
            // Probe the pre-rendered file; if it loads, use it directly.
            var probe = new Image();
            probe.onload = function() { setBodyDrapery(url, hex); };
            probe.onerror = function() {
                console.warn('Pre-rendered drapery missing for', colorName, '→ falling back to live canvas recolor');
                liveCanvasRecolor(hex);
            };
            probe.src = url;
        }

        // Fallback: if no pre-rendered swag, do the recolor in-browser. Same Reinhard
        // luminance-preserving algorithm as render-drapery-swags.js.
        function liveCanvasRecolor(hex) {
            var leftUrl  = '/images/left-curtain-d1.png';
            var rightUrl = '/images/right-curtain-d1.png';
            var splashUrl = '/images/bg-site-splash.jpg';
            loadImg(splashUrl, function(splash) {
                var W = 1920, H = 1080;
                if (splash) { W = splash.naturalWidth || W; H = splash.naturalHeight || H; }
                var canvas = document.createElement('canvas');
                canvas.width = W; canvas.height = H;
                var ctx = canvas.getContext('2d');
                if (splash) ctx.drawImage(splash, 0, 0, W, H);
                else { ctx.fillStyle = '#1a1a1a'; ctx.fillRect(0,0,W,H); }
                var pending = 2;
                function done() { if (--pending === 0) setBodyDrapery(canvas.toDataURL('image/jpeg', 0.85), hex); }
                loadImg(leftUrl,  function(im){ if (im) stampRecolored(ctx, im, hex, 'left',  W, H); done(); });
                loadImg(rightUrl, function(im){ if (im) stampRecolored(ctx, im, hex, 'right', W, H); done(); });
            });
        }

        function loadImg(url, cb) {
            var im = new Image();
            im.crossOrigin = 'anonymous';
            im.onload = function() { cb(im); };
            im.onerror = function() { cb(null); };
            im.src = url;
        }

        // HSL conversion helpers — used for pure luminance-preserving recolor
        // (Reinhard chromaticity transfer). Matches render-drapery-swags.js exactly.
        function _rgbToHsl(r, g, b) {
            r/=255; g/=255; b/=255;
            var mx=Math.max(r,g,b), mn=Math.min(r,g,b);
            var h, s, l=(mx+mn)/2;
            if (mx === mn) { h=0; s=0; }
            else {
                var d=mx-mn;
                s = l > 0.5 ? d/(2-mx-mn) : d/(mx+mn);
                if (mx===r) h=(g-b)/d+(g<b?6:0);
                else if (mx===g) h=(b-r)/d+2;
                else h=(r-g)/d+4;
                h/=6;
            }
            return [h,s,l];
        }
        function _hslToRgb(h, s, l) {
            var r,g,b;
            if (s === 0) { r=g=b=l; }
            else {
                var hue2rgb = function(p,q,t) {
                    if (t<0) t+=1; if (t>1) t-=1;
                    if (t<1/6) return p+(q-p)*6*t;
                    if (t<1/2) return q;
                    if (t<2/3) return p+(q-p)*(2/3-t)*6;
                    return p;
                };
                var q = l<0.5 ? l*(1+s) : l+s-l*s;
                var p = 2*l-q;
                r=hue2rgb(p,q,h+1/3); g=hue2rgb(p,q,h); b=hue2rgb(p,q,h-1/3);
            }
            return [Math.round(r*255), Math.round(g*255), Math.round(b*255)];
        }
        function stampRecolored(destCtx, img, hex, side, W, H) {
            var iw = img.naturalWidth, ih = img.naturalHeight;
            var scale = H / ih;
            var dw = iw * scale, dh = ih * scale;
            var dx = side === 'left' ? 0 : (W - dw);
            var off = document.createElement('canvas');
            off.width = iw; off.height = ih;
            var octx = off.getContext('2d');
            octx.drawImage(img, 0, 0);
            var id = octx.getImageData(0, 0, iw, ih);
            var data = id.data;
            var tr = parseInt(hex.slice(0,2), 16);
            var tg = parseInt(hex.slice(2,4), 16);
            var tb = parseInt(hex.slice(4,6), 16);
            var th = _rgbToHsl(tr, tg, tb);
            for (var i = 0; i < data.length; i += 4) {
                var a = data[i+3];
                if (a === 0) continue;
                var sl = _rgbToHsl(data[i], data[i+1], data[i+2])[2];
                var rgb = _hslToRgb(th[0], th[1], sl);
                data[i]   = rgb[0];
                data[i+1] = rgb[1];
                data[i+2] = rgb[2];
            }
            octx.putImageData(id, 0, 0);
            destCtx.drawImage(off, dx, 0, dw, dh);
        }

        function setBodyDrapery(url, hex) {
            // Body bg (visible below the hero on scroll)
            document.body.style.backgroundImage = 'url(' + url + ')';
            document.body.style.backgroundSize = 'cover';
            document.body.style.backgroundPosition = 'center top';
            document.body.style.backgroundRepeat = 'no-repeat';
            document.body.style.backgroundAttachment = 'fixed';
            // Hero bg — the visible drapery swap. Without this nothing changes
            // visually because the .hero rule covers the body.
            var hero = document.querySelector('.hero');
            if (hero) {
                hero.style.backgroundImage = 'url(' + url + ')';
                hero.style.backgroundSize = 'cover';
                hero.style.backgroundPosition = 'center top';
            }
            // Hide the foreground curtain divs — the pre-rendered swag already
            // contains both curtains recolored, so leaving the originals visible
            // would double-stack neutral curtains over the recolored ones.
            document.querySelectorAll('.curtain').forEach(function(c){ c.style.display = 'none'; });
            // Mark the body so subsequent sections fade their solid white bg and
            // the swag shows through end-to-end of the page.
            document.body.classList.add('has-drapery');
            // Tone down the white veil over the hero so the drapery color reads true
            var sty = document.getElementById('cncp-drapery-overlay-style');
            if (!sty) {
                sty = document.createElement('style');
                sty.id = 'cncp-drapery-overlay-style';
                sty.textContent = '.hero::before { background: linear-gradient(180deg, rgba(0,0,0,0.05) 0%, rgba(0,0,0,0.0) 50%, rgba(0,0,0,0.10) 100%) !important; }';
                document.head.appendChild(sty);
            }
            document.documentElement.style.setProperty('--drapery-color', '#' + hex);
        }

        // Close modal on outside click
        window.onclick = function(event) {
            var modal = document.getElementById('contactModal');
            if (event.target == modal) {
                modal.style.display = 'none';
            }
        }

        // Filter state — driven by the search box + the All/NEW/Classic chips + color-family dots
        var _colorFilter = 'all';
        var _colorFamily = 'all';
        function setColorFilter(f) {
            _colorFilter = f;
            document.querySelectorAll('.color-filter-btn').forEach(function(b){
                b.classList.toggle('active', b.dataset.filter === f);
            });
            filterColors();
        }
        function setColorFamily(f) {
            _colorFamily = f;
            document.querySelectorAll('.color-dot').forEach(function(b){
                b.classList.toggle('active', b.dataset.family === f);
            });
            filterColors();
        }
        // Bucket a hex color into a coarse family label by HSL.
        function hexToFamily(hex) {
            if (!hex) return 'neutral';
            hex = String(hex).replace('#','').trim();
            if (hex.length === 3) hex = hex.split('').map(function(c){return c+c;}).join('');
            if (!/^[0-9a-fA-F]{6}$/.test(hex)) return 'neutral';
            var r = parseInt(hex.substr(0,2),16)/255;
            var g = parseInt(hex.substr(2,2),16)/255;
            var b = parseInt(hex.substr(4,2),16)/255;
            var max = Math.max(r,g,b), min = Math.min(r,g,b);
            var l = (max+min)/2;
            var d = max-min;
            var s = d === 0 ? 0 : d / (1 - Math.abs(2*l-1));
            var h = 0;
            if (d !== 0) {
                if (max === r) h = ((g-b)/d) % 6;
                else if (max === g) h = (b-r)/d + 2;
                else h = (r-g)/d + 4;
                h *= 60; if (h < 0) h += 360;
            }
            // Black / white / neutral first
            if (l < 0.10) return 'black';
            if (l > 0.90 && s < 0.20) return 'white';
            if (s < 0.12) return 'neutral';
            // Brown = warm low-saturation, low-mid lightness
            if (h < 50 && s < 0.55 && l < 0.55) return 'brown';
            if (h >= 345 || h < 15)  return 'red';
            if (h < 40)              return 'orange';
            if (h < 70)              return 'yellow';
            if (h < 165)             return 'green';
            if (h < 200)             return 'teal';
            if (h < 255)             return 'blue';
            if (h < 300)             return 'purple';
            return 'pink';
        }
        // Resolve the family for a chip element using its hydrated hex (data attr) or the swatch image.
        function chipFamily(item) {
            var hex = item.getAttribute('data-family-hex');
            if (hex) return hexToFamily(hex);
            var name = (item.querySelector('.color-name')?.textContent || '');
            var fallback = (typeof getColorHex === 'function') ? getColorHex(name) : '';
            return hexToFamily(fallback);
        }
        function filterColors() {
            var q = (document.getElementById('colorSearch').value || '').toLowerCase().trim();
            var items = document.querySelectorAll('#colorsGrid .color-item');
            var shown = 0;
            items.forEach(function(item) {
                var name = (item.querySelector('.color-name')?.textContent || '').toLowerCase();
                var code = (item.querySelector('.color-code')?.textContent || '').toLowerCase();
                var isNew = !!item.querySelector('.new-badge');
                var matchesText = !q || name.includes(q) || code.includes(q);
                var matchesFilter = _colorFilter === 'all' || (_colorFilter === 'new' && isNew) || (_colorFilter === 'legacy' && !isNew);
                var matchesFamily = _colorFamily === 'all' || chipFamily(item) === _colorFamily;
                var visible = matchesText && matchesFilter && matchesFamily;
                item.style.display = visible ? '' : 'none';
                if (visible) shown++;
            });
            var counter = document.getElementById('color-count');
            if (counter) counter.textContent = shown + ' of ' + items.length + ' shown';
        }

        // Hydrate every color in the `colors[]` array with its TRUE
        // center-pixel hex (extracted by extract-center-hex.js → colors-data.json).
        // One canonical hex per SKU. Falls back to NAME_HEX lookup if a slug
        // is missing.
        function hydrateRealHexes(then) {
            fetch('/colors-data.json?v=2026-05-07', { cache: 'no-cache' })
                .then(function(r){ return r.ok ? r.json() : []; })
                .then(function(rows) {
                    if (!Array.isArray(rows) || !rows.length) { then(); return; }
                    var bySlug = {};
                    for (var i = 0; i < rows.length; i++) bySlug[rows[i].slug] = rows[i].hex;
                    for (var j = 0; j < colors.length; j++) {
                        var slug = (colors[j].name || '').toLowerCase().replace(/ /g, '-');
                        if (bySlug[slug]) colors[j].hex = bySlug[slug];
                    }
                    then();
                })
                .catch(function(){ then(); });
        }

        // Initialize on page load
        function nsBoot(){ hydrateRealHexes(function(){ loadColors(); filterColors(); }); }
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', nsBoot);
        } else {
            nsBoot();
        }
    </script>

    <script src="/script.js?v=drapery2"></script>

    <!-- Start of Zendesk Widget script -->
    <script id="ze-snippet" src="https://static.zdassets.com/ekr/snippet.js?key=4d4f52ea-1e3e-4837-8859-51f9abd5a82b"> </script>
    <!-- End of Zendesk Widget script -->
    <script>
    // Rotating hero — crossfade through 8 suede colors every 6.5s.
    // Pauses when the tab is hidden so we don't burn cycles on a background tab.
    (function () {
        var layers = document.querySelectorAll('.hero .hero-bg');
        if (layers.length < 2) return;
        var idx = 0;
        var ROTATE_MS = 6500;
        var timer = null;
        function step() {
            layers[idx].classList.remove('on');
            idx = (idx + 1) % layers.length;
            layers[idx].classList.add('on');
        }
        function start() { stop(); timer = setInterval(step, ROTATE_MS); }
        function stop() { if (timer) { clearInterval(timer); timer = null; } }
        document.addEventListener('visibilitychange', function () {
            if (document.hidden) stop(); else start();
        });
        start();
    })();
    </script>

    <!-- ──────────────────────────────────────────────────────────────────
         LAF Novasuede-UX module 2026-05-08
         Right-side hamburger · Theme · Tabs · Sort · Density · Search
         Live product grid (1.6k SKUs) · Detail modal
         ────────────────────────────────────────────────────────────────── -->
    <script>
    (function() {
        // ─── Hamburger / side panel ────────────────────────────────────
        var sidePanel = document.getElementById('lafSidePanel');
        var sideOverlay = document.getElementById('lafSideOverlay');
        var hamburger = document.getElementById('lafHamburger');
        window.lafToggleSide = function() {
            var open = !sidePanel.classList.contains('open');
            sidePanel.classList.toggle('open', open);
            sideOverlay.classList.toggle('open', open);
            hamburger.classList.toggle('open', open);
            hamburger.setAttribute('aria-expanded', open ? 'true' : 'false');
            sidePanel.setAttribute('aria-hidden', open ? 'false' : 'true');
            document.body.style.overflow = open ? 'hidden' : '';
        };
        window.lafCloseSide = function() {
            sidePanel.classList.remove('open');
            sideOverlay.classList.remove('open');
            hamburger.classList.remove('open');
            hamburger.setAttribute('aria-expanded', 'false');
            sidePanel.setAttribute('aria-hidden', 'true');
            document.body.style.overflow = '';
        };
        document.addEventListener('keydown', function(e) {
            if (e.key === 'Escape') {
                if (sidePanel.classList.contains('open')) lafCloseSide();
                if (document.getElementById('lafProductModal').classList.contains('open')) lafCloseProduct();
            }
        });

        // ─── Theme toggle (dark/light) ─────────────────────────────────
        var themeIcon = document.getElementById('lafThemeIcon');
        function applyThemeIcon() {
            var t = document.documentElement.getAttribute('data-theme') || 'dark';
            themeIcon.textContent = t === 'light' ? '☀' : '☾';
        }
        applyThemeIcon();
        window.lafToggleTheme = function() {
            var cur = document.documentElement.getAttribute('data-theme') || 'dark';
            var next = cur === 'light' ? 'dark' : 'light';
            document.documentElement.setAttribute('data-theme', next);
            try { localStorage.setItem('laf_theme', next); } catch (e) {}
            applyThemeIcon();
        };

        // ─── Live product catalog ───────────────────────────────────────
        var STATE = {
            products: [],
            tab: 'all',
            sort: localStorage.getItem('laf_sort') || 'newest',
            density: parseInt(localStorage.getItem('laf_density') || '240', 10),
            query: '',
            visible: 0,
            pageSize: 96,
            current: null
        };

        // Tab definitions — each carries a predicate that runs against a product.
        var TABS = [
            { id: 'all',         label: 'All',           pred: function(p) { return true; } },
            { id: 'drapery',     label: 'Drapery',       pred: hasTag('Drapery') },
            { id: 'upholstery',  label: 'Upholstery',    pred: hasTag('Upholstery') },
            { id: 'sheer',       label: 'Sheer',         pred: hasTag('Sheer') },
            { id: 'outdoor',     label: 'Outdoor',       pred: hasTag('Outdoor Upholstery') },
            { id: 'fire-rated',  label: 'Fire Rated',    pred: hasTag('Fire Rated') },
            { id: 'faux-leather',label: 'Faux Leather',  pred: hasTag('Faux Leather') },
            { id: 'textured',    label: 'Textured',      pred: hasTag('Textured') },
            { id: 'solid',       label: 'Solid',         pred: hasTag('Solid') },
            { id: 'patterned',   label: 'Patterned',     pred: function(p) {
                var tags = (p.tags || []).map(function(t){return t.toLowerCase();});
                return tags.some(function(t) {
                    return ['stripe','geometric','herringbone','abstract','floral','damask'].indexOf(t) !== -1;
                });
            }},
            { id: 'le-cirque',   label: 'Le Cirque',     pred: collectionIs('Le Cirque') },
            { id: 'monument',    label: 'Monument',      pred: collectionIs('Monument') },
            { id: 'hollywood',   label: 'Hollywood',     pred: collectionIs('Hollywood Babylon') },
            { id: 'aesthetics',  label: 'Aesthetics',    pred: collectionIs('Aesthetics') },
            { id: 'peak-perf',   label: 'Peak Perf.',    pred: collectionIs('Peak Performance') }
        ];
        function hasTag(tag) {
            var lower = tag.toLowerCase();
            return function(p) { return (p.tags || []).some(function(t) { return t.toLowerCase() === lower; }); };
        }
        function collectionIs(name) {
            var lower = name.toLowerCase();
            return function(p) {
                var c = (p.specs && p.specs.collection || '').toLowerCase();
                return c.indexOf(lower) !== -1 || (p.tags || []).some(function(t){return t.toLowerCase().indexOf(lower) !== -1 && t.toLowerCase().indexOf('collection') !== -1;});
            };
        }

        // ─── Tab rendering ──────────────────────────────────────────────
        function renderTabs() {
            var container = document.getElementById('lafTabs');
            if (!container) return;
            container.innerHTML = TABS.map(function(t) {
                var count = STATE.products.filter(t.pred).length;
                if (t.id !== 'all' && count === 0) return ''; // hide empty tabs
                return '<button type="button" class="laf-tab' + (STATE.tab === t.id ? ' active' : '') +
                       '" role="tab" aria-selected="' + (STATE.tab === t.id ? 'true' : 'false') +
                       '" data-tab="' + t.id + '" onclick="lafSetTab(\'' + t.id + '\')">' +
                       t.label + ' <span class="tab-count">' + count + '</span></button>';
            }).join('');
        }
        window.lafSetTab = function(id) {
            STATE.tab = id;
            STATE.visible = 0;
            renderTabs();
            renderGrid();
        };

        // ─── Filtering, sorting, rendering grid ─────────────────────────
        function activeTab() {
            for (var i = 0; i < TABS.length; i++) if (TABS[i].id === STATE.tab) return TABS[i];
            return TABS[0];
        }
        function filtered() {
            var pred = activeTab().pred;
            var q = STATE.query.toLowerCase().trim();
            var list = STATE.products.filter(pred);
            if (q) {
                list = list.filter(function(p) {
                    var hay = (p.title + ' ' + (p.color||'') + ' ' + (p.pattern||'') + ' ' + (p.sku||'') + ' ' + (p.mfrSku||'') + ' ' + (p.tags||[]).join(' ')).toLowerCase();
                    return hay.indexOf(q) !== -1;
                });
            }
            return sortList(list);
        }
        function sortList(list) {
            var arr = list.slice();
            switch (STATE.sort) {
                case 'newest':
                    arr.sort(function(a, b) { return (b.createdAt||'').localeCompare(a.createdAt||''); }); break;
                case 'sku-asc':
                    arr.sort(function(a, b) { return (a.sku||'').localeCompare(b.sku||''); }); break;
                case 'title-asc':
                    arr.sort(function(a, b) { return (a.title||'').localeCompare(b.title||''); }); break;
                case 'price-asc':
                    arr.sort(function(a, b) { return parseFloat(a.price||0) - parseFloat(b.price||0); }); break;
                case 'price-desc':
                    arr.sort(function(a, b) { return parseFloat(b.price||0) - parseFloat(a.price||0); }); break;
                case 'color':
                    // Group by primary color tag (first color-ish tag)
                    arr.sort(function(a, b) {
                        return colorBucket(a).localeCompare(colorBucket(b)) || (a.title||'').localeCompare(b.title||'');
                    }); break;
                case 'style':
                    arr.sort(function(a, b) {
                        var sa = (a.specs && a.specs.style) || '';
                        var sb = (b.specs && b.specs.style) || '';
                        return sa.localeCompare(sb) || (a.title||'').localeCompare(b.title||'');
                    }); break;
            }
            return arr;
        }
        var COLOR_TAGS = ['black','white','off-white','beige','tan','khaki','light beige','light gray','gray','dark gray','charcoal gray','slate gray','silver','steel blue','blue','dark teal','teal','navy','green','olive green','dark olive green','dark green','dark teal','red','burnt sienna','umber','dark brown','brown','light brown','copper','gold','yellow','amber'];
        function colorBucket(p) {
            var tags = (p.tags || []).map(function(t){return t.toLowerCase();});
            for (var i = 0; i < COLOR_TAGS.length; i++) {
                if (tags.indexOf(COLOR_TAGS[i]) !== -1) return COLOR_TAGS[i];
            }
            return 'zzz';
        }

        function renderGrid() {
            var grid = document.getElementById('lafProductGrid');
            var empty = document.getElementById('lafGridEmpty');
            var more = document.getElementById('lafLoadMore');
            var count = document.getElementById('lafResultCount');
            var list = filtered();
            grid.style.setProperty('--card-min', STATE.density + 'px');

            empty.style.display = list.length === 0 ? '' : 'none';
            grid.style.display = list.length === 0 ? 'none' : '';

            var slice = list.slice(0, STATE.visible || STATE.pageSize);
            if (!STATE.visible) STATE.visible = slice.length;
            grid.innerHTML = slice.map(cardHtml).join('');
            // Lazy-load image fade-in
            grid.querySelectorAll('.laf-card-image img').forEach(function(img) {
                if (img.complete && img.naturalHeight > 0) img.classList.add('loaded');
                else img.addEventListener('load', function() { img.classList.add('loaded'); }, { once: true });
                img.addEventListener('error', function() { img.classList.add('loaded'); img.style.opacity = 0.4; }, { once: true });
            });
            // Wire click → open product modal
            grid.querySelectorAll('.laf-card').forEach(function(c) {
                c.addEventListener('click', function() {
                    var id = c.getAttribute('data-id');
                    var p = STATE.products.find(function(x) { return x.id === id; });
                    if (p) lafOpenProduct(p);
                });
            });

            count.textContent = list.length === 1
                ? '1 fabric'
                : (Math.min(slice.length, list.length).toLocaleString() + ' of ' + list.length.toLocaleString() + ' fabrics');
            more.style.display = slice.length < list.length ? '' : 'none';
        }
        function escapeHtml(s) {
            return (s || '').replace(/[&<>"']/g, function(c) {
                return { '&':'&amp;', '<':'&lt;', '>':'&gt;', '"':'&quot;', "'":'&#39;' }[c];
            });
        }
        function shortTitle(title) {
            // Trim trailing " | Phillipe Romano" / " | Los Angeles Fabrics" for the card.
            return (title || '').replace(/\s*\|\s*(Phillipe Romano|Los Angeles Fabrics|Commercial Fabric).*$/i, '').trim();
        }
        function priceLabel(p) {
            if (!p.price) return '';
            var pr = parseFloat(p.price);
            if (pr <= 5) return 'Sample $' + pr.toFixed(2);
            return '$' + pr.toFixed(2);
        }
        function cardHtml(p) {
            var img = p.image || '';
            var title = escapeHtml(shortTitle(p.title));
            var color = escapeHtml(p.color || '');
            var sku = escapeHtml(p.sku || '');
            var collectionBadge = (p.specs && p.specs.collection) ? '<span class="laf-card-badge">' + escapeHtml(p.specs.collection) + '</span>' : '';
            return '<article class="laf-card" data-id="' + escapeHtml(p.id) + '" tabindex="0" role="button" aria-label="' + title + '">' +
                '<div class="laf-card-image">' + collectionBadge +
                    (img ? '<img loading="lazy" src="' + escapeHtml(img) + '" alt="' + title + '">' : '') +
                '</div>' +
                '<div class="laf-card-body">' +
                    '<div class="laf-card-title">' + title + '</div>' +
                    (color ? '<div class="laf-card-color">' + color + '</div>' : '') +
                    '<div class="laf-card-meta">' + sku + '</div>' +
                    '<div class="laf-card-price">' + priceLabel(p) + '</div>' +
                '</div>' +
            '</article>';
        }

        // ─── Toolbar wiring ─────────────────────────────────────────────
        var search = document.getElementById('lafSearch');
        var sortSel = document.getElementById('lafSort');
        var dens = document.getElementById('lafDensity');
        var densVal = document.getElementById('lafDensityVal');
        var loadMore = document.getElementById('lafLoadMore');

        sortSel.value = STATE.sort;
        dens.value = STATE.density;
        densVal.textContent = STATE.density;

        var searchTimer;
        search.addEventListener('input', function() {
            clearTimeout(searchTimer);
            searchTimer = setTimeout(function() {
                STATE.query = search.value;
                STATE.visible = 0;
                renderGrid();
            }, 120);
        });
        sortSel.addEventListener('change', function() {
            STATE.sort = sortSel.value;
            try { localStorage.setItem('laf_sort', STATE.sort); } catch (e) {}
            STATE.visible = 0;
            renderGrid();
        });
        dens.addEventListener('input', function() {
            STATE.density = parseInt(dens.value, 10);
            densVal.textContent = STATE.density;
            try { localStorage.setItem('laf_density', String(STATE.density)); } catch (e) {}
            var grid = document.getElementById('lafProductGrid');
            grid.style.setProperty('--card-min', STATE.density + 'px');
        });
        loadMore.addEventListener('click', function() {
            STATE.visible = (STATE.visible || STATE.pageSize) + STATE.pageSize;
            renderGrid();
            // Scroll-restore so the previous bottom card stays in view.
        });

        // Infinite scroll — load next page when near bottom.
        window.addEventListener('scroll', function() {
            if (!STATE.products.length) return;
            var loadMoreBtn = document.getElementById('lafLoadMore');
            if (!loadMoreBtn || loadMoreBtn.style.display === 'none') return;
            var rect = loadMoreBtn.getBoundingClientRect();
            if (rect.top < window.innerHeight + 800) {
                STATE.visible = (STATE.visible || STATE.pageSize) + STATE.pageSize;
                renderGrid();
            }
        }, { passive: true });

        // ─── Product detail modal ───────────────────────────────────────
        function specRow(label, value) {
            if (!value) return '';
            return '<div class="pi-spec-row"><span class="pi-spec-label">' + escapeHtml(label) + '</span><span class="pi-spec-value">' + escapeHtml(value) + '</span></div>';
        }
        window.lafOpenProduct = function(p) {
            STATE.current = p;
            var m = document.getElementById('lafProductModal');
            document.getElementById('lafProductTitle').textContent = shortTitle(p.title);
            document.getElementById('lafProductColor').textContent = p.color || '';
            document.getElementById('lafProductPrice').textContent = priceLabel(p) + (p.sku ? ' · ' + p.sku : '');
            document.getElementById('lafProductEyebrow').textContent = (p.specs && p.specs.collection) ? p.specs.collection : 'Los Angeles Fabrics';
            // Description: editorial body only — specs go in the side panel (Steve's "no specs in body" rule).
            document.getElementById('lafProductDesc').innerHTML = p.descriptionHtml || '<em style="opacity:0.5">No description available.</em>';
            // Specs panel
            var sp = p.specs || {};
            document.getElementById('lafProductSpecs').innerHTML = [
                specRow('Width', sp.width),
                specRow('Length', sp.length),
                specRow('Repeat', sp.repeat),
                specRow('Content', sp.content),
                specRow('Finish', sp.finish),
                specRow('Durability', sp.durability),
                specRow('Wyzenbeek', sp.wyzenbeek),
                specRow('Flammability', sp.flammability),
                specRow('Cleaning', sp.cleaning),
                specRow('Style', sp.style),
                specRow('Use', sp.usage),
                specRow('Collection', sp.collection)
            ].join('');
            // Tags: cap at 12 most-relevant
            var tags = (p.tags || []).filter(function(t) {
                return ['Architectural','Commercial','Phillipe Romano','Fire Rated'].indexOf(t) === -1;
            }).slice(0, 12);
            document.getElementById('lafProductTags').innerHTML = tags.map(function(t) {
                return '<span class="pi-tag">' + escapeHtml(t) + '</span>';
            }).join('');
            // Images (hero + thumbs)
            var imgs = (p.images && p.images.length) ? p.images : (p.image ? [{ url: p.image, alt: p.title }] : []);
            var hero = document.getElementById('lafProductHeroImg');
            hero.src = imgs[0] ? imgs[0].url : '';
            hero.alt = imgs[0] ? (imgs[0].alt || p.title) : p.title;
            document.getElementById('lafProductThumbs').innerHTML = imgs.slice(0, 8).map(function(im, i) {
                return '<img src="' + escapeHtml(im.url) + '" alt="' + escapeHtml(im.alt || '') + '" class="' + (i === 0 ? 'active' : '') + '" data-idx="' + i + '">';
            }).join('');
            document.getElementById('lafProductThumbs').querySelectorAll('img').forEach(function(t) {
                t.addEventListener('click', function() {
                    document.getElementById('lafProductHeroImg').src = t.src;
                    document.getElementById('lafProductThumbs').querySelectorAll('img').forEach(function(o) { o.classList.remove('active'); });
                    t.classList.add('active');
                });
            });
            m.classList.add('open');
            document.body.style.overflow = 'hidden';
        };
        window.lafCloseProduct = function() {
            document.getElementById('lafProductModal').classList.remove('open');
            document.body.style.overflow = '';
        };
        window.lafOpenShopifyLink = function() {
            var p = STATE.current;
            if (!p) return;
            var url = p.onlineStoreUrl || ('https://designerwallcoverings.com/products/' + p.handle);
            window.open(url, '_blank', 'noopener');
        };
        window.lafRequestSampleFromProduct = function() {
            var p = STATE.current;
            lafCloseProduct();
            // Re-use the existing sample-request modal; pre-fill with this product's color name.
            try {
                if (typeof openModal === 'function') {
                    openModal();
                    setTimeout(function() {
                        var picker = document.getElementById('selectedColor');
                        var hidden = document.getElementById('selectedColorInput');
                        if (hidden && p) hidden.value = (p.color || '') + ' (' + (p.sku || '') + ')';
                        if (picker && p) {
                            // Best-effort: try to match by color name
                            var match = Array.from(picker.options).find(function(o) {
                                return p.color && o.text.toLowerCase().indexOf(p.color.toLowerCase()) !== -1;
                            });
                            if (match) { picker.value = match.value; if (typeof updatePreview === 'function') updatePreview(); }
                        }
                    }, 60);
                }
            } catch (e) { /* fallback: just open modal */ if (typeof openModal === 'function') openModal(); }
        };

        // ─── Boot ───────────────────────────────────────────────────────
        function boot() {
            fetch('/data/products.json?v=' + Date.now())
                .then(function(r) {
                    if (!r.ok) throw new Error('products.json fetch ' + r.status);
                    return r.json();
                })
                .then(function(d) {
                    STATE.products = (d && Array.isArray(d.products)) ? d.products : [];
                    renderTabs();
                    renderGrid();
                })
                .catch(function(err) {
                    console.error('[laf] product load failed:', err);
                    var rc = document.getElementById('lafResultCount');
                    if (rc) rc.textContent = 'product catalog temporarily unavailable';
                });
        }
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', boot);
        } else {
            boot();
        }
    })();
    </script>
<script src="/dw-fleet-nav.js" defer></script>

</body>
</html>