/* Pages: Home, Anmeldung, Meldeliste, Info */

const { useState: uS, useEffect: uE } = React;

// Optional-Marker "(freiwillig)" in Labels optisch absetzen (helleres Grau, klein).
const optionalStyle = { color: 'var(--text-3)', fontWeight: 400, textTransform: 'none', letterSpacing: 0 };
function labelWithOptional(text) {
  const marker = '(freiwillig)';
  if (typeof text === 'string' && text.includes(marker)) {
    const before = text.slice(0, text.indexOf(marker)).trimEnd();
    return <>{before} <span style={optionalStyle}>{marker}</span></>;
  }
  return text;
}

// Kleiner Info-Button mit Tooltip — Hover am Desktop, Tap auf Mobile.
function InfoTip({ text }) {
  const [open, setOpen] = uS(false);
  return (
    <span className="infotip">
      <button type="button" className="infotip-btn" aria-label="Mehr Infos"
              onClick={(e) => { e.preventDefault(); setOpen(o => !o); }}
              onMouseEnter={() => setOpen(true)}
              onMouseLeave={() => setOpen(false)}>
        <Icon name="info" size={14}/>
      </button>
      {open && <span className="infotip-bubble" role="tooltip">{text}</span>}
    </span>
  );
}

/* ──────────────── HOME ──────────────── */
function HomePage({ onNav }) {
  return (
    <>
      <div className="hero">
        <div className="hero-bg"></div>
        <div className="hero-photo"></div>
        <div className="hero-inner">
          <div className="hero-left">
            <div className="hero-eyebrow"><span className="line"></span>Doppel-Turnier<span className="line"></span></div>
            <h1 className="hero-title hero-title-display">
              <span className="t-line"><span className="ord">1.</span> <span className="word">Paartal</span></span>
              <span className="t-line t-with-edeka">
                <span className="word">Open</span>
                <HeroPoweredBadge/>
              </span>
            </h1>
          </div>
          <Countdown/>
          <PrizeStar/>
        </div>
      </div>

      <div className="meta-section">
        <div className="meta-inner">
          <div className="meta-cta">
            <button type="button" className="cta-banner" onClick={() => onNav('meldeliste')}>
              <Icon name="racquet" size={18}/>
              <span className="cta-banner-title">Hier geht's zur Anmeldung</span>
              <Icon name="arrow-right" size={16}/>
            </button>
          </div>
          <div className="meta-row">
            <div className="item"><span className="lbl">Konkurrenz</span><span className="val">Herren · alle</span></div>
            <div className="item"><span className="lbl">Spielmodus</span><span className="val">Doppel · KO</span></div>
            <div className="item"><span className="lbl">Startgeld</span><span className="val">50 € / Paarung</span></div>
            <div className="item"><span className="lbl">Preisgeld</span><span className="val">1.000 € gesamt</span></div>
            <div className="item"><span className="lbl">Meldeschluss</span><span className="val">04.08.2026</span></div>
            <div className="item"><span className="lbl">Auslosung</span><span className="val">05.08.2026 · 18:00</span></div>
          </div>

          <hr className="meta-divider"/>

          <div className="intro-block">
            <div className="lbl">Beschreibung</div>
            <div className="intro-text">
              <p>
                Die 1. Paartal Open sind die Premiere unseres reinen Doppelturniers
                und finden in dieser Form zum ersten Mal statt. Gespielt wird im
                klassischen KO-System auf Sandplatz. Wer in der ersten Runde
                verliert, rutscht in die Trostrunde und kann sich auch dort noch
                ein Preisgeld erspielen.
              </p>
              <p>
                Eine offizielle LK-Ranglistenwertung gibt es nicht. Die Setzliste
                richtet sich aber nach den aktuellen Leistungsklassen der gemeldeten
                Spieler, sodass die Paarungen sportlich fair zugelost werden können.
              </p>
              <p>
                Alle Details wie Modus, Spielzeiten, Preisgeldstaffelung und
                Teilnahmebedingungen sind in der{' '}
                <a href="Turnierinfos/Ausschreibung_PaartalOpen_2026.html"
                   target="_blank" rel="noopener noreferrer"
                   className="text-link">vollständigen Turnierausschreibung</a>{' '}
                zu finden.
              </p>
              <p>
                <strong>EDEKA Riasanow</strong> ist Hauptsponsor des
                Turniers und ermöglicht die Veranstaltung in dieser Form
                überhaupt erst. Unterstützt wird das Turnier außerdem von
                der <strong>Hallertauer Volksbank</strong> und weiteren
                Partnern aus der Region (siehe unten). Für die Verpflegung
                an allen drei Tagen sorgt das Vereinsteam des
                TSV Baar-Ebenhausen. Zusätzlich erfolgt eine
                Bewirtschaftung durch die Sportgaststätte
                „Gasthof Flotzinger".
              </p>
              <p>
                Insgesamt stehen 6 Sandplätze + 1 Hallenplatz zur Verfügung.
              </p>
            </div>
          </div>

          <hr className="meta-divider"/>

          <div className="atmo-block">
            <div className="atmo-strip" aria-hidden="true">
              <div className="atmo-tile atmo-grill"></div>
              <div className="atmo-tile atmo-bier"></div>
              <div className="atmo-tile atmo-kuchen"></div>
            </div>
            <div className="atmo-labels">
              <span>Grill</span>
              <span>Bewirtschaftung</span>
              <span>Kuchen</span>
            </div>
          </div>
        </div>
      </div>

      <SponsorSection />
    </>
  );
}

function SponsorSection() {
  // EDEKA Riasanow ist alleiniger Hauptsponsor (im Hero). Hier sammeln
  // sich die Co-Sponsoren / Partner. Reihenfolge bewusst gewaehlt:
  // Hallertauer Volksbank zuerst (groesster Finanzpartner), dann die
  // weiteren regionalen Unterstuetzer in der Reihenfolge wie zugesagt.
  const partners = [
    { name: 'Hallertauer Volksbank',     logo: 'uploads/vr.png',             href: 'https://www.hallertauer-volksbank.de/',           logoClass: 'sp-logo-vr' },
    { name: 'DVAG',                      tagline: 'Roland Pietsch',          logo: 'uploads/allfinanz.png',      href: 'https://www.allfinanz.ag/roland.pietsch/index.html', logoClass: 'sp-logo-allfinanz' },
    { name: 'Getränke Hörl',             tagline: 'Claudia Huber',           logo: 'uploads/hoerl_neu.png',      href: 'https://hoerl-getraenke.de/getraenkemarkt/baar-ebenhausen-muenchener-strasse-112.html', logoClass: 'sp-logo-hoerl' },
    { name: 'KFZ Weichselbaumer',        logo: 'uploads/weichselbaumer.png', href: 'https://www.kfz-weichselbaumer.de/',              logoClass: 'sp-logo-weichselbaumer' },
    { name: 'GMW Studio',                logo: 'uploads/GMW2.png',           href: 'https://www.gmw-studio.de/',                      logoClass: 'sp-logo-gmw' },
    { name: 'Schanzer Besaitungsservice', logo: 'uploads/schanzer.svg',      href: 'https://schanzer-besaitungsservice.de/',          logoClass: 'sp-logo-schanzer' },
    { name: 'Scaly Academy',             logo: 'uploads/scaly.png',          href: 'https://scaly.com/',                              logoClass: 'sp-logo-scaly' },
  ];
  return (
    <div className="sponsors-section">
      <div className="sponsors-inner">
        <div className="sponsors-head">
          <div>
            <div className="page-eyebrow">Partner</div>
            <h2 className="page-title" style={{fontSize: 32, marginBottom: 0}}>Unsere Partner</h2>
          </div>
        </div>

        <div className="partner-grid">
          {partners.map((p) => {
            const inner = (
              <>
                <div className={`partner-logo ${p.logoClass}`}>
                  <img src={p.logo} alt={p.name}/>
                </div>
                <div className="partner-name">
                  {p.name}{p.tagline && <span className="partner-tagline">{p.tagline}</span>}
                </div>
              </>
            );
            return p.href
              ? <a key={p.name} className="partner-card" href={p.href} target="_blank" rel="noopener noreferrer">{inner}</a>
              : <div key={p.name} className="partner-card">{inner}</div>;
          })}
        </div>
      </div>
    </div>
  );
}

/* ──────────────── ANMELDUNG ──────────────── */
// Anmeldung läuft als Modal über der Meldeliste — kein eigener Routing-
// Eintrag mehr. Per "Anmeldung zum Turnier"-Button auf der Meldelisten-
// Seite getriggert.
function AnmeldungModal({ onSubmit, onClose }) {
  uE(() => { lockBodyScroll(); return unlockBodyScroll; }, []);
  const emptyPlayer = () => ({ first:'', last:'', birthdate:'', club:'', email:'', phone:'', lk:'ohne LK' });
  const empty = { p1: emptyPlayer(), p2: emptyPlayer(), accept: false };
  const [f, setF] = uS(empty);
  const [errors, setErrors] = uS({});
  const [done, setDone] = uS(false);

  const updPlayer = (which, key) => (e) =>
    setF(prev => ({ ...prev, [which]: { ...prev[which], [key]: e.target.value } }));
  const updAccept = (e) => setF(prev => ({ ...prev, accept: e.target.checked }));

  const validate = () => {
    const e = {};
    const checkPlayer = (which) => {
      const p = f[which];
      // Pflichtfelder: Vorname, Nachname, Geburtsdatum, Verein.
      // E-Mail nur fuer Spieler 1 Pflicht (eine Bestaetigung reicht
      // pro Paarung), Telefon und LK sind freiwillig.
      const required = which === 'p1'
        ? ['first','last','birthdate','club','email']
        : ['first','last','birthdate','club'];
      required.forEach(k => {
        if (!String(p[k] ?? '').trim()) e[`${which}_${k}`] = 'Pflichtfeld';
      });
      if (p.email && !/^\S+@\S+\.\S+$/.test(p.email)) e[`${which}_email`] = 'Ungültige E-Mail';
      if (p.birthdate) {
        const d = new Date(p.birthdate);
        const min = new Date('1920-01-01');
        const max = new Date('2015-12-31');
        if (isNaN(d) || d < min || d > max) e[`${which}_birthdate`] = 'Ungültiges Datum';
      }
    };
    checkPlayer('p1');
    checkPlayer('p2');
    if (!f.accept) e.accept = 'Bitte zustimmen';
    setErrors(e);
    return Object.keys(e).length === 0;
  };

  const trimPlayer = (p) => ({
    first: p.first.trim(),
    last: p.last.trim(),
    birthdate: p.birthdate,
    club: p.club.trim(),
    email: p.email.trim(),
    phone: p.phone.trim(),
    lk: p.lk,
  });

  const submit = (e) => {
    e.preventDefault();
    if (!validate()) return;
    const reg = {
      id: 'r' + Date.now(),
      p1: trimPlayer(f.p1),
      p2: trimPlayer(f.p2),
      ts: todayDE(),
    };
    onSubmit(reg);
    setDone(true);
    setF(empty);
  };

  if (done) {
    return (
      <div className="modal-overlay">
        <div className="modal modal-wide" onClick={e=>e.stopPropagation()} style={{textAlign:'center', padding:'48px 30px'}}>
          <button type="button" className="modal-close" onClick={onClose} aria-label="Schließen"><Icon name="x" size={18}/></button>
          <div style={{width: 72, height: 72, borderRadius:'50%', background:'var(--red-soft)', color:'var(--red)', display:'inline-grid', placeItems:'center', marginBottom: 18}}>
            <Icon name="check" size={32}/>
          </div>
          <h2 className="page-title" style={{fontSize: 28, marginTop: 0}}>Anmeldung eingegangen!</h2>
          <p className="page-sub" style={{margin:'0 auto 24px', maxWidth: 480}}>
            Die Daten sind eingegangen. An die hinterlegte E-Mail-Adresse
            wird in Kürze eine Bestätigungs-E-Mail mit den Zahlungsinfos
            für die Startgebühr von 50 € pro Paarung versendet.
          </p>
          <div style={{display:'flex', gap: 12, justifyContent:'center'}}>
            <button className="btn btn-ghost" onClick={onClose}>Schließen</button>
            <button className="btn btn-primary" onClick={() => setDone(false)}>Weitere Anmeldung</button>
          </div>
        </div>
      </div>
    );
  }

  const renderPlayerCard = (which, label) => {
    const p = f[which];
    const errKey = (k) => `${which}_${k}`;
    const fld = (k, props={}) => (
      <div className="field" key={k}>
        <label className="field-label">{labelWithOptional(props.label)}</label>
        <input
          className="input"
          type={props.type || 'text'}
          value={p[k]}
          onChange={updPlayer(which, k)}
          placeholder={props.placeholder}
          max={props.max}
          min={props.min}
        />
        {errors[errKey(k)] && <div style={{color:'var(--loss)', fontSize: 11, marginTop: 4}}>{errors[errKey(k)]}</div>}
      </div>
    );
    return (
      <div className="card card-pad" style={{marginBottom: 18}} key={which}>
        <div className="label" style={{color:'var(--red)', marginBottom: 12}}>{label}</div>
        <div className="field-grid-2">
          {fld('first', { label: 'Vorname *' })}
          {fld('last',  { label: 'Nachname *' })}
        </div>
        <div className="field-grid-2">
          {fld('birthdate', { label: 'Geburtsdatum *', type: 'date', min: '1920-01-01', max: '2015-12-31' })}
          {fld('club', { label: 'Verein *' })}
        </div>
        <div className="field-grid-2">
          <div className="field">
            <div className="field-label" style={{display:'flex', alignItems:'center', gap:6}}>
              <span>Aktuelle Leistungsklasse <span style={optionalStyle}>(freiwillig)</span></span>
              <InfoTip text="Die LK dient lediglich als Orientierung für die Setzliste. Zum Zeitpunkt der Auslosung wird die aktuelle LK von tennis.de verwendet."/>
            </div>
            <select className="select" value={p.lk} onChange={updPlayer(which, 'lk')}>
              {LK_OPTIONS.map(o => <option key={o} value={o}>{o}</option>)}
            </select>
          </div>
          {fld('phone', { label: 'Telefon (freiwillig)' })}
        </div>
        {fld('email', {
          label: which === 'p1' ? 'E-Mail (für Bestätigung) *' : 'E-Mail (freiwillig)',
          type: 'email',
        })}
      </div>
    );
  };

  return (
    <div className="modal-overlay">
      <div className="modal modal-wide" onClick={e=>e.stopPropagation()}>
        <button type="button" className="modal-close" onClick={onClose} aria-label="Schließen"><Icon name="x" size={18}/></button>
        <h3 style={{margin:'0 0 16px'}}>Anmeldung zum Turnier</h3>

        <div className="note-card" style={{marginBottom: 18}}>
          <Icon name="trophy" size={14}/>
          <span><b>Keine LK-Wertung.</b><br/>Die Matches zählen nicht für die DTB-Leistungsklasse, sondern nur fürs Turnier.</span>
        </div>

        <form onSubmit={submit}>
          {renderPlayerCard('p1', 'Spieler 1')}
          {renderPlayerCard('p2', 'Spieler 2')}

          <div className="card card-pad" style={{background:'var(--cream-2)', marginBottom: 18}}>
            <label style={{display:'flex', alignItems:'flex-start', gap: 10, cursor:'pointer'}}>
              <input type="checkbox" checked={f.accept} onChange={updAccept} style={{marginTop: 3}}/>
              <span style={{fontSize: 13, color:'var(--text-2)', lineHeight: 1.5}}>
                Wir akzeptieren die Turnierregeln und verpflichten uns zur Zahlung
                der Startgebühr von <b style={{color:'var(--text-1)', whiteSpace:'nowrap'}}>50 € / Paarung</b>.
                Beide Spieler erhalten nach Anmeldung eine Bestätigungs-E-Mail mit den
                Zahlungsinformationen. Erst nach Zahlungseingang wird die Paarung
                öffentlich freigeschaltet. Die Daten werden ausschließlich zur
                Turnierorganisation verwendet.
              </span>
            </label>
            {errors.accept && <div style={{color:'var(--loss)', fontSize: 11, marginTop: 6, marginLeft: 24}}>{errors.accept}</div>}
          </div>

          <div style={{display:'flex', justifyContent:'space-between', alignItems:'center', gap: 16, flexWrap:'wrap'}}>
            <button type="button" className="btn btn-ghost" onClick={onClose}>Abbrechen</button>
            <button type="submit" className="btn btn-primary" style={{padding:'13px 22px', fontSize: 14}}>
              <Icon name="check" size={14}/> Verbindlich anmelden
            </button>
          </div>
        </form>
      </div>
    </div>
  );
}

/* ──────────────── MELDELISTE ──────────────── */
function MeldelistePage({ registrations, seeds, isAdmin, onSubmitReg, onAddReg, onUpdateReg, onDeleteReg, onApproveReg }) {
  const [editing, setEditing] = uS(null); // null | 'new' | reg-object
  const [confirmDel, setConfirmDel] = uS(null);
  const [showAnmeldung, setShowAnmeldung] = uS(false);
  // Body-Scroll sperren, solange das Lösch-Bestätigungs-Modal sichtbar
  // ist. RegEditorModal kümmert sich selber, weil's eine eigene
  // Komponente ist — der ConfirmDel ist hier inline.
  uE(() => {
    if (!confirmDel) return;
    lockBodyScroll();
    return unlockBodyScroll;
  }, [confirmDel]);

  // Hauptfeld = erste 32 freigegebene nach Anmeldedatum, Nachrücker = 33+.
  // Pending = noch nicht freigegebene Anmeldungen (warten auf 50€).
  // mainFieldRegs/reserveRegs filtern bereits auf approved.
  const main = mainFieldRegs(registrations);
  const reserves = reserveRegs(registrations);
  const pending = pendingRegs(registrations);
  const seedsMap = seeds || {};
  const sortedMain = sortedBySeed(main);

  const renderRow = (r, i, opts = {}) => {
    const s = seedsMap[r.id];
    // data-cell wird vom Mobile-CSS-Grid genutzt, um die td's klar in
    // Spieler-1- und Spieler-2-Bloecke zu gruppieren — sonst sind
    // Verein/LK auf Mobile nicht eindeutig zuordnenbar.
    return (
      <tr key={r.id} className={s ? 'seeded' : ''}>
        {/* Checkbox-Spalte: nur im Druck sichtbar — leere Quadrat-
            Glyphe zum Abhaken angemeldeter Teilnehmer am Turniertag. */}
        <td className="print-check" data-cell="check" aria-hidden="true">☐</td>
        <td className="num-col" data-cell="num">{i+1}</td>
        <td className="player" data-cell="p1">{r.p1.first} {r.p1.last}</td>
        <td className="meta"   data-cell="c1">{r.p1.club}</td>
        <td className="meta mono" data-cell="lk1">{r.p1.lk || '—'}</td>
        <td className="meta mono" data-cell="yr1">{birthYear(r.p1) || '—'}</td>
        <td className="player col-divider" data-cell="p2">{r.p2.first} {r.p2.last}</td>
        <td className="meta"   data-cell="c2">{r.p2.club}</td>
        <td className="meta mono" data-cell="lk2">{r.p2.lk || '—'}</td>
        <td className="meta mono" data-cell="yr2">{birthYear(r.p2) || '—'}</td>
        {isAdmin && (
          <td className="meta" data-cell="actions" style={{textAlign:'right'}}>
            <button className="link-btn" onClick={() => setEditing(r)}>Bearbeiten</button>
            <span style={{margin:'0 6px', color:'var(--text-4)'}}>·</span>
            <button className="link-btn link-danger" onClick={() => setConfirmDel(r)}>{opts.deleteLabel || 'Löschen'}</button>
          </td>
        )}
      </tr>
    );
  };

  const tableHead = (
    <thead>
      <tr>
        <th className="print-check" aria-hidden="true">✓</th>
        <th>#</th>
        <th>Spieler 1</th>
        <th>Verein</th>
        <th>LK</th>
        <th>Jg.</th>
        <th className="col-divider">Spieler 2</th>
        <th>Verein</th>
        <th>LK</th>
        <th>Jg.</th>
        {isAdmin && <th style={{width: 110, textAlign:'right'}}>Aktion</th>}
      </tr>
    </thead>
  );

  return (
    <div className="page">
      {/* Header-Zeile: Title-Block links, Print-Splitbutton rechts oben
          (wie im Tableau). Print-Button bleibt rechts oben auch im
          Page-Layout aussen am Header — nicht direkt neben dem CTA. */}
      <div className="meldeliste-header">
        <div className="meldeliste-title">
          <div className="page-eyebrow">Meldeliste</div>
          <h1 className="page-title">Angemeldete Paarungen</h1>
          {isAdmin && (
            <p className="page-sub">
              {main.length} von 32 Paarungen gemeldet
              {reserves.length > 0 && <> · {reserves.length} Nachrücker</>}
            </p>
          )}
        </div>
        {isAdmin && (
          /* Admin-Print-Splitbutton — A4 fuer Klemmbrett am Turniertisch,
             A3 zum Aushaengen. */
          <div className="btn-split no-print" role="group" aria-label="Meldeliste drucken">
            <button className="btn-split-half" onClick={() => window.printAs('a4')}
                    title="A4 Hochformat — Klemmbrett">
              <Icon name="print" size={14}/> A4
            </button>
            <button className="btn-split-half" onClick={() => window.printAs('a3')}
                    title="A3 Hochformat — zum Aushaengen">
              <Icon name="print" size={14}/> A3
            </button>
          </div>
        )}
      </div>
      <div style={{display:'flex', gap: 12, flexWrap:'wrap', alignItems:'center', marginTop: 18}}>
        {/* Admin: cremefarbener "Manuell anmelden"-CTA. Public: roter
            "Anmeldung zum Turnier"-CTA. */}
        {isAdmin ? (
          <button type="button" className="cta-banner cta-banner-light" onClick={() => setEditing('new')}>
            <Icon name="plus" size={18}/>
            <span className="cta-banner-title">Manuell anmelden (Admin)</span>
          </button>
        ) : (
          <button type="button" className="cta-banner" onClick={() => setShowAnmeldung(true)}>
            <Icon name="plus" size={18}/>
            <span className="cta-banner-title">Anmeldung zum Turnier</span>
          </button>
        )}
      </div>

      {isAdmin && pending.length > 0 && (
        <div className="card card-pad" style={{marginTop: 22, padding: 18, borderColor:'var(--gold)', borderWidth: 2, borderStyle:'solid', background:'rgba(190,160,90,0.08)'}}>
          <div style={{display:'flex', alignItems:'center', gap: 10, marginBottom: 14}}>
            <Icon name="lock" size={14}/>
            <h3 style={{margin: 0, fontFamily:'var(--font-display)', fontSize: 20}}>
              Wartet auf Freigabe ({pending.length})
            </h3>
          </div>
          <p className="page-sub" style={{marginTop: 0, marginBottom: 14, fontSize: 13}}>
            Diese Paarungen sind eingegangen, aber noch nicht öffentlich. Nach
            Eingang der 50 € Startgebühr hier freigeben — danach erscheinen
            sie in der Meldeliste und werden ggf. ins Bracket gelost.
          </p>
          <div style={{overflowX:'auto'}}>
            <table className="list-table">
              <thead>
                <tr>
                  <th>Paar 1</th>
                  <th>Verein</th>
                  <th>LK</th>
                  <th>Paar 2</th>
                  <th>Verein</th>
                  <th>LK</th>
                  <th>Eingang</th>
                  <th style={{width: 220, textAlign:'right'}}>Aktion</th>
                </tr>
              </thead>
              <tbody>
                {pending.map(r => (
                  <tr key={r.id}>
                    <td className="player">{r.p1.first} {r.p1.last}</td>
                    <td className="meta">{r.p1.club}</td>
                    <td className="meta mono">{r.p1.lk || '—'}</td>
                    <td className="player">{r.p2.first} {r.p2.last}</td>
                    <td className="meta">{r.p2.club}</td>
                    <td className="meta mono">{r.p2.lk || '—'}</td>
                    <td className="meta mono">{r.ts || '—'}</td>
                    <td className="meta" style={{textAlign:'right'}}>
                      <button className="btn btn-primary" style={{padding:'6px 12px', fontSize: 12}} onClick={() => onApproveReg(r.id)}>
                        <Icon name="check" size={12}/> Freigeben
                      </button>
                      <span style={{margin:'0 6px', color:'var(--text-4)'}}>·</span>
                      <button className="link-btn link-danger" onClick={() => setConfirmDel(r)}>Ablehnen</button>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      )}

      <div style={{marginTop: 26, overflowX:'auto'}}>
        <table className="list-table">
          {tableHead}
          <tbody>
            {sortedMain.map((r, i) => renderRow(r, i))}
            {sortedMain.length === 0 && (
              <tr><td colSpan={isAdmin ? 11 : 10} style={{textAlign:'center', padding:'40px 20px', color:'var(--text-3)'}}>Keine Anmeldungen.</td></tr>
            )}
          </tbody>
        </table>
      </div>

      {reserves.length > 0 && (
        <div style={{marginTop: 36}}>
          <h2 style={{fontFamily:'var(--font-display)', fontSize:24, margin:'0 0 6px'}}>Nachrücker</h2>
          <p className="page-sub" style={{marginTop:0, marginBottom:14}}>
            Sortiert nach Anmeldedatum. Wenn eine Hauptfeld-Paarung gelöscht wird, rückt der oberste Nachrücker automatisch nach.
          </p>
          <div style={{overflowX:'auto'}}>
            <table className="list-table">
              {tableHead}
              <tbody>
                {reserves.map((r, i) => renderRow(r, i, { deleteLabel: 'Entfernen' }))}
              </tbody>
            </table>
          </div>
        </div>
      )}

      {showAnmeldung && (
        <AnmeldungModal
          onSubmit={onSubmitReg}
          onClose={() => setShowAnmeldung(false)}
        />
      )}

      {editing && (
        <RegEditorModal
          reg={editing === 'new' ? null : editing}
          onClose={() => setEditing(null)}
          onSave={(reg) => {
            if (editing === 'new') onAddReg(reg); else onUpdateReg(reg);
            setEditing(null);
          }}
        />
      )}

      {confirmDel && (() => {
        const isMain = main.some(r => r.id === confirmDel.id);
        const promoter = isMain && reserves.length > 0 ? reserves[0] : null;
        return (
          <div className="modal-overlay">
            <div className="modal" onClick={e=>e.stopPropagation()} style={{maxWidth: 460}}>
              <button type="button" className="modal-close" onClick={() => setConfirmDel(null)} aria-label="Schließen"><Icon name="x" size={18}/></button>
              <h3>Anmeldung löschen?</h3>
              <div className="sub">
                {confirmDel.p1.first} {confirmDel.p1.last} / {confirmDel.p2.first} {confirmDel.p2.last}
                <br/>
                {promoter
                  ? <>Hauptfeld-Paarung — Nachrücker <b>{promoter.p1.last} / {promoter.p2.last}</b> rückt automatisch nach. Bereits gespielte Folge-Runden werden für dieses Match zurückgesetzt.</>
                  : isMain
                    ? <>Diese Paarung wird auch aus dem Tableau entfernt. Es gibt keinen Nachrücker — der Platz bleibt leer.</>
                    : <>Nachrücker — wird einfach aus der Liste entfernt, kein Einfluss auf das Tableau.</>}
              </div>
              <div style={{display:'flex', justifyContent:'space-between', marginTop: 12, gap: 10}}>
                <button className="btn btn-ghost" onClick={() => setConfirmDel(null)}>Abbrechen</button>
                <button className="btn btn-primary" style={{background:'var(--loss)', borderColor:'var(--loss)'}}
                        onClick={() => { onDeleteReg(confirmDel.id); setConfirmDel(null); }}>
                  <Icon name="check" size={14}/> Löschen
                </button>
              </div>
            </div>
          </div>
        );
      })()}
    </div>
  );
}

// Admin-Editor für Anmeldungen (anlegen + bearbeiten).
// Pflichtfelder bewusst minimal — Vorname/Nachname/Verein reichen, der Rest
// ist optional. Im öffentlichen Anmelde-Formular bleibt alles Pflicht.
// Setzliste wird nicht mehr im Editor gewählt — sie ergibt sich automatisch
// aus den LKs der Paarungen (siehe seedsFor in data.js).
function RegEditorModal({ reg, onClose, onSave }) {
  const blank = () => ({ first:'', last:'', birthdate:'', club:'', email:'', phone:'', lk:'ohne LK' });
  const initial = reg
    ? { p1: { ...blank(), ...reg.p1 }, p2: { ...blank(), ...reg.p2 } }
    : { p1: blank(), p2: blank() };
  const [f, setF] = uS(initial);
  const [err, setErr] = uS('');
  uE(() => { lockBodyScroll(); return unlockBodyScroll; }, []);

  const updP = (which, key) => (e) =>
    setF(s => ({ ...s, [which]: { ...s[which], [key]: e.target.value } }));

  const save = () => {
    // Pflicht: Vorname, Nachname, Geburtsdatum, Verein.
    // E-Mail nur fuer Spieler 1 Pflicht (eine Bestaetigung reicht pro
    // Paarung), Telefon und LK sind freiwillig — gleiche Regel wie im
    // oeffentlichen Anmelde-Formular.
    for (const w of ['p1','p2']) {
      const required = w === 'p1'
        ? ['first','last','birthdate','club','email']
        : ['first','last','birthdate','club'];
      for (const k of required) {
        if (!String(f[w][k] ?? '').trim()) {
          setErr('Bitte Pflichtfelder ausfüllen (Vor-/Nachname, Geburtsdatum, Verein, sowie E-Mail bei Spieler 1).');
          return;
        }
      }
      if (f[w].email && !/^\S+@\S+\.\S+$/.test(f[w].email)) {
        setErr('E-Mail-Adresse ungültig.');
        return;
      }
    }
    setErr('');
    const trim = (p) => ({
      first: p.first.trim(), last: p.last.trim(),
      birthdate: p.birthdate || '',
      club: p.club.trim(),
      email: (p.email || '').trim(),
      phone: (p.phone || '').trim(),
      lk: p.lk || 'ohne LK',
    });
    const out = {
      id: reg ? reg.id : 'r' + Date.now(),
      p1: trim(f.p1),
      p2: trim(f.p2),
      ts: reg ? reg.ts : todayDE(),
    };
    onSave(out);
  };

  const playerCard = (which, label) => {
    const p = f[which];
    return (
      <div className="card card-pad" style={{marginBottom: 14, padding: 14}}>
        <div className="label" style={{color:'var(--red)', marginBottom: 10}}>{label}</div>
        <div className="field-grid-2">
          <div className="field">
            <label className="field-label">Vorname *</label>
            <input className="input" value={p.first} onChange={updP(which,'first')}/>
          </div>
          <div className="field">
            <label className="field-label">Nachname *</label>
            <input className="input" value={p.last} onChange={updP(which,'last')}/>
          </div>
        </div>
        <div className="field-grid-2">
          <div className="field">
            <label className="field-label">Verein *</label>
            <input className="input" value={p.club} onChange={updP(which,'club')}/>
          </div>
          <div className="field">
            <label className="field-label">Leistungsklasse</label>
            <select className="select" value={p.lk} onChange={updP(which,'lk')}>
              {LK_OPTIONS.map(o => <option key={o} value={o}>{o}</option>)}
            </select>
          </div>
        </div>
        <div className="field-grid-2">
          <div className="field">
            <label className="field-label">Geburtsdatum *</label>
            <input className="input" type="date" value={p.birthdate} onChange={updP(which,'birthdate')}
                   min="1920-01-01" max="2015-12-31"/>
          </div>
          <div className="field">
            <label className="field-label">Telefon (freiwillig)</label>
            <input className="input" value={p.phone} onChange={updP(which,'phone')}/>
          </div>
        </div>
        <div className="field">
          <label className="field-label">{which === 'p1' ? 'E-Mail *' : 'E-Mail (freiwillig)'}</label>
          <input className="input" type="email" value={p.email} onChange={updP(which,'email')}/>
        </div>
      </div>
    );
  };

  return (
    <div className="modal-overlay">
      <div className="modal modal-wide" onClick={e=>e.stopPropagation()}>
        <button type="button" className="modal-close" onClick={onClose} aria-label="Schließen"><Icon name="x" size={18}/></button>
        <h3>{reg ? 'Anmeldung bearbeiten' : 'Manuell anmelden (Admin)'}</h3>
        <div className="sub">Pflichtfelder mit *. LK und Telefon sind freiwillig. Setzliste wird automatisch aus den LKs berechnet.</div>

        {playerCard('p1', 'Spieler 1')}
        {playerCard('p2', 'Spieler 2')}

        {err && <div style={{color:'var(--loss)', fontSize: 12, marginBottom: 10}}>{err}</div>}

        <div style={{display:'flex', justifyContent:'space-between', gap: 10}}>
          <button className="btn btn-ghost" onClick={onClose}>Abbrechen</button>
          <button className="btn btn-primary" onClick={save}>
            <Icon name="check" size={14}/> Speichern
          </button>
        </div>
      </div>
    </div>
  );
}

/* ──────────────── INFO ──────────────── */
function InfoPage() {
  // Leaflet-Karte mit eigenem rotem Pin. iframe-Embed von OSM zeigt einen
  // grünen Default-Marker, daher rendern wir die Karte selbst.
  React.useEffect(() => {
    if (typeof L === 'undefined') return;
    const lat = 48.67199, lon = 11.47598;
    const map = L.map('map', { scrollWheelZoom: false }).setView([lat, lon], 16);
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      attribution: '© OpenStreetMap',
      maxZoom: 19,
    }).addTo(map);
    const icon = L.divIcon({
      className: 'red-pin',
      html: '<svg viewBox="0 0 24 24" width="32" height="44" xmlns="http://www.w3.org/2000/svg"><path fill="#c8202b" stroke="#fff" stroke-width="1.5" d="M12 1.5a8 8 0 0 0-8 8c0 5.5 8 13 8 13s8-7.5 8-13a8 8 0 0 0-8-8Z"/><circle fill="#fff" cx="12" cy="9.5" r="3"/></svg>',
      iconSize: [32, 44],
      iconAnchor: [16, 44],
      popupAnchor: [0, -40],
    });
    L.marker([lat, lon], { icon })
      .addTo(map)
      .bindPopup('<b>Tennisanlage TSV Baar-Ebenhausen</b><br/>Am Sportplatz 1');
    return () => map.remove();
  }, []);

  return (
    <div className="page">
      <div className="page-eyebrow">Info & Anfahrt</div>
      <h1 className="page-title">Tennisanlage Baar-Ebenhausen</h1>
      <p className="page-sub" style={{maxWidth: 'none'}}>
        6 Sandplätze + 1 Hallenplatz (Rebound Ace) · Parkplatz direkt an der Anlage.
      </p>

      <div className="info-grid">
        <div>
          <div className="card card-pad" style={{marginBottom: 18}}>
            <div className="label" style={{color:'var(--red)', marginBottom: 14}}>Veranstaltungsort</div>
            <div style={{display:'flex', gap: 14, alignItems:'flex-start'}}>
              <Icon name="pin" size={20}/>
              <div>
                <div style={{fontFamily:'var(--font-display)', fontSize: 20, fontWeight: 600, marginBottom: 4}}>Tennisanlage TSV Baar-Ebenhausen</div>
                <div style={{color:'var(--text-2)', fontSize: 14, lineHeight: 1.55}}>
                  Am Sportplatz 1<br/>85107 Baar-Ebenhausen
                </div>
              </div>
            </div>
          </div>

          <div className="card card-pad">
            <div className="label" style={{color:'var(--red)', marginBottom: 14}}>Kontakt</div>
            <div style={{display:'flex', flexDirection:'column', gap: 12}}>
              <a href="mailto:info@paartal-open.de" style={{display:'flex', gap: 12, alignItems:'center', textDecoration:'none', color:'var(--text-1)'}}>
                <Icon name="mail" size={18}/>
                <span style={{fontSize: 14}}>info@paartal-open.de</span>
              </a>
            </div>
          </div>
        </div>

        <div className="map-wrap">
          <div id="map" style={{width:'100%', height:'100%', minHeight: 320}}></div>
        </div>
      </div>

      <div style={{textAlign:'right', marginTop: 8}}>
        <a href="https://www.openstreetmap.org/?mlat=48.67199&mlon=11.47598#map=17/48.67199/11.47598"
           target="_blank" rel="noopener noreferrer"
           style={{fontSize: 11, color:'var(--text-3)', textDecoration:'none'}}>
          Größere Karte anzeigen ↗
        </a>
      </div>

      <div className="tennis-photo-strip" aria-hidden="true">
        <div className="tennis-photo tp-1"></div>
        <div className="tennis-photo tp-2"></div>
        <div className="tennis-photo tp-3"></div>
        <div className="tennis-photo tp-4"></div>
      </div>
    </div>
  );
}

/* ──────────────── IMPRESSUM & DATENSCHUTZ (kombiniert) ──────────────── */
// Eine gemeinsame Rechtliches-Seite — Impressum oben, Datenschutz
// darunter. Daten aus dem TSV-Hauptauftritt (tsv-baar-ebenhausen.de)
// uebernommen: 1. Vorsitzender, Vorstands-Mail, Registernummer.
function LegalPage() {
  return (
    <div className="page legal-page">
      <div className="page-eyebrow">Rechtliches</div>
      <h1 className="page-title">Impressum & Datenschutz</h1>
      <p className="page-sub">
        Angaben gemäß § 5 TMG sowie Informationen zur Datenverarbeitung
        nach DSGVO.
      </p>

      <h2 className="legal-section-title">Impressum</h2>

      <h3>Veranstalter</h3>
      <p>
        TSV Baar-Ebenhausen e.V.<br/>
        Tennisabteilung<br/>
        Am Sportplatz 1<br/>
        85107 Baar-Ebenhausen
      </p>

      <h3>Vertretungsberechtigt</h3>
      <p>
        1. Vorsitzender: Karlheinz Binder<br/>
        E-Mail: <a href="mailto:vorstand@tsv-baar-ebenhausen.de">vorstand@tsv-baar-ebenhausen.de</a>
      </p>

      <h3>Kontakt Turnierleitung</h3>
      <p>
        E-Mail: <a href="mailto:info@paartal-open.de">info@paartal-open.de</a>
      </p>

      <h3>Registereintrag</h3>
      <p>
        Eintragung im Vereinsregister<br/>
        Registergericht: Amtsgericht Ingolstadt<br/>
        Registernummer: VR 20594
      </p>

      <h3>Verantwortlich für den Inhalt nach § 18 Abs. 2 MStV</h3>
      <p>
        Karlheinz Binder<br/>
        c/o TSV Baar-Ebenhausen e.V.<br/>
        Am Sportplatz 1, 85107 Baar-Ebenhausen
      </p>

      <h3>Haftungsausschluss</h3>
      <p>
        Die Inhalte dieser Seite werden mit größtmöglicher Sorgfalt erstellt.
        Für die Richtigkeit, Vollständigkeit und Aktualität der Inhalte können
        wir jedoch keine Gewähr übernehmen. Für Inhalte externer Links sind
        ausschließlich deren Betreiber verantwortlich.
      </p>

      <h3>Urheberrecht</h3>
      <p>
        Die durch den Veranstalter erstellten Inhalte und Werke unterliegen
        dem deutschen Urheberrecht. Vervielfältigung, Bearbeitung, Verbreitung
        und jede Art der Verwertung außerhalb der Grenzen des Urheberrechts
        bedürfen der schriftlichen Zustimmung.
      </p>

      <h2 className="legal-section-title">Datenschutzerklärung</h2>

      <h3>Verantwortlicher</h3>
      <p>
        TSV Baar-Ebenhausen e.V., Tennisabteilung<br/>
        Am Sportplatz 1, 85107 Baar-Ebenhausen<br/>
        E-Mail: <a href="mailto:info@paartal-open.de">info@paartal-open.de</a>
      </p>

      <h3>Welche Daten erheben wir?</h3>
      <p>Bei der Anmeldung zum 1. Paartal Open erheben wir pro Spieler:</p>
      <ul>
        <li>Vor- und Nachname</li>
        <li>Geburtsdatum</li>
        <li>Vereinszugehörigkeit</li>
        <li>Leistungsklasse (LK, optional)</li>
        <li>E-Mail-Adresse</li>
        <li>Telefonnummer (optional)</li>
      </ul>

      <h3>Wofür verwenden wir die Daten?</h3>
      <p>
        Ausschließlich zur Organisation und Durchführung des Turniers:
        Auslosung, Zeitplan-Kommunikation, Ergebnisverwaltung und
        Anmeldebestätigung. Eine Weitergabe an Dritte findet nicht statt.
      </p>

      <h3>Veröffentlichte Daten</h3>
      <p>
        Auf der Meldeliste und im Tableau werden Vor- und Nachname,
        Vereinszugehörigkeit, Geburtsjahr und Leistungsklasse beider
        Spieler einer Paarung öffentlich angezeigt. E-Mail-Adresse,
        Telefonnummer und vollständiges Geburtsdatum werden <b>nicht</b>{' '}
        öffentlich angezeigt — sie sind ausschließlich für die
        Turnierleitung einsehbar.
      </p>

      <h3>Speicherung &amp; Hosting</h3>
      <p>
        Anmeldungen und Tableau-Stand werden in einer
        Postgres-Datenbank des europäischen Cloud-Anbieters{' '}
        <b>Supabase</b> (Region <span className="mono">eu-central-1</span>,
        Frankfurt am Main) gespeichert. Die Website selbst wird über{' '}
        <b>Vercel</b> und parallel <b>Railway</b> ausgeliefert. Beide
        Anbieter erhalten technisch bedingt deine IP-Adresse als
        Bestandteil jeder HTTP-Anfrage; die Verarbeitung erfolgt
        ausschließlich zum Zweck der Auslieferung.
      </p>

      <h3>Cookies &amp; lokale Speicherung</h3>
      <p>
        Diese Website setzt keine eigenen Tracking-Cookies. Nach einem
        Admin-Login der Turnierleitung speichert Supabase einen
        Auth-Token im <span className="mono">localStorage</span> des
        Browsers, der beim Logout wieder entfernt wird. Für normale
        Besucher der Seite passiert das nicht.
      </p>

      <h3>Externe Dienste</h3>
      <ul>
        <li><b>Google Fonts</b> — Schriftarten werden zur Laufzeit von
          fonts.googleapis.com geladen. Dabei wird deine IP-Adresse an
          Google übertragen.</li>
        <li><b>OpenStreetMap</b> — Die Karte auf der Info & Anfahrt-Seite
          wird via Leaflet von openstreetmap.org eingebunden.</li>
        <li><b>unpkg.com</b> — JavaScript-Libraries (React, Babel,
          Supabase-Client, Leaflet) werden über das CDN ausgeliefert.</li>
        <li><b>Instagram</b> — Der Verweis auf das TSV-Tennis-Profil ist
          ein normaler Link; erst beim Klick verlässt du diese Seite.</li>
      </ul>

      <h3>Deine Rechte</h3>
      <p>
        Du hast jederzeit das Recht auf Auskunft, Berichtigung, Löschung
        und Einschränkung der Verarbeitung deiner Daten sowie auf
        Datenübertragbarkeit. Wende dich dafür an die oben genannte
        E-Mail-Adresse.
      </p>

      <h3>Beschwerderecht</h3>
      <p>
        Du kannst dich bei der zuständigen Datenschutz-Aufsichtsbehörde
        beschweren — in Bayern beim Bayerischen Landesamt für
        Datenschutzaufsicht (BayLDA), Promenade 18, 91522 Ansbach,
        E-Mail:{' '}
        <a href="mailto:poststelle@lda.bayern.de">poststelle@lda.bayern.de</a>.
      </p>
    </div>
  );
}

Object.assign(window, { HomePage, AnmeldungModal, MeldelistePage, InfoPage, LegalPage });
