/* global React, Card, Button, Segmented, Pill, Eyebrow, Progress */
// ============================================================================
// plan.jsx — Tab 2 · Plan (_spec-app-v2.md): one screen, four zoom levels.
//   Week · Month · Season · Career   (Day is the Today tab)
// Data only through window.PlanAPI. Mounted by shell.jsx with { memberDb, isAdmin, go, sub }.
// Design rules kept here: the dream is concrete and dated (every gate shown), and there is
// no progress-to-elite meter anywhere. Every number shown is his own step or his own effort.
// ============================================================================
(() => {
  const { useState, useEffect } = React;
  // Lazy: resolves window.PlanAPI at call time, whatever the script load order.
  const A = new Proxy({}, { get: (_, k) => (window.PlanAPI ? window.PlanAPI[k] : undefined) });

  // ---------------------------------------------------------------- helpers
  const SUBS = [
    { value: 'week', label: 'Week' }, { value: 'month', label: 'Month' },
    { value: 'season', label: 'Season' }, { value: 'career', label: 'Career' },
  ];
  const WD_SHORT = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
  const WD_LETTER = ['M', 'T', 'W', 'T', 'F', 'S', 'S'];
  const AREAS = [
    { key: 'physical', label: 'Physical' }, { key: 'skill', label: 'Skills' },
    { key: 'academic', label: 'School' }, { key: 'profile', label: 'Application' },
  ];
  const CONTEXT_KINDS = ['school', 'team', 'games', 'travel'];
  const EVENT_KIND = {
    game: 'Game', tournament: 'Tournament', cancellation: 'Cancelled', school_test: 'School test', deadline: 'Deadline',
    holiday: 'Holiday', injury: 'Injury', test_day: 'Test day', checkpoint_week: 'Checkpoint week',
    season_change: 'Season change', travel: 'Travel', other: 'Event',
  };
  const SCHOOL_KIND = { test: 'Test', deadline: 'Deadline', homework: 'Homework', handin: 'Homework' };
  const GATE_KIND = { selection: 'Selection', admission: 'Admission', eligibility: 'Eligibility', checkpoint: 'Our checkpoint', terminal: 'Finish line' };

  const d10 = (s) => (s ? String(s).slice(0, 10) : '');
  const hhmm = (t) => (t ? String(t).slice(0, 5) : '');
  const mon = (d) => d.toLocaleString('en-GB', { month: 'short' });
  const dayLabel = (s) => { const d = A.parse(d10(s)); return `${WD_SHORT[A.weekday(d10(s))]} ${d.getDate()} ${mon(d)}`; };
  const monthYear = (s) => A.parse(d10(s)).toLocaleString('en-GB', { month: 'long', year: 'numeric' });
  const monthLabel = (key) => A.parse(key + '-01').toLocaleString('en-GB', { month: 'short', year: 'numeric' });
  function rangeLabel(from, to) {
    const a = A.parse(d10(from)), b = A.parse(d10(to || from));
    if (d10(from) === d10(to || from)) return `${a.getDate()} ${mon(a)}`;
    if (a.getMonth() === b.getMonth() && a.getFullYear() === b.getFullYear()) return `${a.getDate()}–${b.getDate()} ${mon(b)}`;
    return `${a.getDate()} ${mon(a)} – ${b.getDate()} ${mon(b)}`;
  }
  function lastOfMonth(key) { const d = A.parse(key + '-01'); d.setMonth(d.getMonth() + 1); d.setDate(0); return A.ymd(d); }
  function seasonMonths() {
    const out = []; let k = '2026-10';
    while (k <= A.SEASON.end.slice(0, 7)) {
      out.push(k);
      const d = A.parse(k + '-01'); d.setMonth(d.getMonth() + 1); k = A.ymd(d).slice(0, 7);
    }
    return out;
  }
  function monthsBetween(a, b) {
    const x = A.parse(a), y = A.parse(b);
    let m = (y.getFullYear() - x.getFullYear()) * 12 + (y.getMonth() - x.getMonth());
    if (y.getDate() < x.getDate()) m -= 1;
    return m;
  }
  function countdown(target) {
    const t = A.today(), s = d10(target);
    if (!s) return '';
    if (s < t) return 'passed';
    if (s === t) return 'today';
    const m = monthsBetween(t, s);
    if (m < 1) { const d = Math.round((A.parse(s) - A.parse(t)) / 864e5); return d === 1 ? '1 day' : `${d} days`; }
    if (m < 24) return m === 1 ? '1 month' : `${m} months`;
    const y = Math.floor(m / 12), r = m % 12;
    return `${y} years${r ? ` ${r} ${r === 1 ? 'month' : 'months'}` : ''}`;
  }
  const overlaps = (e, from, to) => d10(e.starts_on) <= to && d10(e.ends_on || e.starts_on) >= from;

  // Load anything async; keeps the last data while reloading.
  function useLoad(fn, deps) {
    const [s, setS] = useState({ loading: true, error: null, data: null });
    const [tick, setTick] = useState(0);
    useEffect(() => {
      let alive = true;
      setS(x => ({ ...x, loading: true, error: null }));
      Promise.resolve().then(fn).then(
        d => { if (alive) setS({ loading: false, error: null, data: d }); },
        e => { console.error(e); if (alive) setS({ loading: false, error: (e && e.message) || String(e), data: null }); });
      return () => { alive = false; };
    }, [...deps, tick]);
    return { ...s, reload: () => setTick(t => t + 1) };
  }

  // ---------------------------------------------------------------- shared bits
  const navBtn = { width: 40, height: 40, borderRadius: 20, border: '1px solid var(--border)', background: 'var(--bg-raised)', fontSize: 20, cursor: 'pointer', color: 'var(--fg)', flexShrink: 0 };
  const linkBtn = { background: 'none', border: 'none', color: 'var(--signal)', cursor: 'pointer', fontSize: 13, padding: 0, fontFamily: 'var(--font-sans)' };
  const muted = { color: 'var(--fg-muted)', fontSize: 13 };
  const mono = { fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--fg-subtle)', fontVariantNumeric: 'tabular-nums' };

  function Status({ state, what }) {
    if (state.error) return <Card pad={14} style={{ borderColor: 'var(--danger)', margin: '12px 0', fontSize: 14 }}>Could not load {what}: {state.error}</Card>;
    if (state.loading && !state.data) return <div style={{ ...muted, padding: 24, textAlign: 'center' }}>Loading…</div>;
    return null;
  }
  function Section({ label, children, style }) {
    return <div style={{ marginTop: 28, ...style }}><Eyebrow style={{ marginBottom: 10 }}>{label}</Eyebrow>{children}</div>;
  }
  function Pager({ onPrev, onNext, prevDisabled, nextDisabled, eyebrow, title, below }) {
    return (
      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
        <button aria-label="Previous" onClick={onPrev} disabled={prevDisabled} style={{ ...navBtn, opacity: prevDisabled ? 0.3 : 1 }}>‹</button>
        <div style={{ flex: 1, textAlign: 'center', minWidth: 0 }}>
          {eyebrow && <Eyebrow>{eyebrow}</Eyebrow>}
          <div style={{ fontSize: 20, fontWeight: 500, marginTop: 2 }}>{title}</div>
          {below}
        </div>
        <button aria-label="Next" onClick={onNext} disabled={nextDisabled} style={{ ...navBtn, opacity: nextDisabled ? 0.3 : 1 }}>›</button>
      </div>
    );
  }

  // ================================================================ WEEK
  function WeekView({ memberDb, go }) {
    const thisMonday = A.mondayOf(A.today());
    const [monday, setMonday] = useState(thisMonday);
    const st = useLoad(() => Promise.all([A.getWeek(memberDb, monday), A.getWeek(memberDb, A.addDays(monday, 7))]), [memberDb, monday]);
    const [wk, next] = st.data || [];
    const today = A.today();
    const rel = monday === thisMonday ? 'This week' : monday === A.addDays(thisMonday, 7) ? 'Next week' : monday === A.addDays(thisMonday, -7) ? 'Last week' : '';
    const n = A.weekOfSeason(monday);
    const title = `${n >= 1 ? `Week ${n}` : 'Pre-season'} · ${rangeLabel(monday, A.addDays(monday, 6))}`;

    return (
      <div>
        <Pager onPrev={() => setMonday(A.addDays(monday, -7))} onNext={() => setMonday(A.addDays(monday, 7))} eyebrow={rel || ' '} title={title}
               below={monday !== thisMonday && <button style={{ ...linkBtn, marginTop: 4 }} onClick={() => setMonday(thisMonday)}>Back to this week</button>} />
        <Status state={st} what="the week" />
        {wk && (
          <>
            <Card pad={16} style={{ marginTop: 16 }}>
              <Eyebrow>Effort</Eyebrow>
              {wk.sessionsPlanned > 0 ? (
                <>
                  <div style={{ fontSize: 17, fontWeight: 500, margin: '6px 0 8px' }}>{wk.sessionsDone} of {wk.sessionsPlanned} sessions done</div>
                  <Progress value={wk.sessionsDone} max={wk.sessionsPlanned} color="var(--success)" height={4} />
                  <div style={{ ...mono, marginTop: 8 }}>All tasks: {wk.done} of {wk.total}</div>
                </>
              ) : <div style={{ ...muted, marginTop: 6 }}>No sessions planned this week yet.</div>}
            </Card>

            <HabitGrid days={wk.days} today={today} />

            <div style={{ marginTop: 16, display: 'flex', flexDirection: 'column', gap: 10 }}>
              {wk.days.map((d, i) => <DayCard key={d.day} d={d} i={i} isToday={d.day === today} events={wk.events.filter(e => overlaps(e, d.day, d.day))} />)}
            </div>

            <Section label="School tests and deadlines">
              <SchoolList items={wk.schoolItems} go={go} />
            </Section>
            <Section label="Games and events">
              <EventList events={[...wk.events, ...((next && next.events) || [])]} from={wk.from} />
            </Section>
          </>
        )}
      </div>
    );
  }

  function HabitGrid({ days, today }) {
    return (
      <Card pad={16} style={{ marginTop: 10 }}>
        <Eyebrow style={{ marginBottom: 10 }}>Daily habits</Eyebrow>
        <div style={{ display: 'grid', gridTemplateColumns: '84px repeat(7, minmax(0, 1fr))', rowGap: 8, alignItems: 'center' }}>
          <div />
          {days.map((d, i) => (
            <div key={d.day} style={{ ...mono, textAlign: 'center', color: d.day === today ? 'var(--signal)' : 'var(--fg-subtle)', fontWeight: d.day === today ? 600 : 400 }}>{WD_LETTER[i]}</div>
          ))}
          {A.HABITS.map(h => (
            <React.Fragment key={h.key}>
              <div style={{ fontSize: 12, color: 'var(--fg-muted)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{h.label}</div>
              {days.map(d => {
                const v = d.habits ? d.habits[h.key] : null;
                return (
                  <div key={d.day} style={{ display: 'flex', justifyContent: 'center' }}>
                    {v === null || v === undefined ? <span style={{ width: 10, height: 10 }} /> : (
                      <span title={`${h.label}: ${v ? 'done' : 'not yet'}`} style={{
                        width: 10, height: 10, borderRadius: 999, boxSizing: 'border-box',
                        background: v ? 'var(--success)' : 'transparent', border: v ? 'none' : '1.5px solid var(--border-strong)',
                      }} />
                    )}
                  </div>
                );
              })}
            </React.Fragment>
          ))}
        </div>
      </Card>
    );
  }

  function DayCard({ d, i, isToday, events }) {
    const dt = A.parse(d.day);
    const rows = [
      ...d.blocks.filter(b => CONTEXT_KINDS.includes(b.kind)).map(b => ({ t: hhmm(b.start_time), k: 'b' + b.id, b })),
      ...d.items.slice().sort((a, b) => (a.sort_order || 0) - (b.sort_order || 0)).map(it => ({ t: hhmm(it.start_time) || '99:99', k: 'i' + it.id, it })),
    ].sort((a, b) => a.t.localeCompare(b.t));
    return (
      <Card pad={12} style={{ borderColor: isToday ? 'var(--signal)' : 'var(--border)', borderWidth: isToday ? 1.5 : 1 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginBottom: rows.length || events.length ? 8 : 0, flexWrap: 'wrap' }}>
          <div style={{ fontSize: 15, fontWeight: 500 }}>{WD_SHORT[i]} {dt.getDate()} {mon(dt)}</div>
          {isToday && <Pill color="var(--signal)" bg="transparent" style={{ padding: 0 }}>Today</Pill>}
          <div style={{ flex: 1 }} />
          {events.map(e => <Pill key={e.id}>{e.label}</Pill>)}
        </div>
        {rows.length === 0 && !(d.notes && d.notes.length) && <div style={{ ...muted, fontSize: 12, marginTop: 4 }}>Nothing planned</div>}
        {rows.map(r => r.b ? (
          <div key={r.k} style={{ display: 'flex', gap: 10, padding: '3px 0', color: 'var(--fg-subtle)', fontSize: 12 }}>
            <div style={{ width: 40, fontFamily: 'var(--font-mono)', flexShrink: 0 }}>{hhmm(r.b.start_time)}</div>
            <div style={{ flex: 1, minWidth: 0 }}>{r.b.label}</div>
          </div>
        ) : <WeekItem key={r.k} it={r.it} />)}
        {window.NoteList && d.notes && d.notes.length > 0 && <window.NoteList notes={d.notes.map(n => ({ ...n, anchor_type: n.anchor_type === 'lesson' ? 'class' : n.anchor_type }))} canWrite={false} />}
      </Card>
    );
  }

  function WeekItem({ it }) {
    const done = it.status === 'done', skipped = it.status === 'skipped', draft = it.status === 'draft';
    return (
      <div style={{ display: 'flex', gap: 10, padding: '4px 0', fontSize: 13, alignItems: 'flex-start' }}>
        <div style={{ width: 40, fontFamily: 'var(--font-mono)', fontSize: 12, color: 'var(--fg-muted)', flexShrink: 0, paddingTop: 1 }}>{hhmm(it.start_time)}</div>
        <div style={{ flex: 1, minWidth: 0, overflowWrap: 'anywhere' }}>
          <span style={{ color: done || skipped ? 'var(--fg-muted)' : 'var(--fg)', textDecoration: skipped ? 'line-through' : 'none', fontWeight: 500 }}>{it.title}</span>
          {it.is_checkpoint && <span style={{ ...mono, color: 'var(--warning)' }}> · test</span>}
          {draft && <span style={mono}> · draft</span>}
          {(it.dose || it.ladder) && (
            <div style={{ fontSize: 11, color: 'var(--fg-subtle)', marginTop: 1 }}>
              {[it.dose, it.ladder && (it.ladder.step != null ? `${it.ladder.name} · step ${it.ladder.step} of ${it.ladder.of}` : it.ladder.name)].filter(Boolean).join(' — ')}
            </div>
          )}
        </div>
        <div style={{ width: 16, textAlign: 'center', color: done ? 'var(--success)' : 'var(--fg-faint)', fontSize: 15 }}>{done ? '✓' : skipped ? '–' : ''}</div>
      </div>
    );
  }

  function SchoolList({ items, go }) {
    const xs = (items || []).slice().sort((a, b) => d10(a.due_on).localeCompare(d10(b.due_on)));
    if (!xs.length) return (
      <div style={muted}>Nothing entered for the next two weeks. <button style={linkBtn} onClick={() => go('life', 'school')}>Add tests in Life → School</button></div>
    );
    return (
      <Card pad={0}>
        {xs.map((s, i) => (
          <div key={s.id} style={{ display: 'flex', gap: 10, padding: '10px 14px', borderTop: i ? '1px solid var(--border)' : 'none', alignItems: 'baseline' }}>
            <div style={{ ...mono, width: 78, flexShrink: 0 }}>{dayLabel(s.due_on)}</div>
            <div style={{ flex: 1, minWidth: 0, fontSize: 14, color: s.done ? 'var(--fg-muted)' : 'var(--fg)' }}>
              <span style={{ fontWeight: 500 }}>{s.subject || 'Task'}</span>{s.title ? ` · ${s.title}` : ''}
              <div style={{ ...mono, marginTop: 2 }}>{SCHOOL_KIND[s.kind] || s.kind}</div>
            </div>
            {s.done && <div style={{ color: 'var(--success)' }}>✓</div>}
          </div>
        ))}
      </Card>
    );
  }

  function EventList({ events, from }) {
    const seen = {};
    const xs = (events || []).filter(e => { if (seen[e.id]) return false; seen[e.id] = 1; return d10(e.ends_on || e.starts_on) >= from; })
      .sort((a, b) => d10(a.starts_on).localeCompare(d10(b.starts_on)));
    if (!xs.length) return <div style={muted}>No games or events in the next two weeks.</div>;
    return (
      <Card pad={0}>
        {xs.map((e, i) => (
          <div key={e.id} style={{ display: 'flex', gap: 10, padding: '10px 14px', borderTop: i ? '1px solid var(--border)' : 'none', alignItems: 'baseline' }}>
            <div style={{ ...mono, width: 78, flexShrink: 0 }}>{d10(e.starts_on) === d10(e.ends_on || e.starts_on) ? dayLabel(e.starts_on) : rangeLabel(e.starts_on, e.ends_on)}</div>
            <div style={{ flex: 1, minWidth: 0, fontSize: 14 }}>
              {e.label}{e.start_time ? <span style={mono}> · {hhmm(e.start_time)}</span> : null}
              <div style={{ ...mono, marginTop: 2 }}>{EVENT_KIND[e.kind] || e.kind}</div>
            </div>
          </div>
        ))}
      </Card>
    );
  }

  // ================================================================ MONTH
  function MonthView({ memberDb }) {
    const months = seasonMonths();
    const cur = A.monthKey(A.today());
    const [month, setMonth] = useState(months.includes(cur) ? cur : months[0]);
    const idx = months.indexOf(month);
    const goals = useLoad(() => A.getGoals(memberDb, 'month', month), [memberDb, month]);
    const season = useLoad(() => A.getSeason(memberDb), [memberDb]);
    const from = month + '-01', to = lastOfMonth(month);
    const evs = ((season.data && season.data.events) || []).filter(e => overlaps(e, from, to));
    const checkpoints = evs.filter(e => e.kind === 'checkpoint_week' || e.kind === 'test_day');
    const other = evs.filter(e => e.kind === 'holiday');
    const list = goals.data || [];
    const focus = list.filter(g => g.is_focus);
    const rest = list.filter(g => !g.is_focus);

    return (
      <div>
        <Pager onPrev={() => setMonth(months[idx - 1])} onNext={() => setMonth(months[idx + 1])} prevDisabled={idx <= 0} nextDisabled={idx >= months.length - 1}
               eyebrow={month === cur ? 'This month' : ' '} title={monthLabel(month)} />

        <Card pad={14} style={{ marginTop: 16 }}>
          <Eyebrow>Checkpoint weeks</Eyebrow>
          {season.error ? <div style={{ ...muted, marginTop: 6 }}>Could not load the calendar: {season.error}</div>
            : season.loading && !season.data ? <div style={{ ...muted, marginTop: 6 }}>Loading…</div>
            : checkpoints.length ? checkpoints.map(e => (
              <div key={e.id} style={{ display: 'flex', gap: 10, marginTop: 8, fontSize: 14, alignItems: 'baseline' }}>
                <div style={{ ...mono, width: 96, flexShrink: 0 }}>{rangeLabel(e.starts_on, e.ends_on)}</div>
                <div style={{ flex: 1, minWidth: 0 }}>{e.label}</div>
              </div>
            )) : <div style={{ ...muted, marginTop: 6 }}>No checkpoint week this month — just train.</div>}
          {other.length > 0 && (
            <div style={{ ...mono, marginTop: 10 }}>Also: {other.map(e => `${e.label} (${rangeLabel(e.starts_on, e.ends_on)})`).join(' · ')}</div>
          )}
        </Card>

        <Status state={goals} what="this month’s goals" />
        {goals.data && list.length === 0 && <Card pad={16} style={{ ...muted, marginTop: 16, textAlign: 'center' }}>No goals set for this month yet.</Card>}

        {focus.length > 0 && (
          <Card pad={16} style={{ marginTop: 16, borderColor: 'var(--signal)', borderWidth: 1.5 }}>
            <Eyebrow color="var(--signal)">This month’s focus</Eyebrow>
            {focus.map((g, i) => <GoalRow key={g.id} g={g} first={i === 0} showArea />)}
          </Card>
        )}

        {AREAS.map(a => {
          const gs = rest.filter(g => g.area === a.key);
          if (!gs.length) return null;
          return (
            <Section key={a.key} label={a.label}>
              <Card pad={16}>{gs.map((g, i) => <GoalRow key={g.id} g={g} first={i === 0} />)}</Card>
            </Section>
          );
        })}
      </div>
    );
  }

  function GoalRow({ g, first, showArea, right }) {
    const area = AREAS.find(a => a.key === g.area);
    return (
      <div style={{ paddingTop: first ? 8 : 12, marginTop: first ? 0 : 12, borderTop: first ? 'none' : '1px solid var(--border)', display: 'flex', gap: 10, alignItems: 'flex-start' }}>
        <div style={{ flex: 1, minWidth: 0, overflowWrap: 'anywhere' }}>
          {showArea && area && <div style={{ ...mono, marginBottom: 2 }}>{area.label}</div>}
          <div style={{ fontSize: 14, lineHeight: 1.4 }}>{g.title}</div>
          {g.detail && <div style={{ ...muted, marginTop: 3 }}>{g.detail}</div>}
          {(g.ladder || g.target_step != null) && (
            <div style={{ ...mono, marginTop: 5 }}>
              {g.ladder ? g.ladder.name : 'Ladder'}{g.target_step != null ? ` · target step ${g.target_step}${g.ladder && g.ladder.of ? ` of ${g.ladder.of}` : ''}` : ''}
            </div>
          )}
        </div>
        {right}
      </div>
    );
  }

  // ================================================================ SEASON
  function SeasonView({ memberDb }) {
    const st = useLoad(() => A.getSeason(memberDb), [memberDb]);
    const s = st.data;
    return (
      <div>
        <Status state={st} what="the season" />
        {s && (
          <>
            <PhaseLine s={s} />
            <Section label="Test days, checkpoints and holidays"><SeasonTimeline events={s.events} /></Section>
            <Section label="Where each ladder should be by 31 May"><SeasonGoals goals={s.goals} ladders={s.ladders} /></Section>
          </>
        )}
      </div>
    );
  }

  function PhaseLine({ s }) {
    const t = A.today();
    const start = s.phases[0].from, end = s.end;
    const len = (a, b) => Math.max(1, (A.parse(b) - A.parse(a)) / 864e5 + 1);
    const total = len(start, end);
    const pct = t < start ? 0 : t > end ? 100 : Math.min(100, (100 * (len(start, t) - 1)) / total);
    const wk = A.weekOfSeason(t);
    const here = t < start ? `Season starts ${dayLabel(start)}` : t > end ? 'Season finished' : `You are here · week ${wk}`;
    const tones = ['var(--ink-200, #d4d4d8)', 'var(--ink-300, #a1a1aa)', 'var(--warning)', 'var(--ink-400, #71717a)'];
    return (
      <Card pad={16}>
        <Eyebrow>Season 1 · {rangeLabel(start, end)} {A.parse(end).getFullYear()}</Eyebrow>
        <div style={{ position: 'relative', marginTop: 34 }}>
          <div style={{ position: 'absolute', left: `${pct}%`, bottom: 14, transform: `translateX(${pct > 70 ? '-100%' : pct < 30 ? '0' : '-50%'})`, whiteSpace: 'nowrap', ...mono, color: 'var(--signal)', fontWeight: 600 }}>{here}</div>
          <div style={{ display: 'flex', height: 8, borderRadius: 4, overflow: 'hidden', gap: 2 }}>
            {s.phases.map((p, i) => <div key={p.name} style={{ flexGrow: len(p.from, p.to), flexBasis: 0, minWidth: 4, background: tones[i % tones.length], opacity: t > p.to ? 0.5 : 1 }} />)}
          </div>
          <div style={{ position: 'absolute', left: `calc(${pct}% - 1px)`, top: -6, width: 2, height: 20, background: 'var(--signal)', borderRadius: 1 }} />
        </div>
        <div style={{ marginTop: 16, display: 'flex', flexDirection: 'column', gap: 8 }}>
          {s.phases.map((p, i) => {
            const now = t >= p.from && t <= p.to, past = t > p.to;
            return (
              <div key={p.name} style={{ display: 'flex', gap: 10, alignItems: 'baseline', opacity: past ? 0.5 : 1 }}>
                <span style={{ width: 8, height: 8, borderRadius: 2, background: tones[i % tones.length], flexShrink: 0, alignSelf: 'center' }} />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <span style={{ fontSize: 14, fontWeight: now ? 600 : 500, color: now ? 'var(--signal)' : 'var(--fg)' }}>{p.name}</span>
                  <span style={{ ...muted, fontSize: 12 }}> · {p.what}</span>
                </div>
                <div style={{ ...mono, flexShrink: 0 }}>{rangeLabel(p.from, p.to)}</div>
              </div>
            );
          })}
        </div>
      </Card>
    );
  }

  function SeasonTimeline({ events }) {
    const t = A.today();
    const xs = (events || []).filter(e => ['test_day', 'checkpoint_week', 'holiday', 'season_change'].includes(e.kind));
    if (!xs.length) return <div style={muted}>No test days or holidays entered yet.</div>;
    const rows = [];
    let placed = false;
    xs.forEach(e => {
      if (!placed && d10(e.starts_on) > t) { rows.push({ today: true }); placed = true; }
      rows.push({ e });
    });
    if (!placed) rows.push({ today: true });
    return (
      <Card pad={16}>
        <div style={{ position: 'relative' }}>
          <div style={{ position: 'absolute', left: 5, top: 6, bottom: 6, width: 1, background: 'var(--border-strong)' }} />
          {rows.map((r, i) => r.today ? (
            <div key="today" style={{ display: 'flex', gap: 12, alignItems: 'center', padding: '6px 0', position: 'relative' }}>
              <span style={{ width: 11, height: 11, borderRadius: 999, background: 'var(--signal)', flexShrink: 0, zIndex: 1 }} />
              <div style={{ ...mono, color: 'var(--signal)', fontWeight: 600 }}>Today · {dayLabel(t)}</div>
            </div>
          ) : (() => {
            const e = r.e, past = d10(e.ends_on || e.starts_on) < t;
            const tone = e.kind === 'test_day' ? 'var(--warning)' : e.kind === 'checkpoint_week' ? 'var(--signal)' : 'var(--fg-subtle)';
            return (
              <div key={e.id || i} style={{ display: 'flex', gap: 12, alignItems: 'flex-start', padding: '7px 0', opacity: past ? 0.45 : 1, position: 'relative' }}>
                <span style={{ width: 11, height: 11, borderRadius: 999, background: 'var(--bg-raised)', border: `2px solid ${tone}`, boxSizing: 'border-box', flexShrink: 0, marginTop: 3, zIndex: 1 }} />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ ...mono }}>{rangeLabel(e.starts_on, e.ends_on)}</div>
                  <div style={{ fontSize: 14, marginTop: 1 }}>{e.label}</div>
                </div>
                <Pill style={{ flexShrink: 0 }}>{EVENT_KIND[e.kind] || e.kind}</Pill>
              </div>
            );
          })())}
        </div>
      </Card>
    );
  }

  function SeasonGoals({ goals, ladders }) {
    if (!goals || !goals.length) return <div style={muted}>No season goals set yet.</div>;
    const byProg = {};
    (ladders || []).forEach(l => { if (l.progression) byProg[l.progression.id] = l; });
    return AREAS.map(a => {
      const gs = goals.filter(g => g.area === a.key);
      if (!gs.length) return null;
      return (
        <div key={a.key} style={{ marginBottom: 14 }}>
          <div style={{ fontSize: 13, fontWeight: 600, margin: '0 0 6px' }}>{a.label}</div>
          <Card pad={16}>
            {gs.map((g, i) => {
              const l = g.progression_id ? byProg[g.progression_id] : null;
              let right = null;
              if (l && g.target_step != null) {
                right = l.step >= g.target_step
                  ? <Pill color="var(--success)" bg="transparent" style={{ border: '1px solid var(--success)', flexShrink: 0 }}>✓ On target</Pill>
                  : <Pill style={{ flexShrink: 0 }}>Step {l.step} → {g.target_step}</Pill>;
              }
              return <GoalRow key={g.id} g={g} first={i === 0} right={right} />;
            })}
          </Card>
        </div>
      );
    });
  }

  // ================================================================ CAREER
  function CareerView({ memberDb }) {
    const st = useLoad(() => A.getCareer(memberDb), [memberDb]);
    const [editing, setEditing] = useState(false);
    const [saving, setSaving] = useState(false);
    const [saveErr, setSaveErr] = useState(null);
    const writable = A.canWrite(memberDb);
    const c = st.data;
    const dream = (c && c.dream) || null;

    async function save(patch) {
      setSaving(true); setSaveErr(null);
      try { await A.saveDream(memberDb, patch); setEditing(false); st.reload(); }
      catch (e) { setSaveErr((e && e.message) || String(e)); }
      finally { setSaving(false); }
    }

    return (
      <div>
        <Status state={st} what="the career plan" />
        {c && (
          <>
            {editing
              ? <DreamEditor dream={dream || {}} saving={saving} error={saveErr} onCancel={() => { setEditing(false); setSaveErr(null); }} onSave={save} />
              : (
                <>
                  <Destination dream={dream} writable={writable} onEdit={() => setEditing(true)} />
                  <Section label="My why">
                    <Card pad={16}>
                      {WHY_FIELDS.map((f, i) => (
                        <div key={f.key} style={{ marginTop: i ? 14 : 0 }}>
                          <div style={{ fontSize: 12, color: 'var(--fg-muted)', fontWeight: 500 }}>{f.q}</div>
                          <div style={{ fontSize: 15, lineHeight: 1.45, marginTop: 3, whiteSpace: 'pre-wrap', overflowWrap: 'anywhere', color: dream && dream[f.key] ? 'var(--fg)' : 'var(--fg-subtle)', fontStyle: dream && dream[f.key] ? 'normal' : 'italic' }}>
                            {(dream && dream[f.key]) || 'Not written yet.'}
                          </div>
                        </div>
                      ))}
                      {writable && <div style={{ marginTop: 16 }}><Button kind="secondary" size="sm" onClick={() => setEditing(true)}>Write in my own words</Button></div>}
                    </Card>
                  </Section>
                </>
              )}
            <Section label="The ladders">
              {c.tracks.length === 0 && <div style={muted}>No ladders set up yet.</div>}
              <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
                {c.tracks.map(t => <TrackCard key={t.id} t={t} />)}
              </div>
            </Section>
          </>
        )}
      </div>
    );
  }

  const WHY_FIELDS = [
    { key: 'why', q: 'Why do I want this?' },
    { key: 'commitment', q: 'What I’ll do every week' },
    { key: 'happiness', q: 'What makes me happy along the way' },
    { key: 'helpers', q: 'Who helps me' },
  ];

  function Destination({ dream, writable, onEdit }) {
    const box = { background: 'var(--ink-950)', color: '#fff', borderRadius: 'var(--radius-2)', padding: 20 };
    const sub = { fontFamily: 'var(--font-mono)', fontSize: 11, letterSpacing: '0.06em', color: 'rgba(255,255,255,0.6)' };
    const eb = 'rgba(255,255,255,0.55)';
    if (!dream) return (
      <div style={box}>
        <Eyebrow color={eb}>The destination</Eyebrow>
        <div style={{ fontSize: 18, marginTop: 8, lineHeight: 1.4 }}>Not written yet.</div>
        {writable && <button onClick={onEdit} style={{ ...linkBtn, color: '#fff', textDecoration: 'underline', marginTop: 10 }}>Write it in</button>}
      </div>
    );
    const Stop = ({ label, big, line, when, children, last }) => (
      <div style={{ display: 'flex', gap: 14 }}>
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: 12, flexShrink: 0 }}>
          <span style={{ width: 12, height: 12, borderRadius: 999, border: '2px solid #fff', boxSizing: 'border-box', marginTop: 4 }} />
          {!last && <span style={{ flex: 1, width: 1, background: 'rgba(255,255,255,0.3)', margin: '4px 0' }} />}
        </div>
        <div style={{ flex: 1, minWidth: 0, paddingBottom: last ? 0 : 18 }}>
          <Eyebrow color={eb}>{label}</Eyebrow>
          <div style={{ fontSize: 22, fontWeight: 500, letterSpacing: '-0.015em', marginTop: 4, lineHeight: 1.2, overflowWrap: 'anywhere' }}>{big}</div>
          {line && <div style={{ fontSize: 14, color: 'rgba(255,255,255,0.8)', marginTop: 4 }}>{line}</div>}
          {children}
          {when && countdown(when) && <div style={{ ...sub, marginTop: 6 }}>{countdown(when) === 'passed' ? 'passed' : countdown(when) === 'today' ? 'today' : `in ${countdown(when)}`}</div>}
        </div>
      </div>
    );
    return (
      <div style={box}>
        <div style={{ display: 'flex', alignItems: 'center', marginBottom: 16 }}>
          <Eyebrow color={eb} style={{ flex: 1 }}>The destination</Eyebrow>
          {writable && <button onClick={onEdit} style={{ ...linkBtn, color: 'rgba(255,255,255,0.8)', fontSize: 12 }}>Edit</button>}
        </div>
        <Stop label="College" big={dream.college || 'College not chosen yet'}
              line={[dream.class_year && `Class of ${dream.class_year}`, dream.college_start && `starts ${monthYear(dream.college_start)}`].filter(Boolean).join(' · ')}
              when={dream.college_start} />
        <Stop label={dream.draft_label || 'The draft'} big={dream.draft_date ? monthYear(dream.draft_date) : 'Date to come'} when={dream.draft_date} />
        <Stop label="First professional game" big={dream.first_game_date ? monthYear(dream.first_game_date) : 'Date to come'} when={dream.first_game_date} last>
          {dream.first_game_team
            ? <div style={{ fontSize: 16, marginTop: 4 }}>with {dream.first_game_team}</div>
            : <div style={{ fontSize: 14, marginTop: 4, color: 'rgba(255,255,255,0.75)' }}>
                with … <em>Which team? Write it in.</em>
                {writable && <> <button onClick={onEdit} style={{ ...linkBtn, color: '#fff', textDecoration: 'underline', fontSize: 14 }}>Add team</button></>}
              </div>}
        </Stop>
      </div>
    );
  }

  const fieldStyle = { width: '100%', boxSizing: 'border-box', padding: '9px 10px', border: '1px solid var(--border-strong)', borderRadius: 6, fontSize: 16, fontFamily: 'var(--font-sans)', color: 'var(--fg)', background: 'var(--bg-raised)' };
  function Field({ label, children }) {
    return <label style={{ display: 'block', marginTop: 12 }}><div style={{ fontSize: 12, color: 'var(--fg-muted)', fontWeight: 500, marginBottom: 4 }}>{label}</div>{children}</label>;
  }

  function DreamEditor({ dream, saving, error, onCancel, onSave }) {
    const [f, setF] = useState({
      college: dream.college || '', class_year: dream.class_year != null ? String(dream.class_year) : '', first_game_team: dream.first_game_team || '',
      why: dream.why || '', commitment: dream.commitment || '', happiness: dream.happiness || '', helpers: dream.helpers || '',
    });
    const set = (k) => (e) => setF({ ...f, [k]: e.target.value });
    const clean = (v) => (v.trim() ? v.trim() : null);
    function submit() {
      const y = parseInt(f.class_year, 10);
      onSave({
        college: clean(f.college), class_year: Number.isFinite(y) ? y : null, first_game_team: clean(f.first_game_team),
        why: clean(f.why), commitment: clean(f.commitment), happiness: clean(f.happiness), helpers: clean(f.helpers),
      });
    }
    return (
      <Card pad={16}>
        <Eyebrow>My dream, in my words</Eyebrow>
        <Field label="College"><input style={fieldStyle} value={f.college} onChange={set('college')} placeholder="e.g. Stanford University" /></Field>
        <Field label="Class of (year)"><input style={fieldStyle} value={f.class_year} onChange={set('class_year')} inputMode="numeric" placeholder="e.g. 2036" /></Field>
        <Field label="My first professional game, with which team?"><input style={fieldStyle} value={f.first_game_team} onChange={set('first_game_team')} placeholder="Write the team" /></Field>
        {WHY_FIELDS.map(w => (
          <Field key={w.key} label={w.q}><textarea style={{ ...fieldStyle, minHeight: 72, resize: 'vertical', lineHeight: 1.4 }} value={f[w.key]} onChange={set(w.key)} /></Field>
        ))}
        {error && <div style={{ color: 'var(--danger)', fontSize: 13, marginTop: 10 }}>Could not save: {error}</div>}
        <div style={{ display: 'flex', gap: 8, marginTop: 16 }}>
          <Button onClick={submit} disabled={saving}>{saving ? 'Saving…' : 'Save'}</Button>
          <Button kind="ghost" onClick={onCancel} disabled={saving}>Cancel</Button>
        </div>
      </Card>
    );
  }

  function TrackCard({ t }) {
    const today = A.today();
    const gates = t.gates || [];
    const nextGate = gates.find(g => d10(g.gate_on) >= today);
    const nextDate = nextGate ? d10(nextGate.gate_on) : null;
    const nextGroup = nextDate ? gates.filter(g => d10(g.gate_on) === nextDate) : [];
    const title = String(t.label || t.discipline || '').replace(/^[^·]*·\s*/, '') || t.label;
    return (
      <Card pad={16} style={{ opacity: t.status && t.status !== 'active' ? 0.6 : 1 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, flexWrap: 'wrap' }}>
          <div style={{ fontSize: 17, fontWeight: 500, flex: 1, minWidth: 0 }}>{title}</div>
          {t.status && t.status !== 'active' && <Pill>{t.status}</Pill>}
        </div>
        {t.terminal && <div style={{ ...mono, marginTop: 3 }}>Leads to: {t.terminal}{t.terminal_on ? ` · ${monthYear(t.terminal_on)}` : ''}</div>}
        {nextGroup.length > 0 && (
          <div style={{ marginTop: 12, padding: '10px 12px', borderRadius: 6, background: 'var(--signal-tint, var(--ink-50))', borderLeft: '3px solid var(--signal)' }}>
            <Eyebrow color="var(--signal)">Next gate</Eyebrow>
            <div style={{ fontSize: 15, fontWeight: 500, marginTop: 3 }}>{nextGroup.map(g => g.label).join(' + ')}: {countdown(nextDate)}</div>
          </div>
        )}
        {gates.length === 0 ? <div style={{ ...muted, marginTop: 10 }}>No gates yet.</div> : (
          <div style={{ position: 'relative', marginTop: 14 }}>
            <div style={{ position: 'absolute', left: 5, top: 8, bottom: 8, width: 1, background: 'var(--border-strong)' }} />
            {gates.map(g => {
              const gd = d10(g.gate_on), past = gd < today, isNext = gd === nextDate;
              return (
                <div key={g.id} style={{ display: 'flex', gap: 12, alignItems: 'flex-start', padding: '6px 0', opacity: past ? 0.4 : 1, position: 'relative' }}>
                  <span style={{
                    width: 11, height: 11, borderRadius: 999, boxSizing: 'border-box', flexShrink: 0, marginTop: 4, zIndex: 1,
                    background: isNext ? 'var(--signal)' : past ? 'var(--border-strong)' : 'var(--bg-raised)',
                    border: `2px solid ${isNext ? 'var(--signal)' : 'var(--border-strong)'}`,
                  }} />
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ ...mono, color: isNext ? 'var(--signal)' : 'var(--fg-subtle)' }}>{monthYear(gd)}{past ? ' · passed' : ''}</div>
                    <div style={{ fontSize: 14, fontWeight: isNext ? 600 : 400, marginTop: 1, overflowWrap: 'anywhere' }}>{g.label}</div>
                  </div>
                  <div style={{ ...mono, flexShrink: 0, paddingTop: 2 }}>{GATE_KIND[g.kind] || g.kind}</div>
                </div>
              );
            })}
          </div>
        )}
      </Card>
    );
  }

  // ================================================================ screen
  function PlanScreen({ memberDb, isAdmin, go, sub }) {
    const view = SUBS.some(s => s.value === sub) ? sub : 'week';
    if (!window.PlanAPI) return <div style={{ padding: 32, color: 'var(--fg-muted)' }}>The plan data layer is not loaded.</div>;
    return (
      <div style={{ maxWidth: 720, margin: '0 auto', padding: '16px 16px 120px', fontFamily: 'var(--font-sans)', boxSizing: 'border-box', color: 'var(--fg)' }}>
        <div style={{ display: 'flex', justifyContent: 'center', marginBottom: 18 }}>
          <Segmented options={SUBS} value={view} onChange={v => go('plan', v)} />
        </div>
        {!memberDb ? <div style={{ ...muted, textAlign: 'center', padding: 24 }}>No one selected.</div>
          : view === 'week' ? <WeekView memberDb={memberDb} go={go} />
          : view === 'month' ? <MonthView memberDb={memberDb} />
          : view === 'season' ? <SeasonView memberDb={memberDb} />
          : <CareerView memberDb={memberDb} isAdmin={isAdmin} />}
      </div>
    );
  }

  window.PlanScreen = PlanScreen;
})();
