/* site-bundle.jsx — concatenated site components (deterministic load order: shared → loops → pages) */

;(function(){
const DS = window.HTMSDesignSystem_fc30bf;
const {Logo, Button, SectionLabel, GradientRule} = DS;

function useViewport(){
  const [w,setW] = React.useState(window.innerWidth);
  React.useEffect(()=>{
    const f = ()=>setW(window.innerWidth);
    window.addEventListener('resize',f);
    return ()=>window.removeEventListener('resize',f);
  },[]);
  return {w, mobile: w < 760};
}

function Nav({page, onNav}){
  const {mobile} = useViewport();
  const links = [['materials','Materials'],['newsroom','Newsroom'],['company','Company']];
  return (
    <header style={{background:'#fff',borderBottom:'1px solid var(--line)',position:'sticky',top:0,zIndex:20}}>
      <div style={{maxWidth:1240,margin:'0 auto',padding:mobile?'0 16px':'0 32px',height:mobile?64:76,display:'flex',alignItems:'center',justifyContent:'space-between',gap:12}}>
        <a href="#" onClick={e=>{e.preventDefault();onNav('home')}} style={{display:'block',flex:'none'}}><Logo variant={mobile?'mark':'wide'} size={34}/></a>
        <nav style={{display:'flex',alignItems:'center',gap:mobile?14:30}}>
          {links.map(([id,l])=>(
            <a key={id} href="#" onClick={e=>{e.preventDefault();onNav(id)}}
               style={{fontSize:14.5,fontWeight:page===id?500:400,color:page===id?'var(--navy)':'var(--ink-muted)',paddingBottom:3,borderBottom:page===id?'2px solid var(--blaze)':'2px solid transparent',whiteSpace:'nowrap'}}>{l}</a>))}
          <Button size="sm" onClick={()=>onNav('company')}>{mobile?'Enquiry':'Send enquiry'}</Button>
        </nav>
      </div>
    </header>);
}

function Section({bg='#fff', children, pad, style}){
  const {mobile} = useViewport();
  return (
    <section style={{background:bg,...style}}>
      <div style={{maxWidth:1240,margin:'0 auto',padding:mobile?'52px 20px':(pad||'88px 32px')}}>{children}</div>
    </section>);
}

function SectionHead({eyebrow, title, lead, onDark=false, amber=false}){
  const {mobile} = useViewport();
  return (
    <div style={{marginBottom:mobile?30:44}}>
      <SectionLabel color={amber||onDark?'amber':'flare'} style={{marginBottom:14}}>{eyebrow}</SectionLabel>
      <h1 style={{margin:0,fontWeight:500,fontSize:mobile?29:40,lineHeight:1.16,letterSpacing:'0.01em',color:onDark?'#fff':'var(--navy)',textWrap:'pretty'}}>{title}</h1>
      {lead && <p style={{margin:'14px 0 0',fontSize:mobile?15.5:17,lineHeight:1.62,color:onDark?'var(--slate)':'var(--ink-muted)',maxWidth:'62ch',textWrap:'pretty'}}>{lead}</p>}
    </div>);
}

function CtaBand({onNav}){
  const {mobile} = useViewport();
  return (
    <section style={{background:'var(--navy)',position:'relative'}}>
      <div style={{position:'absolute',left:0,top:0,bottom:0,width:6}}><GradientRule vertical height={6} length="100%"/></div>
      <div style={{maxWidth:1240,margin:'0 auto',padding:mobile?'52px 20px':'72px 32px',display:'flex',alignItems:'center',justifyContent:'space-between',gap:mobile?24:40,flexWrap:'wrap'}}>
        <div>
          <h2 style={{margin:0,fontWeight:500,fontSize:mobile?25:34,lineHeight:1.2,color:'#fff',textWrap:'pretty'}}>Bring us the limit you need to break.</h2>
          <p style={{margin:'10px 0 0',fontSize:mobile?14.5:16,lineHeight:1.6,color:'var(--slate)'}}>Tell us the operating temperature, the weight target and the part. We will tell you what the material can do.</p>
        </div>
        <Button size="lg" onClick={()=>onNav('company')}>Send enquiry</Button>
      </div>
    </section>);
}

function Footer({onNav}){
  const {w, mobile} = useViewport();
  const tight = w < 1100;   // badges drop to their own row before anything squeezes
  const col = {fontSize:13.5,color:'var(--slate)',display:'flex',flexDirection:'column',gap:9};
  const lk = {color:'var(--pale)'};
  return (
    <footer style={{background:'var(--navy)'}}>
      <GradientRule height={5}/>
      <div style={{maxWidth:1240,margin:'0 auto',padding:mobile?'40px 20px 0':'56px 32px 0'}}>
        <div style={{display:'grid',gridTemplateColumns:tight?'1fr':'auto 1fr auto',alignItems:'start',gap:mobile?32:40}}>
          <Logo variant="horizontal" size={mobile?58:76} onDark/>
          <div style={{display:'flex',flexDirection:'column',gap:12,alignSelf:'center',justifySelf:tight?'start':'center',order:tight?2:0}}>
            <SectionLabel color="slate" size={9}>Certifications</SectionLabel>
            <div style={{display:'flex',alignItems:'center',gap:mobile?16:18,flexWrap:mobile?'wrap':'nowrap'}}>
              {[['Cyber Essentials Certified','assets/badges/cyber-essentials-certified.png'],
                ['Cyber Essentials Certified Plus','assets/badges/cyber-essentials-certified-plus.png'],
                ['ISO 9001 certified — quality assurance management system','assets/badges/iso-9001-certified.png']].map(([alt,src])=>(
                <img key={src} src={src} alt={alt} height={mobile?52:58} style={{display:'block',height:mobile?52:58,width:'auto'}}/>))}
            </div>
          </div>
          <div style={{display:'flex',gap:mobile?36:64,flexWrap:'wrap',justifySelf:tight?'start':'end',order:tight?1:0}}>
            <div style={col}>
              <SectionLabel color="slate" size={9} style={{marginBottom:4}}>Site</SectionLabel>
              <a href="#" style={lk} onClick={e=>{e.preventDefault();onNav('materials')}}>Materials</a>
              <a href="#" style={lk} onClick={e=>{e.preventDefault();onNav('newsroom')}}>Newsroom</a>
          <a href="#" style={lk} onClick={e=>{e.preventDefault();onNav('company')}}>Company</a>
              <a href="#" style={lk} onClick={e=>{e.preventDefault();onNav('company')}}>Contact</a>
              <a href="https://www.linkedin.com/company/htmsuk/jobs/" target="_blank" rel="noopener" style={lk}>Careers</a>
            </div>
            <div style={col}>
              <SectionLabel color="slate" size={9} style={{marginBottom:4}}>Contact</SectionLabel>
              <span style={{fontFamily:'var(--font-mono)',fontSize:11,letterSpacing:'0.1em',color:'var(--pale)'}}>INFO@HTMS.TECH</span>
              <span style={{fontFamily:'var(--font-mono)',fontSize:11,letterSpacing:'0.1em',color:'var(--pale)'}}>HTMS-COMPOSITES.COM</span>
              <span style={{fontFamily:'var(--font-mono)',fontSize:11,letterSpacing:'0.1em',color:'#5C6494'}}>HTMS.TECH</span>
            </div>
          </div>
        </div>
        <div style={{display:'flex',justifyContent:'space-between',flexWrap:'wrap',gap:'8px 20px',borderTop:'1px solid var(--steel)',marginTop:mobile?32:44,padding:'18px 0 24px',fontFamily:'var(--font-mono)',fontSize:9,letterSpacing:'0.16em',color:'#5C6494'}}>
          <span>HTMS LTD · 13525962</span>
          <span>© 2026 HTMS COMPOSITES</span>
        </div>
      </div>
    </footer>);
}

Object.assign(window, {useViewport, Nav, Section, SectionHead, CtaBand, Footer, SiteNav: Nav, SiteFooter: Footer});

})();


;(function(){
const homeDS = window.HTMSDesignSystem_fc30bf;

function HomePage({onNav}){
  const {Logo, Icon, Button, SectionLabel, Card, GradientRule, TempBar, TempAxis} = homeDS;
  const {mobile} = useViewport();
  const grid = n => ({display:'grid',gridTemplateColumns:mobile?'1fr':'repeat('+n+',1fr)',gap:14});
  const mats = [
    {name:'OxyDura', band:'amber', bandLabel:'MEDIUM BAND', icon:'layup', desc:'Lightweight medium-temperature operation.'},
    {name:'CarboniteX', band:'flare', bandLabel:'HIGH BAND · CARBON FIBRE IMC', icon:'protection', desc:'Affordable low-density thermal shielding, supplied as pre-preg.'},
    {name:'IgniShield', band:'flare', bandLabel:'HIGH BAND', icon:'temperature', desc:'Thermal protection at the core of the range.'},
    {name:'ThermaLite', band:'blaze', bandLabel:'EXTREME BAND', icon:'mass-reduction', desc:'1000˚C and beyond, at a fraction of the density.'}
  ];
  const stat = (v,l)=>(
    <div key={l}>
      <div style={{fontWeight:500,fontSize:mobile?26:34,color:'#fff',lineHeight:1.1}}>{v}</div>
      <div style={{fontFamily:'var(--font-mono)',fontSize:9,letterSpacing:'0.16em',color:'var(--slate)',marginTop:7}}>{l}</div>
    </div>);
  return (
    <main>
      <section style={{background:'var(--navy)',position:'relative'}}>
        <GradientRule height={5}/>
        <div style={{maxWidth:1240,margin:'0 auto',padding:mobile?'56px 20px 44px':'96px 32px 72px'}}>
          <div style={{display:'grid',gridTemplateColumns:mobile?'1fr':'minmax(0,1.35fr) minmax(0,1fr)',gap:mobile?40:64,alignItems:'center'}}>
            <div>
              <SectionLabel color="amber" style={{marginBottom:20}}>Thermal composites · Performance automotive & drone systems</SectionLabel>
              <h1 style={{margin:0,fontWeight:500,fontSize:mobile?36:60,lineHeight:1.12,letterSpacing:'0.005em',color:'#fff',textWrap:'pretty'}}>Unlocking the power of thermal performance.</h1>
              <p style={{margin:'22px 0 0',fontSize:mobile?16:18,lineHeight:1.65,color:'var(--pale)',maxWidth:'52ch',textWrap:'pretty'}}>We build lightweight inorganic-matrix composites that hold above 1000˚C — so engineers stop trading thermal margin against weight, life and cost.</p>
              <div style={{display:'flex',gap:14,marginTop:34,flexWrap:'wrap'}}>
                <Button size="lg" onClick={()=>onNav('materials')}>Explore the materials</Button>
                <Button size="lg" variant="secondary" onDark onClick={()=>onNav('company')}>Meet the company</Button>
              </div>
            </div>
            <div style={{display:'flex',justifyContent:'center'}}><Logo variant="mark" size={mobile?160:300}/></div>
          </div>
          <div style={{display:'grid',gridTemplateColumns:mobile?'repeat(2,1fr)':'repeat(4,1fr)',gap:mobile?'26px 20px':32,borderTop:'1px solid var(--steel)',marginTop:mobile?44:64,paddingTop:mobile?28:36}}>
            {stat('1000˚C+','CONTINUOUS SERVICE')}
            {stat('4','MATERIAL SYSTEMS')}
            {stat('2','DOMAINS SERVED')}
            <div>
              <div style={{fontWeight:500,fontSize:mobile?15:17,color:'#fff',lineHeight:1.45}}>Rolls-Royce, JLR and Airbus</div>
              <div style={{fontFamily:'var(--font-mono)',fontSize:9,letterSpacing:'0.16em',color:'var(--slate)',marginTop:7}}>WHERE OUR ENGINEERS CAME FROM</div>
            </div>
          </div>
        </div>
      </section>

      <Section>
        <div style={{display:'grid',gridTemplateColumns:mobile?'1fr':'minmax(0,1.15fr) minmax(0,1fr)',gap:mobile?36:72,alignItems:'start'}}>
          <div>
            <SectionLabel style={{marginBottom:14}}>01 — The insight</SectionLabel>
            <h1 style={{margin:0,fontWeight:500,fontSize:mobile?29:40,lineHeight:1.16,color:'var(--navy)',textWrap:'pretty'}}>Today's materials have reached their ceiling.</h1>
            <p style={{margin:'18px 0 0',fontSize:mobile?15.5:17,lineHeight:1.66,color:'var(--ink-body)',maxWidth:'58ch',textWrap:'pretty'}}>High-performance systems demand more heat, and existing materials force a trade: thermal margin against weight, life and cost. The systems of the next decade cannot be built from the materials of the last one.</p>
          </div>
          <div style={{borderTop:'2px solid var(--navy)',paddingTop:26}}>
            <SectionLabel color="slate" tracking="0.18em" style={{marginBottom:16}}>What we do</SectionLabel>
            <p style={{margin:0,fontWeight:500,fontSize:mobile?20:24,lineHeight:1.42,color:'var(--navy)',textWrap:'pretty'}}>We enable manufacturers of high-performance systems to overcome thermal limitations without compromise.</p>
            <div style={{marginTop:24}}><GradientRule height={8} length={210}/></div>
          </div>
        </div>
      </Section>

      <Section style={{borderTop:'1px solid var(--line)'}}>
        <SectionHead eyebrow="02 — The range" title="Colour is the temperature." lead="Four material systems across three bands. On every chart and datasheet, the colour states how hot the application runs — amber for medium, flare for high, blaze for extreme."/>
        <div style={grid(4)}>
          {mats.map(m=>(
            <Card key={m.name} band={m.band} padding={26} style={mobile?null:{minHeight:230}}>
              <Icon name={m.icon} size={34} hot={'var(--'+m.band+')'}/>
              <div style={{fontWeight:500,fontSize:22,color:'var(--navy)',margin:'18px 0 4px'}}>{m.name}</div>
              <div style={{fontFamily:'var(--font-mono)',fontSize:8.5,letterSpacing:'0.14em',color:'var(--slate)',marginBottom:12}}>{m.bandLabel}</div>
              <p style={{margin:0,fontSize:14.5,lineHeight:1.6,color:'var(--ink-body)',flex:1,textWrap:'pretty'}}>{m.desc}</p>
              <a href="#" onClick={e=>{e.preventDefault();onNav('materials')}} style={{fontFamily:'var(--font-mono)',fontSize:10,letterSpacing:'0.14em',marginTop:16}}>DATASHEET →</a>
            </Card>))}
        </div>
      </Section>

      <section style={{background:'var(--navy)'}}>
        <div style={{maxWidth:1240,margin:'0 auto',padding:mobile?'52px 20px':'88px 32px'}}>
          <div style={{display:'grid',gridTemplateColumns:mobile?'1fr':'minmax(0,1fr) minmax(0,1.3fr)',gap:mobile?36:64,alignItems:'start'}}>
            <div>
              <SectionLabel color="amber" style={{marginBottom:14}}>03 — The proof</SectionLabel>
              <h1 style={{margin:0,fontWeight:500,fontSize:mobile?29:40,lineHeight:1.16,color:'#fff',textWrap:'pretty'}}>Performance where others fail.</h1>
              <p style={{margin:'18px 0 0',fontSize:mobile?15:16,lineHeight:1.66,color:'var(--pale)',textWrap:'pretty'}}>An inorganic matrix composite that holds above 1000˚C at a fraction of the density. Scientifically rigorous and technically accurate, always — we never claim what has not been tested.</p>
              <div style={{borderTop:'1px solid var(--steel)',marginTop:26,paddingTop:20,display:'flex',gap:14,alignItems:'center'}}>
                <homeDS.Icon name="qualified" size={30} base="#FFFFFF"/>
                <span style={{fontSize:14.5,lineHeight:1.5,color:'var(--pale)'}}>Qualification programme under way with partners in both domains.</span>
              </div>
            </div>
            <div style={{background:'#fff',padding:mobile?'20px 16px':'30px 30px 24px'}}>
              <SectionLabel color="slate" tracking="0.18em" style={{marginBottom:20}}>Plotting performance</SectionLabel>
              <div style={{display:'flex',flexDirection:'column',gap:11}}>
                <TempBar label="Conventional composite" value={0.30} comparator labelWidth={mobile?104:170}/>
                <TempBar label="OxyDura" value={0.48} band="medium" labelWidth={mobile?104:170}/>
                <TempBar label="CarboniteX" value={0.62} band="high" labelWidth={mobile?104:170}/>
                <TempBar label="ThermaLite" value={0.88} band="extreme" labelWidth={mobile?104:170}/>
              </div>
              <TempAxis labelWidth={mobile?104:170}/>
              <p style={{margin:'16px 0 0',fontFamily:'var(--font-mono)',fontSize:8.5,letterSpacing:'0.1em',color:'var(--slate)'}}>RANGE POSITIONS ILLUSTRATIVE · QUALIFICATION DATA TO FOLLOW</p>
            </div>
          </div>
        </div>
      </section>

      <Section>
        <SectionHead eyebrow="04 — Domains" title="Thermal management for"/>
        <div style={{display:'grid',gridTemplateColumns:mobile?'1fr':'1fr 1fr',gap:14}}>
          {[['dom-auto','Performance Automotive','Heat shielding and thermal protection for performance vehicles.','./graphics/car.html'],['dom-drone','Drone Systems','Lightweight thermal protection for unmanned propulsion and power.','./graphics/drone.html']].map(([id,name,d,src])=>{
            return (
            <div key={id} style={{boxShadow:'0 0 0 1px var(--line)',background:'#fff'}}>
              <div style={{height:mobile?190:260,background:'var(--navy)'}}>
                <iframe src={src} title={name} loading="lazy" scrolling="no" tabIndex="-1" style={{display:'block',width:'100%',height:'100%',border:0,pointerEvents:'none'}}/>
              </div>
              <div style={{padding:mobile?'18px 20px 22px':'22px 26px 26px',display:'flex',alignItems:'center',justifyContent:'space-between',gap:20}}>
                <div>
                  <div style={{fontWeight:500,fontSize:mobile?19:22,color:'var(--navy)'}}>{name}</div>
                  <p style={{margin:'6px 0 0',fontSize:14.5,lineHeight:1.55,color:'var(--ink-muted)'}}>{d}</p>
                </div>
                <span style={{width:7,height:7,background:'var(--flare)',flex:'none'}}></span>
              </div>
            </div>);})}
        </div>
      </Section>

      <CtaBand onNav={onNav}/>
    </main>);
}
window.HomePage = HomePage;

})();

;(function(){
const matDS = window.HTMSDesignSystem_fc30bf;

function MaterialsPage({onNav}){
  const {Icon, Button, SectionLabel, Card, GradientRule, SpecTable} = matDS;
  const {mobile} = useViewport();
  const bands = [['AMBER','MEDIUM'],['FLARE','HIGH'],['BLAZE','EXTREME']];
  const others = [
    {name:'OxyDura', band:'amber', bandLabel:'MEDIUM BAND', icon:'layup', desc:'Lightweight medium-temperature operation for parts that run warm, not white-hot.'},
    {name:'IgniShield', band:'flare', bandLabel:'HIGH BAND', icon:'temperature', desc:'Thermal protection at the core of the range, alongside CarboniteX.'},
    {name:'ThermaLite', band:'blaze', bandLabel:'EXTREME BAND', icon:'mass-reduction', desc:'1000˚C and beyond. Bespoke formulations sit here too.'}
  ];
  return (
    <main>
      <Section pad="72px 32px 56px">
        <SectionHead eyebrow="Materials" title="Four systems, one matrix." lead="Every material is an inorganic-matrix composite, placed on the heat range by its continuous service temperature. Two steps of the scale may sit together; the full range appears only as the gradient."/>
        <GradientRule height={14}/>
        <div style={{display:'flex',justifyContent:'space-between',marginTop:8,fontFamily:'var(--font-mono)',fontSize:mobile?8:9,letterSpacing:'0.14em',color:'var(--slate)'}}>
          {bands.map(([n,b])=><span key={n}>{n} · {b}</span>)}
        </div>
      </Section>

      <section style={{background:'var(--navy)'}}>
        <div style={{maxWidth:1240,margin:'0 auto',padding:mobile?'52px 20px':'72px 32px'}}>
          <div style={{display:'grid',gridTemplateColumns:mobile?'1fr':'minmax(0,1fr) minmax(0,1fr)',gap:mobile?36:56,alignItems:'start'}}>
            <div>
              <SectionLabel color="amber" style={{marginBottom:14}}>Featured · High band</SectionLabel>
              <h1 style={{margin:0,fontWeight:500,fontSize:mobile?32:44,lineHeight:1.1,color:'#fff'}}>CarboniteX</h1>
              <div style={{fontFamily:'var(--font-mono)',fontSize:9,letterSpacing:'0.16em',color:'var(--slate)',margin:'8px 0 0'}}>CARBON FIBRE INORGANIC MATRIX COMPOSITE</div>
              <div style={{height:3,background:'var(--flare)',width:64,margin:'18px 0 0'}}></div>
              <p style={{margin:'20px 0 0',fontSize:mobile?15:16,lineHeight:1.66,color:'var(--pale)',maxWidth:'50ch',textWrap:'pretty'}}>Carbon fibre IMC for affordable low-density thermal shielding. Supplied as pre-preg for autoclave or press cure.</p>
              <figure style={{margin:'28px 0 0'}}>
                <img src="assets/photos/carbonitex-prepreg.jpg" alt="A gloved hand lifting the blue release film from a CarboniteX carbon-fibre pre-preg ply" style={{display:'block',width:'100%',height:'auto'}}/>
                <figcaption style={{fontFamily:'var(--font-mono)',fontSize:8.5,letterSpacing:'0.14em',textTransform:'uppercase',color:'var(--slate)',marginTop:10}}>CarboniteX pre-preg · release film lifted</figcaption>
              </figure>
            </div>
            <div style={{background:'#fff',padding:mobile?'20px 16px':'26px 28px 22px'}}>
              <SpecTable rows={[
                {property:'Continuous service temperature', value:'700+', unit:'˚C'},
                {property:'Max service temperature', value:'1000+', unit:'˚C'},
                {property:'Density', value:'1.6', unit:'kg/m³'},
                {property:'Thickness range', value:'0.6 - 4', unit:'mm'},
                {property:'Cure temperature', value:'180', unit:'˚C'},
                {property:'Shelf life at −18 ˚C', value:'3', unit:'months'}
              ]} footnote="Values populated from qualification data"/>
              <div style={{display:'flex',gap:12,marginTop:20,flexWrap:'wrap'}}>
                <Button onClick={()=>onNav('company')}>Request datasheet</Button>
                <Button variant="secondary" onClick={()=>onNav('company')}>Talk to an engineer</Button>
              </div>
            </div>
          </div>
        </div>
      </section>

      <Section>
        <SectionHead eyebrow="The rest of the range" title="Placed by band, not by preference."/>
        <div style={{display:'grid',gridTemplateColumns:mobile?'1fr':'repeat(3,1fr)',gap:14}}>
          {others.map(m=>(
            <Card key={m.name} band={m.band} padding={26}>
              <div style={{display:'flex',alignItems:'center',justifyContent:'space-between'}}>
                <Icon name={m.icon} size={32} hot={'var(--'+m.band+')'}/>
                <span style={{fontFamily:'var(--font-mono)',fontSize:8.5,letterSpacing:'0.14em',color:'var(--slate)'}}>{m.bandLabel}</span>
              </div>
              <div style={{fontWeight:500,fontSize:22,color:'var(--navy)',margin:'18px 0 8px'}}>{m.name}</div>
              <p style={{margin:'0 0 18px',fontSize:14.5,lineHeight:1.6,color:'var(--ink-body)',flex:1,textWrap:'pretty'}}>{m.desc}</p>
              <div><Button size="sm" variant="secondary" onClick={()=>onNav('company')}>Request datasheet</Button></div>
            </Card>))}
        </div>
        <div style={{display:'flex',gap:12,alignItems:'flex-start',marginTop:22,background:'var(--paper)',borderLeft:'3px solid var(--flare)',padding:'14px 18px'}}>
          <p style={{margin:0,fontSize:13.5,lineHeight:1.6,color:'var(--ink-body)'}}>Datasheets are issued populated or not at all — an em dash in a published field is a claim we have not tested. Request the current revision and we will send tested values.</p>
        </div>
      </Section>

      <CtaBand onNav={onNav}/>
    </main>);
}
window.MaterialsPage = MaterialsPage;

})();

;(function(){
const coDS = window.HTMSDesignSystem_fc30bf;

function CompanyPage(){
  const {Icon, Button, SectionLabel, Card, Field} = coDS;
  const {mobile} = useViewport();
  const [sent,setSent] = React.useState(false);
  const [busy,setBusy] = React.useState(false);
  const [err,setErr] = React.useState('');
  const [form,setForm] = React.useState({name:'',company:'',email:'',application:'',website:''});
  // DS Field forwards the DOM event; accept either contract
  const set = k => v => setForm(f=>({...f,[k]: v && v.target ? v.target.value : v}));
  const submit = async e => {
    e.preventDefault();
    setBusy(true); setErr('');
    try {
      const r = await fetch(new URL('api/enquiry', document.baseURI),{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify(form)});
      if (r.ok) { setSent(true); setForm({name:'',company:'',email:'',application:'',website:''}); }
      else if (r.status===503) setErr('The form is not connected yet — please email info@htms.tech and an engineer will reply.');
      else if (r.status===400) setErr('Please check the name, company and email fields.');
      else setErr('That did not send. Please try again, or email info@htms.tech.');
    } catch(e) {
      setErr('That did not send. Please try again, or email info@htms.tech.');
    }
    setBusy(false);
  };
  const row = {display:'grid',gridTemplateColumns:mobile?'1fr':'180px minmax(0,1fr)',gap:mobile?14:48,alignItems:'start'};
  const pillars = [
    {t:'Thermal Leadership', band:'blaze', d:'Our materials perform where others fail, 1000˚C and beyond.'},
    {t:'Engineering Freedom', band:'flare', d:'We reduce weight without sacrificing performance.'},
    {t:'Sustainable Efficiency', band:'ember', d:'Lighter, hotter-running systems that burn less fuel and emit less CO2.'},
    {t:'Scalable Reliability', band:'amber', d:'We deliver consistently from prototype to production.'}
  ];
  return (
    <main>
      <Section pad="72px 32px">
        <SectionLabel style={{marginBottom:14}}>Company</SectionLabel>
        <div style={{...row,borderTop:'2px solid var(--navy)',padding:mobile?'22px 0 28px':'30px 0 36px'}}>
          <SectionLabel color="slate" size={10} tracking="0.18em" style={{paddingTop:mobile?0:8}}>Vision</SectionLabel>
          <div>
            <p style={{margin:'0 0 14px',fontWeight:500,fontSize:mobile?27:38,lineHeight:1.22,color:'var(--navy)',maxWidth:'22ch',textWrap:'pretty'}}>Unlocking the power of thermal performance.</p>
            <p style={{margin:0,fontSize:mobile?15.5:17,lineHeight:1.62,color:'var(--ink-body)',maxWidth:'52ch',textWrap:'pretty'}}>We're creating a future where thermal limits no longer define what engineers can build.</p>
          </div>
        </div>
        <div style={{...row,borderTop:'1px solid var(--line)',padding:mobile?'22px 0 0':'30px 0 0'}}>
          <SectionLabel color="slate" size={10} tracking="0.18em" style={{paddingTop:mobile?0:6}}>Mission</SectionLabel>
          <p style={{margin:0,fontWeight:500,fontSize:mobile?19:25,lineHeight:1.46,color:'var(--navy)',maxWidth:'36ch',textWrap:'pretty'}}>We are building a new class of thermal composites that enable the design of high-performance systems without compromise.</p>
        </div>
      </Section>

      <Section style={{borderTop:'1px solid var(--line)'}} pad="72px 32px">
        <SectionHead eyebrow="What we stand on" title="Four pillars."/>
        <div style={{display:'grid',gridTemplateColumns:mobile?'1fr':'repeat(4,1fr)',gap:14}}>
          {pillars.map(p=>(
            <Card key={p.t} band={p.band} padding={26}>
              <div style={{fontWeight:500,fontSize:21,lineHeight:1.24,color:'var(--navy)',marginBottom:11,textWrap:'pretty'}}>{p.t}</div>
              <p style={{margin:0,fontSize:15,lineHeight:1.62,color:'var(--ink-body)',textWrap:'pretty'}}>{p.d}</p>
            </Card>))}
        </div>
      </Section>

      <section style={{background:'var(--navy)'}}>
        <div style={{maxWidth:1240,margin:'0 auto',padding:mobile?'52px 20px':'72px 32px'}}>
          <div>
            <div>
              <SectionLabel color="amber" style={{marginBottom:14}}>The team</SectionLabel>
              <h1 style={{margin:0,fontWeight:500,fontSize:mobile?27:38,lineHeight:1.2,color:'#fff',maxWidth:'26ch',textWrap:'pretty'}}>Our engineers came from Rolls-Royce, JLR and Airbus.</h1>
              <p style={{margin:'16px 0 0',fontSize:mobile?15:16,lineHeight:1.66,color:'var(--pale)',maxWidth:'52ch',textWrap:'pretty'}}>People who spent careers up against thermal limits, now building the material that removes them. Accessible to engineers, investors and industry partners alike.</p>
            </div>

          </div>
        </div>
      </section>

      <Section pad="80px 32px">
        <div style={{display:'grid',gridTemplateColumns:mobile?'1fr':'minmax(0,1fr) minmax(0,1.15fr)',gap:mobile?36:64,alignItems:'start'}}>
          <div>
            <SectionHead eyebrow="Contact" title="Send an enquiry." lead="Tell us the operating temperature, the weight target and the part. An engineer replies — not an inbox."/>
            <div style={{display:'flex',flexDirection:'column',gap:10,borderTop:'1px solid var(--line)',paddingTop:20}}>
              <span style={{fontFamily:'var(--font-mono)',fontSize:11,letterSpacing:'0.12em',color:'var(--navy)'}}>INFO@HTMS.TECH</span>
              <span style={{fontFamily:'var(--font-mono)',fontSize:11,letterSpacing:'0.12em',color:'var(--ink-muted)'}}>HTMS LTD · 13525962 · UNITED KINGDOM</span>
            </div>
          </div>
          <div>
            {sent
              ? <div style={{boxShadow:'0 0 0 1px var(--line)',padding:mobile?24:36,display:'flex',flexDirection:'column',gap:14,alignItems:'flex-start'}}>
                  <Icon name="qualified" size={36}/>
                  <div style={{fontWeight:500,fontSize:22,color:'var(--navy)'}}>Enquiry received.</div>
                  <p style={{margin:0,fontSize:15,lineHeight:1.6,color:'var(--ink-body)'}}>An engineer will reply within two working days.</p>
                  <Button variant="secondary" size="sm" onClick={()=>setSent(false)}>Send another</Button>
                </div>
              : <form onSubmit={submit} style={{display:'grid',gridTemplateColumns:mobile?'1fr':'1fr 1fr',gap:16}}>
                  <Field label="Name" placeholder="Full name" required value={form.name} onChange={set('name')}/>
                  <Field label="Company" placeholder="Company" required value={form.company} onChange={set('company')}/>
                  <Field label="Email" type="email" placeholder="name@company.com" required value={form.email} onChange={set('email')} style={{gridColumn:'1 / -1'}}/>
                  <Field label="Application" multiline rows={5} placeholder="Operating temperature, weight target, the part…" value={form.application} onChange={set('application')} style={{gridColumn:'1 / -1'}}/>
                  <div aria-hidden="true" style={{position:'absolute',left:-9999,width:1,height:1,overflow:'hidden'}}>
                    <input tabIndex="-1" autoComplete="off" value={form.website} onChange={set('website')}/>
                  </div>
                  <div style={{gridColumn:'1 / -1',display:'flex',alignItems:'center',gap:18,flexWrap:'wrap'}}>
                    <Button size="lg" disabled={busy}>{busy ? 'Sending…' : 'Send enquiry'}</Button>
                    {err && <span style={{fontSize:13.5,lineHeight:1.5,color:'var(--blaze)',maxWidth:'42ch'}}>{err}</span>}
                  </div>
                </form>}
          </div>
        </div>
      </Section>
    </main>);
}
window.CompanyPage = CompanyPage;

})();


/* NewsroomPage — company news (articles.json) + live articles pulled from
   github.com/richard-grainger/personalwebsite (src/articles/*.md).
   Perspective → Insight · Primer → its own filter. Future-dated files are skipped,
   matching the 11ty rule on the source site. */
;(function(){

const FEED = {
  api:  'https://api.github.com/repos/richard-grainger/personalwebsite/contents/src/articles?ref=main',
  site: 'https://richard-grainger.co.uk/articles/',
  origin: 'https://richard-grainger.co.uk',
  // same assets, straight from the public repo — used only if the live site 404s
  raw: 'https://raw.githubusercontent.com/richard-grainger/personalwebsite/main/src'
};

/* First image in the body becomes the Newsroom card cover, so imported pieces
   get their own covers instead of all sharing the category fallback. */
function firstImage(body){
  const md = /!\[[^\]]*\]\((https?:[^)\s]+)/.exec(body);
  if (md) return md[1];
  const html = /<img\b[^>]*\bsrc=("|')(https?:[^"']+)\1/i.exec(body);
  return html ? html[2] : '';
}

/* Article bodies are written for richard-grainger.co.uk, so their image and link
   paths are root-relative ("/assets/x.jpg") or relative to the article's own page.
   Rewrite both to absolute URLs on the source site, or they 404 against ours. */
function absolutise(body, articleUrl){
  const resolve = p => {
    if (/^(https?:|data:|mailto:|#)/i.test(p)) return p;
    if (p.startsWith('//')) return 'https:' + p;
    if (p.startsWith('/')) return FEED.origin + p;
    return articleUrl + p.replace(/^\.\//, '');
  };
  return body
    // markdown: ![alt](path) and [text](path)
    .replace(/(!?\[[^\]]*\]\()([^)\s]+)/g, (m, head, p) => head + resolve(p))
    // inline HTML: src="path" / href="path"
    .replace(/\b(src|href)=("|')([^"']+)\2/gi, (m, attr, q, p) => attr + '=' + q + resolve(p) + q);
}
const CATS = {
  'Press release': {heat:'var(--blaze)',  cover:'news/images/cover-press-release.jpg'},
  'Milestone':     {heat:'var(--flare)',  cover:'news/images/cover-milestone.jpg'},
  'Insight':       {heat:'var(--flare)',  cover:'news/images/cover-insight.jpg'},
  'Primer':        {heat:'var(--amber)',  cover:'news/images/cover-primer.jpg'}
};
const catOf = c => CATS[c] || CATS['Press release'];

const MONTHS = ['JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP','OCT','NOV','DEC'];
const fmtDate = d => String(d.getUTCDate()).padStart(2,'0')+' '+MONTHS[d.getUTCMonth()]+' '+d.getUTCFullYear();

function frontMatter(text){
  const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text);
  if (!m) return {meta:{}, body:text};
  const meta = {};
  m[1].split(/\r?\n/).forEach(line=>{
    const i = line.indexOf(':');
    if (i < 1) return;
    let v = line.slice(i+1).trim().replace(/^["']|["']$/g,'');
    meta[line.slice(0,i).trim()] = v;
  });
  return {meta, body: text.slice(m[0].length)};
}

async function loadLocal(){
  const roots = ['./','./site/'];
  for (const root of roots) {
    try {
      const manifest = await fetch(root+'news/index.json').then(r=>{ if(!r.ok) throw new Error(r.status); return r.json(); });
      const names = (manifest.articles||[]).filter(f=>/\.md$/i.test(f));
      const now = Date.now();
      const out = [];
      await Promise.all(names.map(async name => {
        try {
          const text = await fetch(root+'news/'+name).then(r=>{ if(!r.ok) throw new Error(r.status); return r.text(); });
          const {meta, body} = frontMatter(text);
          const when = new Date(meta.date || 0);
          if (!meta.title || isNaN(when) || when.getTime() > now) return;
          out.push({
            id: 'news-'+name.replace(/\.md$/i,''),
            ts: when.getTime(),
            date: fmtDate(when),
            cat: meta.category || 'Press release',
            sourceCat: meta.category || 'Press release',
            title: meta.title,
            excerpt: meta.summary || '',
            image: meta.image ? (root==='./site/' ? meta.image.replace(/^\.\/news\//,'./site/news/') : meta.image) : '',
            imageAlt: meta.imageAlt || '',
            external: (meta.external || '').trim(),
            body
          });
        } catch(e){}
      }));
      if (out.length) return {root, items: out.sort((a,b)=>b.ts-a.ts)};
    } catch(e){}
  }
  return {root:'./', items:[]};
}

async function loadExternal(){
  // Version is the shape the cache must have. Add a field to the stored article
  // and add it here, or stale entries from a previous deploy get served silently.
  const CACHE_KEY = 'htms-feed', SHAPE = ['slug','ts','date','cat','sourceCat','title','excerpt','image','body','url'];
  const CACHE_V = SHAPE.join(',');
  const cached = sessionStorage.getItem(CACHE_KEY);
  if (cached) {
    try {
      const c = JSON.parse(cached);
      if (c && c.v === CACHE_V && Array.isArray(c.items)) return c.items;
    } catch(e){}
    sessionStorage.removeItem(CACHE_KEY);
  }
  const listing = await fetch(FEED.api).then(r=>{ if(!r.ok) throw new Error(r.status); return r.json(); });
  const files = listing.filter(f => f.type==='file' && /\.md$/i.test(f.name));
  const now = Date.now();
  const out = [];
  await Promise.all(files.map(async f => {
    try {
      const text = await fetch(f.download_url).then(r=>r.text());
      const {meta, body} = frontMatter(text);
      const when = new Date(meta.date || 0);
      if (!meta.title || isNaN(when) || when.getTime() > now) return;
      const slug = f.name.replace(/\.md$/i,'').replace(/^\d{4}-\d{2}-\d{2}-/,'').trim();
      const url = FEED.site + slug + '/';
      const src = (meta.category||'').trim().toLowerCase();
      out.push({
        id: 'ext-'+slug,
        slug,
        ts: when.getTime(),
        date: fmtDate(when),
        cat: src === 'primer' ? 'Primer' : 'Insight',
        sourceCat: meta.category || 'Perspective',
        title: meta.title,
        excerpt: meta.summary || '',
        body: absolutise(body, url),
        image: firstImage(absolutise(body, url)),
        imageAlt: '',
        url
      });
    } catch(e){}
  }));
  out.sort((a,b)=>b.ts-a.ts);
  try { sessionStorage.setItem(CACHE_KEY, JSON.stringify({v: CACHE_V, items: out})); } catch(e){}
  return out;
}

const PROSE = `
.htms-prose{font-size:17px;line-height:1.72;color:var(--ink-body);max-width:68ch}
.htms-prose h2{font-size:24px;font-weight:500;line-height:1.24;color:var(--navy);margin:44px 0 6px;letter-spacing:0.01em}
.htms-prose h2::after{content:"";display:block;width:56px;height:3px;background:var(--flare);margin-top:14px}
.htms-prose h3{font-size:19px;font-weight:500;color:var(--navy);margin:34px 0 -4px}
.htms-prose p{margin:18px 0}
.htms-prose a{color:var(--flare)}
.htms-prose a:hover{color:var(--blaze)}
.htms-prose strong{font-weight:600;color:var(--navy)}
.htms-prose ul,.htms-prose ol{margin:18px 0;padding-left:20px}
.htms-prose li{margin:7px 0}
.htms-prose li::marker{color:var(--flare)}
.htms-prose blockquote{margin:26px 0;padding:16px 22px;background:var(--paper);border-left:3px solid var(--flare);color:var(--ink-muted)}
.htms-prose blockquote p{margin:0}
.htms-prose code{font-family:var(--font-mono);font-size:15px;background:var(--paper);padding:1px 5px}
.htms-prose img{max-width:100%;display:block}
.htms-prose hr{border:0;border-top:1px solid var(--line);margin:36px 0}
`;

function useVp(){
  const [w,setW] = React.useState(typeof window==='undefined'?1200:window.innerWidth);
  React.useEffect(()=>{
    const f = ()=>setW(window.innerWidth);
    window.addEventListener('resize',f);
    return ()=>window.removeEventListener('resize',f);
  },[]);
  return {w, mobile: w < 760};
}

function ArticleTile({a, mobile, ratio, root}){
  const {heat, cover} = catOf(a.cat);
  const ar = ratio || '16/10';
  const [stage,setStage] = React.useState(0);   // 0 = supplied image, 1 = category cover, 2 = navy tile
  const [retry,setRetry] = React.useState(false);
  const supplied = retry && a.image.startsWith(FEED.origin) ? FEED.raw + a.image.slice(FEED.origin.length) : a.image;
  const src = stage===0 && a.image ? supplied : stage<=1 ? (root||'./')+cover : null;
  const fail = () => {
    if (stage===0 && !retry && a.image.startsWith(FEED.origin)) { setRetry(true); return; }
    setStage(s=>s+1);
  };
  if (src) return (
    <div style={{position:'relative',aspectRatio:ar,background:'var(--paper)',overflow:'hidden'}}>
      <img src={src} alt={stage===0 ? (a.imageAlt||'') : ''} onError={fail}
           style={{position:'absolute',inset:0,width:'100%',height:'100%',objectFit:'cover',display:'block'}}/>
      <div style={{position:'absolute',left:0,right:0,top:0,height:7,background:heat}}/>
    </div>);
  return (
    <div style={{position:'relative',aspectRatio:ar,background:'var(--navy)',display:'flex',alignItems:'flex-end',padding:mobile?'0 20px 20px':'0 24px 24px'}}>
      <div style={{position:'absolute',left:0,right:0,top:0,height:7,background:heat}}/>
      <div style={{fontFamily:'var(--font-mono)',fontSize:10,letterSpacing:'0.16em',textTransform:'uppercase',color:heat}}>{a.sourceCat}</div>
    </div>);
}

function NewsroomPage({onNav}){
  const {mobile} = useVp();
  const [cat,setCat] = React.useState('All');
  const [local,setLocal] = React.useState([]);
  const [ext,setExt] = React.useState([]);
  const [root,setRoot] = React.useState('./');
  const [feedError,setFeedError] = React.useState(false);
  const [open,setOpen] = React.useState(null);

  React.useEffect(()=>{
    loadLocal().then(r=>{ setLocal(r.items); setRoot(r.root); }).catch(()=>{});
    loadExternal().then(setExt).catch(()=>setFeedError(true));
  },[]);

  const all = React.useMemo(()=>{
    return [...local, ...ext].sort((a,b)=>b.ts-a.ts);
  },[local,ext]);

  const cats = ['All','Press release','Milestone','Insight','Primer'];
  const shown = cat==='All' ? all : all.filter(a=>a.cat===cat);
  const featured = shown[0];
  const rest = shown.slice(1);
  const meta = a => a.date+' · '+a.cat.toUpperCase();
  const mono = {fontFamily:'var(--font-mono)',textTransform:'uppercase'};
  const pad = mobile?'0 20px':'0 32px';

  if (open) return <ArticleView article={open} onBack={()=>{setOpen(null);window.scrollTo(0,0);}} mobile={mobile}/>;

  const hostOf = u => { try { return new URL(u).hostname.replace(/^www\./,''); } catch(e){ return 'the publisher'; } };
  const readLink = a => a.external
    ? <a href={a.external} target="_blank" rel="noopener" style={{...mono,fontSize:10,letterSpacing:'0.14em'}}>Read on {hostOf(a.external)} ↗</a>
    : a.body
      ? <a href="#" onClick={e=>{e.preventDefault();setOpen(a);window.scrollTo(0,0);}} style={{...mono,fontSize:10,letterSpacing:'0.14em'}}>Read article →</a>
      : <span style={{...mono,fontSize:10,letterSpacing:'0.14em',color:'var(--slate)'}}>Coming soon</span>;

  return (
    <main>
      <style>{PROSE}</style>
      <section style={{background:'var(--navy)',position:'relative'}}>
        <div style={{position:'absolute',left:0,top:0,right:0,height:5,background:'var(--heat-gradient-h)'}}/>
        <div style={{maxWidth:1240,margin:'0 auto',padding:mobile?'52px 20px 44px':'72px 32px 64px'}}>
          <div style={{...mono,fontSize:11,letterSpacing:'0.16em',color:'var(--amber)',marginBottom:16}}>Newsroom</div>
          <h1 style={{margin:0,fontWeight:500,fontSize:mobile?29:40,lineHeight:1.16,letterSpacing:'0.01em',color:'#fff',maxWidth:'24ch',textWrap:'pretty'}}>News from the scale-up</h1>
          <p style={{margin:'14px 0 0',fontSize:mobile?15.5:17,lineHeight:1.62,color:'var(--slate)',maxWidth:'62ch',textWrap:'pretty'}}>Company updates, production milestones and press announcements, alongside insights and primers on the materials science we work in.</p>
        </div>
      </section>

      <section style={{background:'#fff'}}>
        <div style={{maxWidth:1240,margin:'0 auto',padding:mobile?'40px 20px 60px':'56px 32px 88px'}}>
          <div style={{display:'flex',gap:mobile?16:28,flexWrap:'wrap',borderBottom:'1px solid var(--line)',marginBottom:mobile?32:44}}>
            {cats.map(c=>(
              <a key={c} href="#" onClick={e=>{e.preventDefault();setCat(c)}}
                 style={{...mono,fontSize:11,letterSpacing:'0.16em',padding:'0 2px 12px',borderBottom:'2px solid '+(cat===c?'var(--blaze)':'transparent'),color:cat===c?'var(--navy)':'var(--slate)'}}>{c}</a>))}
          </div>

          {featured &&
            <div style={{display:'grid',gridTemplateColumns:mobile?'1fr':'minmax(320px,7fr) minmax(280px,5fr)',gap:mobile?24:44,alignItems:'center',marginBottom:mobile?44:64}}>
              <ArticleTile a={featured} mobile={mobile} ratio="16/9" root={root}/>
              <div>
                <div style={{...mono,fontSize:10,letterSpacing:'0.16em',color:catOf(featured.cat).heat,marginBottom:14}}>{meta(featured)}</div>
                <h2 style={{margin:0,fontWeight:500,fontSize:mobile?24:30,lineHeight:1.22,color:'var(--navy)',textWrap:'pretty'}}>{featured.title}</h2>
                <p style={{margin:'14px 0 20px',fontSize:mobile?15:16,lineHeight:1.62,color:'var(--ink-muted)',textWrap:'pretty'}}>{featured.excerpt}</p>
                {readLink(featured)}
              </div>
            </div>}

          <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(300px,1fr))',gap:mobile?20:28}}>
            {rest.map(a=>(
              <article key={a.id} style={{background:'#fff',boxShadow:'0 0 0 1px var(--line)',display:'flex',flexDirection:'column'}}>
                <ArticleTile a={a} mobile={mobile} root={root}/>
                <div style={{padding:'22px 24px 26px',display:'flex',flexDirection:'column',gap:10,flex:1}}>
                  <div style={{...mono,fontSize:10,letterSpacing:'0.16em',color:catOf(a.cat).heat}}>{meta(a)}</div>
                  <h3 style={{margin:0,fontWeight:500,fontSize:19,lineHeight:1.3,color:'var(--navy)',textWrap:'pretty'}}>{a.title}</h3>
                  <p style={{margin:0,fontSize:14.5,lineHeight:1.58,color:'var(--ink-muted)',flex:1}}>{a.excerpt}</p>
                  {readLink(a)}
                </div>
              </article>))}
          </div>

          {feedError &&
            <p style={{...mono,fontSize:10,letterSpacing:'0.14em',color:'var(--slate)',marginTop:36}}>Insights feed unavailable — showing company news only.</p>}
        </div>
      </section>

      <section style={{background:'var(--navy)',position:'relative'}}>
        <div style={{position:'absolute',left:0,top:0,bottom:0,width:6,background:'var(--heat-gradient-v)'}}/>
        <div style={{maxWidth:1240,margin:'0 auto',padding:mobile?'44px 20px':'64px 32px',display:'flex',alignItems:'center',justifyContent:'space-between',gap:40,flexWrap:'wrap'}}>
          <div>
            <h2 style={{margin:0,fontWeight:500,fontSize:mobile?24:30,lineHeight:1.2,color:'#fff',textWrap:'pretty'}}>Press enquiries</h2>
            <p style={{margin:'10px 0 0',fontSize:15.5,lineHeight:1.6,color:'var(--slate)',maxWidth:'52ch'}}>For interviews, imagery and background on the material range, contact the team directly.</p>
          </div>
          <a href="mailto:info@htms.tech" style={{...mono,fontSize:11,letterSpacing:'0.14em',background:'var(--flare)',color:'var(--navy)',padding:'12px 20px',display:'inline-block'}}>info@htms.tech</a>
        </div>
      </section>
    </main>);
}

function ArticleView({article, onBack, mobile}){
  const mono = {fontFamily:'var(--font-mono)',textTransform:'uppercase'};
  const prose = React.useRef(null);
  React.useEffect(()=>{
    const el = prose.current;
    if (!el) return;
    const onErr = e => {
      const img = e.target;
      if (img.tagName !== 'IMG') return;
      if (!img.dataset.retried && img.src.startsWith(FEED.origin)) {
        img.dataset.retried = '1';
        img.src = FEED.raw + img.src.slice(FEED.origin.length);
        return;
      }
      // still missing — drop the figure so no caption is left orphaned
      (img.closest('figure') || img).style.display = 'none';
    };
    el.addEventListener('error', onErr, true);
    return () => el.removeEventListener('error', onErr, true);
  },[article]);
  const html = React.useMemo(()=>{
    if (window.marked) return window.marked.parse(article.body);
    return article.body.split(/\n{2,}/).map(p=>'<p>'+p+'</p>').join('');
  },[article]);
  return (
    <main>
      <style>{PROSE}</style>
      <section style={{background:'var(--navy)',position:'relative'}}>
        <div style={{position:'absolute',left:0,top:0,right:0,height:5,background:'var(--heat-gradient-h)'}}/>
        <div style={{maxWidth:900,margin:'0 auto',padding:mobile?'44px 20px 40px':'64px 32px 56px'}}>
          <a href="#" onClick={e=>{e.preventDefault();onBack()}} style={{...mono,fontSize:10,letterSpacing:'0.16em',color:'var(--slate)',display:'inline-block',marginBottom:22}}>← All articles</a>
          <div style={{...mono,fontSize:10,letterSpacing:'0.16em',color:catOf(article.cat).heat,marginBottom:14}}>{article.date} · {article.sourceCat ? article.sourceCat.toUpperCase() : article.cat.toUpperCase()}</div>
          <h1 style={{margin:0,fontWeight:500,fontSize:mobile?27:38,lineHeight:1.18,letterSpacing:'0.01em',color:'#fff',maxWidth:'26ch',textWrap:'pretty'}}>{article.title}</h1>
          {article.excerpt &&
            <p style={{margin:'16px 0 0',fontSize:mobile?15.5:17,lineHeight:1.62,color:'var(--slate)',maxWidth:'60ch',textWrap:'pretty'}}>{article.excerpt}</p>}
        </div>
      </section>

      <section style={{background:'#fff'}}>
        <div style={{maxWidth:900,margin:'0 auto',padding:mobile?'40px 20px 20px':'64px 32px 32px'}}>
          {article.image && !article.url &&
            <figure style={{margin:'0 0 40px'}}>
              <img src={article.image} alt={article.imageAlt||''} onError={e=>{e.target.parentNode.style.display='none'}} style={{width:'100%',display:'block'}}/>
              {article.imageAlt &&
                <figcaption style={{fontFamily:'var(--font-mono)',fontSize:10,letterSpacing:'0.14em',textTransform:'uppercase',color:'var(--slate)',marginTop:10}}>{article.imageAlt}</figcaption>}
            </figure>}
          <div ref={prose} className="htms-prose" dangerouslySetInnerHTML={{__html: html}}/>
        </div>
      </section>

      <section style={{background:'var(--paper)'}}>
        <div style={{maxWidth:900,margin:'0 auto',padding:mobile?'32px 20px 44px':'40px 32px 64px'}}>
          <div style={{borderLeft:'3px solid var(--flare)',padding:'14px 0 14px 20px',display:'flex',justifyContent:'space-between',gap:20,flexWrap:'wrap',alignItems:'center'}}>
            <p style={{margin:0,fontSize:15,lineHeight:1.6,color:'var(--ink-muted)',maxWidth:'54ch'}}>{article.url ? 'Originally published by Dr Richard Grainger.' : 'For interviews, imagery or background on this announcement, contact the team.'}</p>
            {article.url
              ? <a href={article.url} target="_blank" rel="noopener" style={{...mono,fontSize:10,letterSpacing:'0.14em'}}>Read the original →</a>
              : <a href="mailto:info@htms.tech" style={{...mono,fontSize:10,letterSpacing:'0.14em'}}>info@htms.tech</a>}
          </div>
          <div style={{marginTop:28}}>
            <a href="#" onClick={e=>{e.preventDefault();onBack()}} style={{...mono,fontSize:10,letterSpacing:'0.14em'}}>← All articles</a>
          </div>
        </div>
      </section>
    </main>);
}

window.NewsroomPage = NewsroomPage;
})();
