// Flutlicht Manager – Browser-Oberfläche
const { useState, useEffect, useCallback, useMemo, useRef } = React;

// ---------- Hilfen ----------
const store = {
  get(k) { try { return localStorage.getItem(k); } catch (e) { return null; } },
  set(k, v) { try { localStorage.setItem(k, v); } catch (e) {} },
  del(k) { try { localStorage.removeItem(k); } catch (e) {} }
};
let TOKEN = store.get('fm_token');
async function api(path, body) {
  const res = await fetch('/api' + path, {
    method: body ? 'POST' : 'GET',
    headers: { 'Content-Type': 'application/json', ...(TOKEN ? { Authorization: 'Bearer ' + TOKEN } : {}) },
    body: body ? JSON.stringify(body) : undefined
  });
  const data = await res.json().catch(() => ({}));
  if (!res.ok) { const e = new Error(data.error || 'Fehler'); e.status = res.status; e.kicked = !!data.kicked; throw e; }
  return data;
}
const eur = n => Math.round(n).toLocaleString('de-DE') + ' €';
const fmt = n => Math.round(n).toLocaleString('de-DE');
const POS_ORDER = { TW: 0, AB: 1, MF: 2, ST: 3 };
const POS_NAME = { TW: 'Torwart', AB: 'Abwehr', MF: 'Mittelfeld', ST: 'Sturm' };
const MAIN_SKILL = { TW: 'tw', AB: 'abw', MF: 'spa', ST: 'tor' };
const SKILLS = ['tw', 'abw', 'spa', 'tor', 'kon'];
const SKILL_NAME = { tw: 'Torwart', abw: 'Abwehr', spa: 'Spielaufbau', tor: 'Torschuss', kon: 'Kondition' };
const EURO_NAMES = { CP: 'Champions-Pokal', EP: 'Europa-Pokal' };
const compName = (m, st) => m.comp === 'POKAL' ? 'Pokal' : EURO_NAMES[m.comp] || ((st.game.leagues[Number(m.comp.slice(1))] || {}).name || 'Liga');
const isLeague = m => /^L\d/.test(m.comp);
function Flag({ c, h = 13 }) {
  if (!c) return null;
  const [a, b, d] = c.flag;
  const g = `linear-gradient(${c.dir === 'v' ? '90deg' : '180deg'}, ${a} 0 33.3%, ${b} 33.3% 66.6%, ${d} 66.6% 100%)`;
  return <span className="flag" title={c.name} style={{ background: g, width: Math.round(h * 1.5), height: h }} />;
}
const lgName = (st, id) => (st.game.leagues[id] || {}).name || '';
const dayDate = ts => ts ? new Date(ts).toLocaleString('de-DE', { weekday: 'short', day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }) : '–';
const FORMATIONS = {
  '4-4-2': ['TW', 'AB', 'AB', 'AB', 'AB', 'MF', 'MF', 'MF', 'MF', 'ST', 'ST'],
  '4-3-3': ['TW', 'AB', 'AB', 'AB', 'AB', 'MF', 'MF', 'MF', 'ST', 'ST', 'ST'],
  '4-2-3-1': ['TW', 'AB', 'AB', 'AB', 'AB', 'MF', 'MF', 'MF', 'MF', 'MF', 'ST'],
  '3-5-2': ['TW', 'AB', 'AB', 'AB', 'MF', 'MF', 'MF', 'MF', 'MF', 'ST', 'ST'],
  '3-4-3': ['TW', 'AB', 'AB', 'AB', 'MF', 'MF', 'MF', 'MF', 'ST', 'ST', 'ST'],
  '5-3-2': ['TW', 'AB', 'AB', 'AB', 'AB', 'AB', 'MF', 'MF', 'MF', 'ST', 'ST'],
  '5-4-1': ['TW', 'AB', 'AB', 'AB', 'AB', 'AB', 'MF', 'MF', 'MF', 'MF', 'ST']
};
const TACTICS = { defensiv: [1.1, 1, 0.88], normal: [1, 1, 1], offensiv: [0.9, 1, 1.12] };

// Bilder (alle optional – fehlt eine Datei, wird sie einfach ausgeblendet). Liste siehe BILDER.md
const IMG = {
  logo: '/assets/ui/logo.png',
  loginBg: '/assets/ui/hintergrund.jpg',
  headerBg: '/assets/ui/hintergrund.jpg',
  liga: lg => `/assets/liga/liga${Math.min(lg, 4)}.png`,
  schale: '/assets/liga/meisterschale.png',
  pokal: '/assets/pokal/pokal.png',
  pokalBanner: '/assets/pokal/pokal-banner.jpg',
  stadion: cap => `/assets/stadion/stadion-${cap < 15000 ? 1 : cap < 25000 ? 2 : cap < 40000 ? 3 : 4}.jpg`,
  gelaende: key => `/assets/gelaende/${key}.jpg`,
  training: focus => `/assets/training/${focus}.jpg`,
  banner: name => `/assets/banner/${name}.jpg`,
  spielbericht: '/assets/spiel/spielbericht.jpg'
};

function strengthAt(p, pos) {
  const s = p.skills; let v;
  if (pos === 'TW') v = s.tw;
  else if (pos === 'AB') v = s.abw * 0.7 + s.spa * 0.2 + s.kon * 0.1;
  else if (pos === 'MF') v = s.spa * 0.6 + s.abw * 0.15 + s.tor * 0.15 + s.kon * 0.1;
  else v = s.tor * 0.7 + s.spa * 0.2 + s.kon * 0.1;
  return p.pos !== pos ? v * 0.9 : v;
}
function ratings(players, formation, tactic) {
  let gk = 0, ab = 0, mf = 0, st = 0;
  players.forEach((p, i) => {
    if (!p) return;
    const pos = FORMATIONS[formation][i];
    const v = strengthAt(p, pos) * (0.9 + p.form * 0.02);
    if (pos === 'TW') gk = v; else if (pos === 'AB') ab += v; else if (pos === 'MF') mf += v; else st += v;
  });
  const t = TACTICS[tactic];
  return { gk: Math.round(gk), def: Math.round((ab + 0.3 * mf) * t[0]), mid: Math.round((mf + 0.25 * (ab + st)) * t[1]), att: Math.round((st + 0.3 * mf) * t[2]) };
}
const Result = ({ m }) => <span className="res">{m.goals[0]}:{m.goals[1]}{(m.pens || m.extraTime) && <small>{m.pens ? `${m.pens[0]}:${m.pens[1]} i.E.` : 'n.V.'}</small>}</span>;
const resultText = m => `${m.goals[0]}:${m.goals[1]}${m.pens ? ` (${m.pens[0]}:${m.pens[1]} i.E.)` : m.extraTime ? ' n.V.' : ''}`;

// ---------- Kleine Bausteine ----------
function Img({ src, className, alt, style }) {
  const [ok, setOk] = useState(true);
  useEffect(() => setOk(true), [src]);
  if (!ok) return null;
  return <img src={src} className={className} alt={alt || ''} style={style} onError={() => setOk(false)} />;
}
function Banner({ src, title, sub, pos }) {
  const [ok, setOk] = useState(true);
  useEffect(() => setOk(true), [src]);
  if (!ok) return null;
  return <div className="banner">
    <img src={src} alt="" style={pos ? { objectPosition: pos } : undefined} onError={() => setOk(false)} />
    {(title || sub) && <div className="banner-text">{title && <div className="cond banner-title">{title}</div>}{sub && <div>{sub}</div>}</div>}
  </div>;
}
// Vereinswappen: Form + Muster in den Vereinsfarben. Wappen der Vereine werden über die Vereins-ID nachgeschlagen.
let CRESTS = {};
const CREST_SHAPE_NAME = { schild: 'Schild', klassisch: 'Klassisch', spanisch: 'Rundschild', wimpel: 'Wimpel', zinnen: 'Burgzinnen', rund: 'Rund', oval: 'Oval', raute: 'Raute', achteck: 'Achteck', quadrat: 'Quadrat' };
const CREST_PATTERN_NAME = { geteilt: 'Längs geteilt', quer: 'Quer geteilt', streifen: 'Streifen', diagonal: 'Schräg geteilt', kreuz: 'Kreuz', rand: 'Rand', schlicht: 'Einfarbig' };
const CREST_PATHS = {
  schild: 'M20 1 L38 6 V22 C38 34 30 41 20 45 C10 41 2 34 2 22 V6 Z',
  klassisch: 'M3 2 H37 V20 C37 33 29 40 20 45 C11 40 3 33 3 20 Z',
  spanisch: 'M3 2 H37 V25 C37 36 29 44 20 44 C11 44 3 36 3 25 Z',
  wimpel: 'M3 2 H37 V31 L20 45 L3 31 Z',
  zinnen: 'M2 9 V2 H9 V6 H15 V2 H25 V6 H31 V2 H38 V22 C38 34 30 41 20 45 C10 41 2 34 2 22 Z',
  rund: 'M20 4 A19 19 0 1 1 19.99 4 Z',
  oval: 'M20 1 C30 1 36 11 36 23 C36 35 30 45 20 45 C10 45 4 35 4 23 C4 11 10 1 20 1 Z',
  raute: 'M20 1 L39 23 L20 45 L1 23 Z',
  achteck: 'M13 2 H27 L38 13 V33 L27 44 H13 L2 33 V13 Z',
  quadrat: 'M8 3 H32 Q37 3 37 8 V38 Q37 43 32 43 H8 Q3 43 3 38 V8 Q3 3 8 3 Z'
};
function CrestPattern({ pattern, a, b }) {
  switch (pattern) {
    case 'quer': return <><rect x="0" y="0" width="40" height="46" fill={a} /><rect x="0" y="23" width="40" height="23" fill={b} /></>;
    case 'streifen': return <><rect x="0" y="0" width="40" height="46" fill={a} />{[5, 15, 25, 35].map(x => <rect key={x} x={x} y="0" width="5" height="46" fill={b} />)}</>;
    case 'diagonal': return <><rect x="0" y="0" width="40" height="46" fill={a} /><path d="M40 0 V46 H0 Z" fill={b} /></>;
    case 'kreuz': return <><rect x="0" y="0" width="40" height="46" fill={a} /><rect x="16" y="0" width="8" height="46" fill={b} /><rect x="0" y="9" width="40" height="7" fill={b} /></>;
    case 'rand': return <rect x="0" y="0" width="40" height="46" fill={a} />;
    case 'schlicht': return <rect x="0" y="0" width="40" height="46" fill={a} />;
    default: return <><rect x="0" y="0" width="40" height="46" fill={a} /><rect x="20" y="0" width="20" height="46" fill={b} /></>;
  }
}
function Crest({ id, colors, short, size = 36, crest }) {
  const [a, b] = colors || ['#2a9d5c', '#ffffff'];
  const c = crest || (id && CRESTS[id]) || { shape: 'schild', pattern: 'geteilt' };
  const d = CREST_PATHS[c.shape] || CREST_PATHS.schild;
  const cid = 'c' + Math.random().toString(36).slice(2, 8);
  return <svg width={size} height={size * 1.15} viewBox="0 0 40 46" className="crest" aria-hidden="true">
    <defs><clipPath id={cid}><path d={d} /></clipPath></defs>
    <g clipPath={`url(#${cid})`}>
      <CrestPattern pattern={c.pattern} a={a} b={b} />
      <rect x="0" y="18" width="40" height="11" fill="rgba(0,0,0,.55)" />
    </g>
    {c.pattern === 'rand' && <path d={d} fill="none" stroke={b} strokeWidth="5" clipPath={`url(#${cid})`} />}
    <path d={d} fill="none" stroke="rgba(255,255,255,.55)" strokeWidth="1.5" />
    <text x="20" y="26.8" textAnchor="middle" fontSize={short && short.length > 3 ? 8 : 9.5} fontWeight="700" fill="#fff" fontFamily="Barlow Condensed, sans-serif">{short}</text>
  </svg>;
}
const TeamName = ({ t, bold, onClick }) => t ? <span className="tn" onClick={onClick} style={{ cursor: onClick ? 'pointer' : 'default', fontWeight: bold ? 600 : 400 }}>
  <Crest id={t.id} colors={t.colors} short={t.short} size={18} />{t.name}</span> : null;
const Pos = ({ p }) => <span className={'pos pos-' + p}>{p}</span>;
const Status = ({ p }) => <>
  {p.injured > 0 && <span className="tag inj">verletzt {p.injured}</span>}
  {p.suspended > 0 && <span className="tag sus">gesperrt</span>}
  {p.listed && <span className="tag list">Transferliste</span>}
</>;
const Form = ({ f }) => <span className="form">{(f || []).map((x, i) => <i key={i} className={x}>{x}</i>)}</span>;
const Pips = ({ level, max }) => <span className="pips">{Array.from({ length: max }, (_, i) => <i key={i} className={i < level ? 'on' : ''} />)}</span>;
function formatDate(ts) {
  return new Date(ts).toLocaleString('de-DE', { weekday: 'short', day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' });
}
function Countdown({ to }) {
  const [now, setNow] = useState(Date.now());
  useEffect(() => { const i = setInterval(() => setNow(Date.now()), 1000); return () => clearInterval(i); }, []);
  let d = Math.max(0, Math.floor((to - now) / 1000));
  if (d === 0) return <span>läuft gleich …</span>;
  const days = Math.floor(d / 86400); d %= 86400;
  const h = Math.floor(d / 3600), m = Math.floor((d % 3600) / 60), s = d % 60;
  return <span>{days > 0 ? days + 'T ' : ''}{String(h).padStart(2, '0')}:{String(m).padStart(2, '0')}:{String(s).padStart(2, '0')}</span>;
}
function Modal({ children, close }) {
  return <div className="modal-bg" onClick={close}><div className="modal" onClick={e => e.stopPropagation()}>{children}</div></div>;
}
function LeagueSwitch({ value, onChange, st }) {
  return <select value={value} onChange={e => onChange(Number(e.target.value))}>
    {Object.values(st.game.leagues).map(l => <option key={l.id} value={l.id}>{l.name}{l.id === st.myTeam.league ? ' (meine Liga)' : ''}</option>)}
  </select>;
}

// ---------- Login / Registrierung ----------
function LoginLogo() {
  const [ok, setOk] = useState(true);
  return ok
    ? <div style={{ textAlign: 'center', marginBottom: 6 }}><img src={IMG.logo} alt="Flutlicht Manager" className="logo-big" onError={() => setOk(false)} /></div>
    : <div className="brand" style={{ textAlign: 'center', fontSize: 40, marginBottom: 6 }}>Flutlicht Manager</div>;
}
function Auth({ onLogin, notice }) {
  const [mode, setMode] = useState('login');
  const [pub, setPub] = useState(null);
  const [u, setU] = useState(''); const [pw, setPw] = useState('');
  const [league, setLeague] = useState(null);
  const [team, setTeam] = useState('');
  const [err, setErr] = useState(null);
  useEffect(() => { api('/public').then(setPub).catch(() => {}); }, [mode]);
  useEffect(() => { setTeam(''); }, [league]);
  async function submit(e) {
    e.preventDefault(); setErr(null);
    if (mode === 'reg' && !league) { setErr('Bitte wähle zuerst eine Liga.'); return; }
    try {
      const r = await api(mode === 'login' ? '/login' : '/register', { username: u, password: pw, league, teamId: team || undefined });
      TOKEN = r.token; store.set('fm_token', r.token); onLogin();
    } catch (x) { setErr(x.message); api('/public').then(setPub).catch(() => {}); }
  }
  const leagues = pub ? Object.values(pub.leagues) : [];
  const L = league && pub ? pub.leagues[league] : null;
  const teams = pub && league ? pub.freeTeams.filter(t => t.league === league) : [];
  const LEVEL_TEXT = { 1: 'Höchste Liga – starke Kader, großes Budget, harte Konkurrenz', 2: 'Solide Mannschaften, Aufstiegskampf um 3 Plätze', 3: 'Kleinere Vereine, gute Mischung aus Budget und Chancen', 4: 'Ganz unten anfangen: wenig Geld, schwache Kader, nur der Staffelsieger steigt auf' };
  return <div className="auth-bg" style={{ backgroundImage: `linear-gradient(rgba(8,14,11,.3),rgba(8,14,11,.55) 45%,rgba(8,14,11,.92)), url(${IMG.loginBg})` }}>
    <div className={'auth' + (mode === 'reg' ? ' auth-wide' : '')}>
      <LoginLogo />
      <p className="muted" style={{ textAlign: 'center', marginTop: 0 }}>Der Online-Fußballmanager. Übernimm einen Verein und führe ihn zur Meisterschaft.</p>
      <form className="card" onSubmit={submit}>
        <div className="row">
          <button type="button" className={mode === 'login' ? 'primary' : ''} onClick={() => { setMode('login'); setErr(null); }}>Anmelden</button>
          <button type="button" className={mode === 'reg' ? 'primary' : ''} onClick={() => { setMode('reg'); setErr(null); }}>Registrieren</button>
        </div>
        <input placeholder="Trainername" value={u} onChange={e => setU(e.target.value)} autoComplete="username" />
        <input placeholder="Passwort" type="password" value={pw} onChange={e => setPw(e.target.value)} autoComplete={mode === 'login' ? 'current-password' : 'new-password'} />
        {mode === 'reg' && (pub && pub.registration !== 'open'
          ? <div className="muted">Die Registrierung ist derzeit geschlossen.</div>
          : <>
            <label className="muted" style={{ marginTop: 6 }}>1. In welcher Liga willst du starten?</label>
            <div className="league-grid">
              {leagues.map(l => <button type="button" key={l.id} disabled={!l.free}
                className={'league-card' + (league === l.id ? ' on' : '')} onClick={() => setLeague(l.id)}>
                <div className="cond" style={{ fontSize: 18 }}>{l.name}</div>
                <div className={l.free ? 'pos-money' : 'neg-money'} style={{ fontSize: 13 }}>{l.free ? `${l.free} von ${l.size} Plätzen frei` : 'Keine Plätze frei'}</div>
                <div className="muted" style={{ fontSize: 12 }}>Startbudget {eur(l.budget)}</div>
              </button>)}
            </div>
            {L && <>
              <p className="muted" style={{ margin: 0, fontSize: 13 }}>{LEVEL_TEXT[L.level]}</p>
              <label className="muted" style={{ marginTop: 6 }}>2. Verein in der {L.name}</label>
              <select value={team} onChange={e => setTeam(e.target.value)}>
                <option value="">🎲 Verein zulosen</option>
                {teams.map(t => <option key={t.id} value={t.id}>{t.name} – Stärke {t.level} · {fmt(t.stadium)} Plätze</option>)}
              </select>
              <span className="muted" style={{ fontSize: 12 }}>Den Vereinsnamen kannst du später einmal kostenlos ändern.</span>
            </>}
          </>)}
        {notice && mode === 'login' && !err && <div className="kick-note">🔒 {notice}</div>}
        {err && <div style={{ color: 'var(--bad)' }}>{err}</div>}
        <button className="primary" type="submit" disabled={mode === 'reg' && !league}>{mode === 'login' ? 'Anmelden' : team ? 'Verein übernehmen' : L ? `Zufälligen Verein in der ${L.name} übernehmen` : 'Verein übernehmen'}</button>
      </form>
    </div>
  </div>;
}

// ---------- Übersicht ----------
function Overview({ st, open }) {
  const me = st.myTeam;
  const team = id => st.teams.find(t => t.id === id);
  const tab = st.tables[me.league];
  const myRow = tab.findIndex(r => r.id === me.id);
  const row = tab[myRow];
  const expiring = me.players.filter(p => p.contractEnd <= st.game.season).sort((a, b) => b.ovr - a.ovr);
  const lvl = st.game.leagues[me.league].level;
  return <>
    <Banner src={IMG.stadion(me.stadium)} title={me.stadiumName} sub={`${fmt(me.stadium)} Plätze · ${fmt(me.fans)} Fans`} />
    <div className="grid g2">
      <div className="card">
        <h3>Nächstes Ligaspiel · {lgName(st, me.league)}</h3>
        {st.nextMatch ? <>
          <div className="vs" style={{ margin: '10px 0' }}>
            <div className="team"><Crest {...team(st.nextMatch.home)} size={40} /><a onClick={() => open({ type: 'team', id: st.nextMatch.home })}>{team(st.nextMatch.home).name}</a></div>
            <div className="muted">vs.</div>
            <div className="team"><Crest {...team(st.nextMatch.away)} size={40} /><a onClick={() => open({ type: 'team', id: st.nextMatch.away })}>{team(st.nextMatch.away).name}</a></div>
          </div>
          <div className="muted" style={{ textAlign: 'center' }}>Spieltag {st.game.matchday + 1} · {formatDate(st.game.nextMatchAt)} · in <Countdown to={st.game.nextMatchAt} /></div>
        </> : st.game.matchday < st.game.totalMatchdays ? <div className="muted" style={{ padding: '14px 0' }}><b>Spielfrei</b> am {st.game.matchday + 1}. Spieltag ({formatDate(st.game.nextMatchAt)}). Da die Ligen unterschiedlich groß sind, hat jeder Verein über die Saison verteilt einige spielfreie Spieltage.</div>
          : <div className="muted">Kein Spiel angesetzt.</div>}
      </div>
      <div className="card">
        <h3>International</h3>
        {st.euroStatus && st.euroStatus.comp ? <div>
          <b>{st.euroStatus.compName}</b>{st.euroStatus.winner ? ' – 🏆 gewonnen!' : ''}<br />
          {st.euroStatus.next ? <>{st.euroStatus.next.round}: {st.euroStatus.next.hName} – {st.euroStatus.next.aName}<br /><span className="muted">voraussichtlich {dayDate(st.euroStatus.next.at)}</span></>
            : !st.euroStatus.winner && <span className="muted">Dein Verein ist ausgeschieden.</span>}
        </div> : <div className="muted">Dein Verein spielt diese Saison nicht international. Platz 1–4 der 1. Liga führt in den Champions-Pokal, Platz 5–7 und der Pokalsieger in den Europa-Pokal.</div>}
      </div>
      <div className="card">
        <h3>Pokal</h3>
        {st.nextCup ? <div className="row">
          <Img src={IMG.pokal} className="icon-48" />
          <div>
            {st.nextCup.bye
              ? <><b>Freilos</b> in der 1. Runde – dein Verein steigt in der {st.nextCup.round} ein.<br /></>
              : st.nextCup.undrawn ? <><b>{st.nextCup.round}</b>: {st.nextCup.drawLive ? <span className="live-txt"><span className="live-dot" /> wird gerade live ausgelost!</span> : <>Auslosung live am {dayDate(st.nextCup.drawAt)}</>}<br /></>
              : <><b>{st.nextCup.round}</b>: {team(st.nextCup.home).name} – {team(st.nextCup.away).name}<br /></>}
            <span className="muted">voraussichtlich {dayDate(st.nextCup.at)}</span>
          </div>
        </div> : <div className="muted">{st.cupStatus === 'nicht qualifiziert'
          ? 'Dein Verein ist diese Saison nicht im Pokal. Teilnehmer: alle Vereine der 1.–3. Liga und die stärksten Mannschaften jeder 4.-Liga-Staffel.'
          : st.cupStatus === 'Pokalsieger' ? '🏆 Dein Verein hat den Pokal gewonnen!'
          : st.cupWinner ? `Pokalsieger: ${st.cupWinner}` : 'Dein Verein ist im Pokal ausgeschieden.'}</div>}
      </div>
      <div className="card">
        <h3>Tabellenplatz</h3>
        <div className="row"><Img src={IMG.liga(lvl)} className="icon-48" /><div className="big">{myRow + 1}.</div><div className="muted">{lgName(st, me.league)} · {row.pkt} Punkte · {row.tore}:{row.gegen} Tore · {row.sp} Spiele</div></div>
        <div style={{ marginTop: 8 }}><Form f={row.form} /></div>
      </div>
      <div className="card">
        <h3>Letzte Spiele</h3>
        {st.recent.length === 0 && <div className="muted">Noch keine Spiele gespielt.</div>}
        <table className="wrap"><tbody>
          {st.recent.map(m => <tr key={m.id} className="click" onClick={() => open({ type: 'match', id: m.id })}>
            <td className="muted">{isLeague(m) ? `${m.md}. ST` : m.comp === 'POKAL' ? m.round : `${m.comp} · ${m.round}`}<br /><span style={{ fontSize: 11 }}>{new Date(m.playedAt).toLocaleDateString('de-DE', { weekday: 'short', day: '2-digit', month: '2-digit' })}</span></td>
            <td style={{ fontWeight: m.home === me.id ? 600 : 400 }}>{m.homeName}</td>
            <td className="num ovr"><Result m={m} /></td>
            <td style={{ fontWeight: m.away === me.id ? 600 : 400 }}>{m.awayName}</td>
          </tr>)}
        </tbody></table>
      </div>
      {(!me.sponsors.trikot || me.sponsors.bande.length === 0) && me.sponsors.offers > 0 && <div className="card">
        <h3>Sponsoren</h3>
        <p style={{ marginTop: 0 }}>{!me.sponsors.trikot ? 'Du hast noch keinen Trikotsponsor' : 'Deine Werbebanden sind noch leer'} – es liegen {me.sponsors.offers} Angebote vor.</p>
        <p className="muted" style={{ fontSize: 13 }}>Unter „Finanzen → Sponsoren“ wählst du deine Partner aus und kannst nachverhandeln.</p>
      </div>}
      <div className="card">
        <h3>Auslaufende Verträge</h3>
        {expiring.length === 0 ? <div className="muted">Keine Verträge laufen zum Ende dieser Saison aus.</div> : <>
          <p className="muted" style={{ marginTop: 0, fontSize: 13 }}>Diese Spieler gehen am Saisonende ablösefrei, wenn du nicht verlängerst (Kader → Verlängern).</p>
          <table><tbody>{expiring.slice(0, 8).map(p => <tr key={p.id}><td><Pos p={p.pos} /></td><td>{p.name}</td><td className="num">{p.age} J.</td><td className="num ovr">{p.ovr}</td></tr>)}</tbody></table>
          {expiring.length > 8 && <div className="muted">… und {expiring.length - 8} weitere</div>}
        </>}
      </div>
      <div className="card">
        <h3>Nachrichten</h3>
        <ul className="news">
          {st.news.slice(0, 14).map((n, i) => <li key={i} style={{ fontWeight: n.teamId === me.id ? 600 : 400 }}>
            <span className="muted" style={{ fontSize: 12 }}>{formatDate(n.t)} · </span>{n.text}</li>)}
        </ul>
      </div>
      {st.history.length > 0 && <div className="card">
        <div className="row"><h3 style={{ margin: 0 }}>Ehrentafel</h3><span className="spacer" /><Img src={IMG.schale} className="icon-48" /></div>
        <table className="wrap"><tbody>{st.history.slice().reverse().map(h => <tr key={h.season}>
          <td className="muted">Saison {h.season}</td>
          <td>🏆 {h.champions[1]}</td>
          <td className="muted">2. Liga: {h.champions[2] || '–'}<br />3. Liga: {h.champions[3] || '–'}</td>
          <td className="muted">Pokal: {h.cup || '–'}{h.cp ? <><br />CP: {h.cp}</> : null}{h.ep ? <><br />EP: {h.ep}</> : null}{h.champions[me.league] && me.league > 3 ? <><br />{lgName(st, me.league)}: {h.champions[me.league]}</> : null}</td>
        </tr>)}</tbody></table>
      </div>}
    </div>
  </>;
}

// ---------- Kader ----------
// ---------- Spieler-Infofenster ----------
let OPEN_PLAYER = () => {};
const PN = ({ p, children }) => <a className="pn" onClick={e => { e.stopPropagation(); OPEN_PLAYER(p.id); }}>{children || p.name}</a>;
const Stars = ({ n, title }) => <span className="stars" title={title || `Talent: ${n} von 5 Sternen`}>{'★'.repeat(n)}<i>{'★'.repeat(5 - n)}</i></span>;
const potText = p => p.potLo === p.potHi ? String(p.potHi) : `${p.potLo}–${p.potHi}`;
function PotBar({ ovr, lo, hi }) {
  return <div className="potbar" title={`Stärke ${ovr} · Potenzial ${lo === hi ? hi : lo + '–' + hi}`}>
    <i className="now" style={{ width: ovr + '%' }} />
    <i className="range" style={{ left: ovr + '%', width: Math.max(0, lo - ovr) + '%' }} />
    <i className="unsure" style={{ left: Math.max(ovr, lo) + '%', width: Math.max(0, hi - Math.max(ovr, lo)) + '%' }} />
  </div>;
}
function PlayerModal({ id, close, st, act }) {
  const [p, setP] = useState(null);
  const [err, setErr] = useState(null);
  const [mode, setMode] = useState(null);
  const [nf, setNf] = useState(''), [nl, setNl] = useState('');
  // Neuer Spieler: Fenster leeren. Automatische Aktualisierung (alle 30 s): Daten still nachladen,
  // ohne das Fenster oder ein offenes Eingabefeld zu schließen
  useEffect(() => { setP(null); setErr(null); setMode(null); }, [id]);
  useEffect(() => {
    let on = true;
    api('/player/' + id).then(d => { if (on) setP(d); }).catch(e => { if (on) setErr(e.message); });
    return () => { on = false; };
  }, [id, st]);
  // Aktionen für eigene Spieler: die passenden Fenster ersetzen kurz das Infofenster
  const done = r => { if (r) close(); };
  if (p && mode === 'extend') return <ContractModal p={p} close={() => setMode(null)} act={async (...a) => { const r = await act(...a); done(r); return r; }} st={st} mode="extend" />;
  if (p && mode === 'sell') return <SellModal p={p} close={() => setMode(null)} act={async (...a) => { const r = await act(...a); done(r); return r; }} />;
  if (p && mode === 'rename') return <Modal close={() => setMode(null)}>
    <h2>Spieler umbenennen</h2>
    <p className="muted" style={{ marginTop: 0 }}>Aktueller Name: {p.name}. Die Änderung ist kostenlos und gilt sofort – auch in Aufstellung, Live-Ticker und Statistik.</p>
    <label className="muted" style={{ fontSize: 12 }}>Vorname</label>
    <input value={nf} maxLength={20} onChange={e => setNf(e.target.value)} style={{ width: '100%', marginBottom: 10 }} />
    <label className="muted" style={{ fontSize: 12 }}>Nachname</label>
    <input value={nl} maxLength={24} onChange={e => setNl(e.target.value)} style={{ width: '100%' }} />
    <div className="row" style={{ marginTop: 14 }}><span className="spacer" /><button onClick={() => setMode(null)}>Abbrechen</button>
      <button className="primary" disabled={nf.trim().length < 2 || nl.trim().length < 2} onClick={async () => {
        const r = await act('/player/rename', { id: p.id, first: nf, last: nl }, `Umbenannt in ${nf.trim()} ${nl.trim()}`);
        if (r) setMode(null);
      }}>Speichern</button></div>
  </Modal>;
  if (p && mode === 'release') return <Modal close={() => setMode(null)}>
    <h2>{p.name} entlassen?</h2>
    <p>Die Abfindung kostet <b>{eur(p.releaseCost)}</b> (die Hälfte der restlichen Gehälter bis Vertragsende Saison {p.contractEnd}). Der Spieler wird vereinslos.</p>
    <div className="row"><span className="spacer" /><button onClick={() => setMode(null)}>Abbrechen</button>
      <button className="primary" onClick={async () => done(await act('/player/release', { playerId: p.id }, 'Spieler entlassen'))}>Entlassen</button></div>
  </Modal>;
  return <Modal close={close}>
    <div className="pinfo">
      {err && <p style={{ color: 'var(--bad)' }}>{err}</p>}
      {!p && !err && <p className="muted">Lade Spieler …</p>}
      {p && <>
        <div className="row" style={{ alignItems: 'flex-start', flexWrap: 'nowrap' }}>
          <Pos p={p.pos} />
          <div style={{ flex: 1, minWidth: 0 }}>
            <h2 style={{ margin: 0 }}>{p.name}{p.canRename && act && <button className="small rename-btn" title="Namen ändern" onClick={() => { setNf(p.first); setNl(p.last); setMode('rename'); }}>✎</button>}</h2>
            <div className="muted" style={{ fontSize: 13 }}>{POS_NAME[p.pos]} · {p.age} Jahre · {p.teamName || (p.free ? 'vereinslos' : '–')}</div>
            <div style={{ marginTop: 4 }}><Status p={{ ...p, listed: false }} />{p.listed && <span className="tag list">Transferliste {eur(p.listed)}</span>}{p.youth && <span className="tag list">{p.watched ? 'unter Beobachtung' : 'A-Jugend'}</span>}</div>
          </div>
          <button className="small" onClick={close}>✕</button>
        </div>
        <div className="pinfo-grid">
          <div><span>Stärke</span><b className="ovr">{p.ovr}</b></div>
          <div><span>Talent</span><b><Stars n={p.talent} /></b></div>
          <div><span>Potenzial</span><b>{potText(p)}</b></div>
          <div><span>Noch förderbar</span><b className={p.grow[1] > 0 ? 'pos-money' : 'muted'}>{p.grow[1] <= 0 ? 'ausgereizt' : p.grow[0] === p.grow[1] ? `+${p.grow[1]}` : `+${p.grow[0]} bis +${p.grow[1]}`}</b></div>
          <div><span>Alter</span><b>{p.age}</b></div>
          <div><span>Vertrag</span><b>{p.youth ? 'A-Jugend' : p.contractEnd ? `bis S${p.contractEnd}` : p.free ? 'vereinslos' : '–'}</b>{p.contractLeft != null && <small className="muted">{p.contractLeft <= 1 ? 'läuft zum Saisonende aus' : `noch ${p.contractLeft} Saisons`}</small>}</div>
          <div><span>Gehalt</span><b>{p.youth ? '–' : eur(p.salary)}</b>{!p.youth && <small className="muted">pro Spieltag</small>}</div>
          <div><span>Marktwert</span><b>{eur(p.value)}</b></div>
        </div>
        <PotBar ovr={p.ovr} lo={p.potLo} hi={p.potHi} />
        <p className="muted" style={{ fontSize: 12, margin: '6px 0 12px' }}>
          <b style={{ color: 'var(--text)' }}>{p.phase}.</b>{' '}
          {p.grow[1] <= 0 ? 'Der Spieler hat sein Potenzial erreicht – Training hält ihn nur noch auf seinem Niveau.'
            : p.age >= 30 ? 'In diesem Alter kommt kaum noch etwas dazu.'
            : p.exact ? `Er kann sich noch um bis zu ${p.grow[1]} Stärkepunkte verbessern. Je näher er seinem Potenzial kommt, desto langsamer wächst er.`
            : p.youth ? `Beobachtungsstand ${p.scout} % – je länger er unter Beobachtung steht, desto genauer kennt ihr sein Potenzial.`
            : 'Fremde Spieler sind nur grob eingeschätzt – das genaue Potenzial siehst du erst, wenn er bei dir spielt.'}
        </p>
        <div className="pinfo-skills">{SKILLS.map(k => <div key={k} className={MAIN_SKILL[p.pos] === k ? 'main' : ''}>
          <span>{SKILL_NAME[k]}</span><div className="bar"><i style={{ width: p.skills[k] + '%' }} /></div><b>{p.skills[k]}</b>
          {p.train && p.train[k] > 0 && <small className="muted" title="Trainingsfortschritt zum nächsten Punkt">{Math.round(p.train[k] * 100)} %</small>}
        </div>)}</div>
        <div className="row" style={{ fontSize: 12, marginTop: 10, gap: 6 }}><span className="muted">Stärke auf Position:</span>
          {Object.entries(p.posStrength).map(([k, v]) => <span key={k} className={'tag' + (k === p.pos ? ' list' : '')}>{k} {v}</span>)}</div>
        {!p.youth && <div className="row" style={{ fontSize: 12, marginTop: 6, gap: 12 }}>
          <span className="muted">Saison:</span><span>{p.season.games} Spiele · {p.season.goals} Tore · {p.season.assists || 0} Vorlagen</span>
          <span className="muted">Karriere:</span><span>{p.career.games} Spiele · {p.career.goals} Tore</span>
          <span className="muted">Form:</span><span>{(p.form || 5).toFixed(1)}</span>
        </div>}
        {p.own && act && <div className="row pinfo-actions">
          {p.canExtend ? <button className="primary" onClick={() => setMode('extend')}>Vertrag verlängern</button>
            : <button disabled title={`Verlängern geht erst, wenn der Vertrag höchstens noch bis zur nächsten Saison läuft (aktuell bis Saison ${p.contractEnd})`}>Verlängern ab S{p.contractEnd - 1}</button>}
          {p.listed
            ? <button onClick={async () => { const r = await act('/market/unlist', { playerId: p.id }, 'Angebot zurückgezogen'); if (r) close(); }}>Von Transferliste nehmen</button>
            : <button onClick={() => setMode('sell')}>Verkaufen</button>}
          <button className="danger" onClick={() => setMode('release')}>Entlassen</button>
        </div>}
      </>}
    </div>
  </Modal>;
}

// ---------- A-Jugend ----------
function Youth({ st, act }) {
  const [d, setD] = useState(null);
  const [sign, setSign] = useState(null);
  const [years, setYears] = useState(3);
  const [drop, setDrop] = useState(null);
  const [pos, setPos] = useState('');
  const load = () => api('/youth').then(setD).catch(() => {});
  useEffect(() => { load(); }, [st]);
  if (!d) return <div className="card muted">Lade A-Jugend …</div>;
  const rows = d.players.filter(y => !pos || y.pos === pos);
  const full = d.squad >= d.squadMax;
  const doAct = async (path, body, ok) => { const r = await act(path, body, ok); await load(); return r; };
  const o = sign && sign.offers.find(x => x.years === years);
  return <>
    <Banner src={IMG.gelaende('jugend')} title="A-Jugend" sub={`Jugendinternat Stufe ${d.level}`} />
    <div className="card">
      <div className="row" style={{ marginBottom: 10 }}>
        <h2 style={{ margin: 0 }}>A-Jugend</h2>
        <span className="muted">{d.players.length} Spieler · Förderplätze {d.watched}/{d.slots} · Profikader {d.squad}/{d.squadMax}</span>
        <span className="spacer" />
        <select value={pos} onChange={e => setPos(e.target.value)}><option value="">Alle Positionen</option>{Object.keys(POS_NAME).map(p => <option key={p} value={p}>{POS_NAME[p]}</option>)}</select>
      </div>
      <div className="tw"><table className="compact">
        <thead><tr><th>Pos</th><th>Name</th><th className="num">Alter</th><th className="num">Stärke</th><th>Talent</th><th className="num">Potenzial</th>
          <th className="num">TW</th><th className="num">ABW</th><th className="num">SPA</th><th className="num">TOR</th><th className="num">KON</th><th className="num">Beob.</th><th></th></tr></thead>
        <tbody>{rows.length === 0 && <tr><td colSpan="13" className="muted">Keine Spieler.</td></tr>}
          {rows.map(y => <tr key={y.id} className={y.watched ? 'watched' : ''}>
            <td><Pos p={y.pos} /></td>
            <td><PN p={y} />{y.watched && <span className="tag list">beobachtet</span>}{y.lastYear && <span className="tag soon">letztes Jahr</span>}</td>
            <td className="num">{y.age}</td>
            <td className="num ovr">{y.ovr}</td>
            <td><Stars n={y.talent} /></td>
            <td className="num">{potText(y)}</td>
            {SKILLS.map(k => <td key={k} className={'num' + (MAIN_SKILL[y.pos] === k ? ' main' : '')}>{y.skills[k]}</td>)}
            <td className="num muted">{y.scout} %</td>
            <td><span className="row" style={{ gap: 4, flexWrap: 'nowrap' }}>
              {y.watched
                ? <><button className="small primary" disabled={full} title={full ? 'Profikader ist voll' : ''} onClick={() => { setYears(3); setSign(y); }}>Profivertrag</button>
                  <button className="small" onClick={() => doAct('/youth/watch', { id: y.id, on: false }, `${y.name} wird nicht mehr gefördert`)}>Beenden</button></>
                : <button className="small" disabled={d.watched >= d.slots} title={d.watched >= d.slots ? 'Alle Förderplätze belegt' : ''} onClick={() => doAct('/youth/watch', { id: y.id, on: true }, `${y.name} steht jetzt unter Beobachtung`)}>Beobachten</button>}
              <button className="small danger" onClick={() => setDrop(y)}>✕</button>
            </span></td>
          </tr>)}</tbody>
      </table></div>
      <p className="muted" style={{ fontSize: 12 }}>
        <b>So funktioniert die A-Jugend:</b> Jede Saison kommt ein neuer Jahrgang dazu. Spieler <b>unter Beobachtung</b> werden gezielt gefördert und entwickeln sich nach jedem Spieltag deutlich schneller, außerdem wird ihr Potenzial immer genauer sichtbar. Nur beobachtete Spieler können einen <b>Profivertrag</b> für die erste Mannschaft bekommen (1–4 Jahre, halbes Handgeld). Wer 19 wird und keinen Vertrag hat, verlässt den Verein. Ein größeres <b>Jugendinternat</b> bringt mehr Spieler, mehr Förderplätze und bessere Talente.
      </p>
    </div>
    {sign && <Modal close={() => setSign(null)}>
      <h2>Profivertrag für {sign.name}</h2>
      <p className="muted" style={{ marginTop: 0 }}>{POS_NAME[sign.pos]} · {sign.age} Jahre · Stärke {sign.ovr} · Potenzial {potText(sign)} <Stars n={sign.talent} /></p>
      {sign.offers.map(x => <label key={x.years} className={'opt' + (x.years === years ? ' on' : '')}>
        <input type="radio" checked={x.years === years} onChange={() => setYears(x.years)} />
        <span><b>{x.years} Jahr{x.years > 1 ? 'e' : ''}</b> · bis Saison {x.until}</span><span className="spacer" />
        <span className="num">{eur(x.salary)} / ST<br /><span className="muted" style={{ fontSize: 12 }}>Handgeld {eur(x.fee)}</span></span>
      </label>)}
      {o && <p style={{ fontSize: 13 }}>Budget danach: {eur(d.budget - o.fee)}</p>}
      <div className="row"><span className="spacer" /><button onClick={() => setSign(null)}>Abbrechen</button>
        <button className="primary" disabled={!o || d.budget < o.fee} onClick={async () => { const r = await doAct('/youth/sign', { id: sign.id, years }, `${sign.name} spielt jetzt für die erste Mannschaft!`); if (r) setSign(null); }}>Unterschreiben</button></div>
    </Modal>}
    {drop && <Modal close={() => setDrop(null)}>
      <h2>{drop.name} aus der A-Jugend entlassen?</h2>
      <p>Der Spieler verlässt den Verein sofort. Sein Platz wird zum nächsten Jahrgang neu besetzt.</p>
      <div className="row"><span className="spacer" /><button onClick={() => setDrop(null)}>Abbrechen</button>
        <button className="primary" onClick={async () => { await doAct('/youth/release', { id: drop.id }, `${drop.name} hat die A-Jugend verlassen`); setDrop(null); }}>Entlassen</button></div>
    </Modal>}
  </>;
}

function Squad({ st, act }) {
  const [sort, setSort] = useState({ k: 'pos', d: 1 });
  const season = st.game.season;
  const players = useMemo(() => {
    const val = (p, k) => k === 'pos' ? POS_ORDER[p.pos] * 1000 - p.ovr : k in p.skills ? p.skills[k] : k === 'goals' ? p.season.goals : k === 'games' ? p.season.games : k === 'gap' ? p.potHi - p.ovr : p[k];
    return st.myTeam.players.slice().sort((a, b) => (val(a, sort.k) > val(b, sort.k) ? 1 : -1) * sort.d * (sort.k === 'pos' ? 1 : -1));
  }, [st, sort]);
  const Th = ({ k, children, num }) => <th className={'sort ' + (num ? 'num' : '')} onClick={() => setSort(s => ({ k, d: s.k === k ? -s.d : 1 }))}>{children}{sort.k === k ? (sort.d > 0 ? ' ▾' : ' ▴') : ''}</th>;
  const totalSalary = st.myTeam.players.reduce((s, p) => s + p.salary, 0);
  return <>
    <Banner src={IMG.banner('kader')} title="Kader" />
    <div className="card">
      <div className="row" style={{ marginBottom: 10 }}>
        <h2 style={{ margin: 0 }}>Kader</h2>
        <span className="muted">{st.myTeam.players.length} Spieler · Gehälter {eur(totalSalary)} pro Spieltag</span>
      </div>
      <div className="tw"><table className="compact">
        <thead><tr>
          <Th k="pos">Pos</Th><th>Name</th><Th k="age" num>Alter</Th><Th k="ovr" num>Stärke</Th><Th k="talent">Talent</Th><Th k="gap" num>Pot.</Th>
          <Th k="tw" num>TW</Th><Th k="abw" num>ABW</Th><Th k="spa" num>SPA</Th><Th k="tor" num>TOR</Th><Th k="kon" num>KON</Th>
          <Th k="form" num>Form</Th><Th k="games" num>Sp</Th><Th k="goals" num>Tore</Th>
          <Th k="value" num>Marktwert</Th><Th k="salary" num>Gehalt</Th><Th k="contractEnd" num>Vertrag</Th><th></th>
        </tr></thead>
        <tbody>{players.map(p => <tr key={p.id}>
          <td><Pos p={p.pos} /></td>
          <td><PN p={p} /><Status p={p} /></td>
          <td className="num">{p.age}</td>
          <td className="num ovr">{p.ovr}</td>
          <td><Stars n={p.talent} /></td>
          <td className="num muted">{p.potHi > p.ovr ? `+${p.potHi - p.ovr}` : '–'}</td>
          {SKILLS.map(k => <td key={k} className={'num' + (MAIN_SKILL[p.pos] === k ? ' main' : '')}>{p.skills[k]}</td>)}
          <td className="num">{p.form.toFixed(1)}</td>
          <td className="num">{p.season.games}</td>
          <td className="num">{p.season.goals}</td>
          <td className="num">{eur(p.value)}</td>
          <td className="num muted">{eur(p.salary)}</td>
          <td className="num"><Contract p={p} season={season} /></td>
          <td><button className="small" onClick={() => OPEN_PLAYER(p.id)}>Info</button></td>
        </tr>)}</tbody>
      </table></div>
      <p className="muted" style={{ fontSize: 12 }}>TW = Torwart · ABW = Abwehr · SPA = Spielaufbau · TOR = Torschuss · KON = Kondition. Hervorgehoben ist die Hauptfähigkeit der Position. Talent (1–5 Sterne) und Pot. (wie viele Stärkepunkte noch möglich sind) – ein Klick auf den Namen öffnet das Spieler-Infofenster. Form wirkt sich bis zu ±10 % auf die Stärke im Spiel aus. Vertrag = läuft bis Ende der Saison; Verlängern, Verkaufen und Entlassen findest du im Spieler-Infofenster (Klick auf den Namen oder „Info“). Verlängern geht, wenn der Vertrag höchstens noch bis zur nächsten Saison läuft. Auslaufende Spieler gehen am Saisonende ablösefrei.</p>
    </div>
  </>;
}
function Contract({ p, season }) {
  if (!p.contractEnd) return <span className="muted">–</span>;
  const left = p.contractEnd - season;
  return <span style={{ whiteSpace: 'nowrap' }}>S{p.contractEnd}{left <= 0 ? <span className="tag exp">läuft aus</span> : left === 1 ? <span className="tag soon">noch 1 J.</span> : ''}</span>;
}
// Vertragsangebot: Verlängerung (eigener Spieler) oder Verpflichtung (vereinslos)
function ContractModal({ p, close, act, st, mode }) {
  const [info, setInfo] = useState(null);
  const [err, setErr] = useState(null);
  const [years, setYears] = useState(2);
  useEffect(() => {
    api('/contract/' + p.id).then(d => { setInfo(d); if (d.options.length) setYears(d.options[Math.min(1, d.options.length - 1)].years); }).catch(e => setErr(e.message));
  }, [p.id]);
  const o = info && info.options.find(x => x.years === years);
  const budget = st.myTeam.budget;
  return <Modal close={close}>
    <h2>{mode === 'extend' ? `Vertrag verlängern: ${p.name}` : `${p.name} verpflichten`}</h2>
    {err && <p style={{ color: 'var(--bad)' }}>{err}</p>}
    {!info && !err && <p className="muted">Lade Angebot …</p>}
    {info && info.reason && <p className="muted">{info.reason}</p>}
    {info && !info.reason && <>
      <p className="muted" style={{ marginTop: 0 }}>{info.age} Jahre · Stärke {info.ovr}{info.contractEnd ? ` · Vertrag bis Saison ${info.contractEnd} · Gehalt jetzt ${eur(info.salaryNow)}` : ''}. {info.age >= 30 ? 'Ältere Spieler unterschreiben nur kürzere Verträge.' : ''}</p>
      {info.options.map(x => <label key={x.years} className={'opt' + (x.years === years ? ' on' : '')}>
        <input type="radio" checked={x.years === years} onChange={() => setYears(x.years)} />
        <span><b>{mode === 'extend' ? '+' : ''}{x.years} Jahr{x.years > 1 ? 'e' : ''}</b> · bis Saison {x.until}</span>
        <span className="spacer" />
        <span className="num">{eur(x.salary)} / ST<br /><span className="muted" style={{ fontSize: 12 }}>Handgeld {eur(x.fee)}</span></span>
      </label>)}
      {o && <p style={{ fontSize: 13 }}>Gehalt pro Spieltag: <b>{eur(o.salary)}</b>{info.salaryNow && !info.free ? ` (${o.salary >= info.salaryNow ? '+' : ''}${eur(o.salary - info.salaryNow)})` : ''} · Handgeld sofort: <b>{eur(o.fee)}</b> · Budget danach: {eur(budget - o.fee)}</p>}
      <p className="muted" style={{ fontSize: 12 }}>Kurze Verträge (1 Jahr) kosten 15 % mehr Gehalt. Das Gehalt richtet sich nach der aktuellen Stärke – wer sich im Training verbessert hat, will mehr.</p>
    </>}
    <div className="row"><span className="spacer" /><button onClick={close}>Abbrechen</button>
      {info && !info.reason && o && <button className="primary" disabled={budget < o.fee} onClick={async () => {
        const r = mode === 'extend'
          ? await act('/contract/extend', { playerId: p.id, years }, `Vertrag mit ${p.name} bis Saison ${o.until} verlängert`)
          : await act('/market/buy', { playerId: p.id, years }, `${p.name} ist verpflichtet!`);
        if (r) close();
      }}>{mode === 'extend' ? 'Verlängern' : 'Verpflichten'}</button>}
    </div>
  </Modal>;
}
function SellModal({ p, close, act }) {
  const [price, setPrice] = useState(p.value);
  return <Modal close={close}>
    <h2>{p.name} auf die Transferliste</h2>
    <p className="muted">Marktwert {eur(p.value)} · erlaubt {eur(p.value * 0.5)} bis {eur(p.value * 5)}. Computer-Vereine kaufen meist nur bis etwa 115 % des Marktwerts.</p>
    <div className="row"><input type="number" step="5000" value={price} onChange={e => setPrice(e.target.value)} style={{ flex: 1 }} /> €</div>
    <div className="row" style={{ marginTop: 14 }}><span className="spacer" />
      <button onClick={close}>Abbrechen</button>
      <button className="primary" onClick={async () => { const r = await act('/market/list', { playerId: p.id, price: Number(price) }, 'Spieler steht auf der Transferliste'); if (r) close(); }}>Anbieten</button>
    </div>
  </Modal>;
}

// ---------- Aufstellung ----------
function Lineup({ st, act }) {
  const me = st.myTeam;
  const [formation, setFormation] = useState(me.formation);
  const [tactic, setTactic] = useState(me.tactic);
  const [slots, setSlots] = useState(me.resolved.ids);
  const [benchSel, setBenchSel] = useState(me.resolved.bench || []);
  const [pick, setPick] = useState(null);
  const savedLineup = JSON.stringify([me.formation, me.tactic, me.resolved.ids, me.resolved.bench]);
  useEffect(() => { setFormation(me.formation); setTactic(me.tactic); setSlots(me.resolved.ids); setBenchSel(me.resolved.bench || []); setPick(null); }, [savedLineup]);
  const byId = Object.fromEntries(me.players.map(p => [p.id, p]));
  const form = FORMATIONS[formation];
  const chosen = slots.map(id => (id && byId[id]) || null);
  const r = ratings(chosen, formation, tactic);
  const usedIds = new Set(slots.filter(Boolean));
  const sorted = me.players.slice().sort((a, b) => POS_ORDER[a.pos] - POS_ORDER[b.pos] || b.ovr - a.ovr);
  function setSlot(i, id) {
    setSlots(s => { const n = s.slice(); const j = n.indexOf(id); if (id && j >= 0) n[j] = n[i]; n[i] = id || null; return n; });
  }
  const rows = ['ST', 'MF', 'AB', 'TW'].map(pos => form.map((p, i) => ({ p, i })).filter(x => x.p === pos));
  const maxR = Math.max(r.def, r.mid, r.att, 1);
  return <div className="grid lineup-grid">
    <div className="card">
      <div className="row" style={{ marginBottom: 12 }}>
        <h2 style={{ margin: 0 }}>Aufstellung</h2><span className="spacer" />
        <select value={formation} onChange={e => setFormation(e.target.value)}>{Object.keys(FORMATIONS).map(f => <option key={f}>{f}</option>)}</select>
        <select value={tactic} onChange={e => setTactic(e.target.value)}>
          <option value="defensiv">Defensiv</option><option value="normal">Ausgeglichen</option><option value="offensiv">Offensiv</option>
        </select>
      </div>
      <div className="pitch">
        {rows.map((row, ri) => <div className="line" key={ri}>{row.map(({ p: pos, i }) => {
          const pl = chosen[i];
          const warn = !pl || pl.injured > 0 || pl.suspended > 0 || pl.pos !== pos;
          return <div key={i} className={'slot' + (warn ? ' warn' : '')}>
            <div className="lbl">{POS_NAME[pos]}{pl ? ' · ' + Math.round(strengthAt(pl, pos)) : ''}</div>
            <select value={slots[i] || ''} onChange={e => setSlot(i, e.target.value)}>
              <option value="">– leer –</option>
              {sorted.map(p => <option key={p.id} value={p.id}>
                {usedIds.has(p.id) && slots[i] !== p.id ? '• ' : ''}{p.last} ({p.pos} {Math.round(strengthAt(p, pos))}){p.injured ? ' ✚' : ''}{p.suspended ? ' gesperrt' : ''}
              </option>)}
            </select>
          </div>;
        })}</div>)}
      </div>
      <div className="row" style={{ marginTop: 12 }}>
        <span className="muted" style={{ fontSize: 12 }}>Gelber Rahmen = Platz leer, Spieler fehlt oder spielt auf fremder Position (−10 %). Fehlende Spieler werden vor dem Anpfiff automatisch ersetzt. Die Aufstellung gilt für Liga und Pokal.</span>
        <span className="spacer" />
        <button onClick={() => act('/lineup/auto', {}, 'Beste Elf automatisch aufgestellt')}>Automatisch</button>
        <button className="primary" onClick={() => act('/lineup', { formation, tactic, lineup: slots.map(x => x || null), bench: benchSel.filter(id => !slots.includes(id)) }, 'Aufstellung gespeichert')}>Speichern</button>
      </div>
    </div>
    <div className="card">
      <h3>Mannschaftsstärke</h3>
      {[['Torwart', r.gk, 99], ['Abwehr', r.def, maxR], ['Mittelfeld', r.mid, maxR], ['Angriff', r.att, maxR]].map(([l, v, m]) => <div key={l} style={{ margin: '10px 0' }}>
        <div className="row"><span>{l}</span><span className="spacer" /><b>{v}</b></div>
        <div className="bar"><i style={{ width: Math.min(100, v / m * 100) + '%' }} /></div>
      </div>)}
      <p className="muted" style={{ fontSize: 12 }}>Das Mittelfeld entscheidet über Ballbesitz und damit die Zahl der Chancen. Angriff gegen gegnerische Abwehr und Torwart entscheidet, wie viele davon drin sind. Heimteams bekommen +5 %.</p>
      {(() => {
        const BENCH = st.game.constants.BENCH_SIZE || 9;
        const ok = p => !p.injured && !p.suspended;
        // wie auf dem Server: gewählte Bankspieler zuerst, freie Plätze mit den stärksten Übrigen auffüllen
        const bench = [];
        for (const id of benchSel) { const p = byId[id]; if (p && ok(p) && !usedIds.has(id) && bench.length < BENCH && !bench.includes(p)) bench.push(p); }
        for (const p of sorted.filter(p => ok(p) && !usedIds.has(p.id) && !bench.includes(p)).sort((a, b) => b.ovr - a.ovr)) { if (bench.length >= BENCH) break; bench.push(p); }
        const benchIds = new Set(bench.map(p => p.id));
        const stands = sorted.filter(p => !usedIds.has(p.id) && !benchIds.has(p.id));
        const click = p => {
          if (!ok(p)) return;
          const onBench = benchIds.has(p.id);
          if (!pick) { if (!onBench && bench.length < BENCH) { setBenchSel(bench.map(x => x.id).concat(p.id)); return; } setPick(p.id); return; }
          if (pick === p.id) { setPick(null); return; }
          const pickOnBench = benchIds.has(pick);
          if (pickOnBench === onBench) { setPick(p.id); return; } // gleiche Seite: Auswahl wechseln
          const out = pickOnBench ? pick : p.id, inn = pickOnBench ? p.id : pick;
          setBenchSel(bench.map(x => (x.id === out ? inn : x.id)));
          setPick(null);
        };
        const Row = ({ p }) => <div className={'row brow' + (ok(p) ? ' click' : ' off') + (pick === p.id ? ' sel' : '') + (pick && pick !== p.id && ok(p) && benchIds.has(pick) !== benchIds.has(p.id) ? ' target' : '')}
          onClick={() => click(p)}><Pos p={p.pos} /><span>{p.name}</span><span className="spacer" /><span className="ovr">{p.ovr}</span><Status p={p} /></div>;
        return <>
          <h3 style={{ marginTop: 14 }}>Ersatzbank ({bench.length}/{BENCH})</h3>
          {bench.slice().sort((a, b) => POS_ORDER[a.pos] - POS_ORDER[b.pos] || b.ovr - a.ovr).map(p => <Row key={p.id} p={p} />)}
          <p className="muted" style={{ fontSize: 12 }}>{pick ? 'Jetzt den Spieler anklicken, mit dem getauscht werden soll.' : 'Tauschen: einen Spieler auf der Bank und einen auf der Tribüne nacheinander anklicken, dann speichern.'} Im Spiel sind bis zu 5 Wechsel möglich. Verletzte und gesperrte Spieler können nicht auf die Bank.</p>
          {stands.length > 0 && <><h3 style={{ marginTop: 10 }}>Tribüne ({stands.length})</h3>{stands.map(p => <Row key={p.id} p={p} />)}</>}
          <div className="row" style={{ marginTop: 10 }}><span className="spacer" />
            <button className="small" onClick={() => { setBenchSel([]); setPick(null); }}>Bank automatisch</button>
            <button className="small primary" onClick={() => act('/lineup', { formation, tactic, lineup: slots.map(x => x || null), bench: bench.map(p => p.id) }, 'Aufstellung und Ersatzbank gespeichert')}>Speichern</button></div>
        </>;
      })()}
    </div>
  </div>;
}

// ---------- Training ----------
// ---------- Trainingslager (Winter- und Sommerpause) ----------
const PAUSE_NAME = { winter: 'Winterpause', summer: 'Sommerpause' };
function Camp({ st, act }) {
  const [d, setD] = useState(null);
  const [sel, setSel] = useState(null);
  const load = () => api('/camp').then(setD).catch(() => {});
  useEffect(() => { load(); }, [st]);
  if (!d) return null;
  const P = d.pause, pn = PAUSE_NAME[P.type], days = d.days[P.type];
  const doAct = async (path, body, ok) => { const r = await act(path, body, ok); await load(); return r; };
  const range = ([a, b]) => a === b ? `+${a}` : `+${a} bis +${b}`;
  return <div className="card camp-card" style={{ marginBottom: 16 }}>
    <div className="row" style={{ marginBottom: 6 }}>
      <h2 style={{ margin: 0 }}>🏕 Trainingslager</h2><span className="spacer" />
      {P.active ? <span className="tag list">{pn} läuft · noch <Countdown to={P.until} /></span>
        : <span className="muted">{pn} nach dem {P.afterMd}. Spieltag · noch {P.inMd} Spieltag{P.inMd === 1 ? '' : 'e'}{days ? ` · ${days} Tage Pause` : ''}</span>}
    </div>
    {d.booked ? <div className="camp-booked">
      <div>Gebucht für die {pn}: <b>{d.booked.name}</b> ({eur(d.booked.cost)})</div>
      <div className="muted" style={{ fontSize: 12 }}>{P.active ? 'Die Mannschaft ist gerade im Trainingslager – die Wirkung kommt am Ende der Pause.' : 'Die Mannschaft reist zu Beginn der Pause ab, die Wirkung kommt am Ende der Pause.'}</div>
      {!P.active && <button className="small danger" style={{ marginTop: 6 }} onClick={() => doAct('/camp/cancel', {}, 'Trainingslager storniert – Kosten erstattet')}>Stornieren</button>}
    </div> : !d.open ? <p className="muted" style={{ fontSize: 13, margin: '4px 0 0' }}>Buchbar ab {d.window} Spieltage vor der {pn} und während der Pause (in {P.inMd - d.window} Spieltag{P.inMd - d.window === 1 ? '' : 'en'}).</p> : null}
    {(d.open && !d.booked) && <div className="camp-grid">{d.camps.map(c => <div key={c.key} className={'camp-opt' + (sel === c.key ? ' on' : '')} onClick={() => setSel(c.key)}>
      <b>{c.name}</b>
      <small className="muted">{c.desc}</small>
      <div className="camp-fx"><span>Hauptfähigkeit {range(c.main)}</span><span>Kondition {range(c.kon)}</span><span>Form +{String(c.form).replace('.', ',')}</span>{c.inj > 0 && <span className="warn-txt">Verletzungsrisiko {Math.round(c.inj * 100)} %</span>}</div>
      <div className="row"><b className={d.budget < c.cost ? 'neg-money' : ''}>{eur(c.cost)}</b><span className="spacer" />
        <button className="small primary" disabled={d.budget < c.cost} onClick={e => { e.stopPropagation(); doAct('/camp/book', { key: c.key }, `${c.name} gebucht`); }}>Buchen</button></div>
    </div>)}</div>}
    <p className="muted" style={{ fontSize: 12, marginBottom: 0 }}>Es gibt zwei Pausen pro Saison: die Winterpause nach der Hinrunde und die Sommerpause zwischen zwei Saisons. Die Werte gelten pro Spieler (verletzte Spieler bleiben zu Hause). Junge Spieler mit viel Potenzial profitieren am meisten.</p>
  </div>;
}

function Training({ st, act }) {
  const me = st.myTeam;
  const [focus, setFocus] = useState(me.training.focus);
  const [intensity, setIntensity] = useState(me.training.intensity);
  const [indiv, setIndiv] = useState(() => {
    const a = me.training.individual.slice();
    while (a.length < me.trainSlots) a.push({ playerId: '', skill: 'tor' });
    return a.slice(0, me.trainSlots);
  });
  useEffect(() => {
    setFocus(me.training.focus); setIntensity(me.training.intensity);
    const a = me.training.individual.slice();
    while (a.length < me.trainSlots) a.push({ playerId: '', skill: 'tor' });
    setIndiv(a.slice(0, me.trainSlots));
  }, [JSON.stringify([me.training, me.trainSlots])]);
  const sorted = me.players.slice().sort((a, b) => POS_ORDER[a.pos] - POS_ORDER[b.pos] || a.age - b.age);
  const FOCUS_SKILL = { kondition: 'kon', abwehr: 'abw', spielaufbau: 'spa', torschuss: 'tor' };
  const trainedSkill = p => p.pos === 'TW' ? (focus === 'kondition' ? 'kon' : 'tw') : focus === 'ausgewogen' ? MAIN_SKILL[p.pos] : FOCUS_SKILL[focus];
  const indivMap = Object.fromEntries(indiv.filter(x => x.playerId).map(x => [x.playerId, x.skill]));
  const INT = { locker: 'Locker – Form steigt, weniger Fortschritt', normal: 'Normal – ausgewogen', hart: 'Hart – 50 % mehr Fortschritt, Form sinkt, Verletzungsgefahr' };
  const save = () => act('/training', { focus, intensity, individual: indiv.filter(x => x.playerId) }, 'Trainingsplan gespeichert');
  return <div className="grid g2">
    <div className="card">
      <h2>Trainingsplan</h2>
      <h3>Schwerpunkt der Mannschaft</h3>
      <div className="focus-grid">
        {Object.entries(st.game.focus).map(([k, label]) => <div key={k} className={'focus-card' + (focus === k ? ' on' : '')} onClick={() => setFocus(k)}>
          <Img src={IMG.training(k)} className="focus-img" />
          <div className="cond" style={{ fontSize: 17 }}>{label}</div>
        </div>)}
      </div>
      <p className="muted" style={{ fontSize: 12 }}>Ausgewogen trainiert die Hauptfähigkeit jeder Position plus Kondition. Torhüter trainieren immer mit dem Torwarttrainer (außer beim Schwerpunkt Kondition).</p>
      <h3 style={{ marginTop: 14 }}>Intensität</h3>
      {Object.entries(INT).map(([k, label]) => <label key={k} className="radio"><input type="radio" checked={intensity === k} onChange={() => setIntensity(k)} /> {label}</label>)}
      <h3 style={{ marginTop: 14 }}>Einzeltraining ({me.trainSlots} Platz{me.trainSlots > 1 ? 'e' : ''})</h3>
      {indiv.map((x, i) => <div key={i} className="row" style={{ marginBottom: 6 }}>
        <select value={x.playerId} onChange={e => setIndiv(a => a.map((y, j) => j === i ? { ...y, playerId: e.target.value } : y))} style={{ flex: 1 }}>
          <option value="">– kein Spieler –</option>
          {sorted.map(p => <option key={p.id} value={p.id}>{p.name} ({p.pos}, {p.age})</option>)}
        </select>
        <select value={x.skill} onChange={e => setIndiv(a => a.map((y, j) => j === i ? { ...y, skill: e.target.value } : y))}>
          {SKILLS.map(s => <option key={s} value={s}>{SKILL_NAME[s]}</option>)}
        </select>
      </div>)}
      <p className="muted" style={{ fontSize: 12 }}>Einzeltraining verdoppelt den Fortschritt in der gewählten Fähigkeit. Mehr Plätze gibt es mit dem Trainingszentrum (Gelände). Junge Spieler lernen deutlich schneller.</p>
      <button className="primary" onClick={save}>Trainingsplan speichern</button>
    </div>
    <div className="card">
      <h3>Letzte Fortschritte</h3>
      {me.trainLog.length === 0 && <div className="muted">Nach jedem Spieltag wird trainiert. Fortschritte erscheinen hier.</div>}
      <ul className="news">{me.trainLog.slice(0, 18).map((l, i) => <li key={i}><span className="muted" style={{ fontSize: 12 }}>S{l.season} · {l.md}. ST · </span>{l.text}</li>)}</ul>
    </div>
    <div className="card" style={{ gridColumn: '1/-1' }}>
      <h3>Fortschritt der Spieler</h3>
      <div className="tw"><table>
        <thead><tr><th>Pos</th><th>Name</th><th className="num">Alter</th><th>Trainiert</th><th className="num">Wert</th><th style={{ width: 160 }}>bis zum nächsten Punkt</th><th>Einzeltraining</th></tr></thead>
        <tbody>{sorted.map(p => {
          const sk = trainedSkill(p);
          const prog = (p.train && p.train[sk]) || 0;
          return <tr key={p.id}>
            <td><Pos p={p.pos} /></td><td>{p.name}{p.injured > 0 && <span className="tag inj">pausiert</span>}</td><td className="num">{p.age}</td>
            <td>{SKILL_NAME[sk]}</td><td className="num">{p.skills[sk]}</td>
            <td><div className="bar"><i style={{ width: Math.min(100, prog * 100) + '%' }} /></div></td>
            <td className="muted">{indivMap[p.id] ? `${SKILL_NAME[indivMap[p.id]]} (${p.skills[indivMap[p.id]]})` : ''}</td>
          </tr>;
        })}</tbody>
      </table></div>
    </div>
  </div>;
}

// ---------- Spielplan ----------
function Fixtures({ st, open }) {
  const [league, setLeague] = useState(st.myTeam.league);
  const [data, setData] = useState(null);
  const [md, setMd] = useState(Math.min(st.game.matchday, st.game.totalMatchdays - 1));
  useEffect(() => { api('/fixtures?league=' + league).then(setData).catch(() => {}); }, [st, league]);
  if (!data) return <div className="card muted">Lade Spielplan …</div>;
  const games = data.fixtures[md] || [];
  const team = id => st.teams.find(t => t.id === id);
  return <div className="card">
    <div className="row" style={{ marginBottom: 12 }}>
      <h2 style={{ margin: 0 }}>Spielplan Saison {data.season}</h2><span className="spacer" />
      <LeagueSwitch value={league} onChange={setLeague} st={st} />
      <button disabled={md <= 0} onClick={() => setMd(md - 1)}>‹</button>
      <select value={md} onChange={e => setMd(Number(e.target.value))}>{data.fixtures.map((_, i) => <option key={i} value={i}>{i + 1}. Spieltag · {data.dates[i] ? new Date(data.dates[i]).toLocaleDateString('de-DE', { weekday: 'short', day: '2-digit', month: '2-digit' }) : ''}</option>)}</select>
      <button disabled={md >= data.fixtures.length - 1} onClick={() => setMd(md + 1)}>›</button>
    </div>
    <p className="muted">{md < data.matchday ? 'Gespielt am' : md === data.matchday ? 'Nächster Spieltag:' : 'Voraussichtlich:'} {dayDate(data.dates[md])} · Liga dienstags, donnerstags und samstags, Pokal sonntags</p>
    {games.length === 0 && <p className="card-note">Spielfrei – diese Liga pausiert am {md + 1}. Spieltag (kleinere Ligen haben weniger Spieltage).</p>}
    {games.length > 0 && (() => { const ids = new Set(games.flatMap(f => [f.home, f.away])); const free = st.teams.filter(t => t.league === league && !ids.has(t.id)); return free.length ? <p className="muted" style={{ fontSize: 13 }}>Spielfrei: {free.map(t => t.name).join(', ')}</p> : null; })()}
    <table className="wrap"><tbody>{games.map((f, i) => {
      const mine = f.home === st.myTeam.id || f.away === st.myTeam.id;
      return <tr key={i} className={(mine ? 'me ' : '') + (f.matchId ? 'click' : '')} onClick={() => f.matchId && open({ type: 'match', id: f.matchId })}>
        <td style={{ textAlign: 'right', width: '42%' }}><span className="tn" style={{ justifyContent: 'flex-end' }}>{f.homeName}<Crest {...team(f.home)} size={18} /></span></td>
        <td className="num ovr" style={{ textAlign: 'center' }}>{f.result ? `${f.result[0]}:${f.result[1]}` : '–:–'}</td>
        <td style={{ width: '42%' }}><TeamName t={team(f.away)} /></td>
      </tr>;
    })}</tbody></table>
  </div>;
}

// ---------- Tabelle ----------
// Markierung der Tabellenplätze: Meister, Champions-Pokal (2–4), Europa-Pokal (5–7), Aufstieg, Abstieg
function zoneFor(league, i, n, L, eighth) {
  if (league === 1) { if (i === 0) return 'zone-champ'; if (i < 4) return 'zone-cp'; if (i < 7 || (i === 7 && eighth)) return 'zone-ep'; }
  else if (L.up && i < L.up) return 'zone-up';
  if (L.down && i >= n - L.down) return 'zone-down';
  return '';
}
const ZoneLegend = ({ league, L }) => <div className="zone-legend">
  {league === 1 && <><span><i className="z-champ" />Meister + Champions-Pokal</span><span><i className="z-cp" />Champions-Pokal (2–4)</span><span><i className="z-ep" />Europa-Pokal (5–7 + Pokalsieger; ist er schon dabei, rückt Platz 8 nach)</span></>}
  <span><span className="tag cw-tag">🏆 Pokalsieger</span> spielt nächste Saison im Europa-Pokal</span>
  {league !== 1 && L.up > 0 && <span><i className="z-up" />{L.level === 4 ? 'Aufstieg in die 3. Liga' : 'Aufstieg'}</span>}
  {L.down > 0 && <span><i className="z-down" />Abstieg{L.level === 3 ? ' (je einer in jede Staffel der 4. Liga)' : ''}</span>}
</div>;
function Table({ st, open }) {
  const [league, setLeague] = useState(st.myTeam.league);
  const tab = st.tables[league];
  const manager = id => st.teams.find(t => t.id === id)?.manager;
  const L = st.game.leagues[league];
  const cw = st.cupWinnerId;
  const top1 = st.tables[1] || [];
  const eighth = !!cw && top1.slice(0, 7).some(r => r.id === cw);
  const zone = i => zoneFor(league, i, tab.length, L, eighth);
  return <div className="card">
    <div className="row" style={{ marginBottom: 10 }}>
      <Img src={IMG.liga(L.level)} className="icon-48" />
      <h2 style={{ margin: 0 }}>{L.name} · Saison {st.game.season}</h2><span className="spacer" />
      <span className="muted">nach {st.game.matchday} von {st.game.totalMatchdays} Spieltagen</span>
      <LeagueSwitch value={league} onChange={setLeague} st={st} />
    </div>
    <div className="tw"><table>
      <thead><tr><th>#</th><th>Verein</th><th>Trainer</th><th className="num">Sp</th><th className="num">S</th><th className="num">U</th><th className="num">N</th><th className="num">Tore</th><th className="num">Diff</th><th className="num">Pkt</th><th>Form</th></tr></thead>
      <tbody>{tab.map((r, i) => <tr key={r.id} className={'click ' + (r.id === st.myTeam.id ? 'me ' : '') + zone(i)} onClick={() => open({ type: 'team', id: r.id })}>
        <td>{i + 1}.</td><td><TeamName t={r} bold />{r.id === cw && <span className="tag cw-tag" title="Pokalsieger – spielt nächste Saison im Europa-Pokal">🏆 Pokalsieger</span>}</td><td className="muted">{manager(r.id) || 'Computer'}</td>
        <td className="num">{r.sp}</td><td className="num">{r.s}</td><td className="num">{r.u}</td><td className="num">{r.n}</td>
        <td className="num">{r.tore}:{r.gegen}</td><td className="num">{r.diff > 0 ? '+' + r.diff : r.diff}</td><td className="num ovr">{r.pkt}</td><td><Form f={r.form} /></td>
      </tr>)}</tbody>
    </table></div>
    <ZoneLegend league={league} L={L} />
  </div>;
}

// ---------- Pokal ----------
function Cup({ st, open }) {
  const [cup, setCup] = useState(undefined);
  const [sel, setSel] = useState(null);
  const [onlyMine, setOnlyMine] = useState(false);
  useEffect(() => { api('/cup').then(setCup).catch(() => setCup(null)); }, [st]);
  // Während der Live-Auslosung alle 2 Sekunden nachladen, kurz davor jede Minute
  const drawRound = cup && cup.rounds && cup.rounds[Math.min(cup.round, cup.rounds.length - 1)];
  const drawing = drawRound && drawRound.draw && !drawRound.draw.done;
  const drawLive = drawing && drawRound.draw.live;
  useEffect(() => {
    if (!drawing) return;
    const i = setInterval(() => api('/cup').then(setCup).catch(() => {}), drawLive ? 2000 : 30000);
    return () => clearInterval(i);
  }, [drawing, drawLive]);
  if (cup === undefined) return <div className="card muted">Lade Pokal …</div>;
  if (!cup) return <div className="card"><h2>Pokal</h2><p className="muted">Der Pokal startet mit der nächsten Saison.</p></div>;
  const team = id => st.teams.find(t => t.id === id);
  const current = Math.min(cup.round, cup.rounds.length - 1);
  const ri = sel === null ? current : sel;
  const r = cup.rounds[ri];
  const myLeague = st.myTeam.league;
  let ties = r.ties;
  if (onlyMine) ties = ties.filter(t => t.home === st.myTeam.id || t.away === st.myTeam.id || team(t.home).league === myLeague || team(t.away).league === myLeague);
  const myTie = r.ties.find(t => t.home === st.myTeam.id || t.away === st.myTeam.id);
  return <>
    {drawing && <CupDraw r={drawRound} st={st} />}
    <Banner src={IMG.pokalBanner} pos="center 12%" title={`Pokal · Saison ${cup.season}`} sub={cup.winnerName ? `Pokalsieger: ${cup.winnerName}` : `${cup.participants || st.teams.length} Vereine · K.-o.-System · gespielt wird sonntags`} />
    <div className="grid cup-grid">
      <div className="card">
        <div className="row"><Img src={IMG.pokal} className="icon-96" /><div>
          <h2 style={{ margin: 0 }}>Pokal Saison {cup.season}</h2>
          <p className="muted" style={{ margin: '6px 0 0', fontSize: 13 }}>{cup.qualifiers ? <>
            Teilnehmer: alle {cup.participants - cup.qualifiers} Vereine der 1., 2. und 3. Liga und {cup.qualifiers} Viertligisten – die 2 besten jeder Staffel der Vorsaison ({Object.entries(cup.qualifiersByLeague).map(([id, n]) => `${lgName(st, Number(id)).replace('4. Liga ', '')} ${n}`).join(', ')}). </> : null}
            Der Unterklassige hat Heimrecht, das Finale ist auf neutralem Platz. Unentschieden → Verlängerung → Elfmeterschießen.</p>
        </div></div>
        <h3 style={{ marginTop: 14 }}>Rundenplan</h3>
        <table><tbody>{cup.rounds.map((x, i) => <tr key={i} className={'click' + (i === ri ? ' me' : '')} onClick={() => setSel(i)}>
          <td>{x.name}</td><td className="muted">{x.size} Vereine</td>
          <td className="num muted">{x.played ? 'gespielt ' : ''}{new Date(x.at).toLocaleDateString('de-DE', { weekday: 'short', day: '2-digit', month: '2-digit' })}</td>
        </tr>)}</tbody></table>
        <h3 style={{ marginTop: 14 }}>Prämien</h3>
        <table><tbody>
          <tr><td>Weiterkommen je Runde</td><td className="num">20.000 € → 600.000 €</td></tr>
          <tr><td>TV-Geld je Spiel</td><td className="num">10.000 € → 150.000 €</td></tr>
          <tr><td>Pokalsieger</td><td className="num pos-money">2.000.000 €</td></tr>
          <tr><td>Finalist</td><td className="num">1.000.000 €</td></tr>
        </tbody></table>
        {cup.winnerName && <div className="big" style={{ marginTop: 12 }}>🏆 {cup.winnerName}</div>}
      </div>
      <div className="card">
        <div className="row" style={{ marginBottom: 8 }}>
          <h3 style={{ margin: 0 }}>{r.name}</h3>
          <span className="muted">{r.played ? 'gespielt am' : 'voraussichtlich'} {dayDate(r.at)}</span>
          <span className="spacer" />
          <label className="muted" style={{ fontSize: 13, cursor: 'pointer' }}><input type="checkbox" checked={onlyMine} onChange={e => setOnlyMine(e.target.checked)} /> nur meine Liga</label>
        </div>
        {myTie && <p style={{ margin: '0 0 8px' }}>Dein Spiel: <b>{team(myTie.home).name} – {team(myTie.away).name}</b></p>}
        {!myTie && st.cupStatus === 'nicht qualifiziert' && <p className="muted" style={{ margin: '0 0 8px' }}>Dein Verein ist diese Saison nicht für den Pokal qualifiziert.</p>}
        {r.draw && !r.draw.done && r.ties.length === 0 ? <p className="muted">Die Paarungen werden {r.draw.live ? 'gerade live ausgelost – siehe oben.' : `am ${dayDate(r.draw.at)} live ausgelost.`}</p> :
          r.ties.length === 0 ? <p className="muted">Die Auslosung folgt, sobald die Runde davor gespielt ist.</p> :
          <div className="tw" style={{ maxHeight: 640, overflowY: 'auto' }}><table className="wrap"><tbody>{ties.map((t, i) => {
            const mine = t.home === st.myTeam.id || t.away === st.myTeam.id;
            return <tr key={i} className={(mine ? 'me ' : '') + (t.matchId ? 'click' : '')} onClick={() => t.matchId && open({ type: 'match', id: t.matchId })}>
              <td style={{ fontWeight: t.winner === t.home ? 600 : 400 }}><TeamName t={team(t.home)} /> <span className="muted" style={{ fontSize: 11 }}>{lgName(st, team(t.home).league).replace('Liga ', '')}</span></td>
              <td className="num ovr" style={{ textAlign: 'center' }}>{t.result ? <Result m={{ goals: t.result, pens: t.pens, extraTime: t.extraTime }} /> : '–:–'}</td>
              <td style={{ fontWeight: t.winner === t.away ? 600 : 400 }}><TeamName t={team(t.away)} /> <span className="muted" style={{ fontSize: 11 }}>{lgName(st, team(t.away).league).replace('Liga ', '')}</span></td>
            </tr>;
          })}</tbody></table></div>}
      </div>
    </div>
  </>;
}

// Live-Pokalauslosung: Los für Los, mit Lostopf
function CupDraw({ r, st }) {
  const d = r.draw;
  const team = id => st.teams.find(t => t.id === id) || {};
  const mine = id => id === st.myTeam.id;
  const [, force] = useState(0);
  useEffect(() => { const i = setInterval(() => force(x => x + 1), 1000); return () => clearInterval(i); }, []);
  if (!d.live) {
    return <div className="card draw-card">
      <div className="row"><span className="draw-ball">?</span><div>
        <h2 style={{ margin: 0 }}>Live-Auslosung · {r.name}</h2>
        <div className="muted">am {dayDate(d.at)} – noch <Countdown to={d.at} /> · {r.size} Vereine im Lostopf</div>
      </div></div>
    </div>;
  }
  const last = r.ties[r.ties.length - 1];
  const sh = id => <span className={mine(id) ? 'draw-me' : ''}><Crest id={id} colors={team(id).colors} short={team(id).short} size={30} /> {team(id).name} <small className="muted">{lgName(st, team(id).league)}</small></span>;
  const pct = Math.round(d.balls / d.total * 100);
  return <div className="card draw-card live">
    <div className="row" style={{ justifyContent: 'space-between' }}>
      <h2 style={{ margin: 0 }}><span className="live-dot" /> Live-Auslosung · {r.name}</h2>
      <span className="muted">Los {Math.min(d.balls, d.total)} von {d.total}</span>
    </div>
    <div className="progress" style={{ maxWidth: 'none' }}><i style={{ width: pct + '%' }} /></div>
    <div className="draw-stage">
      {d.pending ? <div className="draw-now" key={'p' + d.balls}>
        <div className="draw-label">Gezogen – Heimrecht</div><div className="draw-team pop">{sh(d.pending.home)}</div>
        <div className="draw-vs">gegen</div><div className="draw-team"><span className="draw-ball spin">?</span></div>
      </div> : last ? <div className="draw-now" key={'f' + d.balls}>
        <div className="draw-label">Paarung {r.ties.length}</div><div className="draw-team">{sh(last.home)}</div>
        <div className="draw-vs">gegen</div><div className="draw-team pop">{sh(last.away)}</div>
      </div> : <div className="draw-now"><span className="draw-ball spin">?</span><div className="muted">Die Losfee greift in den Topf …</div></div>}
    </div>
    <div className="draw-cols">
      <div><h3>Gezogene Paarungen</h3>
        <div className="draw-list">{r.ties.slice().reverse().map((t, i) => <div key={r.ties.length - i} className={'draw-pair' + (mine(t.home) || mine(t.away) ? ' me' : '')}>
          <span className="n">{r.ties.length - i}.</span><span className="h">{team(t.home).name}</span><span className="v">–</span><span className="a">{team(t.away).name}</span></div>)}</div>
      </div>
      <div><h3>Lostopf ({d.pot.length})</h3>
        <div className="draw-pot">{d.pot.map(id => <span key={id} className={'pot-ball' + (mine(id) ? ' me' : '')} title={team(id).name}><Crest id={id} colors={team(id).colors} short={team(id).short} size={16} />{team(id).short}</span>)}</div>
      </div>
    </div>
  </div>;
}

// ---------- International ----------
function International({ st, open }) {
  const [data, setData] = useState(null);
  const [abroad, setAbroad] = useState(null);
  const [sub, setSub] = useState(null);
  const [country, setCountry] = useState('val');
  const [stage, setStage] = useState(null);
  useEffect(() => { api('/euro').then(setData).catch(() => {}); }, [st]);
  useEffect(() => { if (sub === 'abroad' && !abroad) api('/abroad').then(setAbroad).catch(() => {}); }, [sub]);
  if (!data) return <div className="card muted">Lade internationale Wettbewerbe …</div>;
  const my = st.euroStatus || {};
  const cur = sub || my.comp || 'cp';
  const countries = Object.fromEntries(data.countries.map(c => [c.id, c]));
  const T = id => data.teams[id] || { name: '?', short: '?', colors: ['#333', '#fff'], country: 'home' };
  const me = st.myTeam.id;
  const TeamCell = ({ id, bold }) => { const t = T(id); return <span className={'tn' + (id === me ? ' my' : '')} style={{ fontWeight: bold ? 600 : 400 }}>
    <Flag c={countries[t.country]} /><Crest id={t.id} colors={t.colors} short={t.short} size={18} />{t.name}</span>; };
  const comp = data.comps[cur];
  const clickMatch = mid => mid && open({ type: 'match', id: mid });
  return <>
    <div className="card" style={{ marginBottom: 16 }}>
      <div className="row">
        <h2 style={{ margin: 0 }}>International · Saison {data.season}</h2><span className="spacer" />
        <Seg value={cur} options={[['cp', 'Champions-Pokal'], ['ep', 'Europa-Pokal'], ['abroad', 'Ligen im Ausland']]} onChange={v => { setSub(v); setStage(null); }} />
      </div>
      <p className="muted" style={{ marginBottom: 0 }}>
        {my.comp ? <>Dein Verein spielt im <b>{my.compName}</b>{my.next ? <> – nächstes Spiel: {my.next.round}, {my.next.hName} – {my.next.aName}, voraussichtlich {dayDate(my.next.at)}</> : my.winner ? ' – und hat ihn gewonnen! 🏆' : ' – ausgeschieden.'}</>
          : <>Platz 1–4 der 1. Liga spielen in der nächsten Saison im Champions-Pokal, Platz 5–7 und der Pokalsieger im Europa-Pokal (ist der Pokalsieger schon über die Liga dabei, rückt Platz 8 nach) – zusammen mit den besten Vereinen aus 11 Ländern. Gespielt wird mittwochs.</>}
      </p>
    </div>
    {cur !== 'abroad' && comp && <div className="grid euro-grid">
      <div className="card">
        <h3>{comp.name}</h3>
        <p className="muted" style={{ fontSize: 13, marginTop: 0 }}>Je Land Platz {comp.places[0]}–{comp.places[1]} · {comp.groups.length} Gruppen à 4 · Gruppensieger und beste Zweite ins Achtelfinale · K.-o.-Runden mit Hin- und Rückspiel · Finale auf neutralem Platz</p>
        <table><tbody>{data.rounds.map((r, i) => <tr key={i} className={i === data.round ? 'me' : ''}>
          <td style={{ whiteSpace: 'normal' }}>{r.name}</td><td className="num muted">{r.played ? 'gespielt ' : ''}{new Date(r.at).toLocaleDateString('de-DE', { weekday: 'short', day: '2-digit', month: '2-digit' })}</td>
        </tr>)}</tbody></table>
        <h3 style={{ marginTop: 14 }}>Prämien (heimische Vereine)</h3>
        <table className="wrap"><tbody>
          <tr><td>Teilnahme</td><td className="num">{eur(comp.prize.start)}</td></tr>
          <tr><td>Sieg in der Gruppe</td><td className="num">{eur(comp.prize.win)}</td></tr>
          <tr><td>Unentschieden in der Gruppe</td><td className="num">{eur(comp.prize.draw)}</td></tr>
          {['Einzug Achtelfinale', 'Einzug Viertelfinale', 'Einzug Halbfinale'].map((l, i) => <tr key={l}><td>{l}</td><td className="num">{eur(comp.prize.ko[i])}</td></tr>)}
          <tr><td>Finalist</td><td className="num">{eur(comp.prize.finalLoser)}</td></tr>
          <tr><td>Sieger</td><td className="num pos-money">{eur(comp.prize.winner)}</td></tr>
        </tbody></table>
        {comp.winnerName && <div className="big" style={{ marginTop: 12 }}>🏆 {comp.winnerName}</div>}
      </div>
      <div>
        {comp.ko.length > 0 && <div className="row" style={{ marginBottom: 10 }}>
          <Seg value={stage === null ? 'groups' : stage} options={[['groups', 'Gruppen']].concat(comp.ko.map((k, i) => [i, k.name]))} onChange={v => setStage(v === 'groups' ? null : v)} />
        </div>}
        {stage === null ? <div className="group-grid">{comp.groups.map(g => <div key={g.name} className="card">
          <h3>Gruppe {g.name}</h3>
          <table className="wrap compact"><thead><tr><th>#</th><th>Verein</th><th className="num">Sp</th><th className="num">Tore</th><th className="num">Pkt</th></tr></thead>
            <tbody>{g.table.map((r, i) => <tr key={r.id} className={(r.id === me ? 'me ' : '') + (comp.advanced.includes(r.id) ? 'zone-up' : '')}>
              <td>{i + 1}.</td><td><TeamCell id={r.id} /></td><td className="num">{r.sp}</td><td className="num">{r.tore}:{r.gegen}</td><td className="num ovr">{r.pkt}</td>
            </tr>)}</tbody></table>
          <details style={{ marginTop: 6 }}><summary className="muted" style={{ fontSize: 12, cursor: 'pointer' }}>Spiele</summary>
            <table className="wrap compact"><tbody>{g.matches.flatMap((md, i) => md.map((m, j) => <tr key={i + '-' + j} className={m.matchId ? 'click' : ''} onClick={() => clickMatch(m.matchId)}>
              <td className="muted">{i + 1}.</td><td>{T(m.h).name}</td><td className="num">{m.res ? `${m.res[0]}:${m.res[1]}` : '–:–'}</td><td>{T(m.a).name}</td></tr>))}</tbody></table>
          </details>
        </div>)}</div>
          : <div className="card">
            <h3>{comp.ko[stage].name}</h3>
            <table className="wrap"><tbody>{comp.ko[stage].ties.map((t, i) => {
              const leg1 = t.legs[0], leg2 = t.legs[1];
              return <tr key={i} className={t.a === me || t.b === me ? 'me' : ''}>
                <td><TeamCell id={t.a} bold={t.winner === t.a} /></td>
                <td className="num"><span className={leg1 && leg1.matchId ? 'clicky' : ''} onClick={() => leg1 && clickMatch(leg1.matchId)}>{leg1 ? `${leg1.res[0]}:${leg1.res[1]}` : '–:–'}</span>
                  {!t.final && <> · <span className={leg2 && leg2.matchId ? 'clicky' : ''} onClick={() => leg2 && clickMatch(leg2.matchId)}>{leg2 ? `${leg2.res[1]}:${leg2.res[0]}` : '–:–'}</span></>}
                  {(t.agg || (t.final && leg1 && leg1.pens)) && <div className="muted" style={{ fontSize: 11 }}>{t.agg ? `Gesamt ${t.agg[0]}:${t.agg[1]}` : ''}{(leg2 && leg2.pens) || (t.final && leg1.pens) ? ' i.E.' : leg2 && leg2.et ? ' n.V.' : ''}</div>}</td>
                <td><TeamCell id={t.b} bold={t.winner === t.b} /></td>
              </tr>;
            })}</tbody></table>
            <p className="muted" style={{ fontSize: 12 }}>Ergebnisse jeweils aus Sicht des links stehenden Vereins: Hinspiel · Rückspiel. Klick aufs Ergebnis öffnet den Spielbericht (bei Spielen mit heimischen Vereinen).</p>
          </div>}
      </div>
    </div>}
    {cur === 'abroad' && (!abroad ? <div className="card muted">Lade Auslandsligen …</div> : (() => {
      const c = abroad.countries.find(x => x.id === country) || abroad.countries[0];
      return <div className="card">
        <div className="row" style={{ flexWrap: 'wrap', gap: 6, marginBottom: 12 }}>
          {abroad.countries.map(x => <button key={x.id} className={'small' + (x.id === c.id ? ' primary' : '')} onClick={() => setCountry(x.id)}><Flag c={x} /> {x.name}</button>)}
        </div>
        <div className="row"><Flag c={c} h={22} /><h2 style={{ margin: 0 }}>{c.name} · 1. Liga</h2><span className="spacer" /><span className="muted">nur Simulation · {abroad.round} von 34 Runden gespielt</span></div>
        <div className="tw"><table className="wrap" style={{ marginTop: 10 }}>
          <thead><tr><th>#</th><th>Verein</th><th className="num">Sp</th><th className="num">S</th><th className="num">U</th><th className="num">N</th><th className="num">Tore</th><th className="num">Diff</th><th className="num">Pkt</th><th className="num">Stärke</th></tr></thead>
          <tbody>{c.table.map((r, i) => <tr key={r.id} className={i < 4 ? 'zone-cp' : i < 7 ? 'zone-ep' : ''}>
            <td>{i + 1}.</td><td><span className="tn"><Crest id={r.id} colors={r.colors} short={r.short} size={18} />{r.name}</span></td>
            <td className="num">{r.sp}</td><td className="num">{r.s}</td><td className="num">{r.u}</td><td className="num">{r.n}</td>
            <td className="num">{r.tore}:{r.gegen}</td><td className="num">{r.diff > 0 ? '+' + r.diff : r.diff}</td><td className="num ovr">{r.pkt}</td><td className="num muted">{r.strength}</td>
          </tr>)}</tbody>
        </table></div>
        <p className="muted" style={{ fontSize: 12 }}>Blau: Platz 1–4 → Champions-Pokal der nächsten Saison · Orange: Platz 5–7 → Europa-Pokal (dazu der Pokalsieger{c.cupWinner ? `: ${c.cupWinner}` : ''}).{c.champions.length ? ` Letzte Meister: ${c.champions.slice(0, 3).map(x => `S${x.season} ${x.name}`).join(', ')}.` : ''}</p>
      </div>;
    })())}
  </>;
}

// ---------- Transfermarkt ----------
function Market({ st, act }) {
  const [data, setData] = useState(null);
  const [tab, setTab] = useState('list');
  const [pos, setPos] = useState('');
  const [confirm, setConfirm] = useState(null);
  useEffect(() => { api('/market').then(setData).catch(() => {}); }, [st]);
  if (!data) return <div className="card muted">Lade Transfermarkt …</div>;
  let rows = tab === 'list' ? data.listings.filter(l => l.sellerId !== st.myTeam.id) : tab === 'free' ? data.freeAgents : data.listings.filter(l => l.sellerId === st.myTeam.id);
  if (pos) rows = rows.filter(p => p.pos === pos);
  return <>
    <Banner src={IMG.banner('transfermarkt')} title="Transfermarkt" />
    <div className="card">
      <div className="row" style={{ marginBottom: 12 }}>
        <h2 style={{ margin: 0 }}>Transfermarkt</h2><span className="spacer" />
        <span className="muted">Budget: <b className={st.myTeam.budget < 0 ? 'neg-money' : 'pos-money'}>{eur(st.myTeam.budget)}</b></span>
      </div>
      <div className="row" style={{ marginBottom: 10 }}>
        <button className={tab === 'list' ? 'primary' : ''} onClick={() => setTab('list')}>Transferliste</button>
        <button className={tab === 'free' ? 'primary' : ''} onClick={() => setTab('free')}>Vereinslose</button>
        <button className={tab === 'mine' ? 'primary' : ''} onClick={() => setTab('mine')}>Meine Angebote</button>
        <span className="spacer" />
        <select value={pos} onChange={e => setPos(e.target.value)}><option value="">Alle Positionen</option>{Object.keys(POS_NAME).map(p => <option key={p} value={p}>{POS_NAME[p]}</option>)}</select>
      </div>
      {tab === 'free' && <p className="muted" style={{ fontSize: 12 }}>Vereinslose Spieler kosten keine Ablöse, nur ein Handgeld je nach Vertragslaufzeit. Hier landen auch Spieler, deren Vertrag ausgelaufen ist. Die Liste wechselt jeden Spieltag teilweise.</p>}
      {tab === 'list' && <p className="muted" style={{ fontSize: 12 }}>Gekaufte Spieler übernehmen ihren laufenden Vertrag (Gehalt und Laufzeit).</p>}
      <div className="tw"><table>
        <thead><tr><th>Pos</th><th>Name</th><th className="num">Alter</th><th className="num">Stärke</th><th>Talent</th><th className="num">TW</th><th className="num">ABW</th><th className="num">SPA</th><th className="num">TOR</th><th className="num">KON</th>
          {tab === 'list' && <th>Verein</th>}<th className="num">Marktwert</th><th className="num">Gehalt</th>{tab !== 'free' && <th className="num">Vertrag</th>}<th className="num">{tab === 'free' ? 'Handgeld (2 J.)' : 'Preis'}</th><th></th></tr></thead>
        <tbody>{rows.length === 0 && <tr><td colSpan="15" className="muted">Keine Spieler gefunden.</td></tr>}
          {rows.map(p => <tr key={p.id}>
            <td><Pos p={p.pos} /></td><td><PN p={p} /></td><td className="num">{p.age}</td><td className="num ovr">{p.ovr}</td><td><Stars n={p.talent} title={`Potenzial geschätzt ${potText(p)}`} /></td>
            {SKILLS.map(k => <td key={k} className={'num' + (MAIN_SKILL[p.pos] === k ? ' main' : '')}>{p.skills[k]}</td>)}
            {tab === 'list' && <td className="muted">{p.sellerName}</td>}
            <td className="num">{eur(p.value)}</td><td className="num muted">{eur(p.salary)}</td>{tab !== 'free' && <td className="num"><Contract p={p} season={st.game.season} /></td>}<td className="num"><b>{eur(p.price)}</b></td>
            <td>{tab === 'mine'
              ? <button className="small" onClick={() => act('/market/unlist', { playerId: p.id }, 'Angebot zurückgezogen')}>Zurückziehen</button>
              : <button className="small primary" disabled={st.myTeam.budget < p.price} onClick={() => setConfirm(p)}>{tab === 'free' ? 'Verpflichten' : 'Kaufen'}</button>}</td>
          </tr>)}</tbody>
      </table></div>
      {confirm && tab === 'free' && <ContractModal p={confirm} close={() => setConfirm(null)} act={act} st={st} mode="sign" />}
      {confirm && tab !== 'free' && <Modal close={() => setConfirm(null)}>
        <h2>{confirm.name} verpflichten?</h2>
        <p>Ablöse: <b>{eur(confirm.price)}</b><br />Gehalt: {eur(confirm.salary)} pro Spieltag · Vertrag bis Saison {confirm.contractEnd}<br />Budget danach: {eur(st.myTeam.budget - confirm.price)}</p>
        <div className="row"><span className="spacer" /><button onClick={() => setConfirm(null)}>Abbrechen</button>
          <button className="primary" onClick={async () => { await act('/market/buy', { playerId: confirm.id }, `${confirm.name} ist verpflichtet!`); setConfirm(null); }}>Verpflichten</button></div>
      </Modal>}
    </div>
  </>;
}

// Fanshop: Boni, Sonderfunktionen und Aktionen
const pc = x => String(x).replace('.', ',');
function ShopExtras({ s, act }) {
  const feats = [[5, 'Fan-Day', '+50 % Shop, alle 10 ST', 'Alle 10 Spieltage für ein Heimspiel: +50 % Fanshop-Einnahmen und +1 % Fans', s.fanDay],
    [10, 'Ltd. Trikots', '20 % Chance: +20 %', 'Limitierte Trikots: bei jedem Heimspiel 20 % Chance auf +20 % Fanshop-Einnahmen', s.limited],
    [15, 'Premium-Merch', 'VIP-Artikel', 'Premium-Merch (VIP-Artikel): +5.000 € je VIP-Logen-Stufe und 0,25 € pro Fan bei jedem Heimspiel', s.premium],
    [20, 'Fan-Festival', '1× pro Saison +100 %', 'Einmal pro Saison für ein Heimspiel: +100 % Fanshop-Einnahmen und +2 % Fans', s.festival]];
  return <div className="shop-x">
    {s.level > 0 && <div className="shop-boni">
      <span>Loyalität <b>+{pc(s.loy)} %</b></span><span>Merch-Chance <b>+{pc(s.merch)} %</b></span><span>Fanwachstum <b>+{pc(s.grow)} %</b></span><span>Shop-Effizienz <b>+{pc(s.eff)} %</b></span>
    </div>}
    {s.next && s.next.feature && <div style={{ fontSize: 12, marginTop: 4 }}>⭐ Stufe {s.next.level} schaltet frei: <b className="pos-money">{s.next.feature}</b></div>}
    <div className="shop-feats">{feats.map(([l, n, d, long, on]) => <div key={l} className={on ? 'on' : ''} title={long}><span>{on ? '✓' : '🔒'}</span><b>{n}</b><small>{on ? d : `ab Stufe ${l}`}</small></div>)}</div>
    {(s.fanDay || s.festival) && <div className="row" style={{ marginTop: 8, gap: 6 }}>
      {s.armed && <span className="tag list">{s.armed === 'festival' ? 'Fan-Festival' : 'Fan-Day'} beim nächsten Heimspiel</span>}
      {s.fanDay && !(s.armed && s.fanDayIn > 0) && <button className="small" disabled={!!s.armed || s.fanDayIn > 0} onClick={() => act('/shop/event', { type: 'fanday' }, 'Fan-Day fürs nächste Heimspiel geplant')}>{s.fanDayIn > 0 ? `Fan-Day in ${s.fanDayIn} ST` : '🎉 Fan-Day'}</button>}
      {s.festival && <button className="small primary" disabled={!!s.armed || s.festivalUsed} onClick={() => act('/shop/event', { type: 'festival' }, 'Fan-Festival fürs nächste Heimspiel geplant')}>{s.festivalUsed ? 'Festival diese Saison erledigt' : '🎪 Fan-Festival'}</button>}
    </div>}
  </div>;
}

// ---------- Stadion & Gelände ----------
function Grounds({ st, act }) {
  const me = st.myTeam, C = st.game.constants;
  const building = me.facilities.filter(f => f.build).length;
  const [confirm, setConfirm] = useState(null);
  return <>
    <Banner src={IMG.stadion(me.stadium)} title={me.stadiumName} sub={`${fmt(me.stadium)} Plätze · ${fmt(me.fans)} Fans`} />
    <div className="card" style={{ marginBottom: 16 }}>
      <div className="row">
        <h2 style={{ margin: 0 }}>Stadion & Vereinsgelände</h2><span className="spacer" />
        <span className="muted">Baustellen {building}/{C.MAX_BUILDS} · Unterhalt {eur(me.upkeep)} pro Spieltag · Budget <b className={me.budget < 0 ? 'neg-money' : 'pos-money'}>{eur(me.budget)}</b></span>
      </div>
      <p className="muted" style={{ fontSize: 12, marginBottom: 0 }}>Bauzeit wird in Spieltagen gezählt. Bezahlt wird beim Baubeginn, die Wirkung gilt ab Fertigstellung. Jede Ausbaustufe erhöht den laufenden Unterhalt.</p>
    </div>
    <div className="fac-grid">
      {me.facilities.map(f => <div key={f.key} className="card fac">
        <Img src={IMG.gelaende(f.key)} className="fac-img" />
        <div className="row"><h3 style={{ margin: 0, color: 'var(--text)' }}>{f.name}</h3><span className="spacer" /><span className="muted">Stufe {f.level}/{f.max}</span></div>
        {f.max <= 10 ? <Pips level={f.level} max={f.max} /> : <div className="bar" style={{ margin: '6px 0' }}><i style={{ width: f.level / f.max * 100 + '%' }} /></div>}
        <p className="muted" style={{ fontSize: 13, margin: '6px 0' }}>{f.desc}</p>
        <div style={{ fontSize: 13 }}>Jetzt: <b>{f.effectNow}</b></div>
        {f.effectNext && <div style={{ fontSize: 13 }}>Nächste Stufe: <b className="pos-money">{f.effectNext}</b>{f.shop && f.shop.next && <> · <b className="pos-money">{f.shop.next.bonus}</b></>}</div>}
        {f.shop && <ShopExtras s={f.shop} act={act} />}
        {f.build ? <div style={{ marginTop: 10 }}>
          <div className="row" style={{ fontSize: 13 }}><span>🏗 Ausbau auf Stufe {f.build.to}</span><span className="spacer" /><span>noch {f.build.left} Spieltag{f.build.left === 1 ? '' : 'e'}</span></div>
          <div className="bar"><i style={{ width: Math.max(5, (1 - f.build.left / f.build.total) * 100) + '%', background: 'var(--warn)' }} /></div>
        </div> : f.cost !== null ? <div className="row" style={{ marginTop: 10 }}>
          <span style={{ fontSize: 13 }}>{eur(f.cost)} · {f.time} Spieltage{f.upkeep ? ` · +${eur(f.upkeep)} Unterhalt` : ''}</span><span className="spacer" />
          <button className="primary small" disabled={me.budget < f.cost || building >= C.MAX_BUILDS} onClick={() => setConfirm(f)}>Ausbauen</button>
        </div> : <div className="muted" style={{ marginTop: 10 }}>Voll ausgebaut ✓</div>}
      </div>)}
    </div>
    {confirm && <Modal close={() => setConfirm(null)}>
      <h2>{confirm.name} auf Stufe {confirm.level + 1}?</h2>
      <p>Kosten: <b>{eur(confirm.cost)}</b><br />Bauzeit: {confirm.time} Spieltage<br />Wirkung: {confirm.effectNext}<br />Budget danach: {eur(me.budget - confirm.cost)}</p>
      <div className="row"><span className="spacer" /><button onClick={() => setConfirm(null)}>Abbrechen</button>
        <button className="primary" onClick={async () => { await act('/build', { key: confirm.key }, `Bau gestartet: ${confirm.name}`); setConfirm(null); }}>Bau starten</button></div>
    </Modal>}
  </>;
}

// ---------- Sponsoren ----------
const lum = hex => { const n = parseInt(hex.slice(1), 16); const r = (n >> 16) & 255, g = (n >> 8) & 255, b = n & 255; return (0.299 * r + 0.587 * g + 0.114 * b) / 255; };
const KIT_NAME = { klassisch: 'Klassisch', einfarbig: 'Einfarbig', invers: 'Invers', streifen: 'Streifen', nadelstreifen: 'Nadelstreifen', ringel: 'Ringel', brustring: 'Brustring', schaerpe: 'Schärpe', geteilt: 'Geteilt' };
const SHIRT = 'M62 18 L86 10 Q100 24 114 10 L138 18 L182 50 L160 84 L146 74 L146 188 L54 188 L54 74 L40 84 L18 50 Z';
function KitPattern({ p, c1, c2 }) {
  const arr = (n, f) => Array.from({ length: n }, (_, i) => f(i));
  switch (p) {
    case 'einfarbig': return null;
    case 'invers': return <><rect x="0" y="0" width="200" height="200" fill={c2} /><path d="M138 18 L182 50 L160 84 L146 74 Z" fill={c1} /><path d="M62 18 L18 50 L40 84 L54 74 Z" fill={c1} /></>;
    case 'streifen': return <>{arr(5, i => <rect key={i} x={58 + i * 18} y="0" width="9" height="200" fill={c2} />)}</>;
    case 'nadelstreifen': return <>{arr(11, i => <rect key={i} x={58 + i * 8.5} y="0" width="2" height="200" fill={c2} opacity=".85" />)}</>;
    case 'ringel': return <>{arr(7, i => <rect key={i} x="0" y={30 + i * 24} width="200" height="12" fill={c2} />)}</>;
    case 'brustring': return <rect x="0" y="82" width="200" height="34" fill={c2} />;
    case 'schaerpe': return <path d="M30 20 L62 20 L170 200 L138 200 Z" fill={c2} />;
    case 'geteilt': return <rect x="100" y="0" width="100" height="200" fill={c2} />;
    default: return <><path d="M138 18 L182 50 L160 84 L146 74 Z" fill={c2} opacity=".9" /><path d="M62 18 L18 50 L40 84 L54 74 Z" fill={c2} opacity=".9" /></>; // klassisch: Ärmel in der Zweitfarbe
  }
}
function Jersey({ colors, sponsor, size = 200, kit }) {
  const [c1, c2] = colors || ['#2a9d5c', '#ffffff'];
  const chest = kit === 'invers' || kit === 'brustring' ? c2 : c1;
  const txt = lum(chest) > 0.55 ? '#111' : '#fff';
  const name = sponsor ? sponsor.name.toUpperCase() : '';
  const fs = Math.min(17, 150 / Math.max(6, name.length) * 1.7);
  const cid = useMemo(() => 'k' + Math.random().toString(36).slice(2, 8), []);
  return <svg width={size} height={size} viewBox="0 0 200 200" aria-label="Trikot">
    <defs><clipPath id={cid}><path d={SHIRT} /></clipPath></defs>
    <g clipPath={`url(#${cid})`}>
      <rect x="0" y="0" width="200" height="200" fill={c1} />
      <KitPattern p={kit || 'klassisch'} c1={c1} c2={c2} />
    </g>
    <path d={SHIRT} fill="none" stroke="rgba(0,0,0,.35)" strokeWidth="2" />
    <path d="M86 10 Q100 24 114 10 L118 14 Q100 32 82 14 Z" fill={kit === 'invers' ? c1 : c2} />
    {sponsor && sponsor.logo ? (sponsor.premium
      ? <image href={sponsor.logo} x="66" y="80" width="68" height="48" preserveAspectRatio="xMidYMid meet" />
      : <image href={sponsor.logo} x="56" y="86" width="88" height="30" preserveAspectRatio="xMidYMid meet" />) : <>
    {sponsor && sponsor.premium && <rect x="44" y="88" width="112" height="28" rx="4" fill="#0d1b2a" stroke="#e0b84c" strokeWidth="2" />}
    {name && <text x="100" y={sponsor && sponsor.premium ? 107 : 108} textAnchor="middle" fontFamily="Barlow Condensed, Inter, sans-serif" fontWeight="700"
      fontSize={sponsor && sponsor.premium ? Math.min(fs, 13) : fs} fill={sponsor && sponsor.premium ? '#e0b84c' : txt} letterSpacing=".5" stroke={sponsor && sponsor.premium ? 'none' : (txt === '#fff' ? 'rgba(0,0,0,.35)' : 'rgba(255,255,255,.35)')} strokeWidth=".6">{name}</text>}
    </>}
  </svg>;
}
function SponsorTag({ sp, link }) {
  if (!sp) return null;
  if (sp.logo && !sp.premium) {
    const img = <img src={sp.logo} alt={sp.name} title={sp.name} className="sp-logo" />;
    return link && sp.url ? <a href={sp.url} target="_blank" rel="noopener">{img}</a> : img;
  }
  return <span className={'sp-tag' + (sp.premium ? ' premium' : '')} style={{ background: sp.colors[0], color: sp.colors[1] }}>
    {sp.premium ? '★ ' : ''}{sp.name}{link && sp.url ? <a href={sp.url} target="_blank" rel="noopener" onClick={e => e.stopPropagation()} style={{ color: sp.colors[1], marginLeft: 6 }}>↗</a> : null}
  </span>;
}
function Boards({ boards, slots }) {
  const items = boards.map(b => ({ sp: b.sponsor || b })).concat(Array.from({ length: Math.max(0, (slots || 0) - boards.length) }, () => null));
  if (!items.length) return null;
  return <div className="boards">{items.map((x, i) => x
    ? <div key={i} className={'board' + (x.sp.premium ? ' premium' : '')} style={{ background: x.sp.colors[0], color: x.sp.colors[1] }}>
      {(() => { const inner = x.sp.logo ? <img src={x.sp.logo} alt={x.sp.name} className="board-logo" /> : x.sp.name;
        return x.sp.url ? <a href={x.sp.url} target="_blank" rel="noopener" style={{ color: 'inherit' }} title={x.sp.name}>{inner}</a> : inner; })()}</div>
    : <div key={i} className="board free">frei</div>)}</div>;
}
function Seg({ value, options, onChange }) {
  return <span className="seg">{options.map(([v, l]) => <button key={v} type="button" className={v === value ? 'on' : ''} onClick={() => onChange(v)}>{l}</button>)}</span>;
}
function OfferCard({ o, info, act, reload, sel, setSel }) {
  const [msg, setMsg] = useState(null);
  const variant = (sel && sel.variant) || 'ausgewogen';
  const years = (sel && sel.years) || 2;
  const t = o.type === 'trikot' ? o.terms[variant][years] : o.terms[years];
  const negotiate = async pct => {
    try { const r = await api('/sponsors/negotiate', { offerId: o.id, pct }); setMsg(r); await reload(); }
    catch (e) { setMsg({ result: 'error', message: e.message }); }
  };
  return <div className={'card offer' + (o.sponsor.premium ? ' premium' : '')}>
    <div className="row"><SponsorTag sp={o.sponsor} /><span className="muted" style={{ fontSize: 12 }}>{o.sponsor.branche}</span><span className="spacer" />
      {o.sponsor.premium && <span className="tag premium-tag">Premium-Partner</span>}</div>
    {o.sponsor.logo && o.sponsor.premium && <a href={o.sponsor.url} target="_blank" rel="noopener"><img src={o.sponsor.logo} alt={o.sponsor.name} className="offer-logo" /></a>}
    <p className="muted" style={{ fontSize: 13, margin: '8px 0' }}>„{o.sponsor.slogan}“{o.sponsor.url && <> · <a href={o.sponsor.url} target="_blank" rel="noopener">{o.sponsor.url.replace('https://', '')} ↗</a></>}</p>
    {o.type === 'trikot' && <div className="row" style={{ marginBottom: 6 }}><span className="muted" style={{ width: 70, fontSize: 12 }}>Vertrag</span>
      <Seg value={variant} options={Object.entries(info.variants)} onChange={v => setSel({ variant: v, years })} /></div>}
    <div className="row" style={{ marginBottom: 8 }}><span className="muted" style={{ width: 70, fontSize: 12 }}>Laufzeit</span>
      <Seg value={years} options={[[1, '1 Jahr'], [2, '2 Jahre'], [3, '3 Jahre']]} onChange={y => setSel({ variant, years: y })} /></div>
    <table className="terms"><tbody>
      {o.type === 'trikot' ? <>
        <tr><td>Fixum pro Liga-Spieltag</td><td className="num pos-money">{eur(t.fix)}</td></tr>
        <tr><td>Siegprämie (Liga)</td><td className="num">{eur(t.win)}</td></tr>
        <tr><td>Saisonziel: Platz {t.goalPlace} oder besser</td><td className="num">{eur(t.goalBonus)}</td></tr>
        <tr><td>Handgeld bei Unterschrift</td><td className="num">{eur(t.signing)}</td></tr>
      </> : <tr><td>Pro Heimspiel (Liga und Pokal)</td><td className="num pos-money">{eur(t.fix)}</td></tr>}
      <tr><td>Vertrag bis Saisonende</td><td className="num">Saison {t.until}</td></tr>
    </tbody></table>
    <div className="row" style={{ marginTop: 8, fontSize: 12 }}>
      <span className="muted">Nachverhandeln{o.raise > 0 ? ` (bisher +${Math.round(o.raise * 100)} %)` : ''}:</span>
      {[10, 20, 30].map(p => <button key={p} className="small" disabled={o.triesLeft <= 0} onClick={() => negotiate(p)}>+{p} %</button>)}
      <span className="muted">{o.triesLeft > 0 ? `noch ${o.triesLeft} Versuch${o.triesLeft > 1 ? 'e' : ''}` : 'keine Versuche mehr'}</span>
    </div>
    {msg && <div className={'neg-msg ' + msg.result}>{msg.message}</div>}
    <div className="row" style={{ marginTop: 10 }}>
      <button className="small danger" onClick={() => act('/sponsors/decline', { offerId: o.id }, 'Angebot abgelehnt')}>Ablehnen</button>
      <span className="spacer" />
      <button className="primary" onClick={() => act('/sponsors/sign', { offerId: o.id, variant, years }, `Vertrag mit ${o.sponsor.name} unterschrieben!`)}>Unterschreiben</button>
    </div>
  </div>;
}
function Sponsors({ st, act }) {
  const [info, setInfo] = useState(null);
  const [sel, setSel] = useState({});
  const [cancel, setCancel] = useState(null);
  const reload = () => api('/sponsors').then(setInfo).catch(() => {});
  useEffect(() => { reload(); }, [st]);
  if (!info) return <div className="card muted">Lade Sponsoren …</div>;
  const me = st.myTeam;
  const doAct = async (p, b, ok) => { const r = await act(p, b, ok); await reload(); return r; };
  const jOffers = info.offers.filter(o => o.type === 'trikot');
  const bOffers = info.offers.filter(o => o.type === 'bande');
  const preview = info.trikot ? info.trikot.sponsor : null;
  const j = info.trikot;
  return <>
    <Banner src={IMG.banner('sponsoren')} title="Sponsoren" sub="Trikot und Werbebanden – such dir deine Partner aus und verhandle nach" />
    <div className="grid sponsor-top">
      <div className="card" style={{ textAlign: 'center' }}>
        <h3>Dein Trikot</h3>
        <Jersey colors={me.colors} kit={me.kit} sponsor={preview} size={220} />
        <div className="cond" style={{ fontSize: 20 }}>{me.name}</div>
        <div className="muted">{preview ? `Trikotsponsor: ${preview.name}` : 'noch ohne Trikotsponsor'}</div>
      </div>
      <div className="card">
        <h3>Trikotsponsor</h3>
        {j ? <>
          <div className="row"><SponsorTag sp={j.sponsor} link /><span className="muted">{info.variants[j.variant]} · bis Saison {j.until}</span></div>
          <table style={{ marginTop: 10 }}><tbody>
            <tr><td>Fixum pro Liga-Spieltag</td><td className="num pos-money">{eur(j.fix)}</td></tr>
            <tr><td>Siegprämie (Liga)</td><td className="num">{eur(j.win)}</td></tr>
            <tr><td>Saisonziel: Platz {j.goalPlace} oder besser</td><td className="num">{eur(j.goalBonus)}</td></tr>
          </tbody></table>
          <p className="muted" style={{ fontSize: 12 }}>Neue Trikot-Angebote gibt es, wenn der Vertrag ausläuft. Vorzeitig kündigen kostet {eur(j.cancelFee)} Vertragsstrafe.</p>
          <button className="small danger" onClick={() => setCancel({ type: 'trikot', name: j.sponsor.name, fee: j.cancelFee })}>Vertrag kündigen</button>
        </> : <p className="muted">Du hast noch keinen Trikotsponsor. Wähle unten ein Angebot: <b>Sicher</b> bringt viel festes Geld, <b>Erfolg</b> wenig Fixum, dafür hohe Sieg- und Zielprämien. Das Saisonziel richtet sich nach der Stärke deines Kaders (aktuell eher Platz {info.expectedRank} in der Liga).</p>}
      </div>
    </div>
    {jOffers.length > 0 && <>
      <h2 style={{ marginTop: 18 }}>Angebote für dein Trikot</h2>
      <div className="offer-grid">{jOffers.map(o => <OfferCard key={o.id} o={o} info={info} act={doAct} reload={reload} sel={sel[o.id]} setSel={v => setSel(x => ({ ...x, [o.id]: v }))} />)}</div>
    </>}
    {!j && jOffers.length === 0 && <p className="muted">Gerade liegen keine Trikot-Angebote vor – alle 6 Liga-Spieltage melden sich neue Interessenten.</p>}
    <div className="card" style={{ marginTop: 18 }}>
      <div className="row"><h3 style={{ margin: 0 }}>Werbebanden</h3><span className="spacer" /><span className="muted">{info.bande.length} von {info.slots} belegt · {info.nextSlotAt ? `nächster Platz mit Tribünen Stufe ${info.nextSlotAt} (bis ${info.slotsMax || 24})` : 'alle Plätze freigeschaltet'}</span></div>
      <Boards boards={info.bande} slots={info.slots} />
      {info.bande.length > 0 && <div className="tw"><table className="wrap" style={{ marginTop: 10 }}><tbody>{info.bande.map(b => <tr key={b.sponsorId}>
        <td><SponsorTag sp={b.sponsor} link /></td><td className="num pos-money">{eur(b.fix)} / Heimspiel</td><td className="num muted">bis Saison {b.until}</td>
        <td className="num"><button className="small danger" onClick={() => setCancel({ type: 'bande', sponsorId: b.sponsorId, name: b.sponsor.name, fee: b.cancelFee })}>Kündigen</button></td>
      </tr>)}</tbody></table></div>}
    </div>
    {bOffers.length > 0 && <>
      <h2 style={{ marginTop: 18 }}>Angebote für deine Banden</h2>
      <div className="offer-grid">{bOffers.map(o => <OfferCard key={o.id} o={o} info={info} act={doAct} reload={reload} sel={sel[o.id]} setSel={v => setSel(x => ({ ...x, [o.id]: v }))} />)}</div>
    </>}
    <p className="muted" style={{ fontSize: 12, marginTop: 12 }}>Nachverhandeln: +10 % klappt meistens, +30 % nur selten. Lehnt ein Sponsor ab, zieht er sein Angebot manchmal ganz zurück. Wer oben in der Tabelle steht, verhandelt leichter. Neue Angebote kommen alle 6 Liga-Spieltage und zu jeder neuen Saison.</p>
    {cancel && <Modal close={() => setCancel(null)}>
      <h2>Vertrag mit {cancel.name} kündigen?</h2>
      <p>Die Vertragsstrafe beträgt <b>{eur(cancel.fee)}</b>. Danach bekommst du neue Angebote.</p>
      <div className="row"><span className="spacer" /><button onClick={() => setCancel(null)}>Abbrechen</button>
        <button className="primary" onClick={async () => { await doAct('/sponsors/cancel', { type: cancel.type, sponsorId: cancel.sponsorId }, 'Vertrag gekündigt'); setCancel(null); }}>Kündigen</button></div>
    </Modal>}
  </>;
}

// ---------- Finanzen ----------
function Finances({ st }) {
  const me = st.myTeam, C = st.game.constants;
  const salaries = me.players.reduce((s, p) => s + p.salary, 0);
  const fac = Object.fromEntries(me.facilities.map(f => [f.key, f.level]));
  const tv = st.game.leagues[me.league].tv;
  const ticket = C.TICKET_PRICE + 3 * fac.dach;
  const att = Math.min(me.stadium, me.fans * (1 + 0.05 * fac.parkhaus));
  return <>
    <Banner src={IMG.banner('finanzen')} title="Finanzen" />
    <div className="grid g2">
      <div className="card">
        <h3>Kontostand</h3>
        <div className={'big ' + (me.budget < 0 ? 'neg-money' : 'pos-money')}>{eur(me.budget)}</div>
        <h3 style={{ marginTop: 14 }}>Pro Spieltag (ungefähr)</h3>
        <table><tbody>
          <tr><td>TV-Gelder ({lgName(st, me.league)})</td><td className="num pos-money">+{eur(tv)}</td></tr>
          <tr><td>Heimspiel: ~{fmt(att)} Zuschauer × {eur(ticket)}</td><td className="num pos-money">+{eur(att * ticket)}</td></tr>
          <tr><td>Heimspiel: VIP-Logen</td><td className="num pos-money">+{eur(30000 * fac.vip)}</td></tr>
          <tr><td>Heimspiel: Fanshop</td><td className="num pos-money">+{eur(me.fans * 0.5 * fac.fanshop)}</td></tr>
          <tr><td>Trikotsponsor{me.sponsors.trikot ? ` (${me.sponsors.trikot.name})` : ' – keiner'}</td><td className="num pos-money">+{eur(me.sponsors.trikot ? me.sponsors.trikot.fix : 0)}</td></tr>
          <tr><td>Heimspiel: Bandenwerbung ({me.sponsors.bande.length} Partner)</td><td className="num pos-money">+{eur(me.sponsors.bande.reduce((x, b) => x + b.fix, 0))}</td></tr>
          <tr><td>Spielergehälter</td><td className="num neg-money">−{eur(salaries)}</td></tr>
          <tr><td>Unterhalt Gelände</td><td className="num neg-money">−{eur(me.upkeep)}</td></tr>
        </tbody></table>
        <p className="muted" style={{ fontSize: 12 }}>Heimspiel-Einnahmen gibt es nur bei jedem zweiten Spiel. Gehälter, TV-Geld und Unterhalt werden an jedem Liga-Spieltag gebucht (Di, Do, Sa). Dazu kommen Saisonprämien je nach Liga und Tabellenplatz sowie Pokalprämien. Handgelder für Vertragsverlängerungen werden sofort fällig.</p>
      </div>
      <div className="card">
        <h3>Buchungen</h3>
        <div className="tw" style={{ maxHeight: 460, overflowY: 'auto' }}><table><tbody>
          {me.ledger.length === 0 && <tr><td className="muted">Noch keine Buchungen.</td></tr>}
          {me.ledger.map((l, i) => <tr key={i}><td className="muted">S{l.season} · {l.md}. ST</td><td style={{ whiteSpace: 'normal' }}>{l.text}</td>
            <td className={'num ' + (l.amount < 0 ? 'neg-money' : 'pos-money')}>{l.amount > 0 ? '+' : ''}{eur(l.amount)}</td></tr>)}
        </tbody></table></div>
      </div>
    </div>
  </>;
}

// ---------- Verein (Name, Kürzel, Farben, Stadionname) ----------
function Club({ st, act }) {
  const me = st.myTeam, C = st.game.constants;
  const [name, setName] = useState(me.name);
  const [short, setShort] = useState(me.short);
  const [stadiumName, setStadiumName] = useState(me.stadiumName);
  const [c1, setC1] = useState(me.colors[0]);
  const [c2, setC2] = useState(me.colors[1]);
  const myCrest = me.crest || { shape: 'schild', pattern: 'geteilt' };
  const [shape, setShape] = useState(myCrest.shape);
  const [pattern, setPattern] = useState(myCrest.pattern);
  const savedClub = [me.name, me.short, me.stadiumName, me.colors[0], me.colors[1], myCrest.shape, myCrest.pattern].join('|');
  useEffect(() => { setName(me.name); setShort(me.short); setStadiumName(me.stadiumName); setC1(me.colors[0]); setC2(me.colors[1]); setShape(myCrest.shape); setPattern(myCrest.pattern); }, [savedClub]);
  const dirty = [name, short, stadiumName, c1, c2, shape, pattern].join('|') !== savedClub;
  const shapes = st.game.crestShapes || Object.keys(CREST_SHAPE_NAME), patterns = st.game.crestPatterns || Object.keys(CREST_PATTERN_NAME);
  const nameChanged = name.trim() !== me.name || short.trim().toUpperCase() !== me.short;
  const cost = nameChanged ? (me.renames > 0 ? C.RENAME_COST : 0) : 0;
  return <>
    <Banner src={IMG.banner('verein')} title={me.name} sub={`${lgName(st, me.league)} · ${me.stadiumName}`} />
    <div className="grid g2">
      <div className="card">
        <h2>Verein bearbeiten</h2>
        <div className="form-grid">
          <label>Vereinsname</label><input value={name} maxLength={30} onChange={e => setName(e.target.value)} />
          <label>Kürzel</label><input value={short} maxLength={4} onChange={e => setShort(e.target.value.toUpperCase().replace(/[^A-Z0-9ÄÖÜ]/g, ''))} style={{ width: 90 }} />
          <label>Stadionname</label><input value={stadiumName} maxLength={40} onChange={e => setStadiumName(e.target.value)} />
          <label>Vereinsfarben</label>
          <div className="row"><input type="color" value={c1} onChange={e => setC1(e.target.value)} /><input type="color" value={c2} onChange={e => setC2(e.target.value)} /></div>
        </div>
        <h3 style={{ marginTop: 14 }}>Wappenform</h3>
        <div className="crest-pick">{shapes.map(x => <button key={x} type="button" className={x === shape ? 'on' : ''} onClick={() => setShape(x)} title={CREST_SHAPE_NAME[x]}>
          <Crest crest={{ shape: x, pattern }} colors={[c1, c2]} short={short || '?'} size={38} /><span>{CREST_SHAPE_NAME[x] || x}</span></button>)}</div>
        <h3 style={{ marginTop: 12 }}>Muster</h3>
        <div className="crest-pick">{patterns.map(x => <button key={x} type="button" className={x === pattern ? 'on' : ''} onClick={() => setPattern(x)} title={CREST_PATTERN_NAME[x]}>
          <Crest crest={{ shape, pattern: x }} colors={[c1, c2]} short={short || '?'} size={38} /><span>{CREST_PATTERN_NAME[x] || x}</span></button>)}</div>
        <p className="muted" style={{ fontSize: 12 }}>
          {me.renames === 0 ? 'Die erste Namensänderung ist kostenlos.' : `Jede weitere Änderung von Name oder Kürzel kostet ${eur(C.RENAME_COST)} Verbandsgebühr.`} Stadionname, Farben und Wappen kannst du jederzeit kostenlos ändern.
        </p>
        <button className="primary" disabled={!dirty || st.myTeam.budget < cost}
          onClick={() => act('/club', { name, short, stadiumName, colors: [c1, c2], crest: { shape, pattern } }, 'Vereinsdaten gespeichert')}>
          Speichern{cost ? ` (${eur(cost)})` : ''}
        </button>
      </div>
      <div className="card" style={{ textAlign: 'center' }}>
        <h3>Vorschau</h3>
        <Crest crest={{ shape, pattern }} colors={[c1, c2]} short={short || '?'} size={120} />
        <div className="cond" style={{ fontSize: 28, marginTop: 8 }}>{name}</div>
        <div className="muted">{stadiumName}</div>
      </div>
    </div>
    <div style={{ marginTop: 14 }}><KitPicker st={st} act={act} colors={[c1, c2]} /></div>
    <div id="app-settings" style={{ marginTop: 14 }}><AppSettings st={st} /></div>
  </>;
}

// Trikotmuster wählen – nur zu Saisonbeginn frei
function KitPicker({ st, act, colors }) {
  const me = st.myTeam, info = me.kitInfo || { patterns: Object.keys(KIT_NAME), open: true, freeUntil: 3, left: 3 };
  const [sel, setSel] = useState(me.kit || 'klassisch');
  useEffect(() => setSel(me.kit || 'klassisch'), [me.kit]);
  const sponsor = me.sponsors && me.sponsors.trikot;
  return <div className="card" style={{ gridColumn: '1/-1' }}>
    <div className="row" style={{ justifyContent: 'space-between' }}>
      <h2 style={{ margin: 0 }}>Trikot</h2>
      {info.open ? <span className="tag list">Frei wählbar – noch {info.left} Spieltag{info.left === 1 ? '' : 'e'}</span> : <span className="tag sus">🔒 Gesperrt bis zum Saisonbeginn</span>}
    </div>
    <p className="muted" style={{ fontSize: 13 }}>{info.open
      ? `Zu Saisonbeginn (bis zum ${info.freeUntil}. Spieltag) kannst du das Trikotmuster kostenlos wechseln. Die Farben kommen aus deinen Vereinsfarben.`
      : `Das Trikotmuster kann nur zu Saisonbeginn (bis zum ${info.freeUntil}. Spieltag) geändert werden. Ein Wechsel während der Saison wird später mit der Premium-Währung möglich – die kommt noch.`}</p>
    <div className="kit-pick">{info.patterns.map(p => <button key={p} type="button" className={(p === sel ? 'on' : '') + (p === me.kit ? ' cur' : '')} disabled={!info.open} onClick={() => setSel(p)}>
      <Jersey colors={colors} kit={p} sponsor={sponsor} size={84} /><span>{KIT_NAME[p] || p}{p === me.kit ? ' ✓' : ''}</span></button>)}</div>
    {info.open && <div className="row" style={{ marginTop: 10 }}>
      <button className="primary" disabled={sel === me.kit} onClick={() => act('/kit', { pattern: sel }, 'Neues Trikot gespeichert')}>Trikot „{KIT_NAME[sel]}“ übernehmen</button>
      <span className="muted" style={{ fontSize: 12 }}>Vereinsfarben änderst du oben unter „Verein bearbeiten“.</span>
    </div>}
  </div>;
}

// ---------- App & Benachrichtigungen ----------
const isStandalone = () => (window.matchMedia && window.matchMedia('(display-mode: standalone)').matches) || window.navigator.standalone === true;
const isAndroid = () => /android/i.test(navigator.userAgent);
const isIOS = () => /iphone|ipad|ipod/i.test(navigator.userAgent) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
function useInstallable() {
  const [, force] = useState(0);
  useEffect(() => { const f = () => force(x => x + 1); window.addEventListener('fm-installable', f); return () => window.removeEventListener('fm-installable', f); }, []);
  return !!window.__installPrompt;
}
async function installApp() {
  const p = window.__installPrompt; if (!p) return false;
  p.prompt(); const r = await p.userChoice.catch(() => null);
  window.__installPrompt = null; window.dispatchEvent(new Event('fm-installable'));
  return r && r.outcome === 'accepted';
}
const b64ToBytes = b => { const pad = '='.repeat((4 - b.length % 4) % 4); const raw = atob((b + pad).replace(/-/g, '+').replace(/_/g, '/')); return Uint8Array.from(raw, c => c.charCodeAt(0)); };
const PUSH_LABELS = { erinnerung: '⏰ Erinnerung 10 Minuten vor Anpfiff', anpfiff: '🟢 Anpfiff meines Spiels', tore: '⚽ Tore und Platzverweise in meinem Spiel', ergebnis: '🏁 Endergebnis', auslosung: '🏆 Pokalauslosung und mein Pokalgegner' };
function InstallBanner({ goSettings }) {
  const installable = useInstallable();
  const [hidden, setHidden] = useState(store.get('fm_install_hide') === '1');
  if (hidden || isStandalone() || !(installable || isIOS() || isAndroid())) return null;
  return <div className="install-banner">
    <img src="/assets/app/icon-192.png" alt="" />
    <div style={{ flex: 1, minWidth: 0 }}><b>Flutlicht als App</b><div className="muted" style={{ fontSize: 12 }}>Mit eigenem Symbol auf dem Startbildschirm und Benachrichtigungen zu Anpfiff, Toren und Auslosung.</div></div>
    {installable ? <button className="primary small" onClick={installApp}>Installieren</button> : <button className="primary small" onClick={goSettings}>So geht's</button>}
    <button className="small" onClick={() => { store.set('fm_install_hide', '1'); setHidden(true); }}>✕</button>
  </div>;
}
function AppSettings({ st }) {
  const installable = useInstallable();
  const secure = window.isSecureContext;
  const pushOk = secure && 'serviceWorker' in navigator && 'PushManager' in window && 'Notification' in window;
  const [info, setInfo] = useState(null);
  const [sub, setSub] = useState(null);
  const [msg, setMsg] = useState(null);
  const [busy, setBusy] = useState(false);
  const load = async () => {
    try { setInfo(await api('/push')); } catch (e) {}
    if (pushOk) { try { const reg = await navigator.serviceWorker.ready; setSub(await reg.pushManager.getSubscription()); } catch (e) {} }
  };
  useEffect(() => { load(); }, []);
  const enable = async () => {
    setBusy(true); setMsg(null);
    try {
      const perm = await Notification.requestPermission();
      if (perm !== 'granted') throw new Error('Benachrichtigungen wurden im Browser nicht erlaubt. Du kannst das in den Browser- bzw. Handy-Einstellungen ändern.');
      const reg = await navigator.serviceWorker.ready;
      const s = await reg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: b64ToBytes(info.publicKey) });
      await api('/push/subscribe', { subscription: s.toJSON() });
      setSub(s); setMsg('Benachrichtigungen sind auf diesem Gerät aktiv.'); load();
    } catch (e) { setMsg(e.message); }
    setBusy(false);
  };
  const disable = async () => {
    setBusy(true);
    try { if (sub) { await api('/push/unsubscribe', { endpoint: sub.endpoint }); await sub.unsubscribe(); } setSub(null); setMsg('Auf diesem Gerät ausgeschaltet.'); load(); } catch (e) { setMsg(e.message); }
    setBusy(false);
  };
  const setPref = async (k, v) => { const prefs = { ...info.prefs, [k]: v }; setInfo({ ...info, prefs }); try { await api('/push/prefs', { prefs }); } catch (e) { setMsg(e.message); } };
  const test = async () => { setMsg(null); try { await api('/push/test', {}); setMsg('Test-Benachrichtigung verschickt – sie sollte gleich erscheinen.'); } catch (e) { setMsg(e.message); } };
  return <div className="card">
    <h2>📱 App & Benachrichtigungen</h2>
    {!secure && <p className="tag sus" style={{ display: 'block', padding: 10, fontSize: 13 }}>Installation als App und Benachrichtigungen funktionieren nur, wenn das Spiel über eine sichere Adresse (https) mit eigener Domain aufgerufen wird. Sobald die Domain eingerichtet ist, geht es hier los.</p>}
    <div className="app-box">
      <div>
        <h3>Als App installieren</h3>
        {isStandalone() ? <p>✓ Du nutzt den Flutlicht Manager bereits als App.</p>
          : installable ? <><p className="muted" style={{ fontSize: 13 }}>Ein Tipp genügt – danach liegt das Flutlicht-Logo auf deinem Startbildschirm und das Spiel startet im Vollbild.</p><button className="primary" onClick={installApp}>App installieren</button></>
          : isIOS() ? <ol className="muted" style={{ fontSize: 13, paddingLeft: 18 }}><li>Diese Seite in <b>Safari</b> öffnen</li><li>Unten auf <b>Teilen</b> (Quadrat mit Pfeil) tippen</li><li><b>„Zum Home-Bildschirm“</b> wählen und bestätigen</li><li>Ab jetzt über das Flutlicht-Symbol starten – nur dann gehen auf dem iPhone auch Benachrichtigungen</li></ol>
          : isAndroid() ? <ol className="muted" style={{ fontSize: 13, paddingLeft: 18 }}><li>Seite in <b>Chrome</b> öffnen (im Samsung-Browser: Menü ☰ → „Seite hinzufügen zu“ → „Startbildschirm“)</li><li>Oben rechts auf das Menü <b>⋮</b> tippen</li><li><b>„App installieren“</b> oder <b>„Zum Startbildschirm hinzufügen“</b> wählen</li><li>Erscheint der Punkt nicht: Seite einmal neu laden und kurz warten</li><li><b>Xiaomi, Redmi, Poco, Huawei, Oppo:</b> Kein Symbol erscheint? Einstellungen → Apps → Chrome → Weitere Berechtigungen → <b>„Desktop-Verknüpfungen“ erlauben</b>, dann erneut installieren. Für zuverlässige Benachrichtigungen bei Chrome zusätzlich <b>Autostart</b> an und <b>Akkusparmodus: Keine Einschränkungen</b></li><li>Findest du das Symbol nicht auf dem Startbildschirm, schau in der App-Übersicht nach „Flutlicht“</li></ol>
          : <p className="muted" style={{ fontSize: 13 }}>Im Browser-Menü (⋮) auf <b>„App installieren“</b> bzw. <b>„Zum Startbildschirm hinzufügen“</b> tippen. Am PC erscheint in Chrome/Edge rechts in der Adresszeile ein Installations-Symbol.</p>}
      </div>
      <div>
        <h3>Benachrichtigungen</h3>
        {!pushOk ? <p className="muted" style={{ fontSize: 13 }}>{!secure ? 'Erst mit https-Domain verfügbar.' : isIOS() && !isStandalone() ? 'Auf dem iPhone/iPad zuerst als App zum Home-Bildschirm hinzufügen (siehe links) und dann aus der App heraus aktivieren.' : 'Dieser Browser unterstützt keine Push-Benachrichtigungen.'}</p>
          : !info ? <p className="muted">Lade …</p>
          : !info.enabled ? <p className="muted" style={{ fontSize: 13 }}>Push ist auf dem Server noch nicht eingerichtet.</p>
          : <>
            {sub ? <div className="row"><span className="tag list">✓ Aktiv auf diesem Gerät</span><button className="small" disabled={busy} onClick={disable}>Ausschalten</button><button className="small" onClick={test}>Test senden</button></div>
              : <button className="primary" disabled={busy} onClick={enable}>Benachrichtigungen aktivieren</button>}
            <div style={{ marginTop: 10 }}>{Object.entries(PUSH_LABELS).map(([k, l]) => <label key={k} className="push-pref"><input type="checkbox" checked={!!info.prefs[k]} onChange={e => setPref(k, e.target.checked)} /> {l}</label>)}</div>
            <p className="muted" style={{ fontSize: 12 }}>Die Auswahl gilt für alle deine Geräte ({info.devices} angemeldet).</p>
          </>}
        {msg && <p style={{ fontSize: 13 }}>{msg}</p>}
      </div>
    </div>
  </div>;
}

// ---------- Live-Spiele ----------
const EV_ICON = { goal: '⚽', yellow: '🟨', red: '🟥', sub: '⇄', injury: '✚', save: '🧤', post: '▮', miss: '✗', chance: '!', tactic: '♟', pen: '◎', info: '⏱', text: '' };
const WEATHER_NAME = { sonne: 'Trocken', regen: 'Regen', wind: 'Wind', kalt: 'Kalt', nebel: 'Nebel' };
const TACTIC_OPTS = [['defensiv', 'Defensiv'], ['normal', 'Ausgeglichen'], ['offensiv', 'Offensiv']];
const PRESS_OPTS = [['tief', 'Tief'], ['normal', 'Normal'], ['hoch', 'Hoch']];
const liveComp = m => m.comp === 'POKAL' ? 'Pokal' : EURO_NAMES[(m.comp || '').toUpperCase()] || m.label;
function Energy({ v }) {
  const c = v >= 70 ? 'var(--accent)' : v >= 50 ? 'var(--warn)' : 'var(--bad)';
  return <span className="energy" title={`Kraft ${v}%`}><i style={{ width: v + '%', background: c }} /></span>;
}
// Eigene Spieler und den eigenen Verein im Kommentartext farbig hervorheben
const escRe = x => x.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
function myMarker(st) {
  const me = st.myTeam;
  const players = me.players.map(p => p.name).filter(Boolean).sort((a, b) => b.length - a.length);
  const all = [me.name].concat(players);
  if (!all.length) return null;
  return { re: new RegExp('(' + all.map(escRe).join('|') + ')', 'g'), team: me.name, set: new Set(players) };
}
function Hl({ text, mk }) {
  if (!mk || !text) return text || null;
  const parts = text.split(mk.re);
  if (parts.length === 1) return text;
  return parts.map((x, i) => i % 2 === 1 ? <b key={i} className={x === mk.team ? 'hl-team' : 'hl-me'}>{x}</b> : x);
}
function LiveTicker({ events, mk, mySide }) {
  return <div className="ticker live-ticker">{events.map((e, i) => <div key={events.length - i} className={'ev ' + e.type + (e.side === 0 ? ' s0' : e.side === 1 ? ' s1' : '') + (mySide >= 0 && e.type !== 'text' && e.side === mySide ? ' mine' : mySide >= 0 && e.side === 1 - mySide ? ' theirs' : '')}>
    <div className="m">{e.mt === 'E' ? 'E' : (e.mt || e.min) + "'"}</div><div className="ic">{EV_ICON[e.type] || ''}</div><div className="t"><Hl text={e.text} mk={mk} /></div></div>)}</div>;
}
function LiveStats({ v }) {
  const S = v.stats;
  const Stat = ({ label, a, b, pct }) => <div className="stat">
    <div className="lbl">{label}</div><div className="num">{a}{pct ? '%' : ''}</div>
    <div className="split"><i style={{ width: (a + b ? a / (a + b) * 100 : 50) + '%', background: 'var(--accent)' }} /><i style={{ flex: 1, background: '#4a5b52' }} /></div>
    <div>{b}{pct ? '%' : ''}</div></div>;
  return <>
    <Stat label="Ballbesitz" a={S.poss[0]} b={S.poss[1]} pct />
    <Stat label="Torschüsse" a={S.shots[0]} b={S.shots[1]} />
    <Stat label="Aufs Tor" a={S.onTarget[0]} b={S.onTarget[1]} />
    <Stat label="Ecken" a={S.corners[0]} b={S.corners[1]} />
    <Stat label="Fouls" a={S.fouls[0]} b={S.fouls[1]} />
    <Stat label="Abseits" a={S.offsides[0]} b={S.offsides[1]} />
    <Stat label="Gelbe Karten" a={S.yellow[0]} b={S.yellow[1]} />
    <Stat label="Rote Karten" a={S.red[0]} b={S.red[1]} />
  </>;
}
function Coach({ v, st, act, reload }) {
  const s = v.sides[v.mySide];
  const [out, setOut] = useState(null);
  const [busy, setBusy] = useState(false);
  const send = async (body, ok) => { setBusy(true); const r = await act('/live/action', { key: v.key, ...body }, ok); setBusy(false); setOut(null); reload(); return r; };
  const closed = v.finished;
  const formations = st.game.formations || Object.keys(FORMATIONS);
  const outPos = out ? (s.lineup.find(p => p.id === out) || {}).pos : null;
  return <div className="card coach">
    <div className="row" style={{ justifyContent: 'space-between' }}>
      <h3 style={{ margin: 0 }}>Trainerbank</h3>
      <span className="tag list">Wechsel übrig: {s.subsLeft}</span>
    </div>
    {closed ? <p className="muted">Das Spiel ist beendet.</p> : <>
      <div className="coach-row"><span className="lbl">Taktik</span><Seg value={s.tactic} options={TACTIC_OPTS} onChange={x => !busy && send({ type: 'tactic', tactic: x }, 'Taktik geändert')} /></div>
      <div className="coach-row"><span className="lbl">Pressing</span><Seg value={s.pressing} options={PRESS_OPTS} onChange={x => !busy && send({ type: 'tactic', pressing: x }, 'Pressing geändert')} /></div>
      <div className="coach-row"><span className="lbl">System</span>
        <select value={s.formation} disabled={busy} onChange={e => send({ type: 'tactic', formation: e.target.value }, 'Formation umgestellt')}>{formations.map(f => <option key={f}>{f}</option>)}</select></div>
      <div className="coach-row"><span className="lbl">Co-Trainer</span>
        <label className="row" style={{ gap: 6, fontSize: 13 }}><input type="checkbox" checked={!!s.auto} onChange={e => send({ type: 'auto', on: e.target.checked }, e.target.checked ? 'Co-Trainer wechselt bei Erschöpfung' : 'Du wechselst selbst')} /> wechselt erschöpfte Spieler automatisch</label></div>
    </>}
    <h3 style={{ marginTop: 14 }}>Auf dem Platz</h3>
    {!closed && <p className="muted" style={{ fontSize: 12, marginTop: 0 }}>{out ? (s.subsLeft > 0 ? 'Jetzt einen Spieler von der Bank einwechseln – oder einen Mitspieler auf dem Platz antippen, um die Positionen zu tauschen.' : 'Mitspieler antippen, um die Positionen zu tauschen.') : 'Spieler antippen: auswechseln oder Position mit einem Mitspieler tauschen (kostet keinen Wechsel).'}</p>}
    <div className="plist">{s.lineup.map(p => <div key={p.id} className={'pl' + (out === p.id ? ' sel' : '') + (!closed ? ' click' : '') + (out && out !== p.id && outPos !== p.pos ? ' swap-target' : '')}
      onClick={() => { if (closed || busy) return; if (!out) return setOut(p.id); if (out === p.id) return setOut(null);
        if (outPos === p.pos) return setOut(p.id);
        send({ type: 'swap', a: out, b: p.id }, 'Positionen getauscht'); }}>
      <Pos p={p.pos} /><span className="nm">{p.name}{p.natural !== p.pos && <small className="muted"> ({p.natural})</small>}{p.yellow && <span className="card-y" />}</span><span className="ovr">{p.ovr}</span><Energy v={p.energy} /></div>)}</div>
    <h3 style={{ marginTop: 14 }}>Bank</h3>
    <div className="plist">{s.bench.length ? (out ? s.bench.slice().sort((x, y) => (y.pos === outPos) - (x.pos === outPos) || y.ovr - x.ovr) : s.bench).map(p => <div key={p.id} className={'pl' + (out ? ' click target' : '')} onClick={() => out && !busy && send({ type: 'sub', out, in: p.id }, 'Wechsel durchgeführt')}>
      <Pos p={p.pos} /><span className="nm">{p.name}</span><span className="ovr">{p.ovr}</span><Energy v={p.energy} /></div>) : <span className="muted">Keine Spieler mehr auf der Bank.</span>}</div>
  </div>;
}
// Tabelle mit den aktuellen Live-Spielständen eingerechnet
function liveRank(base, matches) {
  const rows = base.map((r, i) => ({ ...r, before: i + 1, live: null }));
  const by = Object.fromEntries(rows.map(r => [r.id, r]));
  for (const m of matches) {
    const h = by[m.h.id], a = by[m.a.id];
    if (!h || !a) continue;
    const [gh, ga] = m.goals;
    h.sp++; a.sp++; h.tore += gh; h.gegen += ga; a.tore += ga; a.gegen += gh;
    if (gh > ga) { h.pkt += 3; h.s++; a.n++; h.live = 'S'; a.live = 'N'; }
    else if (gh < ga) { a.pkt += 3; a.s++; h.n++; a.live = 'S'; h.live = 'N'; }
    else { h.pkt++; a.pkt++; h.u++; a.u++; h.live = a.live = 'U'; }
    h.diff = h.tore - h.gegen; a.diff = a.tore - a.gegen;
    h.score = `${gh}:${ga}`; a.score = `${ga}:${gh}`; // aus Sicht des jeweiligen Vereins h.opp = m.a.short; a.opp = m.h.short; h.fin = a.fin = m.finished;
  }
  return rows.sort((x, y) => y.pkt - x.pkt || y.diff - x.diff || y.tore - x.tore || x.before - y.before);
}
function LiveTableRows({ rows, myId, up, down, compact, league, L }) {
  return <div className="tw"><table className="live-table">
    <thead><tr><th>#</th><th></th><th>Verein</th><th className="num">Diff</th><th className="num">Pkt</th><th className="num">Live</th></tr></thead>
    <tbody>{rows.map((r, i) => {
      const d = r.before - (i + 1);
      const zone = L ? ' ' + zoneFor(league, i, rows.length, L) : up && i < up ? ' zone-up' : down && i >= rows.length - down ? ' zone-down' : '';
      return <tr key={r.id} className={(r.id === myId ? 'me' : '') + zone}>
        <td>{i + 1}.</td>
        <td className={'mv ' + (d > 0 ? 'up' : d < 0 ? 'down' : '')}>{d > 0 ? '▲' + d : d < 0 ? '▼' + -d : '–'}</td>
        <td className="tcell"><Crest id={r.id} colors={r.colors} short={r.short} size={16} /> <span>{compact ? r.short : r.name}</span></td>
        <td className="num">{r.diff > 0 ? '+' + r.diff : r.diff}</td><td className="num"><b>{r.pkt}</b></td>
        <td className="num">{r.live ? <span className={'lv ' + r.live + (r.fin ? ' fin' : '')} title={`gegen ${r.opp}${r.fin ? ' – beendet' : ' – läuft'}`}>{r.score}</span> : <span className="muted">–</span>}</td>
      </tr>;
    })}</tbody>
  </table></div>;
}
function LiveTable({ st, sum, group, euro }) {
  const myId = st.myTeam.id;
  const first = group && group.list[0];
  if (!first) return null;
  if (/^L\d/.test(first.comp)) {
    const lg = Number(first.comp.slice(1)), L = st.game.leagues[lg];
    const base = (st.tables[lg] || []).map(r => ({ ...r }));
    return <><p className="muted" style={{ fontSize: 12, margin: '0 0 6px' }}>{L.name} – so stünde die Tabelle, wenn die Spiele jetzt enden.</p>
      <LiveTableRows rows={liveRank(base, group.list)} myId={myId} league={lg} L={L} /><ZoneLegend league={lg} L={L} /></>;
  }
  if (first.comp === 'POKAL') return <p className="muted">Im Pokal gibt es keine Tabelle – hier zählt nur das Weiterkommen. Schau in die Konferenz.</p>;
  const k = String(first.comp).toLowerCase(), comp = euro && euro.comps[k];
  if (!comp) return <p className="muted">Lade Gruppentabellen …</p>;
  if (!comp.groups.length || first.round && !/Gruppe/.test(first.round)) return <p className="muted">In der K.-o.-Runde gibt es keine Tabelle – das Gesamtergebnis steht in der Konferenz.</p>;
  const info = id => euro.teams[id] || {};
  const live = group.list;
  const gs = comp.groups.filter(g => live.some(m => g.table.some(r => r.id === m.h.id)));
  return <><p className="muted" style={{ fontSize: 12, margin: '0 0 6px' }}>Spiele ohne heimische Vereine werden erst nach dem Abpfiff eingerechnet.</p>
    {gs.map(g => <div key={g.name} style={{ marginBottom: 10 }}><div className="muted" style={{ fontSize: 12, fontWeight: 600 }}>Gruppe {g.name}</div>
      <LiveTableRows compact={false} myId={myId} up={2} rows={liveRank(g.table.map(r => ({ ...r, name: info(r.id).name, short: info(r.id).short, colors: info(r.id).colors, diff: r.tore - r.gegen })), live)} /></div>)}</>;
}
function Live({ st, act, open, reloadState }) {
  const [side, setSide] = useState(store.get('fm_live_side') || 'conf');
  const pickSide = x => { setSide(x); store.set('fm_live_side', x); };
  const [euro, setEuro] = useState(null);
  const [sum, setSum] = useState(null);
  const [sel, setSel] = useState(null);
  const [v, setV] = useState(null);
  const [group, setGroup] = useState(null);
  const [err, setErr] = useState(null);
  const loadSum = useCallback(() => api('/live').then(s => { setSum(s); setErr(null); return s; }).catch(e => setErr(e.message)), []);
  const key = sel || (sum && sum.active ? sum.mine || (sum.matches[0] && sum.matches[0].key) : null);
  const loadView = useCallback(() => { if (key) api('/live/match/' + encodeURIComponent(key)).then(setV).catch(() => setV(null)); }, [key]);
  const wasActive = useRef(false);
  useEffect(() => { loadSum(); const i = setInterval(loadSum, 4000); return () => clearInterval(i); }, []);
  useEffect(() => { loadView(); const i = setInterval(loadView, 3000); return () => clearInterval(i); }, [key]);
  useEffect(() => { if (!sum) return; if (wasActive.current && !sum.active) reloadState(); wasActive.current = sum.active; }, [sum]);
  const isEuro = sum && sum.active && sum.type === 'euro';
  useEffect(() => { if (isEuro && side === 'table' && !euro) api('/euro').then(setEuro).catch(() => {}); }, [isEuro, side]);
  if (err) return <div className="card muted">{err}</div>;
  if (!sum) return <div className="card muted">Lade Live-Spiele …</div>;
  if (!sum.active) {
    const myLast = sum.last && sum.last.ids ? st.recent.find(m => Object.values(sum.last.ids).includes(m.id)) : null;
    return <div className="grid g2">
      <div className="card">
        <h2>Live-Spiele</h2>
        <p>Gerade läuft kein Spiel. Alle Spiele werden live übertragen – ein Spiel dauert rund <b>{st.game.liveMinutes || 30} Minuten</b>. Während deines Spiels kannst du Taktik, Pressing und System ändern und bis zu 5 Spieler auswechseln (in der Verlängerung einen mehr).</p>
        <table style={{ maxWidth: 460 }}><tbody>
          <tr><td>Nächster Liga-Spieltag</td><td className="num">{dayDate(st.game.nextMatchAt)}</td></tr>
          {st.nextCup && st.nextCup.at && <tr><td>Pokal · {st.nextCup.round}</td><td className="num">{dayDate(st.nextCup.at)}</td></tr>}
          {st.euroStatus && st.euroStatus.next && <tr><td>{st.euroStatus.compName} · {st.euroStatus.next.round}</td><td className="num">{dayDate(st.euroStatus.next.at)}</td></tr>}
        </tbody></table>
        <p className="muted" style={{ fontSize: 13 }}>Tipp: Die Startelf, Formation und Taktik stellst du vorher unter „Kader → Aufstellung“ ein. Im Spiel übernimmt sonst dein Co-Trainer die Wechsel, wenn Spieler erschöpft sind.</p>
      </div>
      {sum.last && <div className="card">
        <h3>Zuletzt live: {sum.last.title}</h3>
        <p className="muted">Abpfiff {dayDate(sum.last.endedAt)}</p>
        {myLast ? <div className="row"><span>{myLast.homeName} – {myLast.awayName}</span><b><Result m={myLast} /></b><button className="small primary" onClick={() => open({ type: 'match', id: myLast.id })}>Spielbericht</button></div>
          : <p className="muted">Alle Ergebnisse findest du im Spielplan.</p>}
      </div>}
    </div>;
  }
  const groups = [];
  for (const m of sum.matches) { const g = m.comp === 'POKAL' ? `Pokal · ${m.round}` : m.label; let x = groups.find(y => y.name === g); if (!x) groups.push(x = { name: g, list: [] }); x.list.push(m); }
  const mineM = sum.matches.find(m => m.key === sum.mine);
  const curGroup = group || (groups.find(g => g.list.some(m => m.key === key)) || groups[0] || {}).name;
  const g = groups.find(x => x.name === curGroup) || groups[0];
  const pct = v ? Math.min(100, Math.round((v.minute || 0) / (v.phase === 'ET' ? 120 : 90) * 100)) : 0;
  const mineV = v && v.mySide >= 0;
  return <div className="live">
    <div className="row" style={{ marginBottom: 10, justifyContent: 'space-between' }}>
      <h2 style={{ margin: 0 }}><span className="live-dot" /> {sum.title}</h2>
      {mineM && key !== sum.mine && <button className="primary small" onClick={() => setSel(sum.mine)}>Zu meinem Spiel</button>}
    </div>
    {v ? <div className="card match-head live-head" style={{ backgroundImage: `linear-gradient(rgba(14,21,18,.8),rgba(14,21,18,.95)), url(${IMG.spielbericht})` }}>
      <div className="muted" style={{ textAlign: 'center' }}>{liveComp(v)}{v.round ? ' · ' + v.round : ''} · {v.stadium} · Wetter: {WEATHER_NAME[v.weather] || v.weather}</div>
      <div className="vs">
        <div className="team"><Crest id={v.sides[0].id} colors={v.sides[0].colors} short={v.sides[0].short} size={48} /><span>{v.sides[0].name}</span><span className="muted" style={{ fontSize: 12 }}>{v.sides[0].formation} · {TACTIC_OPTS.find(x => x[0] === v.sides[0].tactic)?.[1]}</span></div>
        <div><div className="score">{v.goals[0]}:{v.goals[1]}</div><div className={'clock' + (v.finished ? ' end' : '')}>{v.minuteText}</div></div>
        <div className="team"><Crest id={v.sides[1].id} colors={v.sides[1].colors} short={v.sides[1].short} size={48} /><span>{v.sides[1].name}</span><span className="muted" style={{ fontSize: 12 }}>{v.sides[1].formation} · {TACTIC_OPTS.find(x => x[0] === v.sides[1].tactic)?.[1]}</span></div>
      </div>
      <div className="progress"><i style={{ width: pct + '%' }} /></div>
      <div className="muted" style={{ textAlign: 'center', fontSize: 13 }}>
        {v.carry && <>Hinspiel {v.carry[1]}:{v.carry[0]} · Gesamt {v.goals[0] + v.carry[0]}:{v.goals[1] + v.carry[1]}</>}
        {v.pens && <> · Elfmeterschießen {v.pens[0]}:{v.pens[1]}</>}
        {v.extraTime && !v.pens && <> · Verlängerung</>}
      </div>
    </div> : <div className="card muted">Lade Spiel …</div>}
    {v && <div className="live-grid">
      <div className="card"><h3>Live-Ticker</h3><LiveTicker events={v.events} mySide={v.mySide} mk={v.mySide >= 0 ? myMarker(st) : null} /></div>
      <div>
        {mineV && <Coach v={v} st={st} act={act} reload={loadView} />}
        <div className="card"><h3>Statistik</h3><LiveStats v={v} /></div>
        {!mineV && <div className="card"><h3>Aufstellungen</h3><div className="row" style={{ alignItems: 'flex-start' }}>
          {v.sides.map((s, i) => <div key={i} style={{ flex: 1, minWidth: 0, fontSize: 12 }}>{s.lineup.map(p => <div key={p.id} className="row" style={{ gap: 4, flexWrap: 'nowrap' }}><Pos p={p.pos} /><span style={{ flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{p.name}</span>{p.yellow && <span className="card-y" />}</div>)}</div>)}
        </div></div>}
      </div>
      <div>
        <div className="card">
          <div className="row" style={{ justifyContent: 'space-between', marginBottom: 8 }}>
            <h3 style={{ margin: 0 }}>{side === 'table' ? 'Live-Tabelle' : 'Konferenz'}</h3>
            <Seg value={side} options={[['conf', 'Konferenz'], ['table', 'Live-Tabelle']]} onChange={pickSide} />
          </div>
          {groups.length > 1 && <select value={curGroup} onChange={e => setGroup(e.target.value)} style={{ width: '100%', marginBottom: 8 }}>{groups.map(x => <option key={x.name} value={x.name}>{x.name} ({x.list.length})</option>)}</select>}
          {side === 'table' ? <div className="conf-full"><LiveTable st={st} sum={sum} group={g} euro={euro} /></div> :
          <div className="conf">{g && g.list.map(m => <div key={m.key} className={'conf-row' + (m.key === key ? ' on' : '') + (m.key === sum.mine ? ' mine' : '') + (m.last === 'goal' ? ' flash-goal' : '')} onClick={() => setSel(m.key)}>
            <span className="t h">{m.h.name}</span><b className="sc">{m.goals[0]}:{m.goals[1]}</b><span className="t a">{m.a.name}</span><span className="mn">{m.minuteText}</span></div>)}</div>}
        </div>
        <div className="card"><h3>Tore & Platzverweise</h3>
          <div className="goal-feed">{sum.goals.length ? sum.goals.map((e, i) => <div key={i} className={'gf ' + e.type} onClick={() => setSel(e.key)}><span className="muted">{e.mt}'</span> <b>{e.h}–{e.a}</b> {e.text}</div>) : <span className="muted">Noch keine Tore.</span>}</div>
        </div>
      </div>
    </div>}
  </div>;
}

// ---------- Spielbericht ----------
function MatchView({ id, open, st }) {
  const [m, setM] = useState(null);
  const [err, setErr] = useState(null);
  const [shown, setShown] = useState(null);
  const timer = useRef(null);
  useEffect(() => { api('/match/' + id).then(setM).catch(e => setErr(e.message)); return () => clearInterval(timer.current); }, [id]);
  if (err) return <div className="card muted">{err}</div>;
  if (!m) return <div className="card muted">Lade Spielbericht …</div>;
  const team = tid => st.teams.find(t => t.id === tid);
  const mySideM = m.home === st.myTeam.id ? 0 : m.away === st.myTeam.id ? 1 : -1;
  const mkM = mySideM >= 0 ? myMarker(st) : null;
  const live = shown !== null;
  const evs = live ? m.events.slice(0, shown) : m.events;
  const score = [0, 0];
  evs.forEach(e => { if (e.type === 'goal') score[e.side]++; });
  const shownScore = live ? score : m.goals;
  const minute = live ? (evs.length ? (evs[evs.length - 1].mt || evs[evs.length - 1].min) : 0) : (m.extraTime ? 120 : 90);
  function play() {
    clearInterval(timer.current); setShown(1);
    timer.current = setInterval(() => setShown(s => { if (s >= m.events.length) { clearInterval(timer.current); return null; } return s + 1; }), m.events.length > 40 ? 900 : 1800);
  }
  const S = m.stats;
  const Stat = ({ label, a, b, pct }) => <div className="stat">
    <div className="lbl">{label}</div>
    <div className="num">{a}{pct ? '%' : ''}</div>
    <div className="split"><i style={{ width: (a + b ? a / (a + b) * 100 : 50) + '%', background: 'var(--accent)' }} /><i style={{ flex: 1, background: '#4a5b52' }} /></div>
    <div>{b}{pct ? '%' : ''}</div>
  </div>;
  return <div className="grid" style={{ gridTemplateColumns: 'repeat(auto-fit,minmax(300px,1fr))' }}>
    <div className="card match-head" style={{ gridColumn: '1/-1', backgroundImage: `linear-gradient(rgba(14,21,18,.82),rgba(14,21,18,.95)), url(${IMG.spielbericht})` }}>
      <div className="muted" style={{ textAlign: 'center' }}>{compName(m, st)}{m.round ? ' · ' + m.round : ''} · Saison {m.season} · {!isLeague(m) ? '' : `${m.md}. Spieltag · `}{dayDate(m.playedAt)} · {m.stadium || ''} · {fmt(m.attendance)} Zuschauer</div>
      <div className="vs">
        <div className="team">{team(m.home) ? <Crest {...team(m.home)} size={48} /> : m.info && <Crest {...m.info[0]} size={48} />}{team(m.home) ? <a onClick={() => open({ type: 'team', id: m.home })}>{m.homeName}</a> : <span>{m.homeName}</span>}
          {m.info && <span className="muted" style={{ fontSize: 12 }}>{m.info[0].countryName}</span>}
          {m.sponsors && m.sponsors.jersey[0] && <span className="muted" style={{ fontSize: 12 }}>Trikot: {m.sponsors.jersey[0]}</span>}</div>
        <div className="score">{shownScore[0]}:{shownScore[1]}</div>
        <div className="team">{team(m.away) ? <Crest {...team(m.away)} size={48} /> : m.info && <Crest {...m.info[1]} size={48} />}{team(m.away) ? <a onClick={() => open({ type: 'team', id: m.away })}>{m.awayName}</a> : <span>{m.awayName}</span>}
          {m.info && <span className="muted" style={{ fontSize: 12 }}>{m.info[1].countryName}</span>}
          {m.sponsors && m.sponsors.jersey[1] && <span className="muted" style={{ fontSize: 12 }}>Trikot: {m.sponsors.jersey[1]}</span>}</div>
      </div>
      <div style={{ textAlign: 'center' }} className="muted">{live ? `${minute}. Minute – live` : `Endstand${m.pens ? ` · ${m.pens[0]}:${m.pens[1]} im Elfmeterschießen` : m.extraTime ? ' nach Verlängerung' : ''}`}
        {!live && m.carry && <><br />Hinspiel {m.carry[1]}:{m.carry[0]} · Gesamt {m.goals[0] + m.carry[0]}:{m.goals[1] + m.carry[1]}</>}</div>
      {m.sponsors && m.sponsors.boards.length > 0 && <Boards boards={m.sponsors.boards} slots={0} />}
      <div className="row" style={{ justifyContent: 'center', marginTop: 10 }}>
        {live ? <button onClick={() => { clearInterval(timer.current); setShown(null); }}>Zum Endstand springen</button>
          : <button className="primary" onClick={play}>▶ Ticker nachspielen</button>}
      </div>
    </div>
    <div className="card">
      <h3>Ticker</h3>
      <div className="ticker">{evs.slice().reverse().map((e, i) => <div key={i} className={'ev ' + e.type + (mySideM >= 0 && e.type !== 'text' && e.side === mySideM ? ' mine' : mySideM >= 0 && e.side === 1 - mySideM ? ' theirs' : '')}><div className="m">{e.type === 'pen' ? 'E' : (e.mt || e.min) + "'"}</div><div className="t"><Hl text={e.text} mk={mkM} /></div></div>)}</div>
    </div>
    {!live && <div className="card">
      <h3>Statistik</h3>
      <Stat label="Ballbesitz" a={S.poss[0]} b={S.poss[1]} pct />
      <Stat label="Torschüsse" a={S.shots[0]} b={S.shots[1]} />
      <Stat label="Aufs Tor" a={S.onTarget[0]} b={S.onTarget[1]} />
      {S.corners && <Stat label="Ecken" a={S.corners[0]} b={S.corners[1]} />}
      {S.fouls && <Stat label="Fouls" a={S.fouls[0]} b={S.fouls[1]} />}
      {S.offsides && <Stat label="Abseits" a={S.offsides[0]} b={S.offsides[1]} />}
      <Stat label="Gelbe Karten" a={S.yellow[0]} b={S.yellow[1]} />
      <Stat label="Rote Karten" a={S.red[0]} b={S.red[1]} />
      <h3 style={{ marginTop: 14 }}>Stärke (Abw / Mittelf. / Angr.)</h3>
      <div className="row"><span>{m.ratings[0].def} / {m.ratings[0].mid} / {m.ratings[0].att}</span><span className="spacer" /><span>{m.ratings[1].def} / {m.ratings[1].mid} / {m.ratings[1].att}</span></div>
      <h3 style={{ marginTop: 14 }}>Aufstellungen</h3>
      <div className="row" style={{ alignItems: 'flex-start' }}>
        {m.lineups ? m.lineups.map((lu, s) => <div key={s} style={{ flex: 1, fontSize: 12 }}>{lu.map(x => <div key={x.id}><Pos p={x.pos} /> {x.name}</div>)}</div>)
          : <span className="muted" style={{ fontSize: 12 }}>Für dieses Spiel wurde nur ein Kurzbericht gespeichert.</span>}
      </div>
    </div>}
  </div>;
}

// ---------- Vereinsansicht ----------
function TeamView({ id, st }) {
  const [t, setT] = useState(null);
  useEffect(() => { api('/team/' + id).then(setT).catch(() => {}); }, [id]);
  if (!t) return <div className="card muted">Lade Verein …</div>;
  const tab = st.tables[t.league];
  const rowIdx = tab.findIndex(r => r.id === t.id);
  const facNames = { tribuene: 'Tribünen', dach: 'Dach', vip: 'VIP', parkhaus: 'Parkhaus', fanshop: 'Fanshop', training: 'Training', jugend: 'Jugend', medizin: 'Reha' };
  return <div className="card">
    <div className="row"><Crest id={t.id} crest={t.crest} colors={t.colors} short={t.short} size={56} /><div>
      <h2 style={{ margin: 0 }}>{t.name}</h2>
      <div className="muted">{lgName(st, t.league)} · Platz {rowIdx + 1} · Trainer: {t.manager || 'Computer'} · {t.stadiumName} ({fmt(t.stadium)} Plätze) · {fmt(t.fans)} Fans · {t.formation}</div>
    </div></div>
    {(t.jersey || t.boards.length > 0) && <div className="row" style={{ margin: '10px 0' }}>
      <Jersey colors={t.colors} kit={t.kit} sponsor={t.jersey} size={90} />
      <div style={{ flex: 1, minWidth: 0 }}><div className="muted" style={{ fontSize: 12 }}>Trikotsponsor</div>{t.jersey ? <SponsorTag sp={t.jersey} link /> : <span className="muted">keiner</span>}
        {t.boards.length > 0 && <><div className="muted" style={{ fontSize: 12, marginTop: 6 }}>Bandenwerbung</div><Boards boards={t.boards} slots={0} /></>}</div>
    </div>}
    <div className="row" style={{ margin: '10px 0', fontSize: 12 }}>{Object.entries(t.facilities).filter(([, v]) => v > 0).map(([k, v]) => <span key={k} className="tag list">{facNames[k]} {v}</span>)}</div>
    <div className="tw"><table>
      <thead><tr><th>Pos</th><th>Name</th><th className="num">Alter</th><th className="num">Stärke</th><th>Talent</th><th className="num">Form</th><th className="num">Sp</th><th className="num">Tore</th><th className="num">Marktwert</th></tr></thead>
      <tbody>{t.players.map(p => <tr key={p.id}><td><Pos p={p.pos} /></td><td><PN p={p} /><Status p={p} /></td><td className="num">{p.age}</td><td className="num ovr">{p.ovr}</td><td><Stars n={p.talent} /></td><td className="num">{p.form.toFixed(1)}</td><td className="num">{p.season.games}</td><td className="num">{p.season.goals}</td><td className="num">{eur(p.value)}</td></tr>)}</tbody>
    </table></div>
  </div>;
}

// ---------- Admin ----------
function AdminReset({ act }) {
  const [mode, setMode] = useState('full');
  const [word, setWord] = useState('');
  return <div className="card" style={{ marginTop: 14, borderColor: '#5a2a28' }}>
    <h2>Spielwelt zurücksetzen</h2>
    <p className="muted" style={{ marginTop: 0 }}>Alle Trainer behalten ihr Konto und ihren Verein – niemand muss sich neu registrieren. Vorher wird automatisch eine Sicherung im data-Ordner angelegt.</p>
    <label className="reset-opt"><input type="radio" checked={mode === 'full'} onChange={() => setMode('full')} /> <span><b>Komplett neu</b> – neue Spielwelt mit frischen Kadern, Startbudget, leerem Gelände und neuen Sponsoren. Vereinsname, Kürzel, Farben, Wappen, Stadionname und Liga der Trainer bleiben.</span></label>
    <label className="reset-opt"><input type="radio" checked={mode === 'season'} onChange={() => setMode('season')} /> <span><b>Nur Saison neu starten</b> – zurück auf Saison 1, Spieltag 1. Kader, Budgets, Gelände und Sponsoren bleiben, Tabellen, Statistiken, Pokal und internationale Wettbewerbe starten neu.</span></label>
    <div className="row" style={{ marginTop: 10 }}>
      <input placeholder="NEUSTART eingeben" value={word} onChange={e => setWord(e.target.value.toUpperCase())} style={{ width: 180 }} />
      <button className="danger" disabled={word !== 'NEUSTART'} onClick={async () => { if (await act('/admin/reset', { mode, confirm: word }, mode === 'full' ? 'Neue Spielwelt gestartet' : 'Saison neu gestartet')) setWord(''); }}>Jetzt zurücksetzen</button>
    </div>
  </div>;
}
function Admin({ st, act }) {
  return <><div className="card">
    <h2>Admin</h2>
    <p>Liga-Spieltage laufen automatisch nach Zeitplan <code>{st.game.schedule}</code>, Pokalrunden nach <code>{st.game.cupSchedule}</code> (Wochentage 0 = Sonntag @ Uhrzeit, deutsche Zeit).</p>
    <table style={{ maxWidth: 520 }}><tbody>
      <tr><td>Nächster Liga-Spieltag ({st.game.matchday + 1}/{st.game.totalMatchdays})</td><td className="num">{dayDate(st.game.nextMatchAt)}</td></tr>
      <tr><td>Nächster Pokal-Sonntag</td><td className="num">{dayDate(st.game.nextCupAt)}</td></tr>
    </tbody></table>
    <p className="muted">{st.game.liveMinutes > 0 ? `Live-Modus: Ein Spiel dauert ${st.game.liveMinutes} Minuten (LIVE_MINUTES in der docker-compose.yml, 0 = sofort berechnen). Die Knöpfe pfeifen sofort live an.` : 'Live-Modus ist aus (LIVE_MINUTES=0) – die Knöpfe berechnen sofort.'} Eine Pokalrunde ist sonntags nur fällig, wenn genug Liga-Spieltage gespielt sind – der Knopf zieht die nächste Runde vor.</p>
    <h3 style={{ marginTop: 16 }}>Pokalauslosung</h3>
    {st.cupDraw ? <div className="row">
      <span className="muted">{st.cupDraw.round}: {st.cupDraw.live ? 'läuft gerade live' : `geplant für ${dayDate(st.cupDraw.at)}`} · Spiel {dayDate(st.cupDraw.gameAt)}</span>
      {!st.cupDraw.live && <button className="primary" onClick={() => act('/admin/cupdraw', { mode: 'start' }, 'Pokalauslosung gestartet')}>Auslosung jetzt live starten</button>}
      <button onClick={() => act('/admin/cupdraw', { mode: 'finish' }, 'Pokalauslosung abgeschlossen')}>Auslosung sofort abschließen</button>
    </div> : <p className="muted">Gerade steht keine Pokalauslosung an. Die 1. Runde wird 2 Tage vor dem Spiel gelost, jede weitere Runde 1 Tag nach der vorherigen.</p>}
    <h3 style={{ marginTop: 16 }}>Spiele</h3>
    {st.live ? <div className="row">
      <span className="tag inj">Live: {st.live.title}</span>
      <button className="primary" onClick={() => act('/admin/live/finish', {}, 'Live-Spiele beendet')}>Live-Spiele sofort zu Ende spielen</button>
    </div> : <div className="row">
      <button className="primary" onClick={() => act('/admin/play', {}, st.game.liveMinutes > 0 ? 'Spieltag live angepfiffen' : `Spieltag ${st.game.matchday + 1} ausgetragen`)}>Spieltag {st.game.matchday + 1} {st.game.liveMinutes > 0 ? 'jetzt live anpfeifen' : 'jetzt austragen'}</button>
      <button onClick={() => act('/admin/cup', {}, st.game.liveMinutes > 0 ? 'Pokalrunde live angepfiffen' : 'Pokalrunde ausgetragen')}>Nächste Pokalrunde {st.game.liveMinutes > 0 ? 'anpfeifen' : 'austragen'}</button>
      <button onClick={() => act('/admin/euro', {}, st.game.liveMinutes > 0 ? 'Internationale Runde angepfiffen' : 'Internationale Runde ausgetragen')}>Nächste internationale Runde {st.game.liveMinutes > 0 ? 'anpfeifen' : 'austragen'}</button>
    </div>}
  </div>
  <AdminReset act={act} /></>;
}

// Unternavigation innerhalb eines Bereichs
function SubNav({ value, options, onChange }) {
  return <div className="subnav">{options.map(([k, l]) => <button key={k} className={k === value ? 'on' : ''} onClick={() => onChange(k)}>{l}</button>)}</div>;
}

// ---------- App ----------
function App() {
  const [authed, setAuthed] = useState(!!TOKEN);
  const [notice, setNotice] = useState(null);
  const [st, setSt] = useState(null);
  const [tab, setTab] = useState(() => { try { const t = new URLSearchParams(location.search).get('tab'); return ['live', 'squad', 'cup', 'home', 'club'].includes(t) ? t : 'home'; } catch (e) { return 'home'; } });
  useEffect(() => {
    // Klick auf eine Benachrichtigung bei geöffneter App: passenden Bereich öffnen
    if (!('serviceWorker' in navigator)) return;
    const f = e => { if (e.data && e.data.type === 'open') { try { const t = new URL(e.data.url, location.href).searchParams.get('tab'); if (t) { setTab(t); setView(null); } } catch (x) {} } };
    navigator.serviceWorker.addEventListener('message', f);
    return () => navigator.serviceWorker.removeEventListener('message', f);
  }, []);
  // Unterbereiche: Kader (Kader / Aufstellung / Training) und Finanzen (Finanzen / Sponsoren)
  const [sub, setSub] = useState(() => { try { return JSON.parse(store.get('fm_sub') || '{}'); } catch (e) { return {}; } });
  const pickSub = (k, v) => { const n = { ...sub, [k]: v }; setSub(n); store.set('fm_sub', JSON.stringify(n)); window.scrollTo(0, 0); };
  const [view, setView] = useState(null);
  const [playerId, setPlayerId] = useState(null);
  OPEN_PLAYER = setPlayerId;
  const [flash, setFlash] = useState(null);
  const flashT = useRef(null);
  const say = (text, err) => { setFlash({ text, err }); clearTimeout(flashT.current); flashT.current = setTimeout(() => setFlash(null), 3500); };
  const logout = () => { api('/logout', {}).catch(() => {}); TOKEN = null; store.del('fm_token'); setSt(null); setAuthed(false); };
  // Abgemeldet (z. B. weil das Konto auf einem anderen Gerät angemeldet wurde): zurück zum Login mit Hinweis
  const signedOut = e => { if (e.status !== 401) return false; TOKEN = null; store.del('fm_token'); setNotice(e.kicked ? e.message : null); setSt(null); setAuthed(false); return true; };
  const load = useCallback(async () => {
    try { setSt(await api('/state')); }
    catch (e) { if (!signedOut(e)) say(e.message, true); }
  }, []);
  useEffect(() => { if (!authed) return; load(); const i = setInterval(load, 30000); return () => clearInterval(i); }, [authed]);
  const act = async (path, body, ok) => {
    try { const r = await api(path, body); if (ok) say(ok); await load(); return r || true; }
    catch (e) { if (!signedOut(e)) say(e.message, true); return null; }
  };
  const open = v => { setView(v); window.scrollTo(0, 0); };
  if (!authed) return <Auth notice={notice} onLogin={() => { setNotice(null); setAuthed(true); }} />;
  if (!st) return <div className="app"><p className="muted" style={{ padding: 40 }}>Lade …</p></div>;
  const TABS = [['home', 'Übersicht'], ['live', st.live ? '● Live' : 'Live'], ['squad', 'Kader'], ['fixtures', 'Spielplan'], ['table', 'Tabelle'], ['cup', st.cupDraw && st.cupDraw.live ? '● Pokal' : 'Pokal'],
    ['market', 'Transfermarkt'], ['grounds', 'Stadion & Gelände'], ['fin', 'Finanzen'], ['club', 'Verein']];
  if (st.me.isAdmin) TABS.push(['admin', 'Admin']);
  CRESTS = Object.fromEntries(st.teams.map(t => [t.id, t.crest]));
  const me = st.myTeam;
  const place = st.tables[me.league].findIndex(r => r.id === me.id) + 1;
  return <>
    <header className="top" style={{ backgroundImage: `linear-gradient(90deg,rgba(14,21,18,.96),rgba(14,21,18,.75)), url(${IMG.headerBg})` }}>
      <div className="app top-inner">
        <div className="row">
          <Img src={IMG.logo} className="logo-small" />
          <Crest crest={me.crest} colors={me.colors} short={me.short} size={60} />
          <div><div className="brand">Flutlicht Manager</div><div className="muted" style={{ marginTop: -4 }}>{me.name} · {lgName(st, me.league)}</div></div>
        </div>
        <div className="kpis">
          <div className="kpi"><span>Saison</span><b>{st.game.season} · ST {Math.min(st.game.matchday + 1, st.game.totalMatchdays)}/{st.game.totalMatchdays}</b></div>
          <div className="kpi"><span>Platz</span><b>{place}.</b></div>
          <div className="kpi"><span>Budget</span><b className={me.budget < 0 ? 'neg-money' : ''}>{eur(me.budget)}</b></div>
          {st.live ? <div className="kpi live-kpi" onClick={() => { setTab('live'); setView(null); }}><span>Jetzt</span><b><span className="live-dot" /> LIVE</b></div>
            : <div className="kpi"><span>Anpfiff in</span><b><Countdown to={st.game.nextMatchAt} /></b></div>}
          <button className="small" onClick={logout}>Abmelden ({st.me.username})</button>
        </div>
      </div>
    </header>
    <div className="app">
      <nav className="tabs">{TABS.map(([k, l]) => <button key={k} className={!view && tab === k ? 'on' : ''} onClick={() => { setTab(k); setView(null); }}>{l}</button>)}</nav>
      {view && <div style={{ marginBottom: 12 }}><a onClick={() => setView(null)}>‹ Zurück</a></div>}
      {view && view.type === 'match' && <MatchView id={view.id} open={open} st={st} />}
      {view && view.type === 'team' && <TeamView id={view.id} st={st} />}
      {!view && st.cupDraw && st.cupDraw.live && !(tab === 'cup' && (sub.cup || 'cup') === 'cup') && <div className="live-banner" onClick={() => { setTab('cup'); pickSub('cup', 'cup'); setView(null); }}><span className="live-dot" /> Die Pokalauslosung ({st.cupDraw.round}) läuft gerade live – zur Auslosung ›</div>}
      {!view && st.game.pause && st.game.pause.active && !(tab === 'squad' && sub.squad === 'training') && <div className="live-banner pause-banner" onClick={() => { setTab('squad'); pickSub('squad', 'training'); setView(null); }}>🏕 {PAUSE_NAME[st.game.pause.type]} – weiter geht's in <Countdown to={st.game.nextMatchAt} />. Zum Trainingslager ›</div>}
      {!view && st.live && st.live.mine && tab !== 'live' && <div className="live-banner" onClick={() => { setTab('live'); setView(null); }}><span className="live-dot" /> Dein Spiel läuft gerade live ({st.live.title}) – zum Live-Ticker ›</div>}
      {!view && tab === 'home' && <InstallBanner goSettings={() => { setTab('club'); setView(null); setTimeout(() => { const el = document.getElementById('app-settings'); if (el) el.scrollIntoView({ behavior: 'smooth' }); }, 300); }} />}
      {!view && tab === 'home' && <Overview st={st} open={open} />}
      {!view && tab === 'live' && <Live st={st} act={act} open={open} reloadState={load} />}
      {!view && tab === 'squad' && <SubNav value={sub.squad || 'kader'} onChange={v => pickSub('squad', v)} options={[['kader', 'Kader'], ['lineup', 'Aufstellung'], ['training', 'Training'], ['jugend', 'A-Jugend']]} />}
      {!view && tab === 'squad' && (sub.squad || 'kader') === 'kader' && <Squad st={st} act={act} />}
      {!view && tab === 'squad' && sub.squad === 'lineup' && <Lineup st={st} act={act} />}
      {!view && tab === 'squad' && sub.squad === 'training' && <><Camp st={st} act={act} /><Training st={st} act={act} /></>}
      {!view && tab === 'squad' && sub.squad === 'jugend' && <Youth st={st} act={act} />}
      {!view && tab === 'fixtures' && <Fixtures st={st} open={open} />}
      {!view && tab === 'table' && <Table st={st} open={open} />}
      {!view && tab === 'cup' && <SubNav value={sub.cup || 'cup'} onChange={v => pickSub('cup', v)} options={[['cup', st.cupDraw && st.cupDraw.live ? '● Pokal' : 'Pokal'], ['euro', 'International']]} />}
      {!view && tab === 'cup' && (sub.cup || 'cup') === 'cup' && <Cup st={st} open={open} />}
      {!view && tab === 'cup' && (sub.cup || 'cup') === 'euro' && <International st={st} open={open} />}
      {!view && tab === 'market' && <Market st={st} act={act} />}
      {!view && tab === 'grounds' && <Grounds st={st} act={act} />}
      {!view && tab === 'fin' && <SubNav value={sub.fin || 'fin'} onChange={v => pickSub('fin', v)} options={[['fin', 'Finanzen'], ['sponsors', `Sponsoren${me.sponsors.offers ? ` (${me.sponsors.offers})` : ''}`]]} />}
      {!view && tab === 'fin' && (sub.fin || 'fin') === 'fin' && <Finances st={st} />}
      {!view && tab === 'fin' && sub.fin === 'sponsors' && <Sponsors st={st} act={act} />}
      {!view && tab === 'club' && <Club st={st} act={act} />}
      {!view && tab === 'admin' && <Admin st={st} act={act} />}
      {playerId && <PlayerModal id={playerId} st={st} act={act} close={() => setPlayerId(null)} />}
      {flash && <div className={'flash' + (flash.err ? ' err' : '')}>{flash.text}</div>}
    </div>
  </>;
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
