// careers-shared.jsx — careers content: markdown parsers, loader, hook.
// Openings live in jobs/*.md; page copy + the list of openings in careers.md.
// Both are fetched at runtime; the built-in fallbacks below keep every page
// rendering if a fetch fails (file://, offline, malformed edit).
const CAREERS_PATH = 'careers.md';

const FALLBACK_CAREERS_MD = `
Kicker: PERSONNEL · 2026
Headline: Build the world the machines think in.
Why: Octant is a small team of researchers and engineers turning real sensor data into accurate, editable, simulation-ready worlds. The problems are open, the compute is real, and what we ship runs on actual machines in actual places. We hire people who want the hard version of the problem.
Mail: jobs@octant.systems
Note: Don't see your role? Convince us we need it — jobs@octant.systems
## Openings
- jobs/research-scientist-3d-generative-modeling.md
- jobs/research-engineer-computer-vision-graphics.md
## Benefits
- COMPENSATION | top-of-market salary · meaningful early equity
- HEALTH | medical · dental · vision — fully covered
- SITES | NYC · Pittsburgh · or remote
- HARDWARE | the GPUs you need · no queue
- TIME | flexible hours · three-week vacation floor
- IMMIGRATION | we sponsor visas + relocation
`;

const FALLBACK_JOB_MD = {
'jobs/research-scientist-3d-generative-modeling.md': `
# Research Scientist, 3D Generative Modeling
Team: Research
Location: NYC · Pittsburgh · Remote
Type: Full-time
Ref: OCT-R01
You will invent the models that turn multimodal sensor captures into editable, simulation-ready 3D worlds — and prove they hold up against reality.
## You will
- Advance the state of the art in 3D generative modeling — diffusion, autoregressive, and hybrid approaches over sparse voxel and neural field representations
- Design models that fuse images, LiDAR, thermal, and radar into a single generative representation of a real site
- Own the loop from idea to publication to production — our research ships
- Define how we measure world fidelity — geometric, semantic, and physical
## You bring
- A track record of first-author research in 3D generative modeling, neural fields, or geometric deep learning (or equivalent industry work)
- Strong PyTorch and the systems fluency to train at scale
- Taste for problems where "looks plausible" isn't enough — the world has to be right
## Nice to have
- Experience with sparse spatial data structures (VDB, octrees, hash grids)
- A physics simulation or robotics background
`,
'jobs/research-engineer-computer-vision-graphics.md': `
# Research Engineer, Computer Vision and Graphics
Team: Engineering
Location: NYC · Pittsburgh · Remote
Type: Full-time
Ref: OCT-E01
You will build the pipeline that carries a raw capture — any sensor, any site — through reconstruction, fusion, and out the other side as a world you can run.
## You will
- Build and harden the capture-to-world pipeline: calibration, registration, reconstruction, multimodal fusion
- Write the GPU code that makes seconds-per-world real — CUDA kernels, sparse ops, memory-tight data paths
- Turn research prototypes into reliable, tested systems behind our API
- Own real-world data in all its mess: odd rigs, rolling shutters, dropped frames, weather
## You bring
- Strong C++/Python and real GPU programming experience (CUDA or equivalent)
- Depth in at least one of: multi-view geometry, SLAM, differentiable rendering, real-time graphics
- The habit of measuring before optimizing — and of shipping
## Nice to have
- OpenVDB / NanoVDB or other sparse volumetric tooling
- Experience running ML systems in production
`,
};

function _mdClean(md){
  return String(md).replace(/<!--[\s\S]*?-->/g, '').replace(/&nbsp;/gi, '\u00A0');
}
function _jobSlug(path){ return String(path).split('/').pop().replace(/\.md$/i, ''); }

/* jobs/*.md → { slug, title, meta{team,location,type,ref,apply}, intro[], sections[{h,paras[],items[]}] } */
function parseJobMd(md, path){
  const job = { slug:_jobSlug(path || ''), title:'', meta:{ team:'', location:'', type:'', ref:'', apply:'' }, intro:[], sections:[] };
  if (typeof md !== 'string') return job;
  const META = new Set(['team', 'location', 'type', 'ref', 'apply']);
  let sec = null;
  for (const raw of _mdClean(md).split(/\r?\n/)){
    const t = raw.trim(); if (!t) continue;
    if (t.startsWith('## ')){ sec = { h:t.slice(3).trim(), paras:[], items:[] }; job.sections.push(sec); continue; }
    if (t.startsWith('# ')){ job.title = t.slice(2).trim(); continue; }
    if (/^[-*]\s/.test(t)){ if (sec) sec.items.push(t.replace(/^[-*]\s*/, '')); continue; }
    if (!sec){
      const i = t.indexOf(':');
      if (i > 0){
        const k = t.slice(0, i).trim().toLowerCase();
        if (META.has(k)){ job.meta[k] = t.slice(i + 1).trim(); continue; }
      }
    }
    (sec ? sec.paras : job.intro).push(t);
  }
  return job;
}

/* careers.md → { kicker, headline, why, mail, note, openings[], benefits[{k,v}] } */
function parseCareersMd(md){
  const c = { kicker:'', headline:'', why:'', mail:'jobs@octant.systems', note:'', openings:[], benefits:[] };
  if (typeof md !== 'string') return c;
  let block = '';
  for (const raw of _mdClean(md).split(/\r?\n/)){
    const t = raw.trim(); if (!t) continue;
    if (t.startsWith('## ')){ block = t.slice(3).trim().toLowerCase(); continue; }
    if (t.startsWith('# ')) continue;
    if (block === 'openings'){
      if (/^[-*]\s/.test(t)) c.openings.push(t.replace(/^[-*]\s*/, '').trim());
    } else if (block === 'benefits'){
      if (/^[-*]\s/.test(t)){
        const p = t.replace(/^[-*]\s*/, '').split('|').map((x) => x.trim());
        if (p.length >= 2) c.benefits.push({ k:p[0], v:p.slice(1).join(' | ') });
      }
    } else {
      const i = t.indexOf(':'); if (i < 0) continue;
      const k = t.slice(0, i).trim().toLowerCase(), v = t.slice(i + 1).trim();
      if (k === 'kicker') c.kicker = v;
      else if (k === 'headline') c.headline = v;
      else if (k === 'why') c.why = v;
      else if (k === 'mail') c.mail = v;
      else if (k === 'note') c.note = v;
    }
  }
  return c;
}

async function _fetchMd(path){
  const r = await fetch(path, { cache:'no-cache' });
  if (!r.ok) throw new Error('HTTP ' + r.status);
  return r.text();
}

let _careersPromise = null;
function loadCareersData(){
  if (!_careersPromise){
    _careersPromise = (async () => {
      let copy;
      try {
        copy = parseCareersMd(await _fetchMd(CAREERS_PATH));
        if (!copy.headline || !copy.openings.length) throw new Error('careers.md missing required fields');
      } catch (err){
        console.warn('[octant] careers.md not loaded — using built-in careers copy.', err);
        copy = parseCareersMd(FALLBACK_CAREERS_MD);
      }
      const jobs = [];
      for (const path of copy.openings){
        let md = null;
        try { md = await _fetchMd(path); }
        catch (err){
          md = FALLBACK_JOB_MD[path] || null;
          console.warn('[octant] ' + path + ' not loaded' + (md ? ' — using built-in.' : ' — skipping.'), err);
        }
        if (md == null) continue;
        const job = parseJobMd(md, path);
        if (job.title) jobs.push(job);
      }
      return { copy, jobs };
    })();
  }
  return _careersPromise;
}

function useCareers(){
  const [data, setData] = React.useState(null);
  React.useEffect(() => {
    let live = true;
    loadCareersData().then((d) => { if (live) setData(d); });
    return () => { live = false; };
  }, []);
  return data ? { copy:data.copy, jobs:data.jobs, ready:true } : { copy:null, jobs:[], ready:false };
}

const careersApplyHref = (job, mail) => 'mailto:' + (job.meta.apply || mail || 'jobs@octant.systems') +
  '?subject=' + encodeURIComponent('Application — ' + job.title + (job.meta.ref ? ' [' + job.meta.ref + ']' : ''));

Object.assign(window, { parseJobMd, parseCareersMd, loadCareersData, useCareers, careersApplyHref });
