/* the turn - drill-down pages reachable from anywhere:
   SpeciesPage · AreaDetail · CatchDetail (+ food-web / ethics reads). */
const { VerdictBand: DVerdictBand, SafetyFlag: DSafetyFlag, Button: DButton } = window.TheTurnDesignSystem_583d9e;

/* ===== shared bits ===== */
function QuickFact({ label, value }) {
  return (
    <div style={{ flex: 1, minWidth: 0, padding: '0 4px' }}>
      <div className="overline" style={{ fontSize: 10 }}>{label}</div>
      <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 16, color: 'var(--text)', marginTop: 4, textTransform: 'capitalize' }}>{value}</div>
    </div>
  );
}
function BodySection({ title, children }) {
  return (
    <div style={{ marginBottom: 22 }}>
      <h3 style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 16, color: 'var(--text)', margin: '0 0 8px' }}>{title}</h3>
      <p style={{ margin: 0, fontSize: 15, color: 'var(--text-dim)', lineHeight: 1.5 }}>{children}</p>
    </div>
  );
}
function GradePill({ method, grade }) {
  const tone = grade === 'A' ? 'var(--verdict-go)' : grade === 'B' ? 'var(--verdict-worth)' : 'var(--text-label)';
  return (
    <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6, padding: '12px 6px', background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)' }}>
      <span style={{ fontFamily: 'var(--font-mono)', fontSize: 11, letterSpacing: '.08em', textTransform: 'uppercase', color: 'var(--text-label)' }}>{method}</span>
      <span style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 26, color: tone, lineHeight: 1 }}>{grade}</span>
    </div>
  );
}

/* species hero plate — shows a supplied image (media.species[id]), else an
   on-system striped placeholder with the silhouette + exact drop spec. */
function SpeciesHero({ sp }) {
  const file = (window.TURN.media && window.TURN.media.species) ? window.TURN.media.species[sp.id] : null;
  if (file) {
    return (
      <div style={{ position: 'relative', height: 210, borderRadius: 'var(--r-lg)', overflow: 'hidden', border: '1px solid var(--border)', marginBottom: 18 }}>
        <img src={`images/species/${file}`} alt={sp.common} style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }} />
        <div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, transparent 55%, rgba(7,38,31,0.55) 100%)' }} />
      </div>
    );
  }
  return (
    <div style={{
      position: 'relative', height: 210, borderRadius: 'var(--r-lg)', overflow: 'hidden', marginBottom: 18,
      border: '1px solid var(--border)', background: 'var(--surface-sunk)', display: 'grid', placeItems: 'center',
      backgroundImage: 'repeating-linear-gradient(135deg, transparent 0 11px, rgba(255,255,255,0.022) 11px 12px)',
    }}>
      <FishIcon species={sp.id} size={120} color="var(--text-label)" />
      <span style={{ position: 'absolute', top: 12, right: 14, fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '.12em', textTransform: 'uppercase', color: 'var(--text-disabled)' }}>image</span>
      <span style={{ position: 'absolute', left: 14, bottom: 12, right: 14, fontFamily: 'var(--font-mono)', fontSize: 10.5, letterSpacing: '.02em', color: 'var(--text-label)', lineHeight: 1.4 }}>
        cold, desaturated side profile · dawn light · images/species/{sp.id}.jpg
      </span>
    </div>
  );
}

/* ============================================================
   Species page
   ============================================================ */
function SpeciesPage({ id, onBack, onArea }) {
  if (id === '_foodweb') return <ReadPage onBack={onBack} title="Walker Bay food web" sub="the chain your verdicts sit inside" body={FOODWEB} />;
  if (id === '_ethics') return <ReadPage onBack={onBack} title="Angler ethics" sub="leave the water better than you found it" body={ETHICS} />;
  const sp = speciesById(id);
  if (!sp) return null;
  const T = window.TURN;
  const recent = T.logs.filter(l => l.species && l.species.includes(id));
  const days = sp.season.open ? null : daysUntil(sp.season.until);

  return (
    <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', background: 'var(--bg)' }}>
      <DrillHeader onBack={onBack} title={sp.common} sub={sp.sci} />
      <div style={{ flex: 1, overflowY: 'auto', padding: '16px var(--gutter) 40px' }}>
        {/* status pips */}
        <div style={{ display: 'flex', gap: 7, flexWrap: 'wrap', marginBottom: 14 }}>
          <SassiPip status={sp.sassi} />
          <IucnPip status={sp.iucn} />
          <SeasonPip season={sp.season} />
          <span style={{ marginLeft: 'auto', display: 'inline-flex', alignItems: 'center', gap: 8 }}>
            <span className="overline" style={{ fontSize: 10 }}>lure</span><LureTier tier={sp.lure} />
          </span>
        </div>

        {/* hero plate — supplied image or on-system placeholder */}
        <SpeciesHero sp={sp} />

        {/* season countdown */}
        {days != null && (
          <div style={{ marginBottom: 16 }}>
            <DSafetyFlag tone="caution" title={`Closed - opens ${sp.season.until}`} detail={`${days} days. ${sp.season.note}. Release any you land in the meantime.`} />
          </div>
        )}

        {/* quick facts */}
        <div style={{ display: 'flex', background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)', padding: '14px 10px', marginBottom: 22 }}>
          <QuickFact label="min size" value={`${sp.minSize}cm`} />
          <span style={{ width: 1, background: 'var(--hairline)' }} />
          <QuickFact label="bag" value={sp.bag === 0 ? 'no limit' : `${sp.bag}/person`} />
          <span style={{ width: 1, background: 'var(--hairline)' }} />
          <QuickFact label="season" value={sp.season.open ? 'open' : 'closed'} />
          <span style={{ width: 1, background: 'var(--hairline)' }} />
          <QuickFact label="authority" value={sp.authority} />
        </div>

        <BodySection title="Where they hold">{sp.holds}</BodySection>
        <BodySection title="How to catch them">{sp.catch}</BodySection>
        <BodySection title="Handling for release">{sp.handling}</BodySection>
        <BodySection title="Conservation context">{sp.conservation}</BodySection>

        {/* areas to find them */}
        <div style={{ marginBottom: 22 }}>
          <SectionLabel>where to find them</SectionLabel>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {sp.areas.map(aid => {
              const a = areaById(aid); if (!a) return null;
              return (
                <button key={aid} onClick={() => onArea(aid)} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 14px', background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)', cursor: 'pointer', textAlign: 'left' }}>
                  <VerdictDot verdict={a.today.verdict} confidence={a.today.confidence} size={11} />
                  <span style={{ fontSize: 15, fontWeight: 600, color: 'var(--text)', flex: 1 }}>{a.name}</span>
                  <span style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--text-label)' }}>{a.distKm}km {a.bearing}</span>
                  <Icon name="chevron" size={16} color="var(--text-label)" />
                </button>
              );
            })}
          </div>
        </div>

        {/* recent logs */}
        {recent.length > 0 && (
          <div>
            <SectionLabel>your runs with this fish</SectionLabel>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {recent.map(l => {
                const a = areaById(l.areaId);
                return (
                  <div key={l.id} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '11px 14px', background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)' }}>
                    <span style={{ width: 8, height: 8, borderRadius: '50%', background: 'var(--verdict-go)', flex: '0 0 auto' }} />
                    <span style={{ fontSize: 14, color: 'var(--text)', flex: 1 }}>{l.result}</span>
                    <span style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--text-label)', whiteSpace: 'nowrap' }}>{a ? a.name.split(' ')[0] : ''} · {l.when}</span>
                  </div>
                );
              })}
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

/* instrument-style call plate (mirrors Today's instrument layout) */
function AreaCallPlate({ area, call, onSpecies }) {
  const t = area.today;
  const c = VERDICT[t.verdict].color;
  const low = t.confidence === 'low';
  const line = call ? call.line : (t.verdict === 'go' ? `Go - ${t.species[0]}` : t.verdict === 'scout' ? 'Worth a scout' : 'Skip today');
  const note = call ? call.why[0] : (area.callout || area.scoutQ);
  return (
    <div style={{ border: `2px ${low ? 'dashed' : 'solid'} ${c}`, borderRadius: 'var(--r-lg)', overflow: 'hidden', background: 'var(--surface)', marginBottom: 22 }}>
      {/* hero zone */}
      <div style={{ padding: 'var(--space-5) var(--space-5) var(--space-4)', background: low ? 'transparent' : `color-mix(in srgb, ${c} 12%, var(--bg))`, position: 'relative', overflow: 'hidden' }}>
        {t.species && t.species[0] && (
          <div aria-hidden="true" style={{ position: 'absolute', right: -22, top: '50%', transform: 'translateY(-50%)', opacity: 0.12 }}>
            <FishIcon species={t.species[0]} size={140} color={c} />
          </div>
        )}
        <div style={{ display: 'flex', alignItems: 'center', marginBottom: 10, position: 'relative' }}>
          <span className="overline" style={{ color: c }}>today's call</span>
          <span style={{ marginLeft: 'auto', fontFamily: 'var(--font-mono)', fontSize: 13, color: 'var(--text-label)' }}>score <b style={{ color: 'var(--text)', fontSize: 17 }}>{t.score || '-'}</b></span>
        </div>
        <h2 style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 30, lineHeight: 1.05, letterSpacing: '-0.02em', color: 'var(--text)', margin: 0, position: 'relative' }}>{line}</h2>
        {t.window && t.window !== '-' && <div className="metric" style={{ marginTop: 9, fontSize: 15, color: c }}>{t.window}</div>}
      </div>
      {/* instrument rows */}
      <div style={{ padding: '2px var(--space-5) var(--space-5)' }}>
        {t.verdict !== 'skip' && (
          <React.Fragment>
            <div style={{ display: 'flex', gap: 14, padding: '11px 0', borderTop: '1px solid var(--hairline)' }}>
              <span className="overline" style={{ flex: '0 0 76px', paddingTop: 2 }}>platform</span>
              <span style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 16, color: 'var(--text)', textTransform: 'capitalize' }}>{PLATFORM_LABEL[t.platform]}</span>
            </div>
            <div style={{ display: 'flex', gap: 14, padding: '11px 0', borderTop: '1px solid var(--hairline)' }}>
              <span className="overline" style={{ flex: '0 0 76px', paddingTop: 2 }}>method</span>
              <span style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 16, color: 'var(--text)', textTransform: 'capitalize' }}>{METHOD_LABEL[t.method]}</span>
            </div>
          </React.Fragment>
        )}
        {note && (
          <div style={{ display: 'flex', alignItems: 'flex-start', gap: 9, padding: '12px 0 2px', borderTop: '1px solid var(--hairline)' }}>
            <span style={{ flex: '0 0 auto', width: 16, height: 16, borderRadius: '50%', marginTop: 1, display: 'grid', placeItems: 'center', border: `1.5px ${area.callout ? 'solid' : 'dashed'} ${area.lifecycle === 'guide' ? 'var(--verdict-go)' : 'var(--text-label)'}` }}>
              <span style={{ width: 4, height: 4, borderRadius: '50%', background: area.lifecycle === 'guide' ? 'var(--verdict-go)' : 'var(--text-label)' }} />
            </span>
            <span style={{ fontSize: 14, color: area.lifecycle === 'guide' ? 'var(--verdict-go)' : 'var(--text-dim)', lineHeight: 1.35 }}>{note}</span>
          </div>
        )}
      </div>
    </div>
  );
}

/* ============================================================
   Area detail
   ============================================================ */
/* A named shore mark inside an area: type, risk, what it's known for, the
   local read, and a styled hazard warning (kelp, surge, cut-off). */
function MarkCard({ mark, onSpecies }) {
  const riskTone = {
    low: 'var(--verdict-go)', moderate: 'var(--verdict-caution)',
    high: 'var(--verdict-caution)', extreme: 'var(--verdict-danger)',
  }[mark.risk] || 'var(--text-label)';
  return (
    <div style={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)', padding: 'var(--space-4)' }}>
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
        <span style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 16, color: 'var(--text)', flex: 1, minWidth: 0 }}>{mark.name}</span>
        <span style={{ fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '.05em', textTransform: 'uppercase', color: riskTone, border: `1px solid ${riskTone}`, borderRadius: 'var(--r-pill)', padding: '2px 7px', whiteSpace: 'nowrap', flex: '0 0 auto' }}>{mark.risk} risk</span>
      </div>
      <div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--text-label)', marginTop: 3 }}>
        {mark.type}{mark.methods && mark.methods.length ? ` · ${mark.methods.join(' / ')}` : ''}
      </div>

      {mark.knownFor && mark.knownFor.length > 0 && (
        <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginTop: 10 }}>
          {mark.knownFor.map((id) => {
            const sp = speciesById(id) || speciesById(id.replace(/_/g, '-'));
            const label = sp ? sp.common : id.replace(/[_-]+/g, ' ');
            return (
              <button key={id} onClick={() => sp && onSpecies(sp.id)} style={{ display: 'inline-flex', alignItems: 'center', gap: 5, padding: '4px 9px', background: 'var(--surface-sunk)', border: '1px solid var(--border)', borderRadius: 'var(--r-pill)', cursor: sp ? 'pointer' : 'default', fontSize: 12, color: 'var(--text-dim)' }}>
                {label}
              </button>
            );
          })}
        </div>
      )}

      <p style={{ margin: '12px 0 0', fontSize: 13.5, color: 'var(--text-dim)', lineHeight: 1.45 }}>{mark.note}</p>

      {mark.hazard && (
        <div style={{ display: 'flex', gap: 8, marginTop: 10, padding: '9px 11px', background: 'color-mix(in srgb, var(--verdict-caution) 12%, var(--bg))', border: '1px solid color-mix(in srgb, var(--verdict-caution) 35%, transparent)', borderRadius: 'var(--r-sm)' }}>
          <span style={{ flex: '0 0 auto', color: 'var(--verdict-caution)', fontWeight: 700, fontSize: 13 }}>!</span>
          <span style={{ fontSize: 12.5, color: 'var(--text-dim)', lineHeight: 1.4 }}>{mark.hazard}</span>
        </div>
      )}
    </div>
  );
}

function AreaDetail({ id, onBack, onSpecies, onArea, onLog, onNewLog }) {
  const T = window.TURN;
  const a = areaById(id);
  if (!a) return null;
  const d = a.detail || {};
  const t = a.today;
  const isPrimary = T.today.primary.areaId === id;
  const isBackup = T.today.backup.areaId === id;
  const call = isPrimary ? T.today.primary : isBackup ? T.today.backup : null;
  const recent = T.logs.filter(l => l.areaId === id);
  const v = VERDICT[t.verdict];

  return (
    <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', background: 'var(--bg)' }}>
      <DrillHeader onBack={onBack} title={a.name} sub={`${a.sub} · ${a.distKm}km ${a.bearing}`} />
      <div style={{ flex: 1, overflowY: 'auto', padding: '14px var(--gutter) 40px' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 16, flexWrap: 'wrap' }}>
          <VerdictBadge verdict={t.verdict} />
          <PostureBadge posture={a.posture} />
          <ConfidenceBadge lifecycle={a.lifecycle} style={{ marginLeft: 'auto' }} />
        </div>

        {a.gatedOut ? (
          <div style={{ marginBottom: 22 }}><DSafetyFlag tone="gate" title="Filtered out today" detail={a.gatedOut} /></div>
        ) : (
          <AreaCallPlate area={a} call={call} onSpecies={onSpecies} />
        )}

        {/* scout mission */}
        {d.scoutMission && (
          <div style={{ marginBottom: 22 }}>
            <SectionLabel>scout mission</SectionLabel>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {d.scoutMission.map((m, i) => (
                <div key={i} style={{ display: 'flex', gap: 10, alignItems: 'flex-start' }}>
                  <span style={{ flex: '0 0 auto', width: 20, height: 20, borderRadius: '50%', border: '1.5px solid var(--border-strong)', display: 'grid', placeItems: 'center', fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--text-label)', marginTop: 1 }}>{i + 1}</span>
                  <span style={{ fontSize: 14.5, color: 'var(--text-dim)', lineHeight: 1.4 }}>{m}</span>
                </div>
              ))}
            </div>
          </div>
        )}

        {/* named marks in this area */}
        {a.marks && a.marks.length > 0 && (
          <div style={{ marginBottom: 22 }}>
            <SectionLabel>marks here · {a.marks.length}</SectionLabel>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
              {a.marks.map((m) => <MarkCard key={m.id} mark={m} onSpecies={onSpecies} />)}
            </div>
          </div>
        )}

        {/* hard gates */}
        {d.gates && (
          <div style={{ marginBottom: 22 }}>
            <SectionLabel>hard gates · legal + safety</SectionLabel>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {d.gates.map((g, i) => (
                <div key={i} style={{ display: 'flex', gap: 10, alignItems: 'center', padding: '11px 14px', background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)' }}>
                  <span style={{ width: 18, height: 18, borderRadius: 4, background: 'var(--surface-raised)', border: '1.5px solid var(--border-strong)', display: 'grid', placeItems: 'center', fontSize: 11, color: 'var(--text-label)', flex: '0 0 auto' }}>⌗</span>
                  <span style={{ fontSize: 14, color: 'var(--text)' }}>{g}</span>
                </div>
              ))}
            </div>
          </div>
        )}

        {/* method fit */}
        {d.methodFit && (
          <div style={{ marginBottom: 22 }}>
            <SectionLabel>method fit</SectionLabel>
            <div style={{ display: 'flex', gap: 10 }}>
              <GradePill method="fly" grade={d.methodFit.fly} />
              <GradePill method="spin" grade={d.methodFit.spin} />
              <GradePill method="bait" grade={d.methodFit.bait} />
            </div>
          </div>
        )}

        {/* kayak */}
        {d.kayak && (
          <div style={{ marginBottom: 22 }}>
            <SectionLabel>kayak fit</SectionLabel>
            <div style={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)', padding: 'var(--space-4)' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <span style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 24, color: d.kayak.grade === 'A' ? 'var(--verdict-go)' : d.kayak.grade === 'B' ? 'var(--verdict-worth)' : 'var(--text-label)' }}>{d.kayak.grade}</span>
                <span style={{ fontSize: 14, color: 'var(--text-dim)' }}>{d.kayak.why}</span>
              </div>
              {d.kayak.gates && (
                <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginTop: 12 }}>
                  {d.kayak.gates.map((g, i) => <TagChip key={i} mono={false} style={{ color: 'var(--verdict-caution)', borderColor: 'color-mix(in srgb, var(--verdict-caution) 40%, var(--border))' }}>{g}</TagChip>)}
                </div>
              )}
            </div>
          </div>
        )}

        {/* species hypotheses */}
        {t.species && t.species.length > 0 && (
          <div style={{ marginBottom: 22 }}>
            <SectionLabel>what's likely holding</SectionLabel>
            <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
              {t.species.map(sid => <SpeciesChip key={sid} id={sid} onClick={onSpecies} color={v.color} />)}
            </div>
          </div>
        )}

        {/* evidence chain */}
        {d.evidence && (
          <div style={{ marginBottom: 22 }}>
            <SectionLabel>evidence chain</SectionLabel>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {d.evidence.map((e, i) => (
                <div key={i} style={{ display: 'flex', gap: 10, alignItems: 'flex-start', padding: '11px 14px', background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)' }}>
                  <span style={{ flex: '0 0 auto', width: 22, height: 22, borderRadius: 5, display: 'grid', placeItems: 'center', fontFamily: 'var(--font-mono)', fontWeight: 700, fontSize: 12, background: e.grade === 'A' ? 'color-mix(in srgb, var(--verdict-go) 18%, var(--bg))' : 'var(--surface-raised)', color: e.grade === 'A' ? 'var(--verdict-go)' : 'var(--text-dim)', border: `1px solid ${e.grade === 'A' ? 'var(--verdict-go)' : 'var(--border-strong)'}` }}>{e.grade}</span>
                  <span style={{ fontSize: 14, color: 'var(--text-dim)', lineHeight: 1.4 }}>{e.text}</span>
                </div>
              ))}
            </div>
          </div>
        )}

        {/* recent logs */}
        {recent.length > 0 && (
          <div style={{ marginBottom: 22 }}>
            <SectionLabel>your last runs here</SectionLabel>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {recent.map(l => (
                <button key={l.id} onClick={() => onLog(l.id)} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '11px 14px', background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)', cursor: 'pointer', textAlign: 'left' }}>
                  <span style={{ width: 9, height: 9, borderRadius: '50%', background: OUTCOME[l.outcome].c, flex: '0 0 auto' }} />
                  <span style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--text-label)', flex: '0 0 auto' }}>{l.when}</span>
                  <span style={{ fontSize: 13.5, color: 'var(--text)', flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{l.result || l.outcome}</span>
                  <Icon name="chevron" size={15} color="var(--text-label)" />
                </button>
              ))}
            </div>
          </div>
        )}

        {/* unlock guide */}
        {d.unlock && (
          <div style={{ marginBottom: 22 }}>
            <SectionLabel>what unlocks Guide Mode here</SectionLabel>
            <div style={{ background: a.lifecycle === 'guide' ? 'color-mix(in srgb, var(--verdict-go) 10%, var(--surface))' : 'var(--surface)', border: `1px solid ${a.lifecycle === 'guide' ? 'var(--verdict-go)' : 'var(--border)'}`, borderRadius: 'var(--r-md)', padding: 'var(--space-4)' }}>
              {d.unlock.map((u, i) => (
                <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 9, padding: '4px 0', fontSize: 14, color: 'var(--text-dim)' }}>
                  <span style={{ width: 6, height: 6, borderRadius: '50%', background: a.lifecycle === 'guide' ? 'var(--verdict-go)' : 'var(--text-label)' }} />{u}
                </div>
              ))}
            </div>
          </div>
        )}

        {!a.gatedOut && <DButton variant="primary" size="fat" full onClick={() => onNewLog(id)} icon={<Icon name="plus" size={20} />}>Bank a session here</DButton>}
      </div>
    </div>
  );
}

/* ============================================================
   Catch detail
   ============================================================ */
function CatchDetail({ id, onBack, onArea, onSpecies }) {
  const T = window.TURN;
  const l = T.logs.find(x => x.id === id);
  if (!l) return null;
  const a = areaById(l.areaId);
  const o = OUTCOME[l.outcome];
  const s = l.snapshot;
  const matched = (field) => l[field] && s[field] && l[field] === s[field];

  return (
    <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', background: 'var(--bg)' }}>
      <DrillHeader onBack={onBack} title={a ? a.name : l.areaId} sub={l.date}
        right={<button onClick={() => a && onArea(a.id)} style={{ background: 'var(--surface)', border: '1px solid var(--border-strong)', borderRadius: 'var(--r-pill)', height: 34, padding: '0 12px', color: 'var(--text-dim)', cursor: 'pointer', fontSize: 12, fontFamily: 'var(--font-mono)', letterSpacing: '.04em' }}>area ›</button>} />
      <div style={{ flex: 1, overflowY: 'auto', padding: '16px var(--gutter) 40px' }}>
        {/* outcome */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 20 }}>
          <span style={{ width: 13, height: 13, borderRadius: '50%', background: o.c }} />
          <span style={{ fontFamily: 'var(--font-mono)', fontSize: 13, letterSpacing: '.1em', textTransform: 'uppercase', color: o.c }}>{o.label}</span>
        </div>

        {/* what the app called */}
        <div style={{ marginBottom: 20 }}>
          <SectionLabel>the call it made</SectionLabel>
          <div style={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)', padding: 'var(--space-5)' }}>
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 19, color: 'var(--text)', lineHeight: 1.15 }}>{s.line}</div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 0, marginTop: 12 }}>
              <SnapRow label="window" value={s.window} />
              <SnapRow label="platform" value={s.platform} match={matched('platform')} />
              <SnapRow label="method" value={s.method} match={matched('method')} />
              <SnapRow label="score" value={String(s.score)} mono />
            </div>
          </div>
        </div>

        {/* what you reported */}
        {l.outcome !== 'skipped' && (
          <div style={{ marginBottom: 20 }}>
            <SectionLabel>what actually went down</SectionLabel>
            <div style={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)', padding: 'var(--space-5)' }}>
              <SnapRow label="platform" value={l.platform ? `${PLATFORM_LABEL[l.platform]}${matched('platform') ? ' · matched' : ''}` : '-'} />
              <SnapRow label="method" value={l.method ? `${METHOD_LABEL[l.method]}${matched('method') ? ' · matched' : ''}` : '-'} />
              {l.tackle && <SnapRow label="tackle" value={l.tackle} />}
              {l.result && <SnapRow label="result" value={l.result} />}
              {l.species && l.species.length > 0 && (
                <div style={{ display: 'flex', gap: 7, flexWrap: 'wrap', marginTop: 12 }}>
                  {l.species.map(sid => <SpeciesChip key={sid} id={sid} onClick={onSpecies} color="var(--verdict-go)" />)}
                </div>
              )}
            </div>
          </div>
        )}

        {/* field observations */}
        {l.obs && (
          <div style={{ marginBottom: 20 }}>
            <SectionLabel>what you clocked</SectionLabel>
            <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
              {l.obs.mouth && l.obs.mouth !== 'unknown' && <ObsRead label="mouth" value={l.obs.mouth} />}
              {l.obs.water && l.obs.water !== 'unknown' && <ObsRead label="water" value={l.obs.water} />}
              {l.obs.bait && l.obs.bait !== 'unknown' && <ObsRead label="bait/birds" value={l.obs.bait} />}
            </div>
          </div>
        )}

        {/* conditions snapshot */}
        <div style={{ marginBottom: 20 }}>
          <SectionLabel>conditions, frozen at the time</SectionLabel>
          <div style={{ background: 'var(--surface-sunk)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)', padding: 'var(--space-4) var(--space-5)' }}>
            {Object.entries({ wind: s.cond.wind, swell: s.cond.swell, pressure: s.cond.pressure, cloud: s.cond.cloud, tide: s.cond.tide }).map(([k, val]) => (
              <SnapRow key={k} label={k} value={val} mono />
            ))}
          </div>
        </div>

        {/* notes */}
        {l.notes && (
          <div>
            <SectionLabel>notes</SectionLabel>
            <p style={{ margin: 0, fontSize: 15, color: 'var(--text-dim)', lineHeight: 1.5, fontStyle: 'italic' }}>{l.notes}</p>
          </div>
        )}
      </div>
    </div>
  );
}
function SnapRow({ label, value, match, mono }) {
  return (
    <div style={{ display: 'flex', alignItems: 'baseline', gap: 12, padding: '7px 0', borderTop: '1px solid var(--hairline)' }}>
      <span className="overline" style={{ flex: '0 0 84px' }}>{label}</span>
      <span style={{ flex: 1, fontFamily: mono ? 'var(--font-mono)' : 'var(--font-body)', fontSize: 14.5, color: 'var(--text)', textTransform: mono ? 'none' : 'capitalize' }}>
        {value}
        {match && <span style={{ color: 'var(--verdict-go)', fontFamily: 'var(--font-mono)', fontSize: 11, marginLeft: 6 }}>✓</span>}
      </span>
    </div>
  );
}
function ObsRead({ label, value }) {
  return (
    <div style={{ padding: '8px 12px', background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)' }}>
      <div className="overline" style={{ fontSize: 10 }}>{label}</div>
      <div style={{ fontSize: 14, color: 'var(--text)', fontWeight: 600, marginTop: 3 }}>{value}</div>
    </div>
  );
}

/* ===== read pages (cross-cutting) ===== */
function ReadPage({ onBack, title, sub, body }) {
  return (
    <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', background: 'var(--bg)' }}>
      <DrillHeader onBack={onBack} title={title} sub={sub} />
      <div style={{ flex: 1, overflowY: 'auto', padding: '18px var(--gutter) 40px' }}>
        {body.map((b, i) => b.h ? <BodySection key={i} title={b.h}>{b.p}</BodySection> : <p key={i} style={{ fontSize: 15, color: 'var(--text-dim)', lineHeight: 1.5, marginTop: 0 }}>{b.p}</p>)}
      </div>
    </div>
  );
}
const FOODWEB = [
  { p: 'Every verdict the app makes sits inside this chain. Read it once and the calls start to make sense - why a clean push with baitfish means GO, why a foamy SW blowout means SKIP.' },
  { h: 'The base', p: 'Sand prawn, mud prawn and bloodworm work the estuary flats; mussel, redbait and kelp hold the rock. This is what grunter, steenbras and stumpnose root for - and where the food web starts.' },
  { h: 'The bait shoals', p: 'Mullet, maasbanker and glassies move on the warm push. When they shower and the birds work, the predators are close. Showering bait over a mouth is the single best GO signal in the bay.' },
  { h: 'The hunters', p: 'Garrick and elf run the backline chasing bait; kob hold deep in the dirty-clean seam. They follow the shoals, so the app reads bait presence (from your logs) as a strong positive.' },
  { h: 'Why it matters', p: 'A lure imitates a fleeing baitfish. No bait, no hunters, no point throwing a lure - that is a bait day, and the honest call is SKIP. The chain is the reasoning.' },
];
const ETHICS = [
  { p: 'A personal tool for one angler learning one stretch of coast. The point is to fish it for decades - so the rules here lean conservative.' },
  { h: 'Release well', p: 'Wet hands, support the belly, minimise air time. Revive head-to-current until the fish kicks off on its own. A photo costs the fish air - make it quick or skip it.' },
  { h: 'Keep less than the law allows', p: 'Bag limits are a ceiling, not a target. Keep one fish for a meal; let the breeders and the big old fish go. Endangered and red-list species go back regardless of size.' },
  { h: 'Respect the gates', p: 'Permits current, closed seasons honoured, MPAs left alone. The app gates these hard for a reason - they are the difference between fishing here next year and not.' },
  { h: 'Leave it better', p: 'Carry out more line and litter than you brought. The water remembers.' },
];

Object.assign(window, { SpeciesPage, AreaDetail, CatchDetail });
