One or more of the items in your cart is a recurring or deferred purchase. By continuing, I agree to the cancellation policy and authorize you to charge my payment method at the prices, frequency and dates listed on this page until my order is fulfilled or I cancel, if permitted.
Nörre Nordic
Sorry, there are no products in this collection
Newsletter
Sign up for exciting product launches and offers, inspiring content..we promise not to bug you!
Exclusive offers (without the usual nonsense)
Join us for special offers and delightful surprises. Rather refreshing, wouldn't you say?
/* BB Origin Resolver Test v1.3 */
(async function(){
const NS = window.BB_SHIP || {};
const hasLegacy = typeof NS.getOriginForItem === 'function';
const hasNew = NS.originResolver && typeof NS.originResolver.originForItem === 'function';
const DEFAULT_ORIGIN = NS.DEFAULT_ORIGIN || 'GBR0';
function pickZone(it){
if (hasLegacy) return NS.getOriginForItem(it);
if (hasNew) return (NS.originResolver.originForItem(it)||{}).origin || DEFAULT_ORIGIN;
return '(N/A)';
}
function up(s){ return String(s||'').toUpperCase().trim(); }
function canonical(name){ const a=NS.vendorAliases||{}; const u=up(name); return a[u]||u; }
function isoFromCanon(c){ const vc=NS.vendorCountries||{}; return vc[up(c)]||null; }
function zoneFromISO(i){ const cz=NS.countryToZone||{}; return cz[up(i)]||null; }
function findBB03(tags){
for (const t of (Array.isArray(tags)?tags:[])) {
const r = String(t||'').trim().toUpperCase();
if (r.startsWith('.BB_03')) return r.slice(7).trim();
}
return null;
}
function handleFrom(it){ if (it.handle) return it.handle; const u=it.url||it.product_url||''; const m=u.split('/products/')[1]; return m?m.split('?')[0].split('#')[0]:''; }
async function tagsFor(it){ try{ const h=handleFrom(it); const r=await fetch(`/products/${h}.js`,{credentials:'same-origin',cache:'no-cache'}); if(!r.ok) return []; const p=await r.json(); return Array.isArray(p.tags)?p.tags:[]; }catch{return[];} }
async function expectedZone(it){
const tags = Array.isArray(it.tags) ? it.tags : await tagsFor(it);
const bb = findBB03(tags);
if (bb) { const c = canonical(bb); const iso = isoFromCanon(c); const z = zoneFromISO(iso); if (z) return {z, via:`BB_03 tag -> ${c} -> ${iso}`}; }
const c2 = canonical(it.vendor); const iso2 = isoFromCanon(c2); const z2 = zoneFromISO(iso2);
if (z2) return {z:z2, via:`Vendor -> ${c2} -> ${iso2}`};
return {z: DEFAULT_ORIGIN, via:'DEFAULT_ORIGIN'};
}
async function getCart(){ try{ const r=await fetch('/cart.js',{credentials:'same-origin',cache:'no-cache'}); return await r.json(); }catch{return {items:[]};} }
const cart = await getCart();
const rows = [];
for (const it of (cart.items||[])) {
if (String(it?.properties?._bb_shipping)==='1') continue;
const exp = await expectedZone(it);
const act = pickZone(it);
rows.push({
TYPE:'CART',
TITLE: it.title,
VENDOR_RAW: up(it.vendor),
HANDLE: handleFrom(it)||'(none)',
BB03_NAME: findBB03(Array.isArray(it.tags)?it.tags:[]),
CANON_VENDOR: canonical(it.vendor),
ISO_FROM_VENDOR: isoFromCanon(canonical(it.vendor)),
EXPECTED_ZONE: exp.z,
EXPECTED_VIA: exp.via,
ACTUAL_ZONE: act,
MATCH: String(exp.z)===String(act)
});
}
console.table(rows);
const bad = rows.filter(r=>!r.MATCH);
console.log(`Total ${rows.length}. Matches ${rows.length-bad.length}. Mismatches ${bad.length}.`);
if (bad.length) console.warn('Mismatches:', bad);
})();
{# single source of truth for zones #}
{# exposes stVendorByHandle and vendorByProduct #}
{# reads ST names and derives ISO when needed #}
{# creates BB_SHIP.originResolver if missing #}
{# styling only (labels/spacing) #}