/** * Perry Weather — Job Board (drop-in replacement for the Ashby iframe embed) * --------------------------------------------------------------------------- * The original embed (` * * If no #pw-job-board div is present, the script renders inline at its own * location, so a bare still works. */ (function () { 'use strict'; // ---- Config ------------------------------------------------------------- var BOARD_NAME = 'perryweather'; var MOUNT_ID = 'pw-job-board'; var API_URL = 'https://api.ashbyhq.com/posting-api/job-board/' + BOARD_NAME + '?includeCompensation=true'; // ---- Design tokens (extracted from perryweather.com) -------------------- // Colors var C_NAVY = '#131B25'; // headings, primary text var C_BODY = '#293442'; // body paragraph text var C_MUTED = '#5A6573'; // meta text var C_BORDER = '#E4E8EE'; // card borders var C_BG = '#FFFFFF'; // card surface var C_BG_SOFT = '#F7F9FB'; // page surface var C_ACCENT = '#7AD03A'; // bright lime — Perry Weather accent var C_ACCENT_SOFT = '#F1FEEC'; // pale mint — primary button bg var C_LINK = '#4688B4'; // link blue // Type var FONT_STACK = '"proxima-nova", "Proxima Nova", -apple-system, BlinkMacSystemFont, ' + '"Segoe UI", Helvetica, Arial, sans-serif'; // Radii var R_CARD = '24px'; // dominant card radius var R_INPUT = '12px'; // form fields, smaller cards var R_PILL = '9999px'; // buttons, chips, badges var R_TIGHT = '8px'; // small elements // ---- Style injection ---------------------------------------------------- // Scoped under .pwjb so it cannot leak to surrounding host-page styles. var css = '.pwjb{font-family:' + FONT_STACK + ';color:' + C_BODY + ';line-height:1.55;max-width:1100px;margin:0 auto;padding:48px 24px;' + '-webkit-font-smoothing:antialiased;}' + '.pwjb *,.pwjb *::before,.pwjb *::after{box-sizing:border-box;}' + '.pwjb__header{margin-bottom:32px;}' + '.pwjb__title{font-size:48px;line-height:1.1;font-weight:700;color:' + C_NAVY + ';letter-spacing:-0.02em;margin:0 0 12px;}' + '.pwjb__subtitle{font-size:18px;color:' + C_MUTED + ';margin:0;}' + // Department filter chips '.pwjb__filters{display:flex;flex-wrap:wrap;gap:8px;margin:32px 0 24px;}' + '.pwjb__chip{appearance:none;border:1px solid ' + C_BORDER + ';' + 'background:' + C_BG + ';color:' + C_NAVY + ';' + 'font:inherit;font-size:14px;font-weight:600;' + 'padding:8px 16px;border-radius:' + R_PILL + ';' + 'cursor:pointer;transition:all .15s ease;}' + '.pwjb__chip:hover{border-color:' + C_NAVY + ';}' + '.pwjb__chip[aria-pressed="true"]{background:' + C_NAVY + ';color:#fff;border-color:' + C_NAVY + ';}' + '.pwjb__chip-count{opacity:.6;margin-left:6px;font-weight:500;}' + // Group + cards '.pwjb__group{margin:32px 0;}' + '.pwjb__group-title{font-size:14px;font-weight:700;text-transform:uppercase;' + 'letter-spacing:.08em;color:' + C_MUTED + ';margin:0 0 12px;}' + '.pwjb__list{list-style:none;margin:0;padding:0;display:grid;gap:12px;}' + '.pwjb__card{display:flex;align-items:center;justify-content:space-between;' + 'gap:16px;padding:20px 24px;background:' + C_BG + ';' + 'border:1px solid ' + C_BORDER + ';border-radius:' + R_CARD + ';' + 'text-decoration:none;color:inherit;' + 'transition:transform .15s ease, border-color .15s ease, box-shadow .15s ease;}' + '.pwjb__card:hover{border-color:' + C_NAVY + ';transform:translateY(-1px);box-shadow:0 8px 24px rgba(19,27,37,.06);}' + '.pwjb__card:focus-visible{outline:3px solid ' + C_ACCENT + ';outline-offset:2px;}' + '.pwjb__card-main{min-width:0;flex:1;}' + '.pwjb__role{font-size:18px;font-weight:700;color:' + C_NAVY + ';margin:0 0 6px;letter-spacing:-0.005em;}' + '.pwjb__meta{display:flex;flex-wrap:wrap;gap:8px 16px;font-size:14px;color:' + C_MUTED + ';margin:0;}' + '.pwjb__meta-item{display:inline-flex;align-items:center;gap:6px;}' + '.pwjb__dot{width:3px;height:3px;border-radius:' + R_PILL + ';background:' + C_MUTED + ';display:inline-block;}' + '.pwjb__remote{display:inline-block;padding:3px 10px;border-radius:' + R_PILL + ';background:' + C_ACCENT_SOFT + ';color:' + C_NAVY + ';font-size:12px;font-weight:700;letter-spacing:.02em;' + 'border:1px solid ' + C_ACCENT + ';}' + '.pwjb__cta{flex-shrink:0;display:inline-flex;align-items:center;gap:6px;' + 'padding:10px 20px;border-radius:' + R_PILL + ';' + 'background:' + C_ACCENT_SOFT + ';color:' + C_NAVY + ';' + 'font-weight:700;font-size:14px;border:1px solid ' + C_ACCENT + ';' + 'transition:background .15s ease;}' + '.pwjb__card:hover .pwjb__cta{background:' + C_ACCENT + ';}' + '.pwjb__cta-arrow{transition:transform .15s ease;}' + '.pwjb__card:hover .pwjb__cta-arrow{transform:translateX(2px);}' + // States '.pwjb__state{padding:48px 24px;text-align:center;color:' + C_MUTED + ';background:' + C_BG_SOFT + ';border-radius:' + R_CARD + ';}' + '.pwjb__skeleton{display:grid;gap:12px;}' + '.pwjb__skeleton-row{height:78px;border-radius:' + R_CARD + ';background:linear-gradient(90deg,#eef1f5 0%,#f7f9fb 50%,#eef1f5 100%);' + 'background-size:200% 100%;animation:pwjbShimmer 1.4s infinite linear;}' + '@keyframes pwjbShimmer{0%{background-position:200% 0;}100%{background-position:-200% 0;}}' + // Responsive '@media (max-width:640px){' + '.pwjb{padding:32px 16px;}' + '.pwjb__title{font-size:32px;}' + '.pwjb__card{flex-direction:column;align-items:flex-start;}' + '.pwjb__cta{align-self:flex-start;}' + '}'; function injectStyles() { if (document.getElementById('pwjb-styles')) return; var style = document.createElement('style'); style.id = 'pwjb-styles'; style.appendChild(document.createTextNode(css)); document.head.appendChild(style); } // ---- DOM helpers -------------------------------------------------------- function el(tag, attrs, children) { var node = document.createElement(tag); if (attrs) { for (var k in attrs) { if (k === 'class') node.className = attrs[k]; else if (k === 'text') node.textContent = attrs[k]; else if (k === 'html') node.innerHTML = attrs[k]; else node.setAttribute(k, attrs[k]); } } if (children) { for (var i = 0; i < children.length; i++) { if (children[i] != null) node.appendChild(children[i]); } } return node; } function findMount() { var m = document.getElementById(MOUNT_ID); if (m) return m; // Fallback: insert next to whatever script tag loaded us var scripts = document.getElementsByTagName('script'); var current = document.currentScript || scripts[scripts.length - 1]; var anchor = el('div', { id: MOUNT_ID }); if (current && current.parentNode) { current.parentNode.insertBefore(anchor, current); } else { document.body.appendChild(anchor); } return anchor; } // ---- Renderers ---------------------------------------------------------- function renderSkeleton(root) { root.innerHTML = ''; var wrap = el('div', { class: 'pwjb' }); wrap.appendChild( el('div', { class: 'pwjb__header' }, [ el('h2', { class: 'pwjb__title', text: 'Open Roles' }), el('p', { class: 'pwjb__subtitle', text: 'Help us simplify weather safety for organizations nationwide.' }) ]) ); var skel = el('div', { class: 'pwjb__skeleton' }); for (var i = 0; i < 4; i++) skel.appendChild(el('div', { class: 'pwjb__skeleton-row' })); wrap.appendChild(skel); root.appendChild(wrap); } function renderError(root, message) { root.innerHTML = ''; var wrap = el('div', { class: 'pwjb' }); wrap.appendChild( el('div', { class: 'pwjb__state' }, [ el('p', { text: message || "We couldn't load roles right now. Please try again in a moment." }) ]) ); root.appendChild(wrap); } function describeLocation(job) { if (job.isRemote) return 'Remote'; if (job.location) return job.location; if (job.address && job.address.postalAddress) { var a = job.address.postalAddress; return [a.addressLocality, a.addressRegion].filter(Boolean).join(', '); } return null; } function prettyEmploymentType(t) { if (!t) return null; return ({ FullTime: 'Full-time', PartTime: 'Part-time', Intern: 'Internship', Contract: 'Contract', Temporary: 'Temporary' })[t] || t; } function buildCard(job) { var loc = describeLocation(job); var employment = prettyEmploymentType(job.employmentType); var meta = el('p', { class: 'pwjb__meta' }); if (loc) { meta.appendChild(el('span', { class: 'pwjb__meta-item', text: loc })); } if (employment) { if (loc) meta.appendChild(el('span', { class: 'pwjb__dot' })); meta.appendChild(el('span', { class: 'pwjb__meta-item', text: employment })); } if (job.isRemote && loc !== 'Remote') { meta.appendChild(el('span', { class: 'pwjb__remote', text: 'Remote' })); } var card = el( 'a', { class: 'pwjb__card', href: job.jobUrl || job.applyUrl || '#', target: '_blank', rel: 'noopener noreferrer' }, [ el('div', { class: 'pwjb__card-main' }, [ el('h3', { class: 'pwjb__role', text: job.title }), meta ]), el('span', { class: 'pwjb__cta' }, [ document.createTextNode('View role'), el('span', { class: 'pwjb__cta-arrow', html: '→' }) ]) ] ); return card; } function renderBoard(root, jobs) { // Group by department, preserving first-seen order var groups = []; var byDept = {}; jobs.forEach(function (j) { var dept = j.department || 'Other'; if (!byDept[dept]) { byDept[dept] = []; groups.push(dept); } byDept[dept].push(j); }); var activeDept = 'all'; function rerender() { root.innerHTML = ''; var wrap = el('div', { class: 'pwjb' }); wrap.appendChild( el('div', { class: 'pwjb__header' }, [ el('h2', { class: 'pwjb__title', text: 'Open Roles' }), el('p', { class: 'pwjb__subtitle', text: 'Help us simplify weather safety for organizations nationwide.' }) ]) ); // Department filter chips var filters = el('div', { class: 'pwjb__filters', role: 'group', 'aria-label': 'Filter by department' }); var allChip = el('button', { class: 'pwjb__chip', type: 'button', 'aria-pressed': activeDept === 'all' ? 'true' : 'false' }); allChip.appendChild(document.createTextNode('All')); allChip.appendChild( el('span', { class: 'pwjb__chip-count', text: String(jobs.length) }) ); allChip.addEventListener('click', function () { activeDept = 'all'; rerender(); }); filters.appendChild(allChip); groups.forEach(function (dept) { var chip = el('button', { class: 'pwjb__chip', type: 'button', 'aria-pressed': activeDept === dept ? 'true' : 'false' }); chip.appendChild(document.createTextNode(dept)); chip.appendChild( el('span', { class: 'pwjb__chip-count', text: String(byDept[dept].length) }) ); chip.addEventListener('click', function () { activeDept = dept; rerender(); }); filters.appendChild(chip); }); wrap.appendChild(filters); // Listings var visibleGroups = activeDept === 'all' ? groups : [activeDept]; visibleGroups.forEach(function (dept) { var group = el('section', { class: 'pwjb__group' }); if (activeDept === 'all') { group.appendChild(el('h4', { class: 'pwjb__group-title', text: dept })); } var list = el('ul', { class: 'pwjb__list' }); byDept[dept].forEach(function (job) { list.appendChild(el('li', null, [buildCard(job)])); }); group.appendChild(list); wrap.appendChild(group); }); if (!jobs.length) { wrap.appendChild( el('div', { class: 'pwjb__state' }, [ el('p', { text: "We don't have any open roles right now. Check back soon!" }) ]) ); } root.appendChild(wrap); } rerender(); } // ---- Boot --------------------------------------------------------------- function init() { injectStyles(); var root = findMount(); renderSkeleton(root); fetch(API_URL, { credentials: 'omit' }) .then(function (r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); }) .then(function (data) { var jobs = (data.jobs || []).filter(function (j) { return j.isListed !== false; }); renderBoard(root, jobs); }) .catch(function (err) { // eslint-disable-next-line no-console console.error('[pw-job-board] failed to load:', err); renderError(root); }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();