← back to Bleachresistantfabrics
public/index.html
555 lines
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<!-- GA4 gtag (auto) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-5XZST5KTQC"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-5XZST5KTQC');
</script>
<!-- /GA4 gtag -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bleach Resistant Fabrics — Crypton, Vinyl-Coated & Performance Contract Fabrics for Hospitality & Healthcare</title>
<meta name="description" content="Crypton, vinyl-coated, and performance contract fabrics rated for 1:10 bleach cleanup. Hospitality + healthcare specifications, trade pricing, free samples.">
<meta name="theme-color" content="#0F1A1F">
<link rel="canonical" href="https://bleachresistantfabrics.com/">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@400;500;600&family=Inter:wght@300;400;500;700&display=swap" rel="stylesheet">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Bleach Resistant Fabrics",
"url": "https://bleachresistantfabrics.com/",
"email": "info@bleachresistantfabrics.com",
"telephone": "+1-888-373-4564",
"address": { "@type": "PostalAddress", "streetAddress": "15442 Ventura Bl #102", "addressLocality": "Sherman Oaks", "addressRegion": "CA", "postalCode": "91403", "addressCountry": "US" }
}
</script>
<script>
(function(){ try { var t = localStorage.getItem('brf_theme') || 'dark'; document.documentElement.dataset.theme = t; } catch(e){} })();
</script>
<style>
:root {
--bg: #0F1A1F;
--paper: #EAF1F4;
--muted: #8FA3AB;
--line: rgba(63,182,187,0.22);
--accent: #3FB6BB;
--bg-soft: #16242B;
--cols: 5;
}
html[data-theme="light"] {
--bg: #F5F8F9;
--paper: #0F1A1F;
--muted: #4F6770;
--line: rgba(15,26,31,0.16);
--accent: #1D7B80;
--bg-soft: #E1ECEF;
}
* { margin:0; padding:0; box-sizing:border-box }
html { scroll-behavior:smooth }
body { font-family:'Inter', sans-serif; color:var(--paper); background:var(--bg); -webkit-font-smoothing:antialiased; min-height:100vh; overflow-x:hidden }
h1, h2, h3, .brand, .accent-italic { font-family:'Cormorant Garamond', Georgia, serif; font-weight:500 }
a { color:inherit }
header { position:sticky; top:0; z-index:100; display:flex; justify-content:space-between; align-items:center; padding:18px 28px; background:color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter:blur(10px); border-bottom:1px solid var(--line) }
.brand { font-size:20px; letter-spacing:0.06em; text-transform:uppercase; text-decoration:none }
.brand .dot { display:inline-block; width:10px; height:10px; border-radius:50%; background:var(--accent); margin-right:10px; vertical-align:middle }
.h-actions { display:flex; gap:14px; align-items:center }
.h-link { font-size:11px; letter-spacing:0.22em; text-transform:uppercase; font-weight:700; color:var(--paper); text-decoration:none; background:transparent; border:0; cursor:pointer; padding:6px 0 }
.h-link:hover { color:var(--accent) }
.theme-toggle { width:34px; height:34px; border:1px solid var(--line); background:transparent; color:var(--paper); cursor:pointer; display:inline-flex; align-items:center; justify-content:center; font-size:14px; line-height:1; transition:all .15s; padding:0; border-radius:50% }
.theme-toggle:hover { border-color:var(--accent); color:var(--accent) }
.hero { padding:112px 28px 64px; max-width:1500px; margin:0 auto }
.eyebrow { font-size:10px; letter-spacing:0.4em; text-transform:uppercase; font-weight:700; color:var(--accent); margin-bottom:18px }
h1.headline { font-size:clamp(40px, 6vw, 80px); line-height:1.05; letter-spacing:-0.01em; margin-bottom:20px }
h1.headline .accent-italic { color:var(--accent); font-style:italic }
.lede { font-size:18px; line-height:1.55; color:var(--muted); max-width:720px; margin-bottom:32px }
.cta-row { display:flex; gap:14px; flex-wrap:wrap }
.btn { display:inline-block; padding:14px 26px; font-size:11px; letter-spacing:0.28em; text-transform:uppercase; font-weight:700; text-decoration:none; cursor:pointer; border:1px solid var(--paper); color:var(--paper); background:transparent; transition:all .2s; font-family:inherit }
.btn:hover { background:var(--paper); color:var(--bg) }
.btn.primary { background:var(--accent); border-color:var(--accent); color:#0F1A1F }
.btn.primary:hover { filter:brightness(1.1) }
.specs { display:grid; grid-template-columns:repeat(4, 1fr); gap:0; max-width:1500px; margin:48px auto 0; border-top:1px solid var(--line); border-bottom:1px solid var(--line) }
.spec { padding:22px 26px; border-right:1px solid var(--line) }
.spec:last-child { border-right:0 }
.spec .k { font-size:10px; letter-spacing:0.3em; text-transform:uppercase; font-weight:700; color:var(--muted); margin-bottom:8px }
.spec .v { font-size:15px; font-weight:500 }
@media (max-width:780px) { .specs { grid-template-columns:repeat(2, 1fr) } .spec:nth-child(2) { border-right:0 } .spec:nth-child(1), .spec:nth-child(2) { border-bottom:1px solid var(--line) } }
.section { padding:80px 28px; max-width:1500px; margin:0 auto }
.section-head { display:flex; justify-content:space-between; align-items:flex-end; flex-wrap:wrap; gap:18px; margin-bottom:28px; padding-bottom:20px; border-bottom:1px solid var(--line) }
.section-title { font-size:clamp(32px, 4.4vw, 56px); letter-spacing:-0.01em; line-height:1 }
.section-title .accent-italic { color:var(--accent); font-style:italic }
.controls { display:flex; gap:14px; flex-wrap:wrap; align-items:center; margin-bottom:18px }
.chip { padding:8px 14px; font-size:11px; letter-spacing:0.2em; text-transform:uppercase; font-weight:700; color:var(--paper); background:transparent; border:1px solid var(--line); cursor:pointer; transition:all .2s; font-family:inherit }
.chip:hover { border-color:var(--paper) }
.chip.active { background:var(--paper); color:var(--bg); border-color:var(--paper) }
.search { flex:1 1 240px; max-width:320px; display:flex; align-items:center; gap:8px; border-bottom:1px solid var(--line); padding:6px 0 }
.search input { flex:1; background:transparent; border:0; color:var(--paper); font-family:inherit; font-size:13px; outline:none }
.search input::placeholder { color:var(--muted) }
.toolbar { display:flex; gap:18px; align-items:center; flex-wrap:wrap; padding:14px 0 22px; border-bottom:1px solid var(--line); margin-bottom:24px }
.toolbar label { font-size:10px; letter-spacing:0.3em; text-transform:uppercase; font-weight:700; color:var(--muted) }
.toolbar select { padding:6px 10px; background:transparent; color:var(--paper); border:1px solid var(--line); font-family:inherit; font-size:12px; letter-spacing:0.04em; cursor:pointer }
.toolbar select:focus { outline:none; border-color:var(--accent) }
.toolbar .density { display:flex; align-items:center; gap:10px; flex:1 1 240px; max-width:340px }
.toolbar input[type=range] { flex:1; -webkit-appearance:none; appearance:none; height:1px; background:var(--line); outline:none }
.toolbar input[type=range]::-webkit-slider-thumb { -webkit-appearance:none; appearance:none; width:14px; height:14px; background:var(--accent); cursor:pointer; border-radius:50% }
.toolbar input[type=range]::-moz-range-thumb { width:14px; height:14px; background:var(--accent); cursor:pointer; border-radius:50%; border:0 }
.toolbar .ct { font-size:10px; letter-spacing:0.28em; text-transform:uppercase; font-weight:700; color:var(--muted) }
.grid { display:grid; grid-template-columns:repeat(var(--cols), 1fr); gap:0 }
.card { position:relative; aspect-ratio:1/1; cursor:pointer; overflow:hidden; border:1px solid var(--bg); transition:transform .35s cubic-bezier(0.2,0.8,0.2,1); background:var(--bg-soft) }
.card:hover { transform:scale(1.04); z-index:5 }
.card img { width:100%; height:100%; object-fit:cover; display:block }
.card .overlay { position:absolute; inset:auto 0 0 0; padding:18px 14px 12px; background:linear-gradient(180deg, transparent 0%, rgba(0,0,0,0.85) 100%); transform:translateY(48%); opacity:.85; transition:transform .3s ease, opacity .3s ease }
.card:hover .overlay { transform:translateY(0); opacity:1 }
.card .pat { font-size:13px; font-weight:600; color:#fff; line-height:1.25; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; overflow-wrap:anywhere }
.card .ven { font-size:9px; letter-spacing:0.2em; text-transform:uppercase; color:#fff; opacity:.7; font-weight:500; margin-top:4px }
.card .actions { margin-top:10px; display:flex; gap:6px }
.card .sample-btn { flex:1; padding:7px 10px; background:#fff; color:#000; font-family:inherit; font-size:10px; letter-spacing:0.22em; text-transform:uppercase; font-weight:700; border:0; cursor:pointer; text-align:center; text-decoration:none; transition:all .2s }
.card .sample-btn:hover { background:var(--accent); color:#fff }
@media (max-width:1024px) { .grid { grid-template-columns:repeat(min(var(--cols), 4), 1fr) } }
@media (max-width:720px) {
.grid { grid-template-columns:repeat(2, 1fr) }
/* keep sort visible on mobile — only the column-density slider is meaningless at a fixed 2-col layout */
.toolbar .density { display:none }
.card .sample-btn { padding:12px 10px } /* ≥44px touch target */
}
/* Touch devices have no hover — reveal the card overlay so the title +
sample button are reachable without a hover state. */
@media (hover: none) {
.card .overlay { transform:translateY(0); opacity:1 }
}
.loading { text-align:center; color:var(--muted); padding:32px; font-size:10px; letter-spacing:0.3em; text-transform:uppercase; font-weight:700 }
.stat-line { font-size:10px; letter-spacing:0.3em; text-transform:uppercase; font-weight:600; color:var(--muted); margin-bottom:18px }
.spec-block { display:grid; grid-template-columns:repeat(3, 1fr); gap:0; border-top:1px solid var(--line); border-bottom:1px solid var(--line); margin:32px 0 0 }
.spec-block .item { padding:28px 28px 26px; border-right:1px solid var(--line) }
.spec-block .item:last-child { border-right:0 }
.spec-block h3 { font-size:22px; margin-bottom:10px }
.spec-block p { font-size:13px; color:var(--muted); line-height:1.6 }
@media (max-width:780px) { .spec-block { grid-template-columns:1fr } .spec-block .item { border-right:0; border-bottom:1px solid var(--line) } .spec-block .item:last-child { border-bottom:0 } }
footer { padding:64px 28px 28px; border-top:1px solid var(--line); margin-top:64px }
.foot-grid { max-width:1500px; margin:0 auto; display:grid; grid-template-columns:2fr 1fr 1fr 1fr; gap:48px; margin-bottom:36px }
.foot-brand { font-size:22px; letter-spacing:0.18em; text-transform:uppercase; margin-bottom:10px }
.foot-text { font-size:13px; line-height:1.6; color:var(--muted); max-width:380px }
.foot-col h4 { font-size:10px; letter-spacing:0.3em; text-transform:uppercase; font-weight:700; margin-bottom:14px }
.foot-col a { display:block; font-size:13px; color:var(--muted); text-decoration:none; margin-bottom:6px }
.foot-col a:hover { color:var(--accent) }
.foot-bot { max-width:1500px; margin:0 auto; padding-top:20px; border-top:1px solid var(--line); display:flex; justify-content:space-between; flex-wrap:wrap; gap:14px; font-size:11px; letter-spacing:0.18em; text-transform:uppercase; color:var(--muted) }
@media (max-width:720px) { .foot-grid { grid-template-columns:1fr; gap:28px } }
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible { outline:2px solid var(--accent); outline-offset:2px }
.faq { max-width:920px; margin:0 auto; padding:0 4px }
.faq details { border-bottom:1px solid var(--line); padding:0 }
.faq details:first-of-type { border-top:1px solid var(--line) }
.faq summary { list-style:none; cursor:pointer; padding:22px 4px; display:flex; justify-content:space-between; align-items:center; gap:18px; font-size:17px; font-weight:500; line-height:1.4; color:var(--paper); transition:color .15s }
.faq summary::-webkit-details-marker { display:none }
.faq summary:hover { color:var(--accent) }
.faq summary .plus { flex:0 0 auto; width:22px; height:22px; position:relative; opacity:.6; transition:transform .25s ease, opacity .15s }
.faq summary .plus::before, .faq summary .plus::after { content:''; position:absolute; left:50%; top:50%; background:currentColor; transform-origin:center }
.faq summary .plus::before { width:14px; height:1px; transform:translate(-50%, -50%) }
.faq summary .plus::after { width:1px; height:14px; transform:translate(-50%, -50%); transition:transform .25s ease }
.faq details[open] summary .plus { opacity:1; color:var(--accent) }
.faq details[open] summary .plus::after { transform:translate(-50%, -50%) scaleY(0) }
.faq details[open] summary { color:var(--accent) }
.faq .a { padding:0 4px 26px; font-size:14px; line-height:1.7; color:var(--muted); max-width:760px }
.faq .a a { color:var(--accent); text-decoration:none; border-bottom:1px solid color-mix(in srgb, var(--accent) 40%, transparent) }
.faq .a strong { color:var(--paper); font-weight:600 }
</style>
<meta property="og:type" content="website">
<meta property="og:title" content="Bleach Resistant Fabrics">
<meta property="og:description" content="Crypton, vinyl-coated, and performance contract fabrics rated for 1:10 bleach cleanup. Hospitality + healthcare.">
<meta property="og:url" content="https://bleachresistantfabrics.com/">
<!-- Meta Pixel — INERT until set-fb-pixel.sh is run (does NOT load fbevents.js or hit Meta until flipped) -->
<script>
(function(){
var PIXEL_ID = '{{FACEBOOK_PIXEL_ID}}';
// Inert until flipped — the brace check prevents BOTH the loader IIFE
// AND the init call from running, so fbevents.js is never fetched.
if (PIXEL_ID.indexOf('{{') !== -1) return;
!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
document,'script','https://connect.facebook.net/en_US/fbevents.js');
fbq('init', PIXEL_ID);
fbq('track', 'PageView');
})();
</script>
<!-- noscript fallback is added by set-fb-pixel.sh on flip (omitted here to avoid unflipped img phone-home) -->
<!-- End Meta Pixel -->
</head>
<body>
<!-- Theme toggle (theme1 / theme2) — query-param-gated preview surface
(Steve 2026-05-29). ?theme=2 in URL flips to theme2 and persists in
localStorage. Toggle UI hidden until theme2 mode active. Same key
('wallco-page-theme') as wallco.ai for cross-site continuity. -->
<script>
(function(){
var qs = new URLSearchParams(location.search);
var fromUrl = qs.get('theme');
var saved = null; try { saved = localStorage.getItem('wallco-page-theme'); } catch(e){}
var theme = null;
if (fromUrl === '2' || fromUrl === 'theme2') theme = 'theme2';
else if (fromUrl === '1' || fromUrl === 'theme1') theme = 'theme1';
else if (saved === 'theme2' || saved === 'theme1') theme = saved;
if (theme) {
document.documentElement.setAttribute('data-page-theme', theme);
if (fromUrl) { try { localStorage.setItem('wallco-page-theme', theme); } catch(e){} }
}
})();
</script>
<style id="page-theme-css-fleet">
[data-page-theme="theme2"] body{
background:#fff !important; color:#0a0a0a !important;
}
[data-page-theme="theme2"] h1,
[data-page-theme="theme2"] h2,
[data-page-theme="theme2"] h3{ letter-spacing:-0.01em; font-weight:500; }
[data-page-theme="theme2"] .card,
[data-page-theme="theme2"] [class*="card"]{
background:#fafafa !important; border:1px solid #e5e5e5 !important; box-shadow:none !important;
}
#page-theme-toggle{ display:none; }
[data-page-theme] #page-theme-toggle{ display:inline-flex; }
#page-theme-toggle{
position:fixed; top:96px; right:14px; z-index:90;
gap:0; align-items:center;
background:rgba(255,255,255,.92); border:1px solid #d8d2c5;
border-radius:999px; padding:3px; backdrop-filter:blur(6px);
box-shadow:0 2px 10px rgba(0,0,0,.08);
font:600 11px ui-sans-serif,system-ui; letter-spacing:.06em; text-transform:uppercase;
}
#page-theme-toggle button{
border:0; background:transparent; color:#5a5048; cursor:pointer;
padding:6px 14px; border-radius:999px; font:inherit; transition:all .15s;
}
#page-theme-toggle button[aria-pressed="true"]{ background:#1a1714; color:#faf7f2; }
#page-theme-toggle button:hover:not([aria-pressed="true"]){ color:#1a1714; }
</style>
<div id="page-theme-toggle" role="group" aria-label="Page theme">
<button type="button" data-theme="theme1" aria-pressed="false">Theme 1</button>
<button type="button" data-theme="theme2" aria-pressed="false">Theme 2</button>
</div>
<script>
(function(){
var cur = document.documentElement.getAttribute('data-page-theme') || 'theme1';
var btns = document.querySelectorAll('#page-theme-toggle button');
function sync(t){ btns.forEach(function(b){ b.setAttribute('aria-pressed', b.dataset.theme === t ? 'true' : 'false'); }); }
sync(cur);
btns.forEach(function(b){
b.addEventListener('click', function(){
var t = b.dataset.theme;
document.documentElement.setAttribute('data-page-theme', t);
try { localStorage.setItem('wallco-page-theme', t); } catch(e){}
sync(t);
});
});
})();
</script>
<header>
<a class="brand" href="/"><span class="dot"></span>Bleach Resistant Fabrics</a>
<div class="h-actions">
<a class="h-link" href="#catalog">Catalog</a>
<a class="h-link" href="#specs">Specs</a>
<a class="h-link" href="#faq">FAQ</a>
<a class="h-link" href="mailto:info@bleachresistantfabrics.com">Trade</a>
<button class="theme-toggle" id="themeToggle" aria-label="Toggle theme">☾</button>
</div>
</header>
<section class="hero">
<div class="eyebrow">A Designer Wallcoverings Vertical · Contract · Hospitality + Healthcare</div>
<h1 class="headline">Fabric that survives<br>the <span class="accent-italic">1 : 10 bleach</span> test.</h1>
<p class="lede">Crypton, vinyl-coated Type-II, and performance contract textiles specified by hospitality designers and healthcare facility planners — every SKU here is rated for routine 1-part-bleach to 10-parts-water cleanup, the CDC-aligned protocol. Trade pricing, full Wyzenbeek & NFPA spec sheets, free memo samples.</p>
<div class="cta-row">
<a class="btn primary" href="#catalog">Browse Catalog</a>
<a class="btn" href="mailto:info@bleachresistantfabrics.com?subject=Trade%20Account%20Inquiry">Open Trade Account</a>
</div>
<div class="specs">
<div class="spec"><div class="k">Cleaning Code</div><div class="v">W/S · Bleach 1:10 OK</div></div>
<div class="spec"><div class="k">Wear Test</div><div class="v">100k+ Wyzenbeek double-rubs</div></div>
<div class="spec"><div class="k">Fire Code</div><div class="v">Class A · NFPA 260</div></div>
<div class="spec"><div class="k">Lead Time</div><div class="v">Memos in 3-5 business days</div></div>
</div>
</section>
<section class="section" id="catalog">
<div class="section-head">
<h2 class="section-title">The <span class="accent-italic">catalog</span></h2>
<div class="search">
<input id="q" type="search" placeholder="Search Crypton, vinyl, healthcare…" aria-label="Search catalog">
</div>
</div>
<div class="controls" id="rails">
<button class="chip active" data-rail="all">All</button>
<button class="chip" data-rail="crypton">Crypton</button>
<button class="chip" data-rail="vinyl-coated">Vinyl-Coated</button>
<button class="chip" data-rail="performance">Performance</button>
<button class="chip" data-rail="healthcare">Healthcare</button>
<button class="chip" data-rail="moisture-barrier">Moisture-Barrier</button>
<button class="chip" data-rail="antimicrobial">Antimicrobial</button>
</div>
<div class="toolbar">
<label for="sort">Sort</label>
<select id="sort">
<option value="newest">Newest</option>
<option value="color">Color</option>
<option value="style">Style</option>
<option value="sku">SKU A→Z</option>
<option value="title">Title A→Z</option>
<option value="price-asc">Price ↑</option>
<option value="price-desc">Price ↓</option>
</select>
<div class="density">
<label for="cols">Density</label>
<input id="cols" type="range" min="3" max="8" step="1" value="5">
<span class="ct" id="colsv">5 cols</span>
</div>
</div>
<div class="stat-line" id="stat">Loading catalog…</div>
<div class="grid" id="grid"></div>
<div class="loading" id="more" style="display:none">Loading more…</div>
</section>
<section class="section" id="specs">
<div class="section-head">
<h2 class="section-title">What <span class="accent-italic">bleach-resistant</span> means here</h2>
</div>
<div class="spec-block">
<div class="item">
<h3>1:10 cleanup</h3>
<p>Every fabric in this catalog tolerates a 1-part-bleach to 10-parts-water cleaning solution — the protocol used in CDC-aligned healthcare environments and Marriott-grade hospitality housekeeping.</p>
</div>
<div class="item">
<h3>Crypton + vinyl-coated</h3>
<p>Crypton is moisture-barrier-fused to fiber. Vinyl-coated (Type-II) is a wipe-clean polymer face on a fabric scrim. Both pass the 1:10 test; differences are tactile and acoustic.</p>
</div>
<div class="item">
<h3>Specs on demand</h3>
<p>Trade portal includes full Wyzenbeek, NFPA 260, CA TB 117-2013, ASTM E84 reports per SKU. Email <a style="color:var(--accent);text-decoration:none" href="mailto:info@bleachresistantfabrics.com">info@bleachresistantfabrics.com</a>.</p>
</div>
</div>
</section>
<section class="section" id="faq">
<div class="section-head">
<h2 class="section-title">Specifier <span class="accent-italic">questions</span></h2>
</div>
<div class="faq">
<details>
<summary>Does “bleach-resistant” mean I can pour straight bleach on it?<span class="plus"></span></summary>
<div class="a">No — the rating is for a <strong>1:10 sodium hypochlorite dilution</strong> (one part household bleach to ten parts water), the cleanup concentration recommended by the CDC for healthcare surfaces and used in hospitality housekeeping for blood, vomitus, and norovirus events. Full-strength bleach will eventually discolor any textile; the test is whether the fabric survives the diluted protocol applied repeatedly over a service life.</div>
</details>
<details>
<summary>Crypton vs. vinyl-coated Type-II — which should I spec?<span class="plus"></span></summary>
<div class="a"><strong>Crypton</strong> is a moisture barrier fused into woven fiber; it reads and feels like fabric, drapes naturally, and is preferred for guest-facing upholstery, throw pillows, and patient-room seating where touch matters. <strong>Vinyl-coated Type-II</strong> is a polymer face on a fabric scrim; it wipes faster, lasts longer in heavy-soil zones, and is the workhorse for ICU bays, behavioral-health rooms, dining banquettes, and high-traffic lobby seating. Both pass the 1:10 test — the choice is tactile vs. throughput.</div>
</details>
<details>
<summary>What spec sheets do you provide per SKU?<span class="plus"></span></summary>
<div class="a">Every product page links to: <strong>Wyzenbeek double-rub count</strong> (ASTM D4157), <strong>NFPA 260</strong> (cigarette ignition), <strong>CA TB 117-2013</strong> (smolder resistance), <strong>ASTM E84</strong> (flame spread, Class A required for healthcare), plus the manufacturer's cleaning code (W/S, etc.) and bleach-cleanability statement. Memo samples ship with a printed spec card. For LEED, WELL, or Living Building Challenge submittals, email <a href="mailto:info@bleachresistantfabrics.com?subject=Sustainability%20Specs">info@bleachresistantfabrics.com</a> and we'll pull EPDs and Declare labels where the mill publishes them.</div>
</details>
<details>
<summary>How is pricing handled? Is this a trade-only catalog?<span class="plus"></span></summary>
<div class="a">Catalog browsing and memo samples are open. <strong>Net trade pricing</strong> is released after a one-time account verification (we confirm a current resale certificate or design-firm credentials). End-clients can also order through any designer or specifier on record. There are no MOQs on memo samples; production yardage MOQs are mill-set and vary — usually 5 yards on Crypton and 10 yards on Type-II vinyl.</div>
</details>
<details>
<summary>What about lead times and stocked inventory?<span class="plus"></span></summary>
<div class="a">Memo samples ship in <strong>3–5 business days</strong> from our Sherman Oaks fulfillment. Production yardage is split between in-stock (5–10 day ship) and mill-direct (typically 4–8 weeks depending on construction). The fastest path on a deadline is to filter the catalog by <em>vinyl-coated</em> — that rail skews toward inventory-held SKUs because the contract market reorders them weekly.</div>
</details>
<details>
<summary>Can you match a discontinued fabric or pull a custom colorway?<span class="plus"></span></summary>
<div class="a">Yes — send the original sample (or a clear photo plus the original mill/SKU) to <a href="mailto:info@bleachresistantfabrics.com?subject=Discontinued%20Match%20Request">info@bleachresistantfabrics.com</a>. We work with mill partners on custom colorways down to roughly <strong>50–100 yard runs</strong> depending on the construction; lead time is generally 8–14 weeks. For exact discontinued matches, our hit rate is highest on Crypton-finished wovens because the substrate is widely shared across mills.</div>
</details>
</div>
</section>
<footer>
<div class="foot-grid">
<div>
<div class="foot-brand">Bleach Resistant Fabrics</div>
<div class="foot-text">A Designer Wallcoverings vertical. Curated contract textiles for hospitality, healthcare, and senior-living interiors. Bleach-cleanable, fire-rated, trade-priced.</div>
</div>
<div class="foot-col">
<h4>Catalog</h4>
<a href="#catalog">All Fabrics</a>
<a href="#catalog">Crypton</a>
<a href="#catalog">Vinyl-Coated</a>
<a href="#specs">Spec Reference</a>
<a href="#faq">Specifier FAQ</a>
</div>
<div class="foot-col">
<h4>Trade</h4>
<a href="mailto:info@bleachresistantfabrics.com">Open an Account</a>
<a href="mailto:info@bleachresistantfabrics.com?subject=Memo%20Sample%20Request">Memo Samples</a>
<a href="mailto:info@bleachresistantfabrics.com?subject=Spec%20Sheet%20Request">Spec Sheets</a>
</div>
<div class="foot-col">
<h4>Contact</h4>
<a href="mailto:info@bleachresistantfabrics.com">info@bleachresistantfabrics.com</a>
<a href="tel:+18883734564">888-373-4564</a>
<span style="display:block; font-size:13px; color:var(--muted); line-height:1.55; margin-top:6px">Designer Wallcoverings<br>15442 Ventura Bl #102<br>Sherman Oaks CA 91403</span>
</div>
</div>
<div class="foot-bot">
<span>© <span id="yr"></span> Designer Wallcoverings · DW Family</span>
<span>Bleach Resistant Fabrics</span>
</div>
</footer>
<script>
document.getElementById('yr').textContent = new Date().getFullYear();
// Theme toggle
(function(){
const KEY = 'brf_theme';
const root = document.documentElement;
const btn = document.getElementById('themeToggle');
function paint(){ btn.textContent = (root.dataset.theme === 'light') ? '☀' : '☾'; }
btn.addEventListener('click', () => {
const next = (root.dataset.theme === 'light') ? 'dark' : 'light';
root.dataset.theme = next;
try { localStorage.setItem(KEY, next); } catch(e){}
paint();
});
paint();
})();
// Catalog state
const state = { q: '', rail: 'all', sort: 'newest', page: 1, limit: 36, total: 0, pages: 1, loading: false, exhausted: false };
const $grid = document.getElementById('grid');
const $stat = document.getElementById('stat');
const $more = document.getElementById('more');
// Persisted UI
(function(){
const s = localStorage.getItem('brf_sort'); if (s) { state.sort = s; document.getElementById('sort').value = s; }
const c = localStorage.getItem('brf_cols'); if (c) {
document.documentElement.style.setProperty('--cols', c);
document.getElementById('cols').value = c;
document.getElementById('colsv').textContent = c + ' cols';
}
})();
document.getElementById('cols').addEventListener('input', e => {
const v = e.target.value;
document.documentElement.style.setProperty('--cols', v);
document.getElementById('colsv').textContent = v + ' cols';
try { localStorage.setItem('brf_cols', v); } catch(e){}
});
document.getElementById('sort').addEventListener('change', e => {
state.sort = e.target.value;
state.page = 1;
try { localStorage.setItem('brf_sort', state.sort); } catch(_) {}
load(true);
});
document.getElementById('q').addEventListener('input', e => {
state.q = e.target.value.trim();
state.page = 1;
clearTimeout(window.__qt);
window.__qt = setTimeout(() => load(true), 220);
});
document.querySelectorAll('#rails .chip').forEach(c => c.addEventListener('click', () => {
document.querySelectorAll('#rails .chip').forEach(x => x.classList.remove('active'));
c.classList.add('active');
state.rail = c.dataset.rail;
state.page = 1;
load(true);
}));
function card(p) {
const handle = encodeURIComponent(p.handle_display || p.handle || p.sku || '');
const url = p.product_url || ('https://designerwallcoverings.com/products/' + handle);
const sample = '/sample/' + handle;
// NOTE: vendor name intentionally NOT rendered — DW standing rule "never
// expose vendor names in customer-facing UI." (Was: <div class="ven">${p.vendor}</div>)
return `<a class="card" href="${url}" target="_blank" rel="noopener noreferrer">
<img loading="lazy" src="${p.image_url}" alt="${(p.title || '').replace(/"/g, '"')}">
<div class="overlay">
<div class="pat">${p.title || ''}</div>
<div class="actions">
<span class="sample-btn">Free Memo</span>
</div>
</div>
</a>`;
}
async function load(reset) {
if (reset) {
$grid.innerHTML = '';
state.page = 1;
state.exhausted = false;
$more.style.display = 'none';
}
if (state.loading || state.exhausted) return;
state.loading = true;
if (state.page === 1) $stat.textContent = 'Loading…';
$more.style.display = 'block';
const params = new URLSearchParams({ q: state.q, rail: state.rail, sort: state.sort, page: state.page, limit: state.limit });
try {
const r = await fetch('/api/products?' + params);
const j = await r.json();
state.total = j.total;
state.pages = j.pages;
j.items.forEach(p => $grid.insertAdjacentHTML('beforeend', card(p)));
$stat.textContent = j.total + ' fabrics · page ' + j.page + ' of ' + j.pages;
if (state.page >= j.pages || j.items.length === 0) {
state.exhausted = true;
$more.style.display = 'none';
} else {
state.page++;
$more.style.display = 'block';
}
} catch (e) {
$stat.textContent = 'Failed to load catalog. Refresh to retry.';
$more.style.display = 'none';
}
state.loading = false;
}
// Infinite scroll via IntersectionObserver — fires load() when #more scrolls into view
const io = new IntersectionObserver(entries => {
for (const e of entries) if (e.isIntersecting && !state.exhausted) load(false);
}, { rootMargin: '600px 0px' });
io.observe($more);
load(true);
</script>
<!-- Big Red customer chat — lower-left, woman-avatar, vendor-redacted retail persona. -->
<script data-bigred-widget src="https://chat.designerwallcoverings.com/widget.js"
data-host="https://chat.designerwallcoverings.com"
data-mode="retail"
defer onerror="this.remove()"></script>
<script src="/sku-redact.js" defer></script>
<!-- corner-nav disabled (UL-hamburger rebuild) -->
<script>window.DwHeaderConfig={siteName:"Bleach Resistant Fabrics",accent:"#9B7B4F",navLinks:[{name:'Home',url:'/'},{name:'Collections',url:'#shop'},{name:'About',url:'/about'},{name:'Care',url:'/care.html'},{name:'History',url:'/history.html'}],adminLinks:[{name:'Shopify admin',url:'https://admin.shopify.com',external:true},{name:'Edit hero',url:'#'},{name:'Analytics',url:'#'}]};</script>
<script src="/dw-header.js" defer></script>
<script src="/dw-theme-nav.js" defer></script>
</body>
</html>