APProjects/warn-act-notices
0
1<!doctype html><html lang="en"><head><meta charset="utf-8">2<!-- og:begin -->3<meta property="og:title" content="Watch your employers for layoff notices — free, no signup">4<meta property="og:description" content="Type the companies you care about and see every US WARN Act layoff notice they filed in the last 180 days. 1,764 notices, 1,287 employers, 44 states. Runs in your browser — no account, no email, nothing sent anywhere.">5<meta property="og:url" content="https://approjects-warn-act-notices.static.hf.space/watch-now.html">6<meta property="og:image" content="https://approjects-warn-act-notices.static.hf.space/assets/og-card.png">7<meta name="twitter:card" content="summary_large_image">8<!-- og:end -->9 10<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://cdn.jsdelivr.net; connect-src 'self'; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self' https://app.gumroad.com; upgrade-insecure-requests">11 12<meta name="viewport" content="width=device-width,initial-scale=1">13<title>Watch your employers for layoff notices — free, no signup</title>14<meta name="description" content="Type the companies you care about and see every US WARN Act layoff notice they filed in the last 180 days. 1,764 notices, 1,287 employers, 44 states. Runs in your browser — no account, no email, nothing sent anywhere.">15<link rel="canonical" href="https://approjects-warn-act-notices.static.hf.space/watch-now.html">16<style>17:root{--ink:#1a1a1a;--mute:#5b5b5b;--line:#e2e2e2;--accent:#b02a1f;--blue:#0b5fa5;--wash:#fdf3f2}18*{box-sizing:border-box}19body{font-family:-apple-system,Segoe UI,Helvetica,Arial,sans-serif;max-width:940px;margin:0 auto;padding:1.5rem 1rem 4rem;color:var(--ink);line-height:1.55}20a{color:var(--blue)}21.crumb{font-size:.85rem;color:var(--mute);margin:0 0 1.2rem}22h1{font-size:1.9rem;line-height:1.2;margin:0 0 .6rem}23h2{font-size:1.25rem;margin:2.6rem 0 .7rem}24.lede{font-size:1.06rem;color:#333;max-width:68ch}25.kpis{display:flex;flex-wrap:wrap;gap:12px;margin:1.4rem 0}26.kpi{flex:1 1 150px;border:1px solid var(--line);border-radius:9px;padding:12px 14px;background:#fafafa}27.kpi b{display:block;font-size:1.5rem;line-height:1.15;color:var(--accent)}28.kpi span{font-size:.82rem;color:var(--mute)}29.panel{border:2px solid var(--blue);border-radius:10px;padding:16px;margin:1.6rem 0;background:#f7fbff}30label.fld{display:block;font-weight:600;margin:0 0 .35rem}31textarea{width:100%;min-height:96px;font-size:1.02rem;padding:.6rem .7rem;border:1px solid #9dbcd8;border-radius:7px;font-family:inherit;resize:vertical}32.hint{font-size:.85rem;color:var(--mute);margin:.4rem 0 0}33details.states{margin:1rem 0 0}34details.states summary{cursor:pointer;font-weight:600;min-height:44px;line-height:44px}35.stategrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(165px,1fr));gap:2px 10px;margin:.4rem 0}36.stategrid label{font-size:.9rem;display:flex;align-items:center;gap:6px;min-height:30px}37.btn{display:inline-block;background:var(--accent);color:#fff;text-decoration:none;font-weight:600;padding:13px 22px;min-height:46px;line-height:20px;border:0;border-radius:7px;font-size:1rem;cursor:pointer}38.btn:hover{background:#8e2118}39.btn.ghost{background:#fff;color:var(--accent);border:1px solid var(--accent)}40.row-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin:1rem 0 0}41table{border-collapse:collapse;width:100%;margin:1rem 0}42th,td{border:1px solid var(--line);padding:8px 9px;text-align:left;font-size:.92rem;vertical-align:top}43th{background:#f5f5f5}44td.r,th.r{text-align:right}45tr.fresh td{background:#fffbe6}46.tag{display:inline-block;font-size:.72rem;font-weight:700;color:#7a5c00;background:#ffe9a8;border-radius:4px;padding:1px 6px;margin-left:6px;vertical-align:middle}47.hist{display:block;font-size:.8rem;color:var(--mute);margin-top:3px}48#stat{color:var(--mute);font-size:.95rem;margin:.8rem 0}49.empty{border:1px dashed #c9c9c9;border-radius:9px;padding:22px;color:var(--mute);text-align:center}50.upsell{border:2px solid var(--accent);border-radius:10px;padding:16px 18px;margin:1.2rem 0 0;background:var(--wash)}51.upsell p{margin:0 0 .6rem;max-width:70ch}52.upsell .row-actions{margin-top:.8rem}53.sell{border-left:4px solid var(--accent);background:var(--wash);border-radius:0 9px 9px 0;padding:16px 18px;margin:2rem 0}54.sell h2{margin:0 0 .5rem;font-size:1.15rem}55.cmp{width:100%;border-collapse:collapse;margin:.8rem 0}56.cmp td,.cmp th{font-size:.9rem}57footer{margin-top:3rem;padding-top:1.2rem;border-top:1px solid var(--line);font-size:.87rem;color:var(--mute)}58@media (max-width:560px){59 body{padding:1rem .8rem 3rem} h1{font-size:1.5rem}60 th,td{padding:7px 5px;font-size:.85rem}61 .btn{display:block;width:100%;text-align:center}62 .kpi b{font-size:1.25rem}63}64</style></head><body>65<p class="crumb"><a href="https://approjects-warn-act-notices.static.hf.space/">US WARN layoff notices</a> › Watch your employers</p>66 67<h1>Watch your employers for layoff notices</h1>68<p class="lede">Type the companies you care about. This page checks them against every US WARN Act69layoff notice filed in the last 180 days and shows you the matches. <b>No account, no70email address, no checkout</b> — the matching runs inside your browser and your list is never71sent anywhere.</p>72 73<div class="kpis">74<div class="kpi"><b>1,764</b><span>notices in the last 180 days</span></div>75<div class="kpi"><b>1,287</b><span>distinct employers filing</span></div>76<div class="kpi"><b>169,252</b><span>workers named in them</span></div>77<div class="kpi"><b>44</b><span>states represented</span></div>78</div>79<p class="hint">Counted from the free dataset at build time. Latest notice date in the file: <b>2026-09-23</b>.80The whole archive is 61,431 notices back to 1988.</p>81 82<div class="panel">83<label class="fld" for="terms">Your employers — one per line</label>84<textarea id="terms" placeholder="Boeing Kaiser Rite Aid" autocomplete="off" spellcheck="false"></textarea>85<p class="hint">Case-insensitive, and a name matches on <b>whole words</b>, so <b>Boeing</b> finds86“Boeing Commercial Airplanes” while <b>Ford</b> does <i>not</i> drag in Stanford, Medford or Rockford87— which is what a plain text search of the CSV does. This is the same matcher the paid alerts run.</p>88<details class="states"><summary>… or watch whole states</summary>89<div class="stategrid"><label><input type="checkbox" class="stbox" value="AK"> Alaska</label><label><input type="checkbox" class="stbox" value="AL"> Alabama</label><label><input type="checkbox" class="stbox" value="AR"> Arkansas</label><label><input type="checkbox" class="stbox" value="AZ"> Arizona</label><label><input type="checkbox" class="stbox" value="CA"> California</label><label><input type="checkbox" class="stbox" value="CO"> Colorado</label><label><input type="checkbox" class="stbox" value="CT"> Connecticut</label><label><input type="checkbox" class="stbox" value="DC"> District of Columbia</label><label><input type="checkbox" class="stbox" value="DE"> Delaware</label><label><input type="checkbox" class="stbox" value="FL"> Florida</label><label><input type="checkbox" class="stbox" value="GA"> Georgia</label><label><input type="checkbox" class="stbox" value="HI"> Hawaii</label><label><input type="checkbox" class="stbox" value="IA"> Iowa</label><label><input type="checkbox" class="stbox" value="ID"> Idaho</label><label><input type="checkbox" class="stbox" value="IL"> Illinois</label><label><input type="checkbox" class="stbox" value="IN"> Indiana</label><label><input type="checkbox" class="stbox" value="KS"> Kansas</label><label><input type="checkbox" class="stbox" value="KY"> Kentucky</label><label><input type="checkbox" class="stbox" value="LA"> Louisiana</label><label><input type="checkbox" class="stbox" value="MA"> Massachusetts</label><label><input type="checkbox" class="stbox" value="MD"> Maryland</label><label><input type="checkbox" class="stbox" value="ME"> Maine</label><label><input type="checkbox" class="stbox" value="MI"> Michigan</label><label><input type="checkbox" class="stbox" value="MN"> Minnesota</label><label><input type="checkbox" class="stbox" value="MO"> Missouri</label><label><input type="checkbox" class="stbox" value="MS"> Mississippi</label><label><input type="checkbox" class="stbox" value="MT"> Montana</label><label><input type="checkbox" class="stbox" value="NC"> North Carolina</label><label><input type="checkbox" class="stbox" value="ND"> North Dakota</label><label><input type="checkbox" class="stbox" value="NE"> Nebraska</label><label><input type="checkbox" class="stbox" value="NH"> New Hampshire</label><label><input type="checkbox" class="stbox" value="NJ"> New Jersey</label><label><input type="checkbox" class="stbox" value="NM"> New Mexico</label><label><input type="checkbox" class="stbox" value="NV"> Nevada</label><label><input type="checkbox" class="stbox" value="NY"> New York</label><label><input type="checkbox" class="stbox" value="OH"> Ohio</label><label><input type="checkbox" class="stbox" value="OK"> Oklahoma</label><label><input type="checkbox" class="stbox" value="OR"> Oregon</label><label><input type="checkbox" class="stbox" value="PA"> Pennsylvania</label><label><input type="checkbox" class="stbox" value="RI"> Rhode Island</label><label><input type="checkbox" class="stbox" value="SC"> South Carolina</label><label><input type="checkbox" class="stbox" value="SD"> South Dakota</label><label><input type="checkbox" class="stbox" value="TN"> Tennessee</label><label><input type="checkbox" class="stbox" value="TX"> Texas</label><label><input type="checkbox" class="stbox" value="UT"> Utah</label><label><input type="checkbox" class="stbox" value="VA"> Virginia</label><label><input type="checkbox" class="stbox" value="VT"> Vermont</label><label><input type="checkbox" class="stbox" value="WA"> Washington</label><label><input type="checkbox" class="stbox" value="WI"> Wisconsin</label><label><input type="checkbox" class="stbox" value="WV"> West Virginia</label><label><input type="checkbox" class="stbox" value="WY"> Wyoming</label></div>90</details>91<div class="row-actions">92<button class="btn" id="go" type="button">Show my matches</button>93<button class="btn ghost" id="clear" type="button">Clear</button>94<span class="hint" id="saved"></span>95</div>96</div>97 98<p id="stat"></p>99<div id="results"></div>100 101<div class="sell">102<h2>This page only looks when you open it — so take the feeds instead</h2>103<p>That is the honest limit of a browser-side tool. After you press <b>Show my matches</b> you can104download an <b>.opml</b> file holding one RSS feed per employer you typed — free, no account, no105email address — and your reader checks them for you every morning.</p>106<table class="cmp">107<tr><th></th><th>This page (free)</th><th>WARN Watch ($49/year)</th></tr>108<tr><td>Checks your list</td><td>when you open it, or when your feed reader polls the .opml</td><td>every daily refresh, automatically</td></tr>109<tr><td>Tells you</td><td>on screen, or in your RSS reader</td><td>it comes to you: private alert page + RSS + calendar (.ics) + optional Slack/Discord/Teams</td></tr>110<tr><td>How many terms</td><td>as many as you like</td><td>up to 500 terms plus whole states</td></tr>111<tr><td>History per match</td><td>notice count & first year</td><td>full filing record — every state, every prior notice, worker totals</td></tr>112<tr><td>Separation reminder</td><td>—</td><td>again 14 days before the layoff date</td></tr>113</table>114<p><a class="btn" href="https://approj.gumroad.com/l/warn-watch">Get WARN Watch — $49/year</a></p>115<p class="hint">One-off, no auto-renew, 14-day refund. <a href="https://approjects-warn-act-notices.static.hf.space/watch-sample.html">See a real alert page first</a>116— no email needed to look.</p>117</div>118 119<h2>How this works</h2>120<p>The file behind this page is <a href="https://approjects-warn-act-notices.static.hf.space/data/watch-recent.json">watch-recent.json</a>, rebuilt121by the same daily pipeline that publishes the122<a href="https://huggingface.co/datasets/APProjects/us-warn-act-layoffs-notices-daily">free dataset</a>123and the <a href="https://github.com/APVentureEngine/warn-act-notices">source repo</a>. Notices come124from official WARN Act filings by 48 state agencies, normalized into one schema; 44 of those125states have at least one notice inside the 180-day window. Your watchlist is126stored in this browser only (localStorage) and mirrored into this page's own URL, so you can bookmark127it or send it to a colleague — it never touches a server of ours. Any page on this site can128open the tool pre-filled: <code>watch-now.html?q=Boeing|Kaiser</code> or <code>?s=CA,TX</code>.</p>129<p>What is counted: three 1×1 images on the public jsDelivr CDN — one when this page opens, one if130it opened pre-filled from a link, one the first time a non-empty list is matched. That is a daily total per131file, <a href="https://data.jsdelivr.com/v1/stats/packages/gh/APVentureEngine/warn-act-notices@main/files?period=month">published openly by jsDelivr</a>; no cookie, no ID, and never the names you132typed.</p>133<p>Nothing here is legal or financial advice, and a WARN notice is an employer's filing, not a134guarantee that a layoff happened as described. Corrections: open an issue on the135<a href="https://github.com/APVentureEngine/warn-act-notices/issues">source repo</a>.</p>136 137<footer>138<p><a href="https://approjects-warn-act-notices.static.hf.space/">WARN Feed</a> · <a href="https://approjects-warn-act-notices.static.hf.space/search.html">Search all employers</a>139· <a href="https://approjects-warn-act-notices.static.hf.space/sources.html">Where the data comes from</a>140· <a href="https://approjects-warn-act-notices.static.hf.space/privacy.html">Privacy & terms</a></p>141<p>Free data under CC BY 4.0. An independent automated publisher; not affiliated with any government agency.</p>142<p class="beacon-note" style="font-size:.8rem;color:#777">Loads counted anonymously: one 1×1 CDN image, no cookies, no personal data (<a href="privacy.html">detail</a>).</p>143</footer>144 145<script>146(function(){147 var DATA=null, LS='warnfeed.watch.v1', LSEEN='warnfeed.watch.seen.v1', DEEP=false, USED=false;148 // c601: FEEDS maps a canonical employer name -> its static RSS feed slug, and a149 // state code -> the per-state feed slug. Loaded separately from DATA so that a150 // missing/old watch-feeds.json degrades to "no subscribe block" instead of151 // breaking the matcher, which is the part people came for.152 var FEEDS=null, LASTFEEDS=[];153 // c601: "new since you last looked" must be read ONCE. render() writes154 // LSEEN=asof when it finishes, so a second render (after the feed map arrives)155 // would otherwise compare against itself and silently drop every NEW badge.156 var SEEN0=null;157 function seen0(){158 if(SEEN0===null){ SEEN0=''; try{SEEN0=localStorage.getItem(LSEEN)||''}catch(e){} }159 return SEEN0;160 }161 function px(u){try{var i=new Image();i.src=u}catch(e){}}162 px('https://cdn.jsdelivr.net/gh/APVentureEngine/warn-act-notices@main/assets/px-tool.png');163 var $=function(id){return document.getElementById(id)};164 function stateBoxes(){return Array.prototype.slice.call(document.querySelectorAll('.stbox'))}165 function readForm(){166 var terms=$('terms').value.split('\n').map(function(s){return s.trim()}).filter(Boolean);167 var sts=stateBoxes().filter(function(b){return b.checked}).map(function(b){return b.value});168 return {t:terms,s:sts};169 }170 function writeForm(w){171 $('terms').value=(w.t||[]).join('\n');172 var set={}; (w.s||[]).forEach(function(c){set[c]=1});173 stateBoxes().forEach(function(b){b.checked=!!set[b.value]});174 }175 function save(w){176 try{localStorage.setItem(LS,JSON.stringify(w))}catch(e){}177 var h='';178 if(w.t&&w.t.length) h+='w='+encodeURIComponent(w.t.join('|'));179 if(w.s&&w.s.length) h+=(h?'&':'')+'s='+w.s.join(',');180 try{history.replaceState(null,'',h?('#'+h):location.pathname)}catch(e){}181 $('saved').textContent=(w.t.length||w.s.length)?'Saved in this browser. Bookmark this page to keep it.':'';182 }183 function load(){184 var w=null, qs=location.search.replace(/^\?/,'');185 if(qs){186 var q={}; qs.split('&').forEach(function(kv){var i=kv.indexOf('=');if(i>0){var k=kv.slice(0,i),v=kv.slice(i+1);q[k]=q[k]?q[k]+'|'+v:v}});187 if(q.q||q.s){188 var dq=function(x){try{return decodeURIComponent(x.replace(/\+/g,' '))}catch(e){return x}};189 w={t:q.q?dq(q.q).split('|').map(function(x){return x.trim()}).filter(Boolean):[],190 s:q.s?dq(q.s).toUpperCase().split(/[,|]/).map(function(x){return x.trim()}).filter(Boolean):[]};191 DEEP=true;192 }193 }194 var h=location.hash.replace(/^#/,'');195 if(!w && h){196 var p={}; h.split('&').forEach(function(kv){var i=kv.indexOf('=');if(i>0)p[kv.slice(0,i)]=kv.slice(i+1)});197 if(p.w||p.s) w={t:p.w?decodeURIComponent(p.w).split('|').filter(Boolean):[],198 s:p.s?p.s.split(',').filter(Boolean):[]};199 }200 if(!w){ try{w=JSON.parse(localStorage.getItem(LS)||'null')}catch(e){w=null} }201 if(w){ w.t=(w.t||[]).slice(0,500); w.s=(w.s||[]).slice(0,60); }202 return w||{t:[],s:[]};203 }204 function esc(s){return String(s==null?'':s).replace(/[&<>"]/g,function(c){205 return {'&':'&','<':'<','>':'>','"':'"'}[c]})}206 function num(n){return Number(n||0).toLocaleString('en-US')}207 208 // c477 (2026-09-18): WHOLE-TOKEN MATCHING, IDENTICAL TO THE PAID MATCHER.209 // This used to be `company.indexOf(term)>=0`, i.e. a substring grep, and it was210 // measured against the live file on 2026-09-18: "ford" returned 6 rows of which211 // SIX were wrong (Hanford, Medford, Mulford, Stanford x2, Rockford) and none212 // were Ford; "gm" returned GMRI and "Hotel Mgmt"; "intel" returned213 // IntelliPower; "meta" returned EPTAM Metals. So the free tool — the first214 // thing the landing page asks a stranger to do — demonstrated exactly the215 // failure the paid tier is sold as fixing, while warn_watch.py:notice_matches216 // had done it correctly all along. Mirror that function here: tokenise on217 // [^a-z0-9]+, drop the same STOP words from the typed term, and require every218 // term token to be a whole token of company/canonical. Keep the two matchers219 // in sync — the free tool is the demo of the paid one.220 var STOP={inc:1,llc:1,corp:1,corporation:1,co:1,company:1,ltd:1,the:1,of:1,and:1,incorporated:1,lp:1,llp:1,plc:1};221 function toks(s){return String(s==null?'':s).toLowerCase().split(/[^a-z0-9]+/).filter(Boolean)}222 function termToks(s){var t=toks(s),k=t.filter(function(x){return !STOP[x]});return k.length?k:t}223 function hayFor(i){224 if(!DATA.__hay){225 DATA.__hay=DATA.rows.map(function(r){226 var h={}; toks(r[0]).concat(toks(r[1])).forEach(function(t){h[t]=1}); return h;227 });228 }229 return DATA.__hay[i];230 }231 function match(w){232 if(!DATA) return [];233 var terms=(w.t||[]).map(termToks).filter(function(t){return t.length});234 var sts={}; (w.s||[]).forEach(function(c){sts[c]=1});235 if(!terms.length && !Object.keys(sts).length) return [];236 return DATA.rows.filter(function(r,i){237 if(sts[r[2]]) return true;238 if(!terms.length) return false;239 var hay=hayFor(i);240 for(var j=0;j<terms.length;j++){241 var ts=terms[j], ok=true;242 for(var k=0;k<ts.length;k++){ if(!hay[ts[k]]){ok=false;break} }243 if(ok) return true;244 }245 return false;246 });247 }248 249 // c601 — THE FREE RUNG. Before this, the only step after a match was a Gumroad250 // checkout (19 days, 0 takers, incl. the $0 trial). Every employer that filed251 // inside the window has a static RSS feed; this turns the visitor's own list252 // into an .opml they import once and never think about again. No account, no253 // email, no server. The paid tier stays distinct because it PUSHES.254 function subList(rows,w){255 if(!FEEDS) return [];256 var seen={}, list=[];257 // States first, and a ticked state SUPPRESSES the per-employer feeds it258 // dragged in: ticking California matched 455 employers, and handing someone259 // 455 subscriptions instead of the one state feed is not a feature.260 (w.s||[]).forEach(function(c){261 var s=FEEDS.states&&FEEDS.states[c];262 if(s && !seen['s'+s]){ seen['s'+s]=1; list.push({t:c+' \u2014 every notice in the state', u:'feeds/'+s+'.xml'}); }263 });264 var terms=(w.t||[]).map(termToks).filter(function(t){return t.length});265 if(terms.length){266 rows.forEach(function(r){267 var h={}; toks(r[0]).concat(toks(r[1])).forEach(function(t){h[t]=1});268 var hit=false;269 for(var j=0;j<terms.length && !hit;j++){270 var ts=terms[j], ok=true;271 for(var k=0;k<ts.length;k++){ if(!h[ts[k]]){ok=false;break} }272 if(ok) hit=true;273 }274 if(!hit) return; // this row matched only because of a ticked state275 var lc=(r[1]||'').toLowerCase(), s=FEEDS.emp&&FEEDS.emp[lc];276 if(s && !seen['e'+s]){ seen['e'+s]=1; list.push({t:r[0], u:'feeds/employer/'+s+'.xml'}); }277 });278 }279 return list;280 }281 function subBlock(rows,w){282 var list=subList(rows,w); LASTFEEDS=list;283 if(!list.length) return '';284 var n=list.length;285 var links=list.slice(0,8).map(function(f){286 return '<a href="'+esc(f.u)+'">'+esc(f.t)+'</a>';287 }).join(' · ');288 if(n>8) links+=' · +'+num(n-8)+' more inside the file';289 return '<div class="upsell"><p><b>Free, no account, no email address: put these '+num(n)+290 ' feed'+(n===1?'':'s')+' in your reader.</b> Each one is rebuilt every morning with this '+291 'dataset, so the next notice shows up in your reader whether or not you ever open this page again.</p>'+292 '<div class="row-actions"><a class="btn" id="opml" href="#">Download my subscriptions (.opml)</a></div>'+293 '<p class="hint">Import the file into Feedly, Inoreader, NetNewsWire, Thunderbird or any RSS reader. '+294 'Or grab them one at a time: '+links+'</p></div>';295 }296 function opmlDoc(){297 var x='<?xml version="1.0" encoding="UTF-8"?>\n<opml version="1.0">\n'+298 '<head><title>My WARN watchlist</title></head>\n<body>\n';299 LASTFEEDS.forEach(function(f){300 x+=' <outline type="rss" text="'+esc(f.t)+'" title="'+esc(f.t)+301 '" xmlUrl="https://approjects-warn-act-notices.static.hf.space/'+esc(f.u)+'" htmlUrl="https://approjects-warn-act-notices.static.hf.space/watch-now.html"/>\n';302 });303 return x+'<img src="https://cdn.jsdelivr.net/gh/APVentureEngine/warn-act-notices@main/assets/px.png" alt="" width="1" height="1" referrerpolicy="no-referrer" style="position:absolute;left:-9999px;top:0">304<script id="fl">if(top!==self)for(var a of document.links)if(a.origin!==location.origin){a.target="_blank";a.rel="noopener"}</script>305</body>\n</opml>\n';306 }307 308 function render(w){309 var rows=match(w), lastSeen=seen0();310 var res=$('results');311 if(!w.t.length && !w.s.length){312 $('stat').textContent='';313 res.innerHTML='<div class="empty">Add at least one employer name or tick a state, then press '+314 '<b>Show my matches</b>.</div>';315 return;316 }317 if(!USED){USED=true;px('https://cdn.jsdelivr.net/gh/APVentureEngine/warn-act-notices@main/assets/px-tool-used.png');}318 var fresh=0;319 rows.forEach(function(r){ if(lastSeen && r[8] && r[8]>lastSeen) fresh++; });320 $('stat').innerHTML='<b>'+num(rows.length)+'</b> notice'+(rows.length===1?'':'s')+321 ' in the last '+DATA.window_days+' days match your list'+322 (fresh?(' · <b>'+num(fresh)+' new since you last looked</b>'):'')+'.';323 // c526: the offer, stated on the visitor's OWN list, right under the result.324 var what=[]; if(w.t.length) what.push(w.t.length===1?'name':num(w.t.length)+' names');325 if(w.s.length) what.push(w.s.length===1?'state':num(w.s.length)+' states');326 var lst=what.join(' and '), these=((w.t.length+w.s.length)===1?'this ':'these ')+lst;327 function upsell(n){328 var lead = n329 ? '<b>'+num(n)+(n===1?' notice':' notices')+'</b> matched your '+lst+' today. This page will not look again until you reopen it.'330 : 'None of your '+lst+' filed a WARN notice in the last '+DATA.window_days+' days. The next one can land tomorrow, and this page will not tell you unless you come back.';331 return '<div class="upsell"><p>'+lead+'</p>'+332 '<p>WARN Watch runs this same match after every daily rebuild and sends only what is new to a private alert page, RSS, calendar and Slack — up to 500 names plus whole states.</p>'+333 '<div class="row-actions">'+334 '<a class="btn cta" href="'+esc('https://approj.gumroad.com/l/warn-watch')+'">Keep '+these+' watched daily — $49/year</a>'+335 '<a class="btn ghost cta" href="'+esc('https://approj.gumroad.com/l/warn-free-watch')+'">Try it free for 30 days — no card</a>'+336 '</div>'+337 '<p class="hint">One-off, no auto-renew, 14-day refund. Your list stays in this browser; you paste it again at checkout.</p></div>';338 }339 if(!rows.length){340 res.innerHTML='<div class="empty">No WARN notices from those employers in the last '+341 DATA.window_days+' days. That is usually good news. '+342 '<a href="'+esc('https://approjects-warn-act-notices.static.hf.space')+'/search.html">Search the whole archive back to 1988</a> '+343 'to see what they have filed before.</div>'+subBlock(rows,w)+upsell(0);344 } else {345 var h='<table><tr><th>Notice date</th><th>Employer</th><th>Where</th>'+346 '<th class="r">Workers</th><th>Type</th><th>Effective</th></tr>';347 rows.forEach(function(r){348 var isNew=(lastSeen && r[8] && r[8]>lastSeen);349 var hk=(r[1]||'').toLowerCase(), hi=DATA.history[hk];350 var hist='';351 if(hi && hi[0]>1){352 hist='<span class="hist">'+num(hi[0])+' notices since '+esc(hi[2])+353 ' · '+num(hi[1])+' workers'+(hi[3]>1?(' · '+hi[3]+' states'):'')+'</span>';354 }355 h+='<tr class="'+(isNew?'fresh':'')+'">'+356 '<td>'+esc(r[3])+(isNew?'<span class="tag">NEW</span>':'')+'</td>'+357 '<td><a href="'+esc('https://approjects-warn-act-notices.static.hf.space')+'/search.html?q='+encodeURIComponent(r[0])+'">'+esc(r[0])+'</a>'+hist+'</td>'+358 '<td>'+esc(r[7]||'')+(r[2]?(r[7]?', ':'')+esc(r[2]):'')+'</td>'+359 '<td class="r">'+num(r[5])+'</td>'+360 '<td>'+esc(r[6]||'')+'</td>'+361 '<td>'+esc(r[4]||'')+'</td></tr>';362 });363 res.innerHTML=h+'</table>'+subBlock(rows,w)+upsell(rows.length);364 }365 try{localStorage.setItem(LSEEN,DATA.asof)}catch(e){}366 }367 // c526: count clicks on the in-result CTA (either button) — one pixel, no ID, nothing about the list.368 document.getElementById('results').addEventListener('click',function(e){369 var a=e.target; while(a && a.tagName!=='A') a=a.parentNode;370 if(a && a.className && a.className.indexOf('cta')!==-1) px('https://cdn.jsdelivr.net/gh/APVentureEngine/warn-act-notices@main/assets/px-tool-cta.png');371 });372 // c601: build the .opml in the browser from the visitor's own matches. The list373 // never leaves the page — same promise as the matcher itself.374 document.getElementById('results').addEventListener('click',function(e){375 var a=e.target; while(a && a.tagName!=='A') a=a.parentNode;376 if(!a || a.id!=='opml') return;377 e.preventDefault();378 var txt=opmlDoc();379 try{380 var b=new Blob([txt],{type:'text/x-opml'}), u=URL.createObjectURL(b),381 d=document.createElement('a');382 d.href=u; d.download='warn-watchlist.opml';383 document.body.appendChild(d); d.click();384 setTimeout(function(){URL.revokeObjectURL(u); document.body.removeChild(d)},0);385 }catch(err){386 window.location.href='data:text/x-opml;charset=utf-8,'+encodeURIComponent(txt);387 }388 px('https://cdn.jsdelivr.net/gh/APVentureEngine/warn-act-notices@main/assets/px-tool-opml.png');389 });390 391 function boot(){392 var w=load(); writeForm(w); if(DEEP){save(w);px('https://cdn.jsdelivr.net/gh/APVentureEngine/warn-act-notices@main/assets/px-tool-deep.png');}393 $('go').addEventListener('click',function(){var x=readForm();save(x);render(x)});394 $('clear').addEventListener('click',function(){395 var x={t:[],s:[]}; writeForm(x); save(x); render(x);396 });397 $('stat').textContent='Loading the last '+180+' days of notices\u2026';398 var xhr=new XMLHttpRequest();399 xhr.open('GET','data/watch-recent.json',true);400 xhr.onload=function(){401 if(xhr.status>=200&&xhr.status<300){402 try{DATA=JSON.parse(xhr.responseText)}catch(e){}403 }404 if(!DATA){ $('stat').textContent='Could not load the notice file. Reload the page, or use the search page.'; return; }405 render(w);406 };407 xhr.onerror=function(){$('stat').textContent='Could not load the notice file. Reload the page, or use the search page.'};408 xhr.send();409 // c601: the employer->feed map, fetched independently. If it never arrives410 // the page works exactly as it did before; when it does, re-render so the411 // free subscribe block appears under results already on screen.412 var fx=new XMLHttpRequest();413 fx.open('GET','data/watch-feeds.json',true);414 fx.onload=function(){415 if(fx.status>=200&&fx.status<300){416 try{FEEDS=JSON.parse(fx.responseText)}catch(e){}417 }418 if(FEEDS&&DATA){419 var v=load();420 if((v.t||[]).length||(v.s||[]).length) render(v);421 }422 };423 fx.send();424 }425 if(document.readyState!=='loading') boot(); else document.addEventListener('DOMContentLoaded',boot);426})();427</script>428</body></html>429 