// fs-views.jsx — section views for Short's Resort Field Station (SRFS)

/* ---- photograph figure: paper frame, filed like everything else ---- */
function Photo({ src, alt, tab, cap, ratio, wide, pos }){
  return (
    <figure className={'fsph'+(wide?' wide':'')}>
      <div className="fsph-im" style={ratio?{aspectRatio:ratio}:null}>
        <img src={A(src)} alt={alt} loading="lazy" decoding="async" style={pos?{objectPosition:pos}:null}/>
        {tab ? <span className="mono fsph-tab">{tab}</span> : null}
      </div>
      {cap ? <figcaption className="mono fsph-cap">{cap}</figcaption> : null}
    </figure>
  );
}

/* ============ FRONTISPIECE (home) ============ */
function Frontispiece({ go }){
  return (
    <div className="view"><div className="sheet">
      <Folio left="Frontispiece" right={FS_SITE.abbr+" · No. 01"} />
      <div style={{display:'grid',gridTemplateColumns:'1.5fr 1fr',gap:'48px',alignItems:'center',marginBottom:'20px'}} className="land-grid">
        <div className="hero-copy">
          <div className="eyebrow" style={{marginBottom:'18px'}}>Short&rsquo;s Resort Field Station · Est. 2026</div>
          <h1 className="disp" style={{fontSize:'clamp(38px,5.6vw,74px)'}}>A Hill Country<br/>Restoration Project</h1>
          <p className="deck" style={{marginTop:'22px'}}>
            A working record of putting one small tract above the Lampasas River back toward what the Hill Country can be —
            its seven sections, the instruments we build to tend them and every plan and line of code we use to do it.
          </p>
          <div style={{display:'flex',gap:'12px',marginTop:'30px',flexWrap:'wrap'}}>
            <NavA className="btn solid" to={'journal'}>Read the Field Journal <span className="arr">→</span></NavA>
            <NavA className="btn" to={'ii'}>Instruments &amp; Infrastructure <span className="arr">→</span></NavA>
          </div>
        </div>
        <div style={{display:'flex',justifyContent:'center'}}>
          <Seal house size={260} style={{filter:'drop-shadow(0 24px 40px #0000002e)'}}/>
        </div>
      </div>


      <Photo src="assets/river-bend-2026.jpg" wide ratio="21/9" pos="center 62%"
        alt="The Lampasas River bending past gravel bars below the property, seen through the oaks"
        tab="The Lampasas &middot; July 2026"
        cap="The river along the south boundary &middot; gravel bars, cut banks and the riparian strip the restoration is built around"/>

      <div className="rule-ink"></div>
      <h2 className="eyebrow" style={{marginBottom:'18px'}}>The Station, in Five Parts</h2>
      <div className="entries">
        {FS_SECTIONS.map(s=>(
          <div className="entry" key={s.id} onClick={()=>go(s.id)}>
            {s.plate && <img className="entry-plateimg" src={A(s.plate)} alt="" aria-hidden="true" loading="lazy" decoding="async" style={{objectPosition:s.platepos||'center'}}/>}
            <div className="no">Section {s.no}</div>
            <h3><NavA to={s.id} style={{color:'inherit'}}>{s.title}</NavA></h3>
            <p>{s.blurb}</p>
            <div className="arr">Open →</div>
          </div>
        ))}
      </div>

      <div className="rule-hair"></div>
      <div style={{display:'flex',gap:'34px',alignItems:'center',flexWrap:'wrap',justifyContent:'center',padding:'10px 0'}}>
        <span className="eyebrow">Supported by</span>
        {FS_FUNDERS.map(f=>(
          <a key={f.k} href={f.url} target="_blank" rel="noopener" style={{display:'flex',alignItems:'center',gap:'12px',color:'var(--ink)'}}>
            <AgencyMark kind={f.mark} size={44}/>
            <div>
              <div className="zilla" style={{fontWeight:900,fontSize:'15px',textTransform:'uppercase',letterSpacing:'0.01em'}}>{f.k}</div>
              <div className="mono" style={{fontSize:'9.5px',letterSpacing:'0.1em',color:'var(--char)',opacity:.75,marginTop:'2px'}}>{f.short} ↗</div>
            </div>
          </a>
        ))}
      </div>

    </div></div>
  );
}

/* ============ THE LAND ============ */
const FS_PLATES = [
  { id:'SR · M-01', file:A('assets/plate-m01-sections.png'), title:'Sections of the Property',
    cap:'The master reference — seven working sections, from the Western Headwaters Savanna down to the River Conservation Strip, each keyed to the care classes below.' },
  { id:'SR · M-02', file:A('assets/plate-m02-tifton.png'), title:'Tifton Replanting · Plan 1',
    cap:'Phase 1 of the replanting sequence: the property edge goes native first — windbreak, filter band and river shade — while the interior stays in Tifton 85 forage. Later phases work inward.' },
  { id:'SR · M-03', file:A('assets/plate-m03-floodplain.png'), title:'Flood Plain Map',
    cap:'FEMA 1% and 0.2% annual-chance flood hazard over the tract, adapted from the Lampasas CAD export of Nov 2025. Informational only — not for insurance determination.' },
  { id:'SR · M-04', file:A('assets/plate-m04-soils-r2.png'), title:'Soils Map',
    cap:'What the ground is made of — five NRCS soil units drawn over the tract from the SSURGO survey. Sunev loam carries the working center, the Seawillow slopes and the Oakalla drainage thread through it, Rumley holds the southern strip and Boerne the river edge.' },
  { id:'SR · M-05', file:A('assets/plate-m05-riparian.png'), title:'Riparian Map',
    cap:'The water\u2019s domain — 2,810 ft of Lampasas River frontage, the seasonal feeder that crosses parcel 11122, and the indicative riparian corridor between them. Corridor shading is drawn at one hundred feet — not a delineation.' },
];

/* the locator chip is wiped blank in the plate — the correct §N is set here.
   Rects are % of the 1425×1747 plate. */
const LOC_BADGE = {
  '1':[16.00,12.31,13.12,8.30], '2':[26.67,36.69,12.49,8.30],
  '3':[34.18,56.21,12.98,8.30], '4':[50.88,62.28,13.12,8.36],
  '5':[63.93,39.15,12.77,8.30], '6':[36.77,81.80,12.91,8.30],
  '7':[6.46,58.61,12.49,8.36]
};
function LocBadge({ n }){
  const r = LOC_BADGE[n]; if(!r) return null;
  return <span className="ld-locbadge" style={{left:r[0]+'%',top:r[1]+'%',width:r[2]+'%',height:r[3]+'%'}}>{'\u00A7'+Number(n)}</span>;
}

function PlateFigure({ p, onOpen }){
  return (
    <figure className="plate-fig" onClick={()=>onOpen(p)}>
      <div className="plate-frame">
        <img src={p.file} alt={p.title+' — mapping plate'} loading="lazy"/>
        <span className="tag plate-tag">{p.id}</span>
        <span className="plate-zoom mono">Enlarge ⤢</span>
      </div>
      <figcaption>
        <div className="mono" style={{fontSize:'10px',letterSpacing:'0.18em',textTransform:'uppercase',color:'var(--oxblood)',marginBottom:'4px'}}>Plate {p.id.split('· ')[1]}</div>
        <div className="zilla" style={{fontWeight:900,fontSize:'19px',textTransform:'uppercase',lineHeight:1,marginBottom:'6px'}}>{p.title}</div>
        <p style={{fontFamily:'Newsreader',fontSize:'14.5px',lineHeight:1.4,color:'var(--char)'}}>{p.cap}</p>
      </figcaption>
    </figure>
  );
}

function LandCard({ s, go }){
  return (
    <div className="land-card" onClick={()=>go('land/'+s.slug)}>
      <div className="land-card-map"><img src={A('assets/land/loc-')+s.n.replace(/^0/,'')+'.png'} alt={'Locator plate — '+s.nm} loading="lazy"/></div>
      <div className="land-card-body">
        <div className="land-card-top">
          <span className="land-card-no" style={{color:s.c}}>{s.n}</span>
          <span className="tag" style={{borderColor:s.c,color:'var(--char)'}}><span className="sw" style={{background:s.c}}></span>{s.care}</span>
        </div>
        <h3 className="land-card-nm"><NavA to={'land/'+s.slug} style={{color:'inherit'}}>{s.nm}</NavA></h3>
        <div className="mono land-card-code">{s.code}{s.ac!=='—' && ' · '+s.ac}</div>
        <p className="land-card-ds">{s.ds}</p>
        <div className="land-card-foot">
          <span className="mono land-card-ph">{s.phase.map(p=>'Phase '+p).join(' · ')}</span>
          <span className="mono land-card-go">Open dossier &rarr;</span>
        </div>
      </div>
    </div>
  );
}

function LandView({ go }){
  const [zoom,setZoom] = React.useState(null);
  React.useEffect(()=>{
    if(!zoom) return;
    const onKey = e=>{ if(e.key==='Escape') setZoom(null); };
    window.addEventListener('keydown',onKey);
    return ()=>window.removeEventListener('keydown',onKey);
  },[zoom]);
  return (
    <div className="view"><div className="sheet">
      <Folio left="Section 02 · The Land" right="Restoration Plan · SR–LRP–01" />
      <SectionHead no="02" title="The Land"
        deck="One hundred and three acres on the Lampasas, 98.78 of them filed in seven working restoration sections. What follows is the plan of record — why each section is being restored, in what order, and what we expect it to become." />

      {/* ---- conditions, folded to a blurb; full text lives on its own page ---- */}
      <div className="ld-brief ld-brief-open">
        <div className="ld-brief-lead">
          <p>None of this is a story about neglect. The land was farmed and grazed by people who worked it hard and followed the best advice available at the time. What we inherited is the honest result of those decisions, plus twenty years of a grass doing exactly what it was planted to do.</p>
          <NavA to="land/conditions" className="mono ld-brief-go">Read the conditions in full &rarr;</NavA>
        </div>
        <ul className="ld-brief-list">
          {FS_LAND_ISSUES.map((it,i)=>(
            <li key={it.k}>
              <span className="mono ld-brief-no">{String(i+1).padStart(2,'0')}</span>
              <span className="mono ld-brief-k">{it.k}</span>
              <span className="ld-brief-t">{it.t}</span>
            </li>
          ))}
        </ul>
      </div>

      {/* ---- the seven sections: one plate, a working register ---- */}
      <SectionAtlas go={go}/>

      {/* ---- the sequence: cascade ---- */}
      <div className="ld-seq">
        <h2 className="eyebrow">The sequence</h2>
        <div className="margin-note" style={{maxWidth:'58ch',margin:'0 0 8px'}}>Three phases, deliberately slow. The first fifteen years only gets us to a functioning young grassland and woodland &mdash; the rest is lifetimes of care.</div>
        {FS_LAND_PHASES.map((ph,i)=>(
          <div className={'ld-ph ld-ph-'+(i+1)} key={ph.p}>
            <span className="ld-ph-ghost">{i+1}</span>
            <div className="ld-ph-meta">
              <span className="mono ld-ph-p">{ph.p}</span>
              <span className="mono ld-ph-when">{ph.when}</span>
            </div>
            <h3 className="ld-ph-t" dangerouslySetInnerHTML={{__html:ph.t}}></h3>
            <p className="ld-ph-d" dangerouslySetInnerHTML={{__html:ph.d}}></p>
            <div className="ld-ph-secs">
              {ph.secs.map(n=>{ const s=FS_LAND.find(x=>x.n===n); return s ? (
                <NavA key={n} to={'land/'+s.slug} className="tag" style={{borderColor:s.c,textDecoration:'none'}}><span className="sw" style={{background:s.c}}></span>{s.n} {s.nm}</NavA>
              ) : null; })}
            </div>
          </div>
        ))}
      </div>

      {/* ---- map atlas: one plate, a working register ---- */}
      <PlateAtlas onOpen={setZoom}/>

      {/* ---- the long watch: imagery time sliders ---- */}
      <LongWatchPanel/>

      <div className="ld-foot">
        <div className="mono ld-foot-k">On the plates</div>
        <div className="ld-foot-cols">
          <p>Boundaries are managerial, not legal; acreages are as recorded in the plan of record. Care classes run riparian &amp; hydrologic, woody &amp; shelter, and agricultural.</p>
          <p>The flood plate is adapted from the Lampasas CAD web map and is informational only — never use it for an insurance determination. The same atlas drives the maps in our manuals and the open GeoJSON under Instruments &amp; Infrastructure.</p>
        </div>
      </div>

      {zoom && <Lightbox src={zoom.file} alt={zoom.title+' — full plate'} onClose={()=>setZoom(null)}/>}
    </div></div>
  );
}

/* ============ THE LONG WATCH · imagery time sliders ============ */
const LW_AERIAL = [
  { y:'1981', src:'NHAP · color-infrared' }, { y:'1996', src:'TOP · black & white' },
  { y:'2004', src:'NAIP · color-infrared' }, { y:'2008', src:'TOP' },
  { y:'2010', src:'NAIP' }, { y:'2012', src:'NAIP' }, { y:'2014', src:'NAIP' },
  { y:'2015', src:'TOP' }, { y:'2016', src:'NAIP' }, { y:'2018', src:'NAIP' },
  { y:'2020', src:'NAIP' }, { y:'2022', src:'NAIP' },
];
const LW_S2 = ['2026-01-03','2026-02-07','2026-02-12','2026-02-22','2026-02-27','2026-03-14',
  '2026-03-19','2026-03-24','2026-03-26','2026-03-29','2026-04-08','2026-05-03','2026-05-13',
  '2026-05-28','2026-06-17','2026-06-27','2026-07-02','2026-07-04','2026-07-22','2026-07-27','2026-08-06'];
const LW_IMGV = '58';
function LwSlider({ title, frames, srcFor, labelFor }){
  const [i,setI] = React.useState(frames.length-1);
  React.useEffect(()=>{ frames.forEach(f=>{ const im=new Image(); im.src=srcFor(f)+'?v='+LW_IMGV; }); },[]);
  const f = frames[i];
  return (
    <figure style={{border:'1px solid var(--ink)',background:'var(--bone)',margin:0,boxShadow:'none'}}>
      <div className="mono" style={{fontSize:'9.5px',letterSpacing:'0.18em',textTransform:'uppercase',background:'var(--ink)',color:'var(--bone)',padding:'9px 14px',display:'flex',justifyContent:'space-between'}}>
        <span>{title}</span><span>{labelFor(f)}</span>
      </div>
      <img src={srcFor(f)+'?v='+LW_IMGV} alt={title+' — '+labelFor(f)} style={{width:'100%',aspectRatio:'1400/1559',display:'block',borderBottom:'1px solid var(--ink)',background:'var(--paper-3)',boxShadow:'none'}}/>
      <figcaption style={{padding:'10px 14px 12px'}}>
        <input type="range" min="0" max={frames.length-1} value={i} onChange={e=>setI(+e.target.value)}
          style={{width:'100%',accentColor:'var(--oxblood)',cursor:'pointer'}} aria-label={title+' timeline'}/>
        <div className="mono" style={{display:'flex',justifyContent:'space-between',fontSize:'9px',letterSpacing:'0.1em',color:'var(--char)',opacity:.7}}>
          <span>{labelFor(frames[0])}</span><span>{labelFor(frames[frames.length-1])}</span>
        </div>
      </figcaption>
    </figure>
  );
}
function LongWatchPanel(){
  return (
    <div style={{margin:'34px 0'}}>
      <h2 className="eyebrow">The Long Watch</h2>
      <p className="prose" style={{maxWidth:'72ch',margin:'8px 0 18px'}}>Two ways of watching the same ground. The aerial record is every federal and state survey flight over the tract since 1981 — one pass every few years, sharp enough to see fence lines. The satellite record is this year, a usable frame every week or two. Drag the sliders and the place changes under you: forty years of working ground on the left, one season of restoration on the right.</p>
      <div className="grid g2" style={{gap:'20px',alignItems:'start'}}>
        <LwSlider title="Aerial · 1981–2022" frames={LW_AERIAL}
          srcFor={f=>A('assets/longwatch/aerial/'+f.y+'.jpg')}
          labelFor={f=>f.y+' · '+f.src}/>
        <LwSlider title="Satellite · 2026 season" frames={LW_S2}
          srcFor={d=>A('assets/longwatch/s2/'+d+'.jpg')}
          labelFor={d=>d}/>
      </div>
      <p className="mono" style={{fontSize:'9.5px',letterSpacing:'0.06em',color:'var(--char)',opacity:.8,margin:'10px 0 0',lineHeight:1.6,maxWidth:'90ch'}}>Left: survey flights by USDA (NAIP), Texas (TOP) and USGS (NHAP); the early frames are color-infrared — red is growing vegetation — and 1996 is black and white. Right: Sentinel-2, ten-meter pixels smoothed for the eye — a pulse, not a photograph; the pale ring in the last frames is the first burndown coming through. The bone line is the tract.</p>
    </div>
  );
}

/* ============ SECTION ATLAS · a viewer, driven by a grid ============ */
function SectionAtlas({ go }){
  const [sel,setSel] = React.useState(0);
  const s = FS_LAND[sel];
  const num = s.n.replace(/^0/,'');
  // decode every plate and photo once, so moving across the grid never flashes
  React.useEffect(()=>{
    FS_LAND.forEach(x=>{
      const k = x.n.replace(/^0/,'');
      [A('assets/land/loc-'+k+'.png'), A('assets/land/')+x.img+'-existing.jpg', A('assets/land/')+x.img+'-goal.jpg']
        .forEach(u=>{ const im=new Image(); im.src=u; });
    });
  },[]);
  return (
    <div className="ld-atlas ld-sect">
      <div className="ld-atlas-head">
        <h2 className="eyebrow">The Seven Sections</h2>
        <div className="margin-note" style={{maxWidth:'56ch'}}>Each section carries a file number and an owning department. Move across the seven below to change the plate and the condition pair &mdash; the dossier link opens the full record.</div>
      </div>

      {/* the viewer: locator left, existing / intended right */}
      <div className="ld-sect-view">
        <figure className="ld-sect-loc">
          <span className="ld-locwrap">
            <img src={A('assets/land/loc-'+num+'.png')} alt={'Locator plate for section '+s.n+' — '+s.nm}/>
            <LocBadge n={num}/>
          </span>
          <span className="mono ld-atlas-tab" style={{borderColor:s.c}}><span className="sw" style={{background:s.c}}></span>{s.code}</span>
          <figcaption className="mono">Locator &middot; §{num} within the tract</figcaption>
        </figure>
        <div className="ld-sect-pair">
          <div className="ld-sect-existing">
            <figure>
              <img src={A('assets/land/')+s.img+'-existing.jpg'} alt={'Existing condition — '+s.nm}/>
              <span className="mono ld-sect-k">Existing</span>
              <figcaption className="mono">Photographed on the property</figcaption>
            </figure>
            <NavA to={'land/'+s.slug} className="mono ld-r-go">Open dossier §{s.n} &rarr;</NavA>
          </div>
          <figure>
            <img src={A('assets/land/')+s.img+'-goal.jpg'} alt={'Intended condition — '+s.nm}/>
            <span className="mono ld-sect-k ld-sect-k-b">Intended</span>
            <figcaption className="mono">Visualization &middot; not a photograph</figcaption>
          </figure>
        </div>
        <div className="ld-sect-read">
          <div className="mono ld-sect-read-k" style={{color:s.c}}>§{s.n} · {s.care} · {s.ac} · Phase {s.phase.join(' & ')}</div>
          <h3 className="ld-sect-read-nm">{s.nm}</h3>
          <p>{s.ds}</p>
        </div>
      </div>

      {/* the seven, as a grid */}
      <div className="ld-sect-grid">
        {FS_LAND.map((x,i)=>(
          <button key={x.n} type="button" className={'ld-sc'+(i===sel?' on':'')}
            onMouseEnter={()=>setSel(i)} onFocus={()=>setSel(i)} onClick={()=>setSel(i)}>
            <span className="ld-sc-bar" style={{background:x.c}}></span>
            <span className="ld-sc-no">{x.n}</span>
            <span className="ld-sc-nm">{x.nm}</span>
            <span className="mono ld-sc-meta">{x.care} · {x.ac}</span>
          </button>
        ))}
      </div>
    </div>
  );
}

/* ============ MAP ATLAS · a viewer, driven by a grid ============ */
function PlateAtlas({ onOpen }){
  const [sel,setSel] = React.useState(0);
  const p = FS_PLATES[sel];
  // decode every plate once, so moving across the grid never flashes
  React.useEffect(()=>{
    FS_PLATES.forEach(x=>{ const im=new Image(); im.src=x.file; });
  },[]);
  return (
    <div className="ld-atlas ld-atlas-plates">
      <div className="ld-atlas-head">
        <h2 className="eyebrow">Map Atlas</h2>
        <div className="margin-note" style={{maxWidth:'56ch'}}>Five plates of record. North is up on every one; lines are drawn from our own field plan, not a licensed survey &mdash; if one looks wrong, it probably is. Tell us. Move across the five below to change the plate.</div>
      </div>

      {/* the viewer: plate left, the record right */}
      <div className="ld-plate-view">
        <figure className="ld-plate-loc ld-atlas-wide" onClick={()=>onOpen(p)}>
          <img key={p.id} src={p.file} alt={p.title+' — mapping plate'}/>
          <span className="mono ld-atlas-tab">{p.id}</span>
          <span className="mono ld-atlas-zoom">Enlarge &#8598;</span>
          <figcaption className="mono">Plate &middot; {sel+1} of {FS_PLATES.length}</figcaption>
        </figure>
        <div className="ld-sect-read">
          <div className="mono ld-sect-read-k">Plate {p.id}</div>
          <h3 className="ld-sect-read-nm">{p.title}</h3>
          <p>{p.cap}</p>
          <button className="mono ld-r-go" onClick={()=>onOpen(p)}>View full plate &rarr;</button>
        </div>
      </div>

      {/* the five, as a grid */}
      <div className="ld-sect-grid ld-plate-grid">
        {FS_PLATES.map((x,i)=>(
          <button key={x.id} type="button" className={'ld-sc'+(i===sel?' on':'')}
            onMouseEnter={()=>setSel(i)} onFocus={()=>setSel(i)} onClick={()=>setSel(i)}>
            <span className="ld-sc-bar" style={{background:'var(--ink)'}}></span>
            <span className="ld-sc-no">{x.id.split('M-')[1]}</span>
            <span className="ld-sc-nm">{x.title}</span>
            <span className="mono ld-sc-meta">Plate {x.id}</span>
          </button>
        ))}
      </div>
    </div>
  );
}

/* ============ THE CONDITIONS · its own page ============ */
function LandConditions({ go }){
  return (
    <div className="view"><div className="sheet">
      <Folio left="Section 02 · The Land · Conditions" right="Restoration Plan · SR–LRP–01" />
      <div className="mono dos-back"><NavA to="land">&larr; The Land</NavA></div>
      <SectionHead no="02" title="Why the Ground Needs Work"
        deck="None of this is a story about neglect. The land was farmed and grazed by people who worked it hard and followed the best advice available at the time. What we inherited is the honest result of those decisions, plus twenty years of a grass doing exactly what it was planted to do." />
      <div className="ld-cond-page">
        {FS_LAND_ISSUES.map((it,i)=>(
          <div className="ld-cp" key={it.k}>
            <div className="ld-cp-side">
              <span className="ld-cp-no">{String(i+1).padStart(2,'0')}</span>
              <span className="mono ld-cp-k">{it.k}</span>
            </div>
            <div className="ld-cp-body">
              <h3 className="ld-cp-t">{it.t}</h3>
              <p className="ld-cp-d">{it.d}</p>
            </div>
          </div>
        ))}
      </div>
      <div className="ld-foot">
        <div className="mono ld-foot-k">What follows from this</div>
        <div className="ld-foot-cols">
          <p>Each condition is answered somewhere in the plan of record — section by section, in a deliberate order. The sequence and the seven sections are on <NavA to="land">The Land</NavA>.</p>
          <p>Nothing here is a finding against the people who farmed this ground. It is a starting inventory, written down so the work can be measured against it.</p>
        </div>
      </div>
    </div></div>
  );
}

/* ============ SECTION DOSSIER ============ */
function SectionDossier({ slug, go }){
  const s = FS_LAND.find(x=>x.slug===slug);
  const [zoom,setZoom] = React.useState(null);
  React.useEffect(()=>{
    if(!zoom) return;
    const onKey = e=>{ if(e.key==='Escape') setZoom(null); };
    window.addEventListener('keydown',onKey);
    return ()=>window.removeEventListener('keydown',onKey);
  },[zoom]);
  if(!s) return <NotFound go={go}/>;
  const i = FS_LAND.indexOf(s);
  const prev = FS_LAND[(i-1+FS_LAND.length)%FS_LAND.length];
  const next = FS_LAND[(i+1)%FS_LAND.length];
  const num = s.n.replace(/^0/,'');
  return (
    <div className="view"><div className="sheet">
      <Folio left={'Section 02 · The Land · §'+num} right={s.code} />
      <div className="mono dos-back"><NavA to="land">&larr; The Land</NavA></div>

      <div className="dos-head">
        <div className="dos-no" style={{color:s.c}}>{num}</div>
        <div>
          <div className="mono dos-filed" style={{color:s.c}} dangerouslySetInnerHTML={{__html:'Filed under · '+s.dept+' · '+s.code}}></div>
          <h1 className="dos-nm">{s.nm}</h1>
          <div className="dos-sub" dangerouslySetInnerHTML={{__html:s.sub}}></div>
        </div>
        <div className="dos-seal"><Seal dept={DEPT_BY[s.deptKey]} size={104} title=""/></div>
      </div>

      <div className="dos-cols">
        <div>
          <div className="dos-spec">
            {s.spec.map(r=>(
              <div className="dos-spec-row" key={r[0]}>
                <span className="mono dos-spec-k">{r[0]}</span>
                <span className="dos-spec-v" dangerouslySetInnerHTML={{__html:r[1]}}></span>
              </div>
            ))}
          </div>

          <h2 className="dos-h">{s.care==='Ag' ? 'Why this section stays in production' : 'Why this section is restored'}</h2>
          <p className="dos-why" dangerouslySetInnerHTML={{__html:s.why}}></p>

          <div className="dos-char" style={{borderColor:s.c}}>
            <span className="mono">Landscape Character</span>
            <div dangerouslySetInnerHTML={{__html:s.character}}></div>
          </div>
        </div>

        <aside className="dos-aside">
          <figure className="dos-loc" onClick={()=>setZoom({src:A('assets/land/loc-')+num+'.png',t:'Locator — '+s.nm})}>
            <span className="ld-locwrap">
              <img src={A('assets/land/loc-')+num+'.png'} alt={'Locator plate — '+s.nm} loading="lazy"/>
              <LocBadge n={num}/>
            </span>
            <figcaption className="mono">Locator &middot; §{num} within the tract</figcaption>
          </figure>
          <div className="note"><span className="k">Care class</span>
            {s.care==='Riparian' ? 'Riparian and hydrologic — managed first for water: bank hold, infiltration, sediment capture.'
              : s.care==='Woody' ? 'Woody and shelter — managed for structure: canopy, windbreak, cover for birds.'
              : 'Agricultural — kept in working use, with native margins and a scheduled conversion.'}
          </div>
          <div className="note"><span className="k">In the sequence</span>
            {s.phase.map(p=>'Phase '+p).join(' and ')} &mdash; see the sequence on <NavA to="land">The Land</NavA>.
          </div>
        </aside>
      </div>

      <h2 className="eyebrow" style={{margin:'44px 0 14px'}}>Existing &amp; Intended Condition</h2>
      <div className="dos-pair">
        <figure className="dos-plate" onClick={()=>setZoom({src:A('assets/land/')+s.img+'-existing.jpg',t:'Existing — '+s.nm})}>
          <img src={A('assets/land/')+s.img+'-existing.jpg'} alt={'Existing condition — '+s.nm} loading="lazy"/>
          <span className="mono dos-plate-k">Plate {num}·A &mdash; Existing</span>
          <figcaption className="mono">Photographed on the property</figcaption>
        </figure>
        <figure className="dos-plate" onClick={()=>setZoom({src:A('assets/land/')+s.img+'-goal.jpg',t:'Intended — '+s.nm})}>
          <img src={A('assets/land/')+s.img+'-goal.jpg'} alt={'Intended condition — '+s.nm} loading="lazy"/>
          <span className="mono dos-plate-k" style={{borderColor:'var(--oxblood)',color:'var(--oxblood)'}}>Plate {num}·B &mdash; Intended</span>
          <figcaption className="mono">AI-generated visualization &middot; not a photograph</figcaption>
        </figure>
      </div>
      <div className="margin-note dos-disclose">The intended-condition plates are AI-generated images, made from our own site photographs and our written description of the target vegetation. They are a design aid for what this ground could look like decades on, not a record of anything that exists. The existing-condition plate beside each one is a real photograph taken here.</div>

      <div className="dos-nav">
        <NavA to={'land/'+prev.slug} className="mono dos-nav-a">&larr; §{prev.n.replace(/^0/,'')} {prev.nm}</NavA>
        <NavA to={'land/'+next.slug} className="mono dos-nav-a" style={{textAlign:'right'}}>§{next.n.replace(/^0/,'')} {next.nm} &rarr;</NavA>
      </div>

      {zoom && <Lightbox src={zoom.src} alt={zoom.t} onClose={()=>setZoom(null)}/>}
    </div></div>
  );
}

/* ============ FIELD JOURNAL ============ */
function catName(id){ const c=FS_JOURNAL_CATS.find(x=>x.id===id); return c?c.t:''; }
/* entry card image — driven off the entry's own fig/figalt fields (single source of truth) */
function EntryImg({ e, h }){
  const dept = (window.FS_SEALS && FS_SEALS.DEPTS||[]).find(d=>d.key===e.deptKey);
  return (
    <div className="jimg" style={{height:h}}>
      {e.fig
        ? <img src={e.fig} alt={e.figalt||''} loading="lazy" decoding="async"
            style={{objectFit:e.figfit||'cover',objectPosition:e.figpos||'center',padding:e.figfit==='contain'?'10px':0}}/>
        : <div className="jimg-seal">{dept ? <Seal dept={dept} size={Math.min(h-24,132)} title=""/> : null}</div>}
    </div>
  );
}
function FeatureCard({ e, go }){
  return (
    <div onClick={()=>go('journal/'+e.slug)} className="feat"
      style={{border:'1.5px solid var(--ink)',background:'var(--paper)',cursor:'pointer',display:'flex',flexDirection:'column',transition:'.18s'}}>
      <EntryImg e={e} h={208}/>
      <div style={{padding:'22px 26px 26px',display:'flex',flexDirection:'column',gap:'12px',flex:1}}>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',gap:'12px'}}>
        <span className="tag" style={{color:'var(--oxblood)',borderColor:'var(--oxblood)'}}><span className="sw" style={{background:'var(--oxblood)'}}></span>{catName(e.cat)}</span>
      </div>
      <div className="mono" style={{fontSize:'10px',letterSpacing:'0.18em',textTransform:'uppercase',color:'var(--char)',opacity:.7}}>{e.date} · {e.folio}</div>
      <h3 style={{fontFamily:'Zilla Slab',fontWeight:900,fontSize:'clamp(24px,2.7vw,34px)',lineHeight:0.98,textTransform:'uppercase',letterSpacing:'-0.01em',marginTop:'auto'}}><NavA to={'journal/'+e.slug} style={{color:'inherit'}}>{e.title}</NavA></h3>
      <p style={{fontFamily:'Newsreader',fontStyle:'italic',fontSize:'16px',color:'var(--char)',lineHeight:1.4}}>{e.deck}</p>
      <div className="mono" style={{fontSize:'11px',letterSpacing:'0.14em',textTransform:'uppercase',color:'var(--oxblood)'}}>Read the dispatch &rarr;</div>
      </div>
    </div>
  );
}
function JournalView({ go }){
  // derived, not hand-flagged: newest two by publish date, so this cannot drift
  const featured = [...FS_JOURNAL].sort((a,b)=> b.date.localeCompare(a.date)).slice(0,2);
  return (
    <div className="view"><div className="sheet">
      <Folio left="Section 01 · Field Journal" right={FS_JOURNAL.length+' entries on file'} />
      <SectionHead no="01" title="Field Journal"
        deck="A field desk, not a feed. The latest dispatches sit up top; everything else is filed by section below." />

      <div style={{display:'flex',alignItems:'baseline',gap:'16px',margin:'34px 0 14px',flexWrap:'wrap'}}>
        <h2 className="eyebrow" style={{margin:0}}>Latest Dispatches</h2>
        <a className="mono" href="/feed.xml" style={{marginLeft:'auto',fontSize:'10px',letterSpacing:'0.14em',textTransform:'uppercase',color:'var(--char)',borderBottom:'1px solid var(--rule)',paddingBottom:'2px'}}>Subscribe · RSS ↗</a>
      </div>
      <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:'16px'}} className="feat-grid">
        {featured.map(e=><FeatureCard key={e.slug} e={e} go={go}/>)}
      </div>

      {FS_JOURNAL_CATS.map(c=>{
        const items = FS_JOURNAL.filter(e=>e.cat===c.id);
        if(!items.length) return null;
        return (
          <div key={c.id} style={{marginTop:'46px'}}>
            <div style={{display:'flex',alignItems:'baseline',gap:'14px',borderBottom:'2px solid var(--ink)',paddingBottom:'10px',flexWrap:'wrap'}}>
              <h2 style={{fontFamily:'Zilla Slab',fontWeight:900,fontSize:'22px',textTransform:'uppercase',letterSpacing:'0.01em',margin:0}}>{c.t}</h2>
              <span style={{fontFamily:'Newsreader',fontStyle:'italic',fontSize:'14px',color:'var(--char)'}}>{c.d}</span>
              <span className="fol" style={{marginLeft:'auto'}}>{items.length} on file</span>
            </div>
            {items.map(e=>(
              <div className="lrow lrow-img" key={e.slug} onClick={()=>go('journal/'+e.slug)}>
                <EntryImg e={e} h={92}/>
                <div className="ld">{e.date}<br/>{e.dept}</div>
                <div className="lt"><h3><NavA to={'journal/'+e.slug} style={{color:'inherit'}}>{e.title}</NavA></h3><p>{e.deck}</p></div>
                <div className="lm">
                  <span className="fol">{e.folio} · Read →</span>
                </div>
              </div>
            ))}
          </div>
        );
      })}
    </div></div>
  );
}
function SeedMixGallery(){
  const IM = window.FS_SEEDMIX_IMG||{}, SL = window.FS_SEEDMIX_SLUG||{};
  const rows = FS_SEEDMIX.map(s=>({...s, slug:SL[s.lat], im:IM[SL[s.lat]]})).filter(r=>r.im);
  if(!rows.length) return null;
  return (
    <div className="smx">
      <div className="smx-head">
        <div>
          <div className="mono smx-no">Plate S&middot;01</div>
          <div className="zilla smx-t">What Goes In The Ground</div>
        </div>
        <div className="mono smx-sub">{rows.length} of {FS_SEEDMIX.length} species pictured<br/>Reference images &mdash; not made on this property</div>
      </div>
      <div className="smx-grid">
        {rows.map(r=>(
          <figure className="smx-c" key={r.slug}>
            <img src={A(r.im.f)} alt={r.sp+' \u2014 '+r.lat} loading="lazy" decoding="async" style={r.im.kind==='plate'?{objectFit:'contain',background:'var(--bone)'}:null}/>
            <figcaption>
              <span className="mono smx-pct"><span>{r.pct}%</span><span className="smx-kind">{r.t==='G'?'Grass':'Forb'}</span></span>
              <span className="smx-nm">{r.sp}</span>
              <span className="smx-lat">{r.lat}</span>
              <span className="mono smx-cr">{r.im.kind==='plate'?'Illustration':'Photo'} &middot; {r.im.a||'Unattributed'} &middot; {r.im.s ? <a href={r.im.s} target="_blank" rel="noopener">{r.im.l} &middot; {r.im.src||'Commons'} &#8599;</a> : r.im.l}</span>
            </figcaption>
          </figure>
        ))}
      </div>
      <div className="mono smx-foot">Images from Wikimedia Commons and iNaturalist, reused under the licence noted on each &middot; mix designed by Texas Parks &amp; Wildlife under PUB &middot; seed from <a href={FS_SEED_PROVIDER.url} target="_blank" rel="noopener">{FS_SEED_PROVIDER.k}</a></div>
    </div>
  );
}
function WetMixGallery(){
  const IM = window.FS_SEEDMIX_IMG||{}, CR = window.FS_CREDITS||{};
  const rows = (window.FS_WETMIX||[]).map(s=>{
    let im = IM[s.slug];
    if(!im && s.credit && CR[s.credit] && CR[s.credit].form){
      const f = CR[s.credit].form;
      im = { f:String(f.file).replace(/^\/+/,''), a:f.artist, l:f.license, s:f.source };
    }
    return {...s, im};
  }).filter(r=>r.im);
  if(!rows.length) return null;
  return (
    <div className="smx">
      <div className="smx-head">
        <div>
          <div className="mono smx-no">Plate S&middot;02</div>
          <div className="zilla smx-t">The Wetland Pocket Palette</div>
        </div>
        <div className="mono smx-sub">{rows.length} of {(window.FS_WETMIX||[]).length} species pictured<br/>Reference images &mdash; not made on this property</div>
      </div>
      <div className="smx-grid">
        {rows.map(r=>(
          <figure className="smx-c" key={r.slug}>
            <img src={A(r.im.f)} alt={r.sp+' — '+r.lat} loading="lazy" decoding="async" style={r.im.kind==='plate'?{objectFit:'contain',background:'var(--bone)'}:null}/>
            <figcaption>
              <span className="mono smx-pct"><span>{r.zone}</span><span className="smx-kind">{r.kind}</span></span>
              <span className="smx-nm">{r.sp}</span>
              <span className="smx-lat">{r.lat}</span>
              <span className="mono smx-cr">{r.im.kind==='plate'?'Illustration':'Photo'} &middot; {r.im.a||'Unattributed'} &middot; {r.im.s ? <a href={r.im.s} target="_blank" rel="noopener">{r.im.l} &middot; {r.im.src||'Commons'} &#8599;</a> : r.im.l}</span>
            </figcaption>
          </figure>
        ))}
      </div>
      <div className="mono smx-foot">Images from Wikimedia Commons and iNaturalist, reused under the licence noted on each &middot; palette checked against records within ~40 miles &middot; planted as plugs and dormant stock, not drilled</div>
    </div>
  );
}
function SeedMixPlate(){
  const grasses = FS_SEEDMIX.filter(s=>s.t==='G');
  const forbs = FS_SEEDMIX.filter(s=>s.t==='F');
  const total = Math.round(FS_SEEDMIX.reduce((a,s)=>a+s.pct,0));
  const Col = ({title, list, c}) => (
    <div>
      <div className="mono" style={{fontSize:'9.5px',letterSpacing:'0.16em',textTransform:'uppercase',color:'var(--char)',padding:'10px 14px',borderBottom:'1px solid var(--rule)',display:'flex',justifyContent:'space-between'}}>
        <span><span style={{display:'inline-block',width:'8px',height:'8px',background:c,transform:'rotate(45deg)',marginRight:'8px'}}></span>{title}</span>
        <span>Blend %</span>
      </div>
      {list.map(s=>{
        const im = (window.FS_SEEDMIX_IMG||{})[(window.FS_SEEDMIX_SLUG||{})[s.lat]];
        return (
        <div key={s.lat} style={{display:'grid',gridTemplateColumns:'40px 1fr auto',gap:'11px',padding:'8px 14px',borderTop:'1px solid var(--rule-soft)',alignItems:'center'}}>
          {im ? <img src={A(im.f)} alt="" loading="lazy" style={{width:'40px',height:'40px',objectFit:'cover',border:'1px solid var(--ink)',display:'block',background:'var(--paper-3)'}}/> : <span></span>}
          <div><div style={{fontFamily:'Newsreader',fontSize:'15px',lineHeight:1.2}}>{s.sp}</div><div className="mono" style={{fontSize:'9px',letterSpacing:'0.04em',fontStyle:'italic',color:'var(--char)',opacity:.7}}>{s.lat}</div></div>
          <div className="zilla" style={{fontWeight:900,fontSize:'16px'}}>{s.pct}</div>
        </div>
      );})}
    </div>
  );
  return (
    <div style={{border:'1.5px solid var(--ink)',margin:'22px 0',background:'var(--bone)'}}>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-end',gap:'12px',padding:'14px 16px',borderBottom:'2px solid var(--ink)'}}>
        <div><div className="mono" style={{fontSize:'10px',letterSpacing:'0.2em',textTransform:'uppercase',color:'var(--oxblood)'}}>Plate S·01</div><div className="zilla" style={{fontWeight:900,fontSize:'22px',textTransform:'uppercase',lineHeight:1,marginTop:'4px'}}>Native Seed Mix</div></div>
        <div className="mono" style={{fontSize:'9.5px',letterSpacing:'0.12em',textTransform:'uppercase',color:'var(--char)',textAlign:'right',opacity:.8}}>Designed by TPWD · PUB<br/>{FS_SEEDMIX.length} species · {total}% blend</div>
      </div>
      <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:'0'}} className="seedcols">
        <div style={{borderRight:'1px solid var(--rule)'}}><Col title="Grasses" list={grasses} c="var(--sage-deep)"/></div>
        <Col title="Wildflowers & Legumes" list={forbs} c="var(--ochre-deep)"/>
      </div>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',gap:'12px',padding:'12px 16px',borderTop:'2px solid var(--ink)',flexWrap:'wrap'}}>
        <span className="mono" style={{fontSize:'10px',letterSpacing:'0.12em',textTransform:'uppercase',color:'var(--char)'}}>Total &middot; {total}% PLS blend</span>
        <span className="mono" style={{fontSize:'10px',letterSpacing:'0.1em',textTransform:'uppercase'}}>Seed · <a href={FS_SEED_PROVIDER.url} target="_blank" rel="noopener">{FS_SEED_PROVIDER.k}, {FS_SEED_PROVIDER.place} ↗</a></span>
      </div>
    </div>
  );
}

function linkify(text){
  if(typeof text!=='string') return text;
  const parts=[]; let last=0, i=0, m;
  const re=/\[([^\]]+)\]\(([^)]+)\)|((?:https?:\/\/|www\.)[^\s)]+|[a-z0-9-]+(?:\.[a-z0-9-]+)*\.(?:com|org|gov|net|edu)(?:\/[^\s)]*)?)/gi;
  while((m=re.exec(text))){
    if(m.index>last) parts.push(text.slice(last,m.index));
    if(m[1]){ parts.push(<a key={i++} href={m[2]} target="_blank" rel="noopener">{m[1]}</a>); }
    else { const u=m[3], href=u.startsWith('http')?u:'https://'+u; parts.push(<a key={i++} href={href} target="_blank" rel="noopener">{u.replace(/^https?:\/\//,'')}</a>); }
    last=re.lastIndex;
  }
  if(last<text.length) parts.push(text.slice(last));
  return parts;
}
function Block({ b }){
  if(b.t==='p') return <p className={b.cls==='dropcap'?'dropcap':''}>{linkify(b.c)}</p>;
  if(b.t==='h3') return <h2>{b.c}</h2>;
  if(b.t==='note') return <div className="note"><span className="k">{b.k}</span>{linkify(b.c)}</div>;
  if(b.t==='seedmix') return <SeedMixGallery/>;
  if(b.t==='wetmix') return <WetMixGallery/>;
  if(b.t==='plate') return (
    <figure className={'entry-plate'+(b.wide?' wide':'')}>
      <img src={b.src} alt={b.alt||b.c} loading="lazy"/>
      <figcaption className="mono">
        <b>{b.k} · </b>{b.c}
      </figcaption>
    </figure>
  );
  if(b.t==='spec') return (
    <div style={{border:'1px solid var(--ink)',margin:'18px 0'}}>
      <div className="mono" style={{fontSize:'9.5px',letterSpacing:'0.18em',textTransform:'uppercase',background:'var(--ink)',color:'var(--bone)',padding:'9px 14px'}}>{b.k}</div>
      {b.rows.map((r,i)=>(
        <div key={i} style={{display:'grid',gridTemplateColumns:'240px 1fr',gap:'14px',padding:'10px 14px',borderTop:i?'1px solid var(--rule-soft)':'none'}} className="specrow">
          <span className="mono" style={{fontSize:'10px',letterSpacing:'0.1em',textTransform:'uppercase',color:'var(--char)'}}>{r[0]}</span>
          <span style={{fontFamily:'Newsreader',fontSize:'15px'}}>{r[1]}</span>
        </div>
      ))}
    </div>
  );
  if(b.t==='list') return <ul style={{listStyle:'none',margin:'6px 0 20px'}}>{b.c.map((li,i)=>(
    <li key={i} className="mono" style={{fontSize:'13px',letterSpacing:'0.03em',padding:'9px 0 9px 22px',borderTop:'1px solid var(--rule-soft)',position:'relative',lineHeight:1.5}}>
      <span style={{position:'absolute',left:'3px',top:'15px',width:'7px',height:'7px',background:'var(--ink)',transform:'rotate(45deg)'}}></span>{li}
    </li>))}</ul>;
  return null;
}
function JournalEntry({ slug, go }){
  const e = FS_JOURNAL.find(x=>x.slug===slug);
  if(!e) return <NotFound go={go}/>;
  const idx = FS_JOURNAL.indexOf(e);
  const prev = FS_JOURNAL[idx+1], next = FS_JOURNAL[idx-1];
  return (
    <div className="view"><div className="sheet">
      <Folio left={'Section 01 · '+e.folio} right="Field Journal" />
      <article>
      <div className="entry-head">
        <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',gap:'20px'}}>
          <div className="eyebrow">Field Entry {e.folio}</div>
        </div>
        <h1 className="disp" style={{fontSize:'clamp(34px,5vw,60px)',maxWidth:'20ch'}}>{e.title}</h1>
        <div className="byline">
          <time dateTime={e.date.split(' · ').join('-')}>{e.date}</time><span className="sep"></span>
          <span>{catName(e.cat)}</span><span className="sep"></span>
          <span>{e.dept}</span>
        </div>
        <p className="deck" style={{marginTop:'6px'}}>{e.deck}</p>
      </div>
      <div className="figplate">
        {e.fig && <img src={e.fig} alt={e.figalt||e.title} style={{width:'100%',height:'100%',objectFit:'cover',display:'block'}}/>}
        <span className="cap">{e.figcap}</span>
      </div>
      <div className="prose" style={{marginTop:'26px'}}>
        {e.body.map((b,i)=><Block key={i} b={b}/>)}
      </div>
      </article>
      <div className="rule-hair"></div>
      <div style={{display:'flex',justifyContent:'space-between',gap:'16px',flexWrap:'wrap'}}>
        <NavA className="btn" to={'journal'}>← All entries</NavA>
        <div style={{display:'flex',gap:'10px'}}>
          {prev && <NavA className="btn" to={'journal/'+prev.slug}>← {prev.folio}</NavA>}
          {next && <NavA className="btn" to={'journal/'+next.slug}>{next.folio} →</NavA>}
        </div>
      </div>
    </div></div>
  );
}

/* ============ INSTRUMENTS & INFRASTRUCTURE ============ */
function InstrumentsView({ go }){
  return (
    <div className="view"><div className="sheet">
      <Folio left="Section 03 · Instruments & Infrastructure" right="Built · In Progress · Planned" />
      <SectionHead no="03" title={<span>Instruments &amp;<br/>Infrastructure</span>}
        deck="The things we build to do the work — from the propagation bench to the bioacoustic listening node — and the open software that runs them. Not inventions; a small-scale kit of parts a family holding can actually build and maintain." />

      {/* roadmap of builds */}
      <h2 className="eyebrow" style={{margin:'36px 0 14px'}}>The Builds</h2>
      <div className="margin-note" style={{maxWidth:'64ch',margin:'0 0 20px'}}>Fig. 03&middot;1 &mdash; Status marks read built, in progress or planned. Nothing here is a mockup we can&rsquo;t hand you the plans for.</div>
      <div className="grid g3" style={{marginBottom:'8px'}}>
        {FS_II_ITEMS.map(it=>(
          <div className="card" key={it.k}>
            <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',gap:'10px',marginBottom:'12px'}}>
              <span className="tag" style={{color:FS_II_STATUS[it.status],borderColor:FS_II_STATUS[it.status]}}><span className="sw" style={{background:FS_II_STATUS[it.status]}}></span>{it.status}</span>
            </div>
            <div className="mono" style={{fontSize:'9.5px',letterSpacing:'0.16em',color:'var(--char)',opacity:.6,marginBottom:'4px'}}>{it.k}</div>
            <h4>{it.t}</h4>
            <div className="cd" style={{marginTop:'8px'}}>{it.d}</div>
            {it.demo && <button className="btn" style={{marginTop:'16px'}} onClick={()=>{ const el=document.getElementById('flagship'); if(el) el.scrollIntoView({behavior:'smooth'}); }}>About the flagship ↓</button>}
          </div>
        ))}
      </div>

      {/* flagship: propagation unit — live board returns when the controller API is online */}
      <div id="flagship" className="rule-ink"></div>
      <div style={{display:'flex',alignItems:'center',gap:'14px',marginBottom:'14px',flexWrap:'wrap'}}>
        <div>
          <div className="eyebrow">Flagship · Built</div>
          <div className="zilla" style={{fontWeight:900,fontSize:'26px',textTransform:'uppercase',lineHeight:1}}>Propagation Unit No. 01</div>
        </div>
      </div>
      <p className="prose" style={{maxWidth:'70ch'}}>The bench is built, and its controller software is feature-complete and verified in simulation &mdash; bringing it up on the unit&rsquo;s own Piconium is the work underway now. Its live board — bench status, reservoir level, per-bay moisture and temperature — will publish here once the controller is on the bench and its API is online. Until it does, this page shows no readings: we would rather show nothing than dress the sheet with placeholder numbers. The build itself is documented in <NavA to={'journal/propagation-unit-1'}>the Field Journal</NavA>.</p>

      {/* software / open source */}
      <div className="rule-ink"></div>
      <h2 className="eyebrow" style={{marginBottom:'8px'}}>The Software · Open Source</h2>
      <div className="repo">
        <span className="tag" style={{background:'var(--ink)',color:'var(--bone)',border:'none'}}>REPO</span>
        <a className="path" href={'https://'+FS_REPO.path} target="_blank" rel="noopener">{FS_REPO.path} ↗</a>
        <div className="meta"><span>License · {FS_REPO.license}</span><span>{FS_REPO.lang}</span></div>
      </div>
      <div style={{display:'grid',gridTemplateColumns:'1.1fr 1fr',gap:'40px',marginTop:'26px'}} className="land-grid">
        <div>
          {FS_MODULES.map(m=>(
            <div className="modrow" key={m.k}>
              <div className="mk-col"><span className="mk">{m.k}</span><span className="ml">{m.l}</span></div>
              <div><div className="mt">{m.t}</div><div className="md">{m.d}</div></div>
            </div>
          ))}
        </div>
        <div>
          <div className="codeblock">
            <div><span className="c"># get the station kit</span></div>
            <div><span className="p">git</span> clone https://{FS_REPO.path}</div>
            <div><span className="p">cd</span> propagation-controller</div>
            <div style={{marginTop:'8px'}}><span className="c"># run the whole unit in simulation — no hardware needed</span></div>
            <div><span className="p">make</span> run-sim</div>
            <div style={{marginTop:'8px'}}><span className="c"># cross-compile the static binary for the Piconium</span></div>
            <div><span className="p">make</span> linux</div>
          </div>
          <div className="note"><span className="k">Restoration R&amp;D</span>
            Everything we build — automation, wildlife monitoring, weather tracking, the restoration work itself — is free and open source. Almost none of it is new. We&rsquo;re mostly bolting existing tools and other people&rsquo;s open source together so a small project can build at the scale it actually needs.
          </div>
        </div>
      </div>
    </div></div>
  );
}

/* ============ R&D LIBRARY ============ */
function spImg(code, slot){ return A('assets/species/'+code.toLowerCase()+'-'+slot+'.jpg'); }
function SpeciesShelf({ go }){
  const [q,setQ] = React.useState('');
  const ql = q.trim().toLowerCase();
  const woody = FS_SPECIES.filter(s=>!ql || (s.common+' '+s.latin+' '+s.group+' '+s.role+' '+s.method).toLowerCase().includes(ql));
  const herb  = FS_LIB_SPECIES.filter(s=>!ql || (s.sp+' '+s.tag+' '+s.type+' '+s.sec+' '+s.method+' '+s.prov).toLowerCase().includes(ql));
  const total = FS_SPECIES.length + FS_LIB_SPECIES.length;
  const shownN = woody.length + herb.length;
  const herbGroups = [
    { t:'Grass', nm:'Grasses',                c:'var(--sage-deep)' },
    { t:'Forb',  nm:'Wildflowers & Legumes',  c:'var(--ochre-deep)' },
    { t:'Wetland', nm:'Wetland Pocket', sub:'Section 04 marsh', kind:'Wetland Note', c:'var(--river-deep)' },
  ];
  return (
    <div>
      <div className="rule-ink"></div>
      <div style={{display:'flex',alignItems:'baseline',justifyContent:'space-between',gap:'16px',flexWrap:'wrap',marginBottom:'12px'}}>
        <h2 className="eyebrow">Species Notes</h2>
        <span className="tag" style={{color:'var(--ochre-deep)',borderColor:'var(--ochre-deep)'}}><span className="sw" style={{background:'var(--ochre-deep)'}}></span>Pending NPSOT review</span>
      </div>
      <p className="prose" style={{maxWidth:'72ch',marginBottom:'16px'}}>These are the species we are focusing on for the restoration — the woody trees, shrubs and vines we grow on the propagation bench, plus the PUB grass-and-forb drill mix. The list is long, but it is <em>not</em> exhaustive; it is a working set, not the whole flora of the tract. Each opens as its own plate — for the woody species, the full propagation profile from our manual — and we&rsquo;re reconciling every note against NPSOT and USDA before it hardens.</p>
      <div style={{display:'flex',alignItems:'center',gap:'12px',margin:'0 0 20px',flexWrap:'wrap'}}>
        <input className="field" style={{margin:0,maxWidth:'360px'}} value={q} onChange={e=>setQ(e.target.value)}
          placeholder="Search species — common or Latin name…" aria-label="Search species"/>
        <span className="mono" style={{fontSize:'10px',letterSpacing:'0.14em',textTransform:'uppercase',color:'var(--char)',opacity:.7}}>{shownN} of {total} shown</span>
      </div>

      {FS_SP_GROUPS.map(g=>{
        const items = woody.filter(s=>s.group===g.id);
        if(!items.length) return null;
        return (
          <div key={g.id} style={{marginBottom:'22px'}}>
            <div style={{display:'flex',alignItems:'center',gap:'10px',padding:'8px 4px',borderBottom:'1px solid var(--ink)',marginBottom:'12px'}}>
              <span style={{width:'10px',height:'10px',background:'var(--river-deep)',transform:'rotate(45deg)'}}></span>
              <span className="mono" style={{fontSize:'10px',letterSpacing:'0.18em',textTransform:'uppercase'}}>{g.t}</span>
              <span className="mono" style={{marginLeft:'auto',fontSize:'10px',letterSpacing:'0.12em',color:'var(--char)',opacity:.6}}>{items.length}</span>
            </div>
            <div className="grid g2">
              {items.map(s=>(
                <div className="card feat spcard" key={s.slug} onClick={()=>go('species/'+s.slug)} style={{display:'flex',gap:'14px',alignItems:'stretch',cursor:'pointer',padding:'0'}}>
                  <img src={spImg(s.code,'form')} alt={s.common} loading="lazy" style={{width:'104px',flex:'none',objectFit:'cover',borderRight:'1px solid var(--ink)',background:'var(--paper-3)'}}/>
                  <div style={{padding:'16px 16px 16px 2px',display:'flex',flexDirection:'column',gap:'4px',minWidth:0}}>
                    <div className="mono" style={{fontSize:'9px',letterSpacing:'0.16em',textTransform:'uppercase',color:'var(--char)',opacity:.6}}>Propagation Plate</div>
                    <div style={{fontFamily:'Zilla Slab',fontWeight:900,fontSize:'19px',lineHeight:1,textTransform:'uppercase'}}><NavA to={'species/'+s.slug} style={{color:'inherit'}}>{s.common}</NavA></div>
                    <div style={{fontFamily:'Newsreader',fontStyle:'italic',fontSize:'14px',color:'var(--char)'}}>{s.latin}</div>
                    <div className="mono" style={{fontSize:'9.5px',letterSpacing:'0.06em',color:'var(--char)',opacity:.75,marginTop:'2px'}}>Difficulty {s.diff} · {s.method}</div>
                    <div className="mono" style={{fontSize:'9.5px',letterSpacing:'0.12em',textTransform:'uppercase',color:'var(--oxblood)',marginTop:'auto',paddingTop:'6px'}}>Open plate →</div>
                  </div>
                </div>
              ))}
            </div>
          </div>
        );
      })}

      {herbGroups.map(g=>{
        const items = herb.filter(s=>s.type===g.t);
        if(!items.length) return null;
        const IM = window.FS_SEEDMIX_IMG||{}, SL = window.FS_SEEDMIX_SLUG||{};
        return (
          <div key={g.t} style={{marginBottom:'22px'}}>
            <div style={{display:'flex',alignItems:'center',gap:'10px',padding:'8px 4px',borderBottom:'1px solid var(--ink)',marginBottom:'12px'}}>
              <span style={{width:'10px',height:'10px',background:g.c,transform:'rotate(45deg)'}}></span>
              <span className="mono" style={{fontSize:'10px',letterSpacing:'0.18em',textTransform:'uppercase'}}>{g.nm} <span style={{opacity:.6}}>· {g.sub||'PUB seed mix'}</span></span>
              <span className="mono" style={{marginLeft:'auto',fontSize:'10px',letterSpacing:'0.12em',color:'var(--char)',opacity:.6}}>{items.length}</span>
            </div>
            <div className="grid g2">
              {items.map(s=>{
                const im = IM[s.slug] || IM[SL[s.tag]];
                const mx = (window.FS_SEEDMIX||[]).find(m=>m.lat===s.tag);
                return (
                <div className="card feat spcard" key={s.slug} onClick={()=>go('species/'+s.slug)} style={{display:'flex',gap:'14px',alignItems:'stretch',cursor:'pointer',padding:'0'}}>
                  {im
                    ? <img src={A(im.f)} alt={s.sp} loading="lazy" style={{width:'104px',flex:'none',objectFit:'cover',borderRight:'1px solid var(--ink)',background:'var(--paper-3)'}}/>
                    : <span style={{width:'104px',flex:'none',borderRight:'1px solid var(--ink)',background:'var(--paper-3)'}}></span>}
                  <div style={{padding:'16px 16px 16px 2px',display:'flex',flexDirection:'column',gap:'4px',minWidth:0}}>
                    <div className="mono" style={{fontSize:'9px',letterSpacing:'0.16em',textTransform:'uppercase',color:'var(--char)',opacity:.6}}>{g.kind||'Seed-Mix Note'}{mx?' · '+mx.pct+'% of blend':''}</div>
                    <div style={{fontFamily:'Zilla Slab',fontWeight:900,fontSize:'19px',lineHeight:1,textTransform:'uppercase'}}><NavA to={'species/'+s.slug} style={{color:'inherit'}}>{s.sp}</NavA></div>
                    <div style={{fontFamily:'Newsreader',fontStyle:'italic',fontSize:'14px',color:'var(--char)'}}>{s.tag}</div>
                    <div className="mono" style={{fontSize:'9.5px',letterSpacing:'0.06em',color:'var(--char)',opacity:.75,marginTop:'2px'}}>{s.method} · {s.sec}</div>
                    <div className="mono" style={{fontSize:'9.5px',letterSpacing:'0.12em',textTransform:'uppercase',color:'var(--oxblood)',marginTop:'auto',paddingTop:'6px'}}>Open note →</div>
                  </div>
                </div>
              );})}
            </div>
          </div>
        );
      })}

      {shownN===0 && <p className="prose" style={{fontStyle:'italic',color:'var(--char)'}}>No species match “{q}” — try a common name like “bluestem” or a genus like “Quercus.”</p>}
      <div className="note"><span className="k">Help us get it right</span>
        If you know Central Texas provenance and propagation better than our notes do — especially through <a href="https://npsot.org/" target="_blank" rel="noopener">NPSOT</a> — we&rsquo;d welcome the correction at <a href="mailto:shortsresorttx@gmail.com">shortsresorttx@gmail.com</a>.
      </div>
    </div>
  );
}
function LibraryView({ go }){
  return (
    <div className="view"><div className="sheet">
      <Folio left="Section 04 · R&D Library" right="References · Species Notes" />
      <SectionHead no="04" title="R&D Library"
        deck="Good source material for Central Texas native propagation is harder to find than it should be. This is our working shelf — credited resources we lean on, and our own species notes as we build them out." />

      <h2 className="eyebrow" style={{margin:'36px 0 14px'}}>Resources &amp; References</h2>
      <div className="grid g2" style={{marginBottom:'8px'}}>
        {FS_LIB_RESOURCES.map(r=>(
          <a className="card" key={r.t} href={r.url} target="_blank" rel="noopener" style={{color:'var(--ink)',display:'block'}}>
            <div className="mono" style={{fontSize:'9.5px',letterSpacing:'0.16em',textTransform:'uppercase',color:'var(--river-deep)',marginBottom:'6px'}}>{r.org}</div>
            <h4>{r.t}</h4>
            <div className="cd" style={{marginTop:'8px'}}>{r.d}</div>
            <div className="mono" style={{fontSize:'10px',letterSpacing:'0.14em',textTransform:'uppercase',color:'var(--char)',opacity:.7,marginTop:'14px'}}>Visit ↗</div>
          </a>
        ))}
      </div>

      <SpeciesShelf go={go}/>
    </div></div>
  );
}

/* ============ ABOUT ============ */
function AboutView({ go }){
  return (
    <div className="view"><div className="sheet">
      <Folio left="Section 05 · About" right="The Institution" />
      <SectionHead no="05" title="About the Station" seal="house"
        deck="What this place is, who stands behind it, and who supports the work." />
      <div style={{display:'grid',gridTemplateColumns:'1.3fr 1fr',gap:'44px',marginTop:'34px'}} className="land-grid">
        <div className="prose" style={{maxWidth:'none'}}>
          <p className="lead">Short&rsquo;s Resort Field Station (SRFS) is the public face of a land restoration on a 103-acre tract above the Lampasas River in the Texas Hill Country &mdash; 98.78 acres of it under active restoration.</p>
          <p>The name&rsquo;s a small joke — there&rsquo;s no resort, just a cabin, a river, and seven sections of ground that generations of work wore down: decades of grazing, then a hay monoculture that crowded out most of what used to be native here. It came to the family, and we decided to fix it in the open — and to run the work like an institution so it outlasts any one season.</p>

          <h2>How the land came to us</h2>
          <Photo src="assets/cabin-river-2015.jpg" ratio="16/9" pos="center 55%"
            alt="The cabin above the Lampasas River, gravel yard and oaks, river visible beyond"
            tab="The cabin &middot; 2015"
            cap="Built in the early 1960s and still standing &middot; no running water, no electricity, four generations on"/>
          <p>The tract, 103 acres on the Lampasas, came into the family four generations ago. Our great-grandfather, known to everyone as &ldquo;Short,&rdquo; farmed wheat in Killeen, Texas, until the federal government took his farm in the 1940s to build what became Fort Hood. Displaced, he was granted this ground in Lampasas County and did here what he had always done: he grew wheat for seed, and by reputation some of the highest-quality wheat seed in the region.</p>
          <p>The land he took over was native trees and grasses over heavy cedar. Family accounts have ship-anchor chains strung between two tractors and dragged across the ground to pull every tree out by the roots. Cleared, it became a wheat farm and a steady income for years. In the early 1960s Short built the cabin down by the river with his children — our grandfather and great-aunts. The family home was across the road on separate acreage, where they raised goats and sheep and cut hay; the cabin was the other thing: somewhere to cool off in the Lampasas after a day in the fields. They named it, half in earnest, &ldquo;Short&rsquo;s Resort.&rdquo; The original hand-painted sign was the work of our great-aunts. It has never had running water or electricity; for years propane ran a refrigerator, a stove, a heater and a few lamps, and only remnants of those remain. The outhouse washed away in a flood long ago.</p>

          <h2>Fallow, then leased for grazing</h2>
          <Photo src="assets/hayfield-2026.jpg" ratio="16/9" pos="center 58%"
            alt="The open hayfield stretching to the tree line, cut and drying, with a fence line in the foreground"
            tab="The working field &middot; 2026"
            cap="The agricultural plain, cut for hay &middot; open ground where the grazing years and then the Tifton left little else standing"/>
          <p>When Short retired from farming he let the field go mostly fallow but kept cattle on it under lease. By the standards we hold now it was grazed harder than the ground could carry — the common practice of the day — and there was no fence holding the cattle off the river. The banks eroded, and so did the central drainage corridor; manure ran off, seasonal floods pushed through, and between them larger old trees went down while little new growth came up. Wild pig and white-tailed deer numbers ran unchecked on top of it. When Short passed and our grandfather took over, the land was kept much the same way for years, as it always had been.</p>
          <p>That began to change on a visit to work on the old cabin, when the state of the ground was plain enough to act on. We fenced a hundred feet off the river bank and around the cabin to keep the cattle off the water. It slowed the bank erosion; the overgrazing and the corridor washout on the agricultural plain continued.</p>

          <h2>The Tifton years, and the turn</h2>
          <Photo src="assets/tifton-bale-2026.jpg" ratio="16/9" pos="center 55%"
            alt="A round bale of Tifton 85 hay on the green field, tree line on the horizon"
            tab="Tifton 85 &middot; 2026"
            cap="A round bale off the meadow &middot; twenty years of hay, and the monoculture that came with it"/>
          <p>Eventually the cattle came off entirely and our grandfather planted the field in Tifton 85 bermudagrass, on USDA advice at the time: a low-maintenance grass that could be cut for hay and carry a small, steady income. For nearly twenty years it was fertilized and cut and looked the part, but over time it built a monoculture across much of the property — an outcome no one intended — that pushed out the native grasses, shrubs and trees, and with them the dove, quail and raptors that had used the ground.</p>
          <p>Our grandfather passed in 2023 and the land came to the fourth generation of the family. We grew up coming to the cabin with our grandparents: camping, fishing and swimming in the cool Lampasas, and taking our first deer on this ground. Since 2023 we have been building the proposal now underway: clear the Tifton monoculture and return the ground to native trees, grasses and woody shrubs, to bring back the native birds and riparian woodland species, slow the erosion as far as we can and restore the oak prairie and river habitat this stretch of the Hill Country carried for most of its history. Four generations on, the cabin still stands, and the work is only the newest form of tending the same ground.</p>

          <h2>An institution, deliberately</h2>
          <p>The station&rsquo;s organized into departments: Forestry &amp; Fire, Water &amp; Power, Wildlife Affairs, Agriculture &amp; Grazing, Hospitality, and the Office of the Steward — each with its own seal and its own patch of responsibility. It&rsquo;s smaller than that sounds. But the structure is the point: it keeps the work legible, hands it off cleanly, and keeps the records honest.</p>
          <div className="note"><span className="k">On names</span>
            You won&rsquo;t find anyone&rsquo;s name here. The station speaks for the work, not for people — partly for privacy, and partly because the land doesn&rsquo;t care who planted the tree, only that it got planted right.
          </div>
          <h2>How it&rsquo;s supported</h2>
          <p>The restoration is supported by two specific grants. Cost-share comes through the USDA Natural Resources Conservation Service under the <a href={FS_FUNDERS[0].url} target="_blank" rel="noopener">Environmental Quality Incentives Program (NRCS EQIP)</a>, administered by the Lampasas County field office. Grassland conversion is supported by a <a href={FS_FUNDERS[1].url} target="_blank" rel="noopener">Texas Parks &amp; Wildlife Pastures for Upland Birds (PUB)</a> grant, which is turning our Tifton 85 hayfield back into native prairie. Public support leaves a public trail: practices and outcomes are posted in <NavA to={'journal'} style={{cursor:'pointer'}}>the Field Journal</NavA> as they&rsquo;re completed.</p>
          
        </div>
        <div>
          <h2 className="eyebrow" style={{marginBottom:'12px',textAlign:'center'}}>Our Partners</h2>
          <div style={{border:'1px solid var(--ink)',marginBottom:'26px'}}>
            {FS_FUNDERS.map(f=>(
              <a key={f.k} href={f.url} target="_blank" rel="noopener" style={{display:'flex',gap:'14px',alignItems:'center',padding:'14px 16px',borderTop:'1px solid var(--rule-soft)',color:'var(--ink)'}}>
                <AgencyMark kind={f.mark} size={46}/>
                <div>
                  <div className="zilla" style={{fontWeight:900,fontSize:'15px',textTransform:'uppercase'}}>{f.k}</div>
                  <div className="mono" style={{fontSize:'10px',letterSpacing:'0.08em',color:'var(--char)',opacity:.8,marginTop:'3px'}}>{f.program}</div>
                  <div className="mono" style={{fontSize:'9px',letterSpacing:'0.1em',color:'var(--char)',opacity:.6,marginTop:'2px'}}>{f.office} ↗</div>
                </div>
              </a>
            ))}
          </div>
          <div className="eyebrow" style={{marginBottom:'12px',textAlign:'center'}}><NavA to={'departments'} style={{cursor:'pointer',color:'inherit'}}>The Departments →</NavA></div>
          <div className="legend">
            {FS_SEALS.DEPTS.map(d=>(
              <div className="lr" key={d.key} onClick={()=>go('departments')} style={{gridTemplateColumns:'58px 1fr',cursor:'pointer'}}>
                <Seal dept={d} size={54} title={d.nm}/>
                <div><div className="nm"><NavA to={'departments'} style={{color:'inherit'}}>{d.nm}</NavA></div><div className="ds mono" style={{fontStyle:'normal',fontSize:'10px',letterSpacing:'0.12em',textTransform:'uppercase'}}>Section {d.plate} · {d.code}</div></div>
              </div>
            ))}
          </div>
        </div>
      </div>

      <div className="tl-wrap">
        <h2 className="eyebrow" style={{marginBottom:'6px'}}>A Land History</h2>
        <p style={{fontFamily:'Newsreader',fontStyle:'italic',fontSize:'16px',color:'var(--char)',marginBottom:'26px',maxWidth:'640px'}}>Four generations on the same ground. A project meant to outlast the people running it.</p>
        <ol className="tl">
          {FS_TIMELINE.map(e=>(
            <li className="tl-row" key={e.yr}>
              <div className="tl-yr">{e.yr}</div>
              <div className="tl-body">
                <div className="tl-t">{e.t}</div>
                <div className="tl-d">{e.d}</div>
              </div>
            </li>
          ))}
          <li className="tl-row tl-future">
            <div className="tl-yr">Onward</div>
            <div className="tl-body">
              <div className="tl-t">A multigenerational project</div>
              <div className="tl-d">Later phases work inward for years, and a restored grassland will want fire and care for lifetimes after. The work is built to be handed forward.</div>
              <span className="tl-inf" aria-hidden="true">∞</span>
            </div>
          </li>
        </ol>
      </div>

      <div className="rule-hair" style={{marginTop:'40px'}}></div>
      <div style={{textAlign:'center',padding:'18px 0 6px'}}>
        <div className="eyebrow" style={{marginBottom:'10px'}}>Contact</div>
        <a href="mailto:shortsresorttx@gmail.com" className="mono" style={{fontSize:'13px',letterSpacing:'0.08em',color:'var(--ink)',borderBottom:'1px solid var(--rule)',paddingBottom:'3px'}}>shortsresorttx@gmail.com</a>
      </div>
    </div></div>
  );
}

/* ============ SUPPORT (buried) ============ */
function SupportView({ go }){
  return (
    <div className="view"><div className="sheet">
      <Folio left="Appendix · Get Involved" right="Unlisted" />
      <SectionHead no="06" title="Get Involved"
        deck="This page isn't linked from the main sections — if you found it, thank you for digging." />
      <div className="prose" style={{marginTop:'30px'}}>
        <p>The station mostly needs three things, in this order: <strong>people who use the open plans</strong> and tell us what broke, <strong>native seed and cuttings</strong> from local provenance and — a distant third — <strong>a little money</strong> toward materials the grants don&rsquo;t cover.</p>
        <p>If you want to help, the most useful thing is to take the <NavA to={'ii'} style={{cursor:'pointer'}}>open source and plans</NavA>, build something on your own ground and share what you learn. That&rsquo;s the whole point of doing this in the open.</p>
        <div className="note"><span className="k">Materials fund</span>
          There is a small materials fund for fencing, seed and sensor parts. It is intentionally low-key and not a priority — reach out at <a href="mailto:shortsresorttx@gmail.com">shortsresorttx@gmail.com</a> if you&rsquo;d like to contribute.
        </div>
        <p><a href="mailto:shortsresorttx@gmail.com">Say hello — shortsresorttx@gmail.com</a></p>
      </div>
    </div></div>
  );
}

/* ---------------------------------------------------------------------------
   Field Notes — the app the station builds for its own record-keeping.
   Two unlisted pages: this one is the application home page Google's OAuth
   consent screen points at, /fieldnotes/privacy is its policy. Neither is in
   the nav or the sitemap until the app is actually ready to hand to people.
--------------------------------------------------------------------------- */
function FieldNotesView({ go }){
  return (
    <div className="view"><div className="sheet">
      <Folio left="Instruments &amp; Infrastructure · Field Notes" right="Unlisted · In progress" />
      <SectionHead no="03" title="Field Notes"
        deck="An offline field journal for people who own and manage land. We built it because we needed it, and we're still the only ones using it." />

      <div className="prose" style={{marginTop:'30px'}}>
        <p>Field Notes is an Android app for recording what you see and what you do on a piece of ground. A plant, a fence line, a check-in on 40 cuttings you put in last spring &mdash; photograph it, say what it is, and it&rsquo;s saved. It works with no signal, because the places where you need it are the places that don&rsquo;t have any.</p>

        <p>Everything lives on the phone in a plain SQLite database. There is no account to make, no server to sign into, and nothing is uploaded anywhere unless you set up a backup and choose where it goes. That isn&rsquo;t a privacy stance we bolted on afterwards; it&rsquo;s the only architecture that works when you&rsquo;re standing in a creek bottom with one bar.</p>

        <h2>What it does</h2>
        <p>A capture is a photo, a location, a time and whatever you know about what you&rsquo;re looking at. From there the app builds the things that are actually hard to keep track of by hand: which mother plant a batch of cuttings came from, how many of them are still alive two seasons later, what a photo point looked like the last time you stood on it, and where the boundary of a management zone is.</p>

        <div className="dos-spec" style={{margin:'22px 0'}}>
          <div className="dos-spec-row"><span className="mono dos-spec-k">Offline</span><span className="dos-spec-v">Offline vector basemaps, GPS tracks, and captures that never wait on a network or a satellite fix to save.</span></div>
          <div className="dos-spec-row"><span className="mono dos-spec-k">Lineage</span><span className="dos-spec-v">Mother plant &rarr; batch &rarr; plant-out &rarr; check-in. Survival is derived from the record, never typed in.</span></div>
          <div className="dos-spec-row"><span className="mono dos-spec-k">Return visits</span><span className="dos-spec-v">Photo points with a ghost overlay of the last frame, so the second photo lines up with the first.</span></div>
          <div className="dos-spec-row"><span className="mono dos-spec-k">Portable</span><span className="dos-spec-v">Full export to open formats &mdash; SQLite, CSV, GeoJSON, GPX, original photos with their EXIF intact.</span></div>
          <div className="dos-spec-row"><span className="mono dos-spec-k">Backup</span><span className="dos-spec-v">Encrypted on the phone, to a computer on your own network, or to your own Google Drive. You pick how far the copy travels.</span></div>
        </div>

        <h2>Where it is</h2>
        <p>Working, on one phone, in one pasture. The core is built and the app is being used daily on the property this site documents. It is not in the Play Store and there is no sign-up list, because handing it to strangers before we&rsquo;ve broken it ourselves would waste their time.</p>

        <p>Things we haven&rsquo;t finished: syncing between two phones, a desktop companion that runs on something other than a laptop we control, and species identification from a photo (the code is written, the field testing isn&rsquo;t done). We&rsquo;ll say so here when those are real.</p>

        <h2>On the data</h2>
        <p>Coordinates of private land are sensitive, and we treat them that way in the app for the same reason this site publishes species observations at section level and keeps precise points off the map. What the app collects stays on the device unless you move it. The full accounting &mdash; including the two cases where a coordinate does leave, and why &mdash; is on the <NavA to={'fieldnotes/privacy'} style={{cursor:'pointer'}}>privacy page</NavA>.</p>

        <div className="note"><span className="k">Why we built it</span>
          We tried doing this with a notes app, a camera roll and a spreadsheet for about a year. It works right up until you want to answer a real question &mdash; how did the cuttings from that one Bur Oak do &mdash; and then you find the photos are in one place, the dates are wrong, and the location is whatever the phone felt like recording. The app is the answer to that, and not much more.
        </div>

        <p><a href="mailto:shortsresorttx@gmail.com">Questions &mdash; shortsresorttx@gmail.com</a></p>
      </div>

      <div className="rule-hair"></div>
      <div style={{display:'flex',gap:'12px',flexWrap:'wrap'}}>
        <NavA className="btn" to={'ii'}>&larr; Instruments &amp; Infrastructure</NavA>
        <NavA className="btn" to={'fieldnotes/privacy'}>Privacy &rarr;</NavA>
      </div>
    </div></div>
  );
}

function FieldNotesPrivacy({ go }){
  return (
    <div className="view"><div className="sheet">
      <Folio left="Field Notes · Privacy" right="Unlisted" />
      <div className="mono dos-back"><NavA to="fieldnotes">&larr; Field Notes</NavA></div>
      <SectionHead no="03" title={<span>Field Notes<br/>Privacy</span>}
        deck="What the app collects, where it goes, and the short list of cases where anything leaves your phone." />

      <div className="prose" style={{marginTop:'30px'}}>
        <p className="mono" style={{fontSize:'10px',letterSpacing:'.14em',textTransform:'uppercase',opacity:.7}}>Last updated 28 August 2026 &middot; applies to the Field Notes Android app</p>

        <h2>The short version</h2>
        <p>Field Notes has no account, no analytics, no advertising and no telemetry. We do not operate a server that your records go to, and we cannot see your data. Everything the app records is stored on your phone. It leaves only when you set up a backup or ask for a species identification, and this page lists every one of those cases.</p>

        <h2>What the app stores on your phone</h2>
        <p>Photos, audio notes, GPS coordinates and accuracy, timestamps, and whatever you type &mdash; species, tags, conditions, notes. Plus the structure you build: properties, management zones, boundaries, plant batches, check-ins, tracks. It is kept in a SQLite database and a folder of files in the app&rsquo;s private storage, and you can export the whole thing at any time in open formats.</p>
        <p>The app asks for camera, microphone, location and storage permissions. Camera and microphone are used only while you are making a capture. Location is used to place a record and to draw your position on the map; a capture that has no fix is saved and flagged as unlocated rather than given a made-up coordinate.</p>

        <h2>When data leaves the phone</h2>
        <p>Five cases. Backups, identification and the context lookup only happen because you asked; map imagery loads whenever the map is open, and can be switched off.</p>

        <h3>1 &middot; Backup to your own computer</h3>
        <p>The app pushes an encrypted copy across your local network to a machine you own, running the Field Notes desktop receiver. Nothing touches the internet, there is no account, and the receiving computer only ever holds files it cannot read.</p>

        <h3>2 &middot; Backup to your own Google Drive</h3>
        <p>If you connect a Google account, the app requests one permission &mdash; <span className="mono">drive.appdata</span> &mdash; which grants access to a hidden folder Drive creates for this app. That folder is not visible in your Drive, no other app can open it, and the app cannot read anything that was already in your Drive. It cannot see your documents, your photos or your email.</p>
        <p>The phone encrypts every object before it is uploaded, so Google stores ciphertext under meaningless names and holds no key. Signing in creates no account with us and sends us nothing; the access token stays on your device. You can revoke it at <a href="https://myaccount.google.com/permissions" target="_blank" rel="noopener">myaccount.google.com/permissions</a> and the app keeps working, minus the uploads.</p>
        <div className="note"><span className="k">Google API Services</span>
          Field Notes&rsquo; use of information received from Google APIs adheres to the <a href="https://developers.google.com/terms/api-services-user-data-policy" target="_blank" rel="noopener">Google API Services User Data Policy</a>, including the Limited Use requirements. Drive data is used only to store and retrieve your own backups, is never transferred to anyone, is never used for advertising, and is never read by a human.
        </div>

        <h3>3 &middot; Species identification, if you set it up</h3>
        <p>Identification is off until you supply your own API key. There are two paths and you may use either, both, or neither.</p>
        <p><strong>Pl@ntNet</strong> receives the photograph and nothing else &mdash; no coordinates, no notes, no identifiers. Their handling of it is governed by <a href="https://plantnet.org/en/legal-notice/" target="_blank" rel="noopener">Pl@ntNet&rsquo;s own terms</a>, under your key.</p>
        <p><strong>A language model of your choosing</strong> (Anthropic, OpenAI, or any compatible endpoint you point it at) receives the photograph plus a short description of the circumstances &mdash; the month, the ground, what you have already recorded nearby, and the location <em>rounded to roughly a kilometre</em>. That rounding is deliberate: it is enough for a model to rule out the wrong hemisphere and not enough to find your gate. Your key, your account, their terms.</p>
        <p>Nothing a machine suggests is ever written down as fact. Every suggestion is kept as a suggestion until you accept it.</p>

        <h3>4 &middot; Map tiles</h3>
        <p>The map&rsquo;s standard view is satellite imagery, drawn from the <a href="https://basemap.nationalmap.gov" target="_blank" rel="noopener">USGS National Map</a> whenever the map is on screen and the phone has signal; downloading an offline basemap requests tiles from <a href="https://build.protomaps.com" target="_blank" rel="noopener">Protomaps</a>. A tile request necessarily reveals which part of the world you are looking at, in the same way loading any map does &mdash; it does not carry your records and is not tied to an identity. If you would rather the map ask nothing of anyone, switch the satellite layer off under Layers and use only the offline basemap; imagery tiles can also use mobile data, which is worth knowing on a metered plan.</p>

        <h3>5 &middot; Weather and soil context, if you switch it on</h3>
        <p>Off by default. Turned on in Settings, it looks up the weather on the date of a record and the soil under it, so a note written in a wet March still reads correctly three years later. It sends <strong>the date and a rounded location</strong> to two public services: <a href="https://open-meteo.com" target="_blank" rel="noopener">Open-Meteo</a> for the weather archive and the <a href="https://sdmdataaccess.sc.egov.usda.gov" target="_blank" rel="noopener">USDA-NRCS Soil Data Access</a> for the soil survey. No photo, no note, no account, no device identifier.</p>
        <p>The location is rounded to about a kilometre before it goes &mdash; the same rounding the identification path uses. Weather at that resolution is the same weather; a soil map unit occasionally resolves to the one next door. That is a trade we are happy to make in exchange for not broadcasting a gate.</p>
        <div className="note"><span className="k">This one changed</span>
          Until 28 August 2026 this lookup ran automatically and sent the full-precision coordinate. It contradicted the app&rsquo;s own first rule &mdash; that nothing leaves the device without you doing something &mdash; and writing this page is what surfaced it. It is now off unless you turn it on, and rounded when you do. Records made before the change still hold their weather and soil; nothing further is sent about them.
        </div>

        <h2>What we never do</h2>
        <p>No analytics or crash reporting SDK. No advertising identifier. No selling or sharing of anything, because there is nowhere for it to be sold from. No account, so no profile. We have no ability to read a backup you have made, encrypted or not &mdash; there is no service on our end that receives one.</p>

        <h2>Encryption and the thing you can actually lose</h2>
        <p>Encrypted backups use a key wrapped by both a passphrase you choose and a twelve-word recovery phrase the app shows you once. Either one opens it. Neither is stored anywhere but your device and wherever you wrote the recovery phrase down.</p>
        <p>The consequence is worth saying plainly: if you lose the passphrase <em>and</em> the recovery phrase, the backup is gone. Not gone-as-in-file-a-support-ticket. Gone. We cannot recover it, and an app that could would be an app whose promises were false.</p>

        <h2>Deleting things</h2>
        <p>Records you delete are marked deleted and dropped from the app; uninstalling removes the database and the files with it. Copies you have already sent to Drive or to your own computer are yours to remove &mdash; Drive&rsquo;s hidden app folder can be cleared from Google&rsquo;s storage settings, and revoking the permission stops any further uploads.</p>

        <h2>Children</h2>
        <p>The app is not directed at children and collects nothing about anyone&rsquo;s identity, age or contacts.</p>

        <h2>Changes</h2>
        <p>If this changes we will change this page and move the date at the top. There is no mailing list to notify because we don&rsquo;t have your address.</p>

        <h2>Contact</h2>
        <p>Short&rsquo;s Resort Field Station, Lampasas County, Texas. <a href="mailto:shortsresorttx@gmail.com">shortsresorttx@gmail.com</a>.</p>
      </div>

      <div className="rule-hair"></div>
      <div style={{display:'flex',gap:'12px',flexWrap:'wrap'}}>
        <NavA className="btn" to={'fieldnotes'}>&larr; Field Notes</NavA>
      </div>
    </div></div>
  );
}

function NotFound({ go }){
  return (
    <div className="view"><div className="sheet">
      <Folio left="—" right="Off the map" />
      <SectionHead no="—" title="Off the Map" deck="That page isn't in the atlas. Try the sections below." />
      <div style={{display:'flex',gap:'12px',marginTop:'24px',flexWrap:'wrap'}}>
        <NavA className="btn solid" to={'home'}>Frontispiece</NavA>
        <NavA className="btn" to={'journal'}>Field Journal</NavA>
      </div>
    </div></div>
  );
}

function RemovedNote({ slug, go }){
  const r = (window.FS_REMOVED||[]).find(x=>x.slug===slug);
  if(!r) return <NotFound go={go}/>;
  return (
    <div className="view"><div className="sheet">
      <Folio left="Species Register \u00b7 Correction" right="Removed from the set" />
      <div className="mono dos-back"><NavA to="library">&larr; R&amp;D Library</NavA></div>
      <article>
      <SectionHead no="\u2014" title={r.name}
        deck={'This species was removed from the restoration set on '+r.date+'. Its plate is retired; this note stands in its place.'} />
      <div className="prose" style={{marginTop:'22px'}}>
        <p><em>{r.latin}</em></p>
        <h2>Why it was removed</h2>
        <p>{r.reason}</p>
        <h2>What replaces it</h2>
        <p>{r.replacement}{r.replacementSlug && <React.Fragment>{' '}<NavA to={'species/'+r.replacementSlug}>See its plate \u2192</NavA></React.Fragment>}</p>
        <div className="note"><span className="k">Why publish this</span>A public database that documents its own removals is more trustworthy than one that only ever grows. The correction is worth more than the record was.</div>
      </div>
      </article>
      <div className="rule-hair"></div>
      <div style={{display:'flex',gap:'12px',flexWrap:'wrap'}}>
        <NavA className="btn" to={'library'}>\u2190 R&amp;D Library</NavA>
      </div>
    </div></div>
  );
}

function creditCaption(c){
  if(!c) return null;
  const who = c.artist && c.artist.trim() ? c.artist : 'Unknown';
  return <span>{c.note?<em style={{fontStyle:'normal',color:'var(--oxblood)'}}>{c.note} </em>:null}{c.title} — {who} · {c.license} · <a href={c.source} target="_blank" rel="noopener">Wikimedia Commons ↗</a></span>;
}
function SpeciesPlate({ sp, go }){
  const idx = FS_SPECIES.indexOf(sp);
  const prev = FS_SPECIES[idx-1], next = FS_SPECIES[idx+1];
  const cr = (window.FS_CREDITS||{})[sp.code] || (window.FS_CREDITS||{})[String(sp.code).toLowerCase()] || {};
  const grp = (FS_SP_GROUPS.find(g=>g.id===sp.group)||{}).t || '';
  const quick = [['Difficulty',sp.diff],['Growth rate',sp.growth],['Drought',sp.drought],['Flood',sp.flood],['Wildlife',sp.wildlife],['Browse Pressure',sp.deer],['Method',sp.method],['Container',sp.container]];
  const site  = [['Primary',sp.sitePrimary],['Secondary',sp.siteSecondary],['Avoid',sp.siteAvoid],['Soils',sp.soils],['Hydrology',sp.hydrology]];
  const care  = [['Seed collection',sp.seed],['Stratification',sp.strat],['Propagation',sp.prop],['Container progression',sp.progression],['Nursery watering',sp.water],['Light',sp.light],['Outplanting',sp.outplant]];
  const Photo = ({ slot }) => {
    const c = cr[slot];
    if(!c) return null;
    return (
      <figure style={{margin:0,border:'1px solid var(--ink)',background:'var(--paper-3)'}}>
        <img src={spImg(sp.code,slot)} alt={sp.common+' — '+slot} loading="lazy" style={{width:'100%',aspectRatio:'4/3',objectFit:'cover',display:'block',borderBottom:'1px solid var(--ink)'}}/>
        <figcaption className="mono" style={{fontSize:'9px',letterSpacing:'0.06em',color:'var(--char)',padding:'8px 10px',lineHeight:1.5}}>
          <b style={{textTransform:'uppercase',letterSpacing:'0.14em',opacity:.7}}>{slot==='form'?'Form':'Detail'} · </b>{creditCaption(c)}
        </figcaption>
      </figure>
    );
  };
  const Rows = ({ data }) => (
    <div style={{border:'1px solid var(--ink)'}}>
      {data.map((r,i)=>(
        <div key={i} style={{display:'grid',gridTemplateColumns:'150px 1fr',gap:'14px',padding:'10px 14px',borderTop:i?'1px solid var(--rule-soft)':'none'}} className="specrow">
          <span className="mono" style={{fontSize:'9.5px',letterSpacing:'0.12em',textTransform:'uppercase',color:'var(--char)'}}>{r[0]}</span>
          <span style={{fontFamily:'Newsreader',fontSize:'15px',lineHeight:1.45}}>{r[1]}</span>
        </div>
      ))}
    </div>
  );
  return (
    <div className="view"><div className="sheet">
      <Folio left="Propagation Manual · Species Plate" right="R&D Library · Species" />
      <div className="mono dos-back"><NavA to="library">&larr; R&amp;D Library</NavA></div>
      <article>
      <div className="entry-head">
        <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',gap:'20px'}}>
          <div className="eyebrow">Species Plate · {grp}</div>
        </div>
        <h1 className="disp" style={{fontSize:'clamp(34px,5vw,60px)'}}>{sp.common}</h1>
        <div className="byline">
          <span style={{fontStyle:'italic',fontFamily:'Newsreader',textTransform:'none',letterSpacing:0,fontSize:'17px'}}>{sp.latin}</span><span className="sep"></span>
          <span>Short&rsquo;s Resort Field Station</span>
        </div>
      </div>

      <div style={{display:'grid',gridTemplateColumns:(cr.form&&cr.detail)?'1fr 1fr':'1fr',gap:'14px',margin:'18px 0 8px'}} className="land-grid">
        <Photo slot="form"/>
        <Photo slot="detail"/>
      </div>

      <div style={{display:'grid',gridTemplateColumns:'1.25fr 1fr',gap:'36px',marginTop:'22px'}} className="land-grid">
        <div className="prose" style={{maxWidth:'none'}}>
          <h2>Restoration Role</h2>
          <p className="lead" style={{fontSize:'19px'}}>{sp.role}</p>
          <h2>Site Suitability</h2>
          <Rows data={site}/>
          <h2 style={{marginTop:'28px'}}>Propagation &amp; Care</h2>
          <Rows data={care}/>
          <h2 style={{marginTop:'28px'}}>Establishment Watering</h2>
          <ul style={{listStyle:'none',margin:'6px 0 8px'}}>
            {sp.establish.map((li,i)=>(
              <li key={i} className="mono" style={{fontSize:'13px',letterSpacing:'0.03em',padding:'8px 0 8px 22px',borderTop:'1px solid var(--rule-soft)',position:'relative',lineHeight:1.5}}>
                <span style={{position:'absolute',left:'3px',top:'14px',width:'7px',height:'7px',background:'var(--ink)',transform:'rotate(45deg)'}}></span>{li}
              </li>
            ))}
          </ul>
          <div className="note"><span className="k">Field notes</span>{sp.notes}</div>
        </div>
        <div>
          <div style={{border:'1px solid var(--ink)',position:'sticky',top:'86px'}}>
            <div className="mono" style={{fontSize:'9.5px',letterSpacing:'0.18em',textTransform:'uppercase',background:'var(--ink)',color:'var(--bone)',padding:'9px 14px'}}>Quick Reference</div>
            {quick.map((r,i)=>(
              <div key={i} style={{display:'grid',gridTemplateColumns:'1fr auto',gap:'10px',padding:'9px 14px',borderTop:i?'1px solid var(--rule-soft)':'none',alignItems:'baseline'}}>
                <span className="mono" style={{fontSize:'9.5px',letterSpacing:'0.12em',textTransform:'uppercase',color:'var(--char)'}}>{r[0]}</span>
                <span style={{fontFamily:'Zilla Slab',fontWeight:700,fontSize:'14px',textAlign:'right'}}>{r[1]}</span>
              </div>
            ))}
          </div>
        </div>
      </div>

      <div className="note" style={{marginTop:'26px'}}><span className="k">Sources</span>
        Propagation data from the Short&rsquo;s Resort Native Tree &amp; Shrub Propagation Manual (SR-PROP-01). Photographs via Wikimedia Commons under the licenses noted on each image; credit to the individual photographers above.
      </div>
      <div className="rule-hair"></div>
      <div style={{display:'flex',justifyContent:'space-between',gap:'16px',flexWrap:'wrap'}}>
        <NavA className="btn" to={'library'}>← R&amp;D Library</NavA>
        <div style={{display:'flex',gap:'10px'}}>
          {prev && <NavA className="btn" to={'species/'+prev.slug}>← {prev.common}</NavA>}
          {next && <NavA className="btn" to={'species/'+next.slug}>{next.common} →</NavA>}
        </div>
      </div>
      </article>
    </div></div>
  );
}
function SpeciesPage({ slug, go }){
  const woody = (window.FS_SPECIES||[]).find(x=>x.slug===slug);
  if(woody) return <SpeciesPlate sp={woody} go={go}/>;
  const s = FS_LIB_SPECIES.find(x=>x.slug===slug);
  if(!s) return <NotFound go={go}/>;
  const idx = FS_LIB_SPECIES.indexOf(s);
  const prev = FS_LIB_SPECIES[idx-1], next = FS_LIB_SPECIES[idx+1];
  const spec = [['Section',s.sec],['Method',s.method],['Seed treatment',s.treat],['Provenance',s.prov]];
  return (
    <div className="view"><div className="sheet">
      <Folio left="Section 04 · Species Note" right="R&D Library" />
      <div className="mono dos-back"><NavA to="library">&larr; R&amp;D Library</NavA></div>
      <article>
      <div className="entry-head">
        <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',gap:'20px'}}>
          <div className="eyebrow">Specimen Note · {s.sec}</div>
        </div>
        <h1 className="disp" style={{fontSize:'clamp(34px,5vw,58px)'}}>{s.sp}</h1>
        <div className="byline">
          <span style={{fontStyle:'italic',fontFamily:'Newsreader',textTransform:'none',letterSpacing:0,fontSize:'17px'}}>{s.tag}</span><span className="sep"></span>
          <span>Short&rsquo;s Resort Field Station</span>
        </div>
        <span className="tag" style={{color:'var(--ochre-deep)',borderColor:'var(--ochre-deep)',alignSelf:'flex-start'}}><span className="sw" style={{background:'var(--ochre-deep)'}}></span>Pending NPSOT review</span>
      </div>
      <div style={{display:'grid',gridTemplateColumns:'1.3fr 1fr',gap:'36px',marginTop:'26px'}} className="land-grid">
        <div className="prose" style={{maxWidth:'none'}}>
          <p className="lead">{s.note}</p>
          <div className="note"><span className="k">Status</span>A working first pass, being reconciled against <a href="https://npsot.org/" target="_blank" rel="noopener">NPSOT</a> and <a href="https://plants.usda.gov/" target="_blank" rel="noopener">USDA PLANTS</a>. Corrections welcome at <a href="mailto:shortsresorttx@gmail.com">shortsresorttx@gmail.com</a>.</div>
        </div>
        <div>
          {(()=>{
            const im=(window.FS_SEEDMIX_IMG||{})[s.slug]||(window.FS_SEEDMIX_IMG||{})[(window.FS_SEEDMIX_SLUG||{})[s.tag]];
            if(!im) return null;
            return (
              <figure style={{border:'1px solid var(--ink)',background:'var(--bone)',marginBottom:'14px'}}>
                <img src={A(im.f)} alt={s.sp+' — '+s.tag} loading="eager" style={{width:'100%',aspectRatio:'4/3',objectFit:'cover',display:'block',borderBottom:'1px solid var(--ink)',background:'var(--paper-3)'}}/>
                <figcaption className="mono" style={{fontSize:'9.5px',letterSpacing:'0.06em',color:'var(--char)',padding:'9px 12px',lineHeight:1.5}}>
                  {im.kind==='plate'?'Illustration':'Photo'} &middot; {im.a||'Unattributed'} &middot; {im.s ? <a href={im.s} target="_blank" rel="noopener">{im.l} &middot; {im.src||'Commons'} &#8599;</a> : im.l}{im.kind==='plate'?null:<React.Fragment><br/>Not photographed on this property</React.Fragment>}
                </figcaption>
              </figure>
            );
          })()}
          <div style={{border:'1px solid var(--ink)'}}>
            <div className="mono" style={{fontSize:'9.5px',letterSpacing:'0.18em',textTransform:'uppercase',background:'var(--ink)',color:'var(--bone)',padding:'9px 14px'}}>Propagation Record</div>
            {spec.map((r,i)=>(
              <div key={i} style={{display:'grid',gridTemplateColumns:'120px 1fr',gap:'12px',padding:'10px 14px',borderTop:i?'1px solid var(--rule-soft)':'none'}}>
                <span className="mono" style={{fontSize:'9.5px',letterSpacing:'0.1em',textTransform:'uppercase',color:'var(--char)'}}>{r[0]}</span>
                <span style={{fontFamily:'Newsreader',fontSize:'15px'}}>{r[1]}</span>
              </div>
            ))}
          </div>
          <div className="mono" style={{fontSize:'10px',letterSpacing:'0.1em',color:'var(--char)',opacity:.7,marginTop:'12px',textAlign:'center'}}>Shareable page &middot; #/species/{s.slug}</div>
        </div>
      </div>
      <div className="rule-hair"></div>
      <div style={{display:'flex',justifyContent:'space-between',gap:'16px',flexWrap:'wrap'}}>
        <NavA className="btn" to={'library'}>← R&amp;D Library</NavA>
        <div style={{display:'flex',gap:'10px'}}>
          {prev && <NavA className="btn" to={'species/'+prev.slug}>← {prev.sp}</NavA>}
          {next && <NavA className="btn" to={'species/'+next.slug}>{next.sp} →</NavA>}
        </div>
      </div>
      </article>
    </div></div>
  );
}

/* ============ THE DEPARTMENTS ============ */
function DepartmentsView({ go }){
  return (
    <div className="view"><div className="sheet">
      <Folio left="The Departments" right="Seals of the Field Station" />
      <div style={{marginBottom:'8px'}}>
        <div className="eyebrow">The Institution · Short&rsquo;s Resort Field Station</div>
        <h1 className="disp">The Departments</h1>
        <p className="deck">Six seals, six sections of responsibility. The station is small — mostly one family and its friends — but the structure is real: it makes the work legible, hands it off cleanly and keeps the records honest.</p>
      </div>
      <div className="rule-ink"></div>
      {FS_DEPT_PROFILES.map(p=>{
        const d = DEPT_BY[p.key];
        return (
          <div key={p.key} className="land-grid" style={{display:'grid',gridTemplateColumns:'190px 1fr',gap:'36px',alignItems:'center',padding:'34px 0',borderBottom:'1px solid var(--rule-soft)'}}>
            <div style={{display:'flex',justifyContent:'center'}}><Seal dept={d} size={172} title={d.nm}/></div>
            <div>
              <div className="mono" style={{fontSize:'10px',letterSpacing:'0.18em',textTransform:'uppercase',color:'var(--oxblood)',marginBottom:'6px'}}>
                Section {d.plate} · {d.code}{d.s2.toUpperCase()!==d.nm.toUpperCase() ? <React.Fragment> · {d.s2}</React.Fragment> : null}
              </div>
              <h2 className="zilla" style={{fontWeight:900,fontSize:'26px',textTransform:'uppercase',lineHeight:1.05,marginBottom:'10px',margin:'0 0 10px'}}>{d.nm}</h2>
              <p style={{fontFamily:'Newsreader',fontSize:'17px',lineHeight:1.6,color:'var(--char)',maxWidth:'62ch'}}>{p.blurb}</p>
            </div>
          </div>
        );
      })}
      <div style={{marginTop:'28px',display:'flex',gap:'10px',flexWrap:'wrap'}}>
        <NavA className="btn" to={'about'}>About the station →</NavA>
        <NavA className="btn" to={'journal'}>Read the Field Journal →</NavA>
      </div>
    </div></div>
  );
}

Object.assign(window, { Frontispiece, LandView, JournalView, JournalEntry, InstrumentsView, LibraryView, SpeciesPage, AboutView, SupportView, DepartmentsView, RemovedNote, NotFound });
