// ===== Datentabellen, Story-Sections, Zeitstrahl-Anreicherungen =====
// Liest FLIGHTS_FULL aus data.jsx und leitet alles ab.

const { useState: _exU, useMemo: _exM, useEffect: _exE, useRef: _exR } = React;

// ---------- Aggregations ----------
const _byYM = (() => {
  const out = {};
  for (const f of FLIGHTS_FULL) {
    const ym = f.date.slice(0, 7);
    out[ym] = (out[ym] || 0) + 1;
  }
  return out;
})();

const _byCity = (() => {
  const out = {};
  for (const f of FLIGHTS_FULL) {
    for (const code of [f.von, f.nach]) {
      if (!out[code]) out[code] = { code, n: 0, first: f.date, last: f.date, dates: [] };
      out[code].n += 1;
      out[code].dates.push(f.date);
      if (f.date < out[code].first) out[code].first = f.date;
      if (f.date > out[code].last) out[code].last = f.date;
    }
  }
  return out;
})();

const _routeRanking = (() => {
  // ROUTES_FULL ist bereits sortiert nach n desc
  const reverseLookup = (a, b) => ROUTES_FULL.find((r) => r.from === b && r.to === a);
  return ROUTES_FULL.map((r) => ({ ...r, pair: reverseLookup(r.from, r.to)?.n || 0 }));
})();

const _flights2005 = FLIGHTS_FULL.filter((f) => f.date.startsWith("2005"));
const _new2005Codes = (() => {
  const seen = new Set();
  for (const f of FLIGHTS_FULL) {
    if (f.date < "2005") { seen.add(f.von); seen.add(f.nach); }
  }
  const newOnes = new Set();
  for (const f of _flights2005) {
    if (!seen.has(f.von)) newOnes.add(f.von);
    if (!seen.has(f.nach)) newOnes.add(f.nach);
  }
  return [...newOnes].filter((c) => c !== "MUC" && c !== "FRA");
})();

const _milestones = (() => {
  const out = [];
  // Round-number flights
  for (const i of [99, 499, 999]) {
    if (i < FLIGHTS_FULL.length) {
      const f = FLIGHTS_FULL[i];
      const meta = airportFull(f.nach);
      out.push({
        date: f.date, kind: "round",
        title: `${i + 1}. Flug`,
        text: `${f.von} → ${f.nach}${meta ? " · " + meta.stadt : ""}${f.fnr ? " · LH" + f.fnr : ""}`,
      });
    }
  }
  // Erster Flug nach Wehrdienst (>= 2003-10-01)
  const firstPostMil = FLIGHTS_FULL.find((f) => f.date >= "2003-10-01" && f.date < "2004");
  if (firstPostMil) {
    const meta = airportFull(firstPostMil.nach);
    out.push({
      date: firstPostMil.date, kind: "return",
      title: "Erster Flug nach dem Wehrdienst",
      text: `${firstPostMil.von} → ${firstPostMil.nach}${meta ? " · " + meta.stadt : ""}${firstPostMil.fnr ? " · LH" + firstPostMil.fnr : ""}`,
    });
  }
  // Erster Flug nach China (echtes "Asien-Jahr" begann 2005)
  const firstChina = FLIGHTS_FULL.find((f) => ["BJS", "SHA", "HKG", "CAN"].includes(f.nach));
  if (firstChina) {
    const meta = airportFull(firstChina.nach);
    out.push({
      date: firstChina.date, kind: "first",
      title: "Erster Flug nach China",
      text: `${firstChina.von} → ${firstChina.nach}${meta ? " · " + meta.stadt : ""}${firstChina.fnr ? " · LH" + firstChina.fnr : ""}`,
    });
  }
  // Erster Flug ueber 10.000 km (gibt es ueberhaupt?)
  const firstUltra = FLIGHTS_FULL.find((f) => f.km >= 10000);
  if (firstUltra) {
    const meta = airportFull(firstUltra.nach);
    out.push({
      date: firstUltra.date, kind: "first",
      title: "Erster Flug über 10.000 km",
      text: `${firstUltra.von} → ${firstUltra.nach}${meta ? " · " + meta.stadt : ""} · ${firstUltra.km.toLocaleString("de-DE")} km`,
    });
  }
  return out.sort((a, b) => a.date.localeCompare(b.date));
})();

// Hilfsfunktion: ISO-Datum -> "8. Januar 2005"
function _fmtDate(iso) {
  const [y, m, d] = iso.split("-");
  const months = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"];
  return `${parseInt(d, 10)}. ${months[parseInt(m, 10) - 1]} ${y}`;
}

// ============================================================
// A4 · 2005-SPOTLIGHT
// ============================================================
function Section2005Spotlight() {
  const [open, setOpen] = useState(false);
  const stats = useMemo(() => ({
    n: _flights2005.length,
    km: _flights2005.reduce((s, f) => s + f.km, 0),
    h: _flights2005.reduce((s, f) => s + f.h, 0),
    cities: new Set(_flights2005.flatMap((f) => [f.von, f.nach])).size,
    countries: new Set(_flights2005.flatMap((f) => {
      const a = airportFull(f.von), b = airportFull(f.nach);
      return [a?.land, b?.land];
    }).filter(Boolean)).size,
  }), []);

  return (
    <Section
      id="spotlight-2005"
      kicker="§ 04"
      eyebrow="Das wiedergefundene Jahr"
      dark
      title={<>Sieben Jahre stand <span className="text-amber italic font-light">hier nichts.</span></>}
      intro="2005 war im Original-Logbuch leer — keine Aufzeichnungen, kein Bordbuch. 2026 tauchten archivierte Spesenabrechnungen wieder auf und mit ihnen 94 Flüge, fünf brandneue Ziele und der Beginn meines ersten echten Asien-Jahres."
    >
      <Reveal>
        <div className="grid md:grid-cols-2 gap-8 items-start">
          {/* Story */}
          <div className="ticket-dark border border-amber/30 rounded-sm p-8">
            <div className="font-mono text-[10px] tracking-[0.25em] uppercase text-amber mb-3">Was das Jahr brachte</div>
            <p className="font-display text-2xl text-cream leading-relaxed">
              »Peking. Bangkok. Teheran. Tripolis. Washington. — Fünf Erstbesuche in einem Jahr.«
            </p>
            <p className="mt-4 text-cream/75 leading-relaxed">
              Bis 2004 war meine Welt Europa und Nordamerika. 2005 dreht sich das: Im Januar geht's nach Peking, im Februar nach Bangkok, im März nach Teheran, im Juli nach Tokio.
              Asien öffnet sich, fast wie ein zweiter Liniennetz-Plan, der sich über den ersten legt.
            </p>
            <p className="mt-4 text-cream/55 text-sm leading-relaxed border-t border-cream/15 pt-4">
              Quelle: archivierte Spesenabrechnungen aus 2005, im Mai 2026 nachgetragen.
              Flugnummern fehlen, alles andere stimmt minutenscharf.
            </p>
          </div>

          {/* Stats grid */}
          <div className="grid grid-cols-2 gap-3">
            <div className="ticket-dark border border-cream/15 rounded-sm p-5">
              <div className="font-display text-4xl text-amber leading-none">{stats.n}</div>
              <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55 mt-1">Flüge</div>
            </div>
            <div className="ticket-dark border border-cream/15 rounded-sm p-5">
              <div className="font-display text-4xl text-amber leading-none">{Math.round(stats.km / 1000)}<span className="text-xl text-cream/55">k</span></div>
              <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55 mt-1">Kilometer</div>
            </div>
            <div className="ticket-dark border border-cream/15 rounded-sm p-5">
              <div className="font-display text-4xl text-amber leading-none">{Math.round(stats.h)}</div>
              <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55 mt-1">Stunden in der Luft</div>
            </div>
            <div className="ticket-dark border border-cream/15 rounded-sm p-5">
              <div className="font-display text-4xl text-amber leading-none">{_new2005Codes.length}</div>
              <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55 mt-1">Neue Ziele</div>
            </div>
            <div className="ticket-dark border border-cream/15 rounded-sm p-5 col-span-2">
              <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-amber mb-2">Erstbesuche 2005</div>
              <div className="flex flex-wrap gap-2">
                {_new2005Codes.map((code) => {
                  const a = airportFull(code);
                  return (
                    <span key={code} className="font-mono text-[11px] tracking-[0.1em] uppercase bg-amber/15 text-amber border border-amber/40 rounded-sm px-2 py-1">
                      {code} · {a?.stadt || code}
                    </span>
                  );
                })}
              </div>
            </div>
          </div>
        </div>

        {/* Expand to all 94 flights */}
        <div className="mt-6">
          <button
            onClick={() => setOpen(!open)}
            className="font-mono text-[11px] tracking-[0.25em] uppercase text-amber border border-amber/40 rounded-sm px-4 py-2 hover:bg-amber hover:text-night transition-colors"
          >
            {open ? "Liste einklappen" : `▼ alle ${stats.n} Flüge 2005 ansehen`}
          </button>
          {open && (
            <div className="mt-4 ticket-dark border border-cream/15 rounded-sm p-5 max-h-96 overflow-y-auto">
              <table className="w-full text-sm">
                <thead>
                  <tr className="font-mono text-[10px] tracking-[0.18em] uppercase text-cream/55 border-b border-cream/15">
                    <th className="text-left pb-2 pr-3">Datum</th>
                    <th className="text-left pb-2 pr-3">Strecke</th>
                    <th className="text-right pb-2 pr-3">km</th>
                    <th className="text-right pb-2">Block</th>
                  </tr>
                </thead>
                <tbody className="font-mono text-[12px] text-cream/85">
                  {_flights2005.map((f, i) => (
                    <tr key={i} className="border-b border-cream/5">
                      <td className="py-1 pr-3">{f.date}</td>
                      <td className="py-1 pr-3">{f.von} → {f.nach} <span className="text-cream/45">· {airportFull(f.nach)?.stadt}</span></td>
                      <td className="py-1 pr-3 text-right text-cream/70">{f.km.toLocaleString("de-DE")}</td>
                      <td className="py-1 text-right text-cream/70">{f.h.toFixed(2)}h</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}
        </div>
      </Reveal>
    </Section>
  );
}

// ============================================================
// A3 · STRECKEN-RANKING (Top-Tabelle, sortierbar)
// ============================================================
function SectionRouteRanking() {
  const [sortBy, setSortBy] = useState("n");
  const [sortDir, setSortDir] = useState("desc");
  const [filter, setFilter] = useState("all");
  const [showAll, setShowAll] = useState(false);

  const filtered = useMemo(() => {
    let rows = _routeRanking;
    if (filter === "muc") rows = rows.filter((r) => r.from === "MUC");
    else if (filter === "fra") rows = rows.filter((r) => r.from === "FRA");
    else if (filter === "other") rows = rows.filter((r) => r.from !== "MUC" && r.from !== "FRA");
    else if (filter === "longhaul") rows = rows.filter((r) => r.distance >= 3500);
    else if (filter === "shorthaul") rows = rows.filter((r) => r.distance < 3500);
    const sorted = [...rows].sort((a, b) => {
      const av = a[sortBy], bv = b[sortBy];
      const cmp = typeof av === "number" ? av - bv : String(av).localeCompare(String(bv));
      return sortDir === "asc" ? cmp : -cmp;
    });
    return sorted;
  }, [sortBy, sortDir, filter]);

  const toggleSort = (col) => {
    if (sortBy === col) setSortDir(sortDir === "asc" ? "desc" : "asc");
    else { setSortBy(col); setSortDir("desc"); }
  };

  const displayed = showAll ? filtered : filtered.slice(0, 25);
  const maxN = filtered[0]?.n || 1;

  const Hdr = ({ col, children, right }) => (
    <th
      onClick={() => toggleSort(col)}
      className={`pb-2 ${right ? "text-right" : "text-left"} pr-3 cursor-pointer hover:text-amber transition-colors`}
    >
      {children}
      {sortBy === col && <span className="ml-1 text-amber">{sortDir === "asc" ? "▲" : "▼"}</span>}
    </th>
  );

  return (
    <Reveal delay={1} className="mt-16">
      <div className="flex items-baseline gap-4 mb-4">
        <span className="font-mono text-[11px] tracking-[0.25em] uppercase text-amber">§ 03c</span>
        <span className="font-mono text-[11px] tracking-[0.25em] uppercase text-cream/40">Strecken-Ranking</span>
      </div>
      <h3 className="font-display text-3xl md:text-5xl leading-[1.05] text-cream max-w-3xl">
        231 Strecken,<br/>
        <span className="text-amber italic font-light">sortiert wie es passt.</span>
      </h3>
      <p className="mt-4 text-cream/70 max-w-2xl leading-relaxed">
        Jede gemeinsame Hin-Richtung als eigene Zeile. Sortierbar nach Anzahl Flügen, Distanz oder Ära.
        Spalte „Heimkehr-Paar" zeigt den korrespondierenden Rückflug, soweit existent.
      </p>

      <div className="mt-6 flex flex-wrap gap-2">
        {[
          { k: "all", l: "Alle 231" },
          { k: "muc", l: "Nur MUC-Start" },
          { k: "fra", l: "Nur FRA-Start" },
          { k: "other", l: "Aus dem Ausland" },
          { k: "longhaul", l: "Langstrecke ≥ 3500 km" },
          { k: "shorthaul", l: "Kurz/Mittel < 3500 km" },
        ].map((c) => (
          <button
            key={c.k}
            onClick={() => setFilter(c.k)}
            className={`px-3 py-1.5 rounded-sm border font-mono text-[11px] tracking-[0.12em] uppercase transition-colors ${
              filter === c.k ? "bg-amber text-night border-amber" : "bg-night/30 text-cream/70 border-cream/15 hover:border-amber/60 hover:text-amber"
            }`}
          >{c.l}</button>
        ))}
      </div>

      <div className="mt-6 ticket-dark border border-cream/15 rounded-sm overflow-x-auto">
        <table className="w-full text-sm min-w-[700px]">
          <thead>
            <tr className="font-mono text-[10px] tracking-[0.18em] uppercase text-cream/55 border-b border-cream/15">
              <th className="text-right pb-2 pl-4 pr-3 w-12">#</th>
              <Hdr col="from">Strecke</Hdr>
              <Hdr col="n" right>Flüge</Hdr>
              <Hdr col="distance" right>km</Hdr>
              <Hdr col="fleet">Flotte</Hdr>
              <Hdr col="from_y">Ära</Hdr>
              <Hdr col="pair" right>↔ Rückflug</Hdr>
              <th className="pb-2 pr-4 w-32 text-left">Anteil</th>
            </tr>
          </thead>
          <tbody className="font-mono text-[12px] text-cream/85">
            {displayed.map((r, i) => {
              const a = airportFull(r.from), b = airportFull(r.to);
              return (
                <tr key={r.from + "-" + r.to} className="border-b border-cream/5 hover:bg-amber/5">
                  <td className="py-2 pl-4 pr-3 text-right text-cream/40">{i + 1}</td>
                  <td className="py-2 pr-3">
                    <span className="text-amber">{r.from}</span>
                    <span className="text-cream/45 mx-1">→</span>
                    <span className="text-amber">{r.to}</span>
                    <span className="text-cream/50 ml-2 text-[11px]">{b?.stadt}</span>
                  </td>
                  <td className="py-2 pr-3 text-right text-cream font-semibold">{r.n}</td>
                  <td className="py-2 pr-3 text-right text-cream/70">{r.distance.toLocaleString("de-DE")}</td>
                  <td className="py-2 pr-3 text-cream/70">{r.fleet}</td>
                  <td className="py-2 pr-3 text-cream/70">{r.from_y === r.to_y ? r.from_y : `${String(r.from_y).slice(-2)}-${String(r.to_y).slice(-2)}`}</td>
                  <td className="py-2 pr-3 text-right text-cream/55">{r.pair ? r.pair + "×" : "—"}</td>
                  <td className="py-2 pr-4">
                    <div className="h-1.5 bg-cream/10 rounded-sm overflow-hidden">
                      <div className="h-full bg-amber" style={{ width: `${(r.n / maxN) * 100}%` }} />
                    </div>
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>

      {filtered.length > 25 && (
        <button
          onClick={() => setShowAll(!showAll)}
          className="mt-4 font-mono text-[11px] tracking-[0.25em] uppercase text-amber border border-amber/40 rounded-sm px-4 py-2 hover:bg-amber hover:text-night transition-colors"
        >
          {showAll ? "Top 25 anzeigen" : `▼ alle ${filtered.length} Strecken zeigen`}
        </button>
      )}
    </Reveal>
  );
}

// ============================================================
// C2 · AUTO-MEILENSTEINE (rendered inline mit Zeitstrahl)
// Exportiert: getMergedTimeline() — mischt ZEITSTRAHL + _milestones
// ============================================================
function getMergedTimeline() {
  const isoFromGerman = (d) => {
    // "25. April 2001" -> "2001-04-25"
    const months = { Januar: "01", Februar: "02", März: "03", April: "04", Mai: "05", Juni: "06", Juli: "07", August: "08", September: "09", Oktober: "10", November: "11", Dezember: "12" };
    const m = d.match(/^(\d+)\.\s+(\w+)\s+(\d{4})/);
    if (m) return `${m[3]}-${months[m[2]] || "01"}-${String(parseInt(m[1])).padStart(2, "0")}`;
    const m2 = d.match(/^(\d{4})/);
    if (m2) return `${m2[1]}-01-01`;
    return "9999-99-99";
  };
  const main = ZEITSTRAHL.map((t) => ({ ...t, _iso: isoFromGerman(t.datum), _major: true }));
  const minors = _milestones.map((m) => ({
    datum: _fmtDate(m.date),
    jahr: parseInt(m.date.slice(0, 4)),
    titel: m.title,
    text: m.text,
    typ: "auto",
    _iso: m.date,
    _major: false,
  }));
  return [...main, ...minors].sort((a, b) => a._iso.localeCompare(b._iso));
}

// ============================================================
// A1 · COUNTRY-BREAKDOWN (3-level Accordion)
// ============================================================
function SectionCountryBreakdown() {
  const [openCont, setOpenCont] = useState(new Set(["EU"]));
  const [openCountry, setOpenCountry] = useState(new Set());
  const [search, setSearch] = useState("");
  const [regionFilter, setRegionFilter] = useState("all");

  const tree = useMemo(() => {
    // group AIRPORTS_FULL by region -> land, attach flight counts (touches = von+nach)
    const touchCount = {};
    for (const f of FLIGHTS_FULL) {
      touchCount[f.von] = (touchCount[f.von] || 0) + 1;
      touchCount[f.nach] = (touchCount[f.nach] || 0) + 1;
    }
    const grouped = {};
    for (const a of AIRPORTS_FULL) {
      if (!grouped[a.region]) grouped[a.region] = {};
      if (!grouped[a.region][a.land]) grouped[a.region][a.land] = [];
      grouped[a.region][a.land].push({ ...a, n: touchCount[a.code] || 0, isHub: a.code === "MUC" || a.code === "FRA" });
    }
    // sort and compute sums
    const out = [];
    for (const region of ["EU", "NA", "AS", "AF", "SA"]) {
      if (!grouped[region]) continue;
      const countries = Object.entries(grouped[region]).map(([land, cities]) => {
        cities.sort((a, b) => b.n - a.n);
        return { land, cities, n: cities.reduce((s, c) => s + c.n, 0) };
      });
      countries.sort((a, b) => b.n - a.n);
      out.push({
        region,
        countries,
        n: countries.reduce((s, c) => s + c.n, 0),
        nCountries: countries.length,
        nCities: countries.reduce((s, c) => s + c.cities.length, 0),
      });
    }
    return out;
  }, []);

  const maxCityN = useMemo(() => Math.max(...tree.flatMap((r) => r.countries.flatMap((c) => c.cities.map((x) => x.n)))), [tree]);

  const matchesSearch = (text) => !search || text.toLowerCase().includes(search.toLowerCase());

  const toggleCont = (r) => {
    const s = new Set(openCont);
    s.has(r) ? s.delete(r) : s.add(r);
    setOpenCont(s);
  };
  const toggleCountry = (k) => {
    const s = new Set(openCountry);
    s.has(k) ? s.delete(k) : s.add(k);
    setOpenCountry(s);
  };

  return (
    <Section
      id="laender-detail"
      kicker="§ 03d"
      eyebrow="Geografie im Detail"
      dark
      title={<>47 Länder, <span className="text-amber italic font-light">86 Flughäfen.</span></>}
      intro="Drei Ebenen, eine Wahrheit: Kontinent, Land, Stadt — sortiert nach Häufigkeit. Klick zum Aufklappen, Suche nach Stadtname oder Code."
    >
      <Reveal>
        {/* Filter bar */}
        <div className="flex flex-wrap gap-3 items-center mb-6">
          <input
            type="text"
            placeholder="Stadt oder Code suchen…"
            value={search}
            onChange={(e) => setSearch(e.target.value)}
            className="bg-night/40 border border-cream/15 text-cream rounded-sm px-3 py-2 font-mono text-[12px] placeholder-cream/40 focus:border-amber focus:outline-none w-64"
          />
          <div className="flex flex-wrap gap-1.5">
            {[
              { k: "all", l: "Alle" },
              { k: "EU", l: "Europa", c: REGION_META.EU.color },
              { k: "NA", l: "Nordamerika", c: REGION_META.NA.color },
              { k: "AS", l: "Asien", c: REGION_META.AS.color },
              { k: "AF", l: "Afrika", c: REGION_META.AF.color },
              { k: "SA", l: "Südamerika", c: REGION_META.SA.color },
            ].map((c) => (
              <button
                key={c.k}
                onClick={() => setRegionFilter(c.k)}
                className={`px-2.5 py-1.5 rounded-sm border font-mono text-[10px] tracking-[0.15em] uppercase transition-colors flex items-center gap-1.5 ${
                  regionFilter === c.k ? "bg-amber text-night border-amber" : "bg-night/30 text-cream/70 border-cream/15 hover:border-amber/60"
                }`}
              >
                {c.c && <span className="w-2 h-2 rounded-full" style={{ background: c.c }} />}
                {c.l}
              </button>
            ))}
          </div>
        </div>

        {/* Tree */}
        <div className="space-y-3">
          {tree.filter((r) => regionFilter === "all" || regionFilter === r.region).map((r) => (
            <div key={r.region} className="ticket-dark border border-cream/15 rounded-sm overflow-hidden">
              <button
                onClick={() => toggleCont(r.region)}
                className="w-full flex items-center justify-between px-5 py-4 text-left hover:bg-amber/5 transition-colors"
              >
                <div className="flex items-center gap-3">
                  <span className="font-mono text-amber w-4">{openCont.has(r.region) ? "▼" : "▶"}</span>
                  <span className="inline-block w-3 h-3 rounded-full" style={{ background: REGION_META[r.region]?.color }} />
                  <span className="font-display text-2xl text-cream">{REGION_META[r.region]?.label || r.region}</span>
                  <span className="font-mono text-[11px] tracking-[0.2em] uppercase text-cream/55">
                    {r.nCountries} Länder · {r.nCities} Städte
                  </span>
                </div>
                <span className="font-display text-2xl text-amber">{r.n.toLocaleString("de-DE")}</span>
              </button>
              {openCont.has(r.region) && (
                <div className="border-t border-cream/10">
                  {r.countries.map((c) => {
                    const ckey = `${r.region}/${c.land}`;
                    const filteredCities = search ? c.cities.filter((x) => matchesSearch(x.stadt) || matchesSearch(x.code)) : c.cities;
                    if (search && filteredCities.length === 0) return null;
                    return (
                      <div key={ckey} className="border-b border-cream/5 last:border-b-0">
                        <button
                          onClick={() => toggleCountry(ckey)}
                          className="w-full flex items-center justify-between px-5 py-2.5 text-left hover:bg-amber/5 transition-colors pl-12"
                        >
                          <div className="flex items-center gap-3">
                            <span className="font-mono text-amber/70 text-sm w-3">{openCountry.has(ckey) ? "▾" : "▸"}</span>
                            <span className="font-display text-base text-cream/90">{c.land}</span>
                            <span className="font-mono text-[10px] tracking-[0.18em] uppercase text-cream/45">{c.cities.length} {c.cities.length === 1 ? "Stadt" : "Städte"}</span>
                          </div>
                          <span className="font-mono text-base text-amber/85">{c.n}</span>
                        </button>
                        {openCountry.has(ckey) && (
                          <div className="pl-20 pr-5 pb-3 space-y-1">
                            {filteredCities.map((x) => (
                              <div key={x.code} className="flex items-center gap-3 py-1 text-sm">
                                <span className="font-mono text-cream/85 w-12">{x.code}</span>
                                <span className="text-cream/80 flex-1 truncate">
                                  {x.stadt}
                                  {x.isHub && <span className="ml-2 font-mono text-[9px] tracking-[0.2em] uppercase text-amber">Hub</span>}
                                </span>
                                <div className="flex-1 max-w-[180px] h-1 bg-cream/10 rounded-sm overflow-hidden">
                                  <div className="h-full bg-amber/70" style={{ width: `${(x.n / maxCityN) * 100}%` }} />
                                </div>
                                <span className="font-mono text-cream/85 w-10 text-right">{x.n}</span>
                              </div>
                            ))}
                          </div>
                        )}
                      </div>
                    );
                  })}
                </div>
              )}
            </div>
          ))}
        </div>

        <div className="mt-4 font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">
          Σ {tree.reduce((s, r) => s + r.n, 0).toLocaleString("de-DE")} touches = 2 × {FLIGHTS_FULL.length} Flüge
        </div>
      </Reveal>
    </Section>
  );
}

// ============================================================
// A2 · ERSTE/LETZTE BESUCHE (Top-25 mit Mini-Timeline)
// ============================================================
function SectionFirstLastVisits() {
  const [showRareOnly, setShowRareOnly] = useState(false);

  const cards = useMemo(() => {
    const all = Object.values(_byCity).filter((c) => c.code !== "MUC" && c.code !== "FRA"); // hubs separat
    if (showRareOnly) {
      // Single-visit cities
      return all.filter((c) => c.n === 1).sort((a, b) => a.first.localeCompare(b.first));
    }
    return all.sort((a, b) => b.n - a.n).slice(0, 25);
  }, [showRareOnly]);

  const visitsBar = (dates) => {
    // x-axis 2001-01 -> 2011-12 = 132 months
    const startMs = new Date("2001-01-01").getTime();
    const endMs = new Date("2011-12-31").getTime();
    return dates.map((d) => {
      const t = new Date(d).getTime();
      return ((t - startMs) / (endMs - startMs)) * 100;
    });
  };

  return (
    <Section
      id="erste-letzte-besuche"
      kicker="§ 03e"
      eyebrow="Pro Stadt"
      dark
      title={<>Erster Anflug, <span className="text-amber italic font-light">letzter Anflug.</span></>}
      intro="Top-25 Ziele mit Erinnerungs-Zeitachse von 2001 bis 2011. Jeder Punkt ein Besuch. Toggle für die seltenen Ziele, die nur einmal besucht wurden."
    >
      <Reveal>
        <div className="mb-6 flex flex-wrap gap-2">
          <button
            onClick={() => setShowRareOnly(false)}
            className={`px-3 py-1.5 rounded-sm border font-mono text-[11px] tracking-[0.15em] uppercase transition-colors ${
              !showRareOnly ? "bg-amber text-night border-amber" : "bg-night/30 text-cream/70 border-cream/15 hover:border-amber/60"
            }`}
          >Top-25 nach Häufigkeit</button>
          <button
            onClick={() => setShowRareOnly(true)}
            className={`px-3 py-1.5 rounded-sm border font-mono text-[11px] tracking-[0.15em] uppercase transition-colors ${
              showRareOnly ? "bg-amber text-night border-amber" : "bg-night/30 text-cream/70 border-cream/15 hover:border-amber/60"
            }`}
          >Nur 1× besucht</button>
        </div>

        <div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
          {cards.map((c) => {
            const meta = airportFull(c.code);
            const positions = visitsBar(c.dates);
            return (
              <div key={c.code} className="ticket-dark border border-cream/15 rounded-sm p-4">
                <div className="flex items-baseline justify-between mb-1">
                  <div className="font-display text-xl text-cream">{meta?.stadt || c.code}</div>
                  <div className="font-mono text-sm text-amber">{c.code} · {c.n}×</div>
                </div>
                <div className="font-mono text-[10px] tracking-[0.18em] uppercase text-cream/45 mb-3">{meta?.land}</div>

                {/* Mini timeline */}
                <div className="relative h-6 bg-cream/5 rounded-sm overflow-hidden">
                  {positions.map((p, i) => (
                    <div
                      key={i}
                      className="absolute top-1/2 -translate-y-1/2 w-1 h-3 bg-amber rounded-full"
                      style={{ left: `${p}%` }}
                      title={c.dates[i]}
                    />
                  ))}
                </div>
                <div className="flex justify-between font-mono text-[9px] tracking-[0.15em] uppercase text-cream/45 mt-1">
                  <span>{c.first.slice(0, 7)}</span>
                  <span className="text-cream/30">→</span>
                  <span>{c.last.slice(0, 7)}</span>
                </div>

                {/* Date range narrative */}
                {c.n > 1 && (
                  <div className="mt-2 font-mono text-[10px] text-cream/55">
                    {_fmtDate(c.first)} — {_fmtDate(c.last)}
                  </div>
                )}
                {c.n === 1 && (
                  <div className="mt-2 font-mono text-[10px] italic text-cream/55">
                    Genau einmal: {_fmtDate(c.first)}
                  </div>
                )}
              </div>
            );
          })}
        </div>

        {showRareOnly && (
          <div className="mt-4 font-mono text-[10px] tracking-[0.18em] uppercase text-cream/55">
            {cards.length} Städte nur ein einziges Mal angeflogen — die seltenen Schätze.
          </div>
        )}
      </Reveal>
    </Section>
  );
}

// ============================================================
// B2 · SAISONALITÄT-HEATMAP (Jahr × Monat)
// ============================================================
// Markierte Zeitraeume — werden als spannender bar mit eigener farbe + label gerendert,
// statt einzelner leerer zellen
// Navy = unvermeidbare pause (lehrgang, wehrdienst, praktikum)
// Tuerkis (vollbreite) = vollzeit-weiterbildung (sprachreisen mexiko)
// Tuerkis (25% bar) = berufsbegleitendes studium (parallel zum fliegen)
const MARKED_PERIODS = [
  { year: "2001", fromMonth: 4, toMonth: 6, label: "FB-Lehrgang",      bg: "#1a3aa0", text: "#fbf6ea" },
  { year: "2003", fromMonth: 1, toMonth: 9, label: "Grundwehrdienst",  bg: "#1a3aa0", text: "#fbf6ea" },
  { year: "2007", fromMonth: 3, toMonth: 4, label: "Spanisch · Cancún", bg: "#4ec5b8", text: "#05164d" },
  { year: "2008", fromMonth: 1, toMonth: 2, label: "Spanisch · Playa", bg: "#4ec5b8", text: "#05164d" },
  { year: "2010", fromMonth: 3, toMonth: 8, label: "Praktikum FRA/JO", bg: "#1a3aa0", text: "#fbf6ea" },
  { year: "2012", fromMonth: 3, toMonth: 12, label: "Lufthansa Konzern · FRA · Referent Beratung/Strategie", bg: "#c98a00", text: "#fbf6ea" },
];

// Berufsbegleitende studien — als 25%-bar am unteren rand der zellen, ohne
// die flight-counts zu verdecken
const STUDY_OVERLAYS = [
  { fromYM: "2004-03", toYM: "2007-03", label: "Telekolleg · Fachhochschulreife (Wirtschaft)", color: "#4ec5b8" },
  { fromYM: "2008-09", toYM: "2011-08", label: "Studium IUBH · Luftverkehrsmanagement", color: "#4ec5b8" },
  { fromYM: "2011-09", toYM: "2012-12", label: "Executive MBA · Ashridge (LH-gefördert)", color: "#4ec5b8" },
];

function _studyForYM(ym) {
  return STUDY_OVERLAYS.find((p) => ym >= p.fromYM && ym <= p.toYM);
}

function SectionMonthHeatmap() {
  const years = useMemo(() => [...new Set(FLIGHTS_FULL.map((f) => f.date.slice(0, 4)))].sort(), []);
  const monthNames = ["Jan", "Feb", "Mrz", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"];

  const matrix = useMemo(() => {
    const m = {};
    for (const f of FLIGHTS_FULL) {
      const key = f.date.slice(0, 7); // YYYY-MM
      m[key] = (m[key] || 0) + 1;
    }
    return m;
  }, []);

  const maxN = Math.max(...Object.values(matrix));
  const [hovered, setHovered] = useState(null);

  const yearTotals = years.map((y) => ({ y, n: FLIGHTS_FULL.filter((f) => f.date.startsWith(y)).length }));
  const monthTotals = monthNames.map((_, mi) => FLIGHTS_FULL.filter((f) => parseInt(f.date.slice(5, 7)) === mi + 1).length);

  const colorFor = (n) => {
    if (!n) return "rgba(246,240,226,0.04)";
    const t = Math.sqrt(n / maxN);
    return `rgba(249,176,0,${0.15 + t * 0.85})`;
  };

  return (
    <Section
      id="heatmap"
      kicker="§ 05b"
      eyebrow="Saisonalität"
      title={<>132 Zellen, <span className="text-amber italic font-light">jede ein Monat.</span></>}
      intro="Wann war ich in der Luft, wann am Boden? Die Lücken erzählen oft mehr als die vollen Felder. Navy-Balken sind unvermeidbare Pausen (Lehrgang, Wehrdienst, Praktikum), volle Türkis-Balken die Sprachreisen nach Mexiko, der Amber-Balken ab März 2012 markiert den Wechsel aus der Kabine auf den Konzern-Stab. Der dünne Türkis-Streifen am unteren Zellenrand zeigt: berufsbegleitendes Studium läuft parallel."
    >
      <Reveal>
        <div className="overflow-x-auto">
          <div className="inline-block min-w-full">
            {/* Header row: month names */}
            <div className="grid grid-cols-[60px_repeat(12,1fr)_60px] gap-1 mb-1">
              <div></div>
              {monthNames.map((m, mi) => (
                <div key={mi} className="text-center font-mono text-[10px] tracking-[0.15em] uppercase text-night/55">{m}</div>
              ))}
              <div className="text-right font-mono text-[10px] tracking-[0.18em] uppercase text-amber-deep">Σ</div>
            </div>

            {/* Year rows */}
            {years.map((y, yi) => {
              const period = MARKED_PERIODS.find((p) => p.year === y);
              const cells = [];
              for (let mi = 0; mi < 12; mi++) {
                const m = mi + 1;
                if (period && m >= period.fromMonth && m <= period.toMonth) {
                  // Nur einmal pro periode rendern (am ersten monat)
                  if (m === period.fromMonth) {
                    const span = period.toMonth - period.fromMonth + 1;
                    // Pruefe ob STUDY_OVERLAYS in diese periode reinragt
                    // (z.B. IUBH-overlap mit 2010-Praktikum, MBA-overlap mit 2012-LH-Konzern)
                    const firstYM = `${y}-${String(period.fromMonth).padStart(2, "0")}`;
                    const studyOverlay = _studyForYM(firstYM);
                    cells.push(
                      <div
                        key={`period-${y}`}
                        className="rounded-sm flex flex-col overflow-hidden"
                        style={{
                          gridColumn: `span ${span}`,
                        }}
                        title={`${period.label} · ${monthNames[period.fromMonth - 1]} – ${monthNames[period.toMonth - 1]} ${y}${studyOverlay ? " · + " + studyOverlay.label : ""}`}
                      >
                        <div
                          className="flex-1 flex items-center justify-center px-3"
                          style={{ background: period.bg, color: period.text }}
                        >
                          <span className="font-mono text-[10px] md:text-[11px] tracking-[0.25em] uppercase whitespace-nowrap">
                            {period.label}
                          </span>
                        </div>
                        {studyOverlay && (
                          <div style={{ height: "25%", background: studyOverlay.color }} />
                        )}
                      </div>
                    );
                  }
                  continue;
                }
                const key = `${y}-${String(m).padStart(2, "0")}`;
                const n = matrix[key] || 0;
                const study = _studyForYM(key);
                cells.push(
                  <div
                    key={mi}
                    onMouseEnter={() => setHovered({ y, m, n, study })}
                    onMouseLeave={() => setHovered(null)}
                    className="aspect-square rounded-sm flex flex-col overflow-hidden cursor-default transition-transform hover:scale-110"
                    style={{ background: colorFor(n) }}
                    title={`${monthNames[mi]} ${y}: ${n} Flüge${study ? " · " + study.label : ""}`}
                  >
                    <div className="flex-1 flex items-center justify-center">
                      {n > 0 && <span className="font-mono text-[10px] text-night/85 font-medium">{n}</span>}
                    </div>
                    {study && (
                      <div style={{ height: "25%", background: study.color }} />
                    )}
                  </div>
                );
              }
              return (
                <div key={y} className="grid grid-cols-[60px_repeat(12,1fr)_60px] gap-1 mb-1">
                  <div className="font-mono text-sm text-night flex items-center">{y}</div>
                  {cells}
                  <div className="text-right font-mono text-sm text-night flex items-center justify-end font-semibold">{yearTotals[yi].n}</div>
                </div>
              );
            })}

            {/* Footer row: month totals */}
            <div className="grid grid-cols-[60px_repeat(12,1fr)_60px] gap-1 mt-2 pt-2 border-t border-night/15">
              <div className="font-mono text-[10px] tracking-[0.18em] uppercase text-amber-deep flex items-center">Σ</div>
              {monthTotals.map((n, mi) => (
                <div key={mi} className="text-center font-mono text-sm text-night font-semibold">{n}</div>
              ))}
              <div className="text-right font-display text-base text-night flex items-center justify-end font-bold">{FLIGHTS_FULL.length}</div>
            </div>
          </div>
        </div>

        {/* Hover detail or anomaly legend */}
        <div className="mt-6 grid md:grid-cols-2 gap-6">
          <div>
            <div className="font-mono text-[10px] tracking-[0.25em] uppercase text-amber-deep mb-2">Ausgewählte Zelle</div>
            {hovered ? (
              <div>
                <div className="font-display text-xl text-night">
                  {monthNames[hovered.m - 1]} {hovered.y}: <span className="text-amber-deep">{hovered.n} {hovered.n === 1 ? "Flug" : "Flüge"}</span>
                </div>
                {hovered.study && (
                  <div className="mt-1 font-mono text-[11px] tracking-[0.15em] uppercase" style={{ color: "#0a857a" }}>
                    + {hovered.study.label}
                  </div>
                )}
              </div>
            ) : (
              <div className="text-night/55 italic">Mit Maus über eine Zelle fahren.</div>
            )}
          </div>
          <div>
            <div className="font-mono text-[10px] tracking-[0.25em] uppercase text-amber-deep mb-2">Neben dem Fliegen</div>
            <ul className="font-mono text-[12px] text-night/75 space-y-1 leading-relaxed">
              <li>• <span className="text-night">2001 Jan–März</span> · vor der Einstellung am 25. April</li>
              <li>• <span className="text-night">2001 Apr–Jun</span> · Flugbegleiter-Lehrgang in Frankfurt</li>
              <li>• <span className="text-night">2003 Jan–Sep</span> · Grundwehrdienst <span className="text-night/50">· 4./Gebirgsjäger Btl. 232 · Jägerkaserne Strub · Vereidigung 13.3.2003 · ab April Netzwerk-Admin</span></li>
              <li>• <span style={{color:"#0a857a"}}>2004 März – 2007 März</span> · Telekolleg <span className="text-night/50">· Fachhochschulreife Wirtschaft, FOS Freising</span></li>
              <li>• <span style={{color:"#0a857a"}}>2007 Mär–Apr</span> · Spanisch + PADI-Divemaster in Cancún</li>
              <li>• <span style={{color:"#0a857a"}}>2008 Jan–Feb</span> · Spanisch in Playa del Carmen (A2)</li>
              <li>• <span style={{color:"#0a857a"}}>2008 Sep – 2011 Aug</span> · Studium IUBH Bad Honnef <span className="text-night/50">· Luftverkehrsmanagement, berufsbegleitend</span></li>
              <li>• <span className="text-night">2010 Mär–Aug</span> · Pflichtpraktikum am Boden</li>
              <li>• <span style={{color:"#0a857a"}}>2011 Sep – 2012 Dez</span> · Executive MBA Ashridge <span className="text-night/50">· berufsbegleitend, LH-gefördert</span></li>
              <li>• <span style={{color:"#a87100"}}>2012 Mär – 07/2015</span> · Lufthansa Konzern <span className="text-night/50">· Referent Beratung/Strategie, FRA · ab 8. März 2012 bis Juli 2015</span></li>
            </ul>
          </div>
        </div>
      </Reveal>
    </Section>
  );
}

// ============================================================
// B1 · KONTINENT-GESCHICHTEN
// ============================================================
const _CONTINENT_STORIES = {
  EU: {
    title: "Europa",
    tagline: "Das gespannte Liniennetz über dem Heimatkontinent.",
    text: "85 % aller Flüge — mein Alltag, mein Liniennetz, mein gespanntes Spinnennetz über dem Kontinent. London und Düsseldorf häufiger als Frankfurt, Stockholm gleichauf mit Barcelona, Rom regelmäßig wie ein Pendlerflug. Hier kannte ich die Crews, die Hotels, die Abflugzeiten am Wochentag.",
  },
  NA: {
    title: "Nordamerika",
    tagline: "Atlantikhopping zwischen den Zeitzonen.",
    text: "Zehn US-Städte und drei kanadische — der westliche Block des Liniennetzes. Chicago war die häufigste Drehscheibe, San Francisco der weiteste Stadt-Punkt im Westen, Vancouver der westlichste Anflug überhaupt. Charlotte tauchte 2004 plötzlich auf und wurde für drei Jahre fester Bestandteil.",
  },
  AS: {
    title: "Asien",
    tagline: "Das Liniennetz öffnet sich nach Osten.",
    text: "Bis 2004 war Asien für mich Istanbul und Tel Aviv. 2005 öffnete sich der Kontinent: Peking, Bangkok, Shanghai, Tokio kamen dazu, Hongkong wurde regulär, später Singapur, Delhi, Seoul. 16 Länder, 21 Städte — der weiteste Punkt im Osten ist Tokio, fast 10.000 km Großkreis.",
  },
  AF: {
    title: "Afrika",
    tagline: "Zwei punktuelle Stiche durch den Kontinent.",
    text: "Nur zwei Städte, vier Bewegungen insgesamt: Johannesburg als südlichster Punkt der ganzen Karriere und Tripolis als kurzer Stop. Afrika ist die größte Lücke im Liniennetz — keine West- oder Ostküste, kein Nordafrika abseits Libyens.",
  },
  SA: {
    title: "Südamerika",
    tagline: "Ein einziger Stern: São Paulo.",
    text: "Sechs Mal São Paulo zwischen 2006 und 2010, sonst nichts. Der ganze Kontinent steht und fällt mit einer Stadt — der längste Flug südlich des Äquators auf der gesamten Karriere.",
  },
};

function SectionContinentStories() {
  const summary = useMemo(() => {
    const touch = {};
    for (const f of FLIGHTS_FULL) {
      const a = airportFull(f.von), b = airportFull(f.nach);
      if (a) touch[a.region] = (touch[a.region] || 0) + 1;
      if (b) touch[b.region] = (touch[b.region] || 0) + 1;
    }
    const out = {};
    for (const region of ["EU", "NA", "AS", "AF", "SA"]) {
      const airportsInRegion = AIRPORTS_FULL.filter((a) => a.region === region);
      const countries = new Set(airportsInRegion.map((a) => a.land));
      const topCities = airportsInRegion
        .map((a) => ({ ...a, n: (FLIGHTS_FULL.filter((f) => f.nach === a.code).length + FLIGHTS_FULL.filter((f) => f.von === a.code).length) }))
        .sort((a, b) => b.n - a.n)
        .slice(0, 5);
      const eckpunkt = ECKPUNKTE.find((e) => airportFull(e.code)?.region === region);
      out[region] = {
        ...(_CONTINENT_STORIES[region] || {}),
        nCountries: countries.size,
        nCities: airportsInRegion.length,
        nTouches: touch[region] || 0,
        topCities,
        eckpunkt,
      };
    }
    return out;
  }, []);

  return (
    <Section
      id="kontinent-stories"
      kicker="§ 05c"
      eyebrow="Fünf Kontinente"
      dark
      title={<>Fünf Kontinente, <span className="text-amber italic font-light">fünf Geschichten.</span></>}
      intro="Hinter jeder Region steht ein eigener Erinnerungs-Faden. Hier in einem Absatz pro Kontinent zusammengefasst, mit Top-Zielen und Eckpunkt-Markern."
    >
      <Reveal>
        <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-5">
          {Object.entries(summary).map(([region, s]) => (
            <div
              key={region}
              className="ticket-dark border rounded-sm p-5 relative"
              style={{ borderColor: (REGION_META[region]?.color || "#888") + "55" }}
            >
              <div className="absolute top-0 left-0 right-0 h-1 rounded-t-sm" style={{ background: REGION_META[region]?.color }} />
              <div className="font-mono text-[10px] tracking-[0.25em] uppercase" style={{ color: REGION_META[region]?.color }}>
                {s.title}
              </div>
              <div className="font-mono text-[10px] tracking-[0.18em] uppercase text-cream/55 mt-1">
                {s.nCountries} {s.nCountries === 1 ? "Land" : "Länder"} · {s.nCities} {s.nCities === 1 ? "Stadt" : "Städte"} · {s.nTouches} touches
              </div>

              <div className="mt-4 font-display text-xl text-cream leading-tight">
                {s.tagline}
              </div>
              <p className="mt-3 text-cream/80 text-sm leading-relaxed">
                {s.text}
              </p>

              <div className="mt-4 pt-3 border-t border-cream/10">
                <div className="font-mono text-[9px] tracking-[0.2em] uppercase text-cream/45 mb-2">Top 5</div>
                <div className="flex flex-wrap gap-1.5">
                  {s.topCities.map((c) => (
                    <span key={c.code} className="font-mono text-[10px] tracking-[0.08em] bg-night/40 border border-cream/15 rounded-sm px-2 py-0.5 text-cream/85">
                      {c.code} · {c.stadt}
                    </span>
                  ))}
                </div>
              </div>

              {s.eckpunkt && (
                <div className="mt-3 font-mono text-[10px] tracking-[0.18em] uppercase text-amber/85">
                  ★ {s.eckpunkt.label}: {s.eckpunkt.stadt}
                </div>
              )}
            </div>
          ))}
        </div>
      </Reveal>
    </Section>
  );
}

// ============================================================
// B3 · FLUGDAUER-HISTOGRAMM
// ============================================================
function SectionFlightDuration() {
  const stats = useMemo(() => {
    const durations = FLIGHTS_FULL.map((f) => f.h).filter((h) => h > 0);
    const sorted = [...durations].sort((a, b) => a - b);
    return {
      min: sorted[0],
      max: sorted.at(-1),
      median: sorted[Math.floor(sorted.length / 2)],
      avg: durations.reduce((s, x) => s + x, 0) / durations.length,
    };
  }, []);

  const buckets = useMemo(() => {
    // 0.5h buckets from 0 to 14
    const out = Array.from({ length: 28 }, (_, i) => ({ from: i * 0.5, to: (i + 1) * 0.5, short: 0, long: 0 }));
    for (const f of FLIGHTS_FULL) {
      if (f.h <= 0) continue;
      const idx = Math.min(27, Math.floor(f.h * 2));
      if (f.km >= 3500) out[idx].long += 1;
      else out[idx].short += 1;
    }
    return out;
  }, []);

  const maxBucket = Math.max(...buckets.map((b) => b.short + b.long));
  const longest = FLIGHTS_FULL.reduce((a, b) => (b.h > a.h ? b : a));
  const shortest = FLIGHTS_FULL.reduce((a, b) => (b.h > 0 && b.h < a.h ? b : a), FLIGHTS_FULL[0]);

  return (
    <Section
      id="flugdauer"
      kicker="§ 09b"
      eyebrow="Flugdauer-Verteilung"
      dark
      title={<>Von der halben Stunde<br/><span className="text-amber italic font-light">bis zu 13 Stunden 13.</span></>}
      intro="Block-Zeit pro Flug, in 30-Minuten-Bins. Kurz-/Mittelstrecke (< 3.500 km) und Langstrecke gestapelt. Der Peak bei 1 h sind die deutschen Inlands-Hops nach Frankfurt, Hamburg, Düsseldorf, Köln."
    >
      <Reveal>
        <div className="ticket-dark border border-cream/15 rounded-sm p-6">
        <svg viewBox="0 0 1400 360" className="w-full" preserveAspectRatio="xMidYMid meet">
          {/* y-axis lines */}
          {[0, 50, 100, 150, 200, 250, 300].map((v) => {
            const y = 320 - (v / maxBucket) * 280;
            return (
              <g key={v}>
                <line x1="40" y1={y} x2="1380" y2={y} stroke="#f6f0e2" strokeOpacity="0.08" />
                <text x="35" y={y + 3} fontSize="10" fontFamily="JetBrains Mono" fill="#f6f0e2" fillOpacity="0.5" textAnchor="end">{v}</text>
              </g>
            );
          })}
          {/* Bars */}
          {buckets.map((b, i) => {
            const bw = 1340 / buckets.length;
            const x = 40 + i * bw;
            const shortH = (b.short / maxBucket) * 280;
            const longH = (b.long / maxBucket) * 280;
            return (
              <g key={i}>
                <rect x={x + 1} y={320 - shortH - longH} width={bw - 2} height={longH} fill="#ff7e6a" fillOpacity="0.85" />
                <rect x={x + 1} y={320 - shortH} width={bw - 2} height={shortH} fill="#f9b000" fillOpacity="0.85" />
                {i % 2 === 0 && (
                  <text x={x + bw / 2} y="345" fontSize="10" fontFamily="JetBrains Mono" fill="#f6f0e2" fillOpacity="0.55" textAnchor="middle">
                    {b.from}h
                  </text>
                )}
              </g>
            );
          })}
          {/* Median line */}
          <line
            x1={40 + (stats.median / 14) * 1340}
            y1="20"
            x2={40 + (stats.median / 14) * 1340}
            y2="320"
            stroke="#fbf6ea"
            strokeOpacity="0.6"
            strokeDasharray="4 4"
          />
          <text
            x={40 + (stats.median / 14) * 1340 + 6}
            y="30"
            fontSize="11"
            fontFamily="JetBrains Mono"
            fill="#fbf6ea"
            fillOpacity="0.85"
          >Median {stats.median.toFixed(2)} h</text>
        </svg>
        <div className="mt-3 flex flex-wrap gap-4 font-mono text-[10px] tracking-[0.2em] uppercase text-cream/65">
          <span className="flex items-center gap-2"><span className="w-3 h-3 rounded-sm bg-amber/85" />A320 Kurz/Mittel</span>
          <span className="flex items-center gap-2"><span className="w-3 h-3 rounded-sm" style={{ background: "rgba(255,126,106,0.85)" }} />A340/A330 Langstrecke</span>
        </div>
      </div>

      <div className="mt-4 grid grid-cols-2 md:grid-cols-4 gap-3 text-sm">
        <div className="ticket-dark border border-cream/15 rounded-sm p-4">
          <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">Kürzester</div>
          <div className="font-display text-xl text-cream mt-0.5">{shortest.h.toFixed(2)} h</div>
          <div className="font-mono text-[10px] text-cream/55 mt-1">{shortest.von} → {shortest.nach}</div>
        </div>
        <div className="ticket-dark border border-cream/15 rounded-sm p-4">
          <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">Längster</div>
          <div className="font-display text-xl text-amber mt-0.5">{longest.h.toFixed(2)} h</div>
          <div className="font-mono text-[10px] text-cream/55 mt-1">{longest.von} → {longest.nach}</div>
        </div>
        <div className="ticket-dark border border-cream/15 rounded-sm p-4">
          <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">Median</div>
          <div className="font-display text-xl text-cream mt-0.5">{stats.median.toFixed(2)} h</div>
        </div>
        <div className="ticket-dark border border-cream/15 rounded-sm p-4">
          <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">Durchschnitt</div>
          <div className="font-display text-xl text-cream mt-0.5">{stats.avg.toFixed(2)} h</div>
        </div>
      </div>
      </Reveal>
    </Section>
  );
}

// ============================================================
// B4 · WOCHENTAG-VERTEILUNG
// ============================================================
function SectionWeekday() {
  const counts = useMemo(() => {
    const wd = [0, 0, 0, 0, 0, 0, 0];
    for (const f of FLIGHTS_FULL) {
      const d = new Date(f.date);
      const day = (d.getDay() + 6) % 7; // 0=Mo
      wd[day] += 1;
    }
    return wd;
  }, []);
  const labels = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"];
  const short = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"];
  const max = Math.max(...counts);
  const min = Math.min(...counts);
  const mostDay = labels[counts.indexOf(max)];
  const leastDay = labels[counts.indexOf(min)];

  return (
    <Section
      id="wochentag"
      kicker="§ 09c"
      eyebrow="Sieben Tage Dienst"
      dark
      title={<>Wochenende? <span className="text-amber italic font-light">Diensttag.</span></>}
      intro="Der Sonntag war der häufigste Flugtag, der Freitag der seltenste — aber nur um 37 Flüge Differenz auf 1.146 insgesamt. Im Flugbegleiter-Alltag gibt es kein klares Wochenmuster, der Schichtplan war demokratisch."
    >
      <Reveal>
        <div className="ticket-dark border border-cream/15 rounded-sm p-6">
        <div className="grid grid-cols-7 gap-3 items-end h-48">
          {counts.map((n, i) => {
            const h = (n / max) * 100;
            const isMax = n === max;
            return (
              <div key={i} className="flex flex-col items-center justify-end h-full">
                <div className="font-mono text-sm text-cream font-semibold mb-2">{n}</div>
                <div
                  className={`w-full rounded-t-sm transition-all ${isMax ? "bg-amber" : "bg-amber/55"}`}
                  style={{ height: `${h}%` }}
                />
              </div>
            );
          })}
        </div>
        <div className="grid grid-cols-7 gap-3 mt-2">
          {short.map((s, i) => (
            <div key={i} className="text-center font-mono text-[11px] tracking-[0.18em] uppercase text-cream/65">{s}</div>
          ))}
        </div>
        <div className="mt-4 font-mono text-[11px] text-cream/65 leading-relaxed">
          häufigster Tag: <span className="text-amber">{mostDay}</span> mit {max} Flügen ·
          seltenster: <span className="text-cream">{leastDay}</span> mit {min}
        </div>
      </div>
      </Reveal>
    </Section>
  );
}

// ============================================================
// C1 · LOGBUCH-SUCHE
// ============================================================
function SectionLogbookSearch() {
  const [query, setQuery] = useState("");
  const [yearFilter, setYearFilter] = useState("all");
  const [haulFilter, setHaulFilter] = useState("all");
  const [acFilter, setAcFilter] = useState("all");
  const [sortBy, setSortBy] = useState("date");
  const [sortDir, setSortDir] = useState("asc");
  const [page, setPage] = useState(0);
  const PAGE_SIZE = 50;

  const filtered = useMemo(() => {
    setPage(0);
    const q = query.toLowerCase().trim();
    let rows = FLIGHTS_FULL.filter((f) => {
      if (yearFilter !== "all" && !f.date.startsWith(yearFilter)) return false;
      if (haulFilter === "long" && f.km < 3500) return false;
      if (haulFilter === "short" && f.km >= 3500) return false;
      if (acFilter !== "all") {
        if (acFilter === "unknown" && f.ac) return false;
        else if (acFilter !== "unknown" && f.ac !== acFilter) return false;
      }
      if (!q) return true;
      const fromMeta = airportFull(f.von);
      const toMeta = airportFull(f.nach);
      const hay = `${f.date} ${f.fnr || ""} ${f.von} ${f.nach} ${fromMeta?.stadt || ""} ${toMeta?.stadt || ""} ${fromMeta?.land || ""} ${toMeta?.land || ""} ${f.ac || ""} ${f.pos || ""} ${f.dep || ""} ${f.arr || ""}`.toLowerCase();
      return hay.includes(q);
    });
    // Sort
    const sorted = [...rows].sort((a, b) => {
      let av, bv;
      switch (sortBy) {
        case "date":    av = a.date; bv = b.date; break;
        case "fnr":     av = a.fnr || "zzz"; bv = b.fnr || "zzz"; break;
        case "route":   av = a.von + a.nach; bv = b.von + b.nach; break;
        case "km":      av = a.km; bv = b.km; break;
        case "h":       av = a.h; bv = b.h; break;
        case "ac":      av = a.ac || "zzz"; bv = b.ac || "zzz"; break;
        case "pos":     av = a.pos || "zzz"; bv = b.pos || "zzz"; break;
        case "dep":     av = a.dep || "zzz"; bv = b.dep || "zzz"; break;
        case "arr":     av = a.arr || "zzz"; bv = b.arr || "zzz"; break;
        default:        av = a.date; bv = b.date;
      }
      const cmp = typeof av === "number" ? av - bv : String(av).localeCompare(String(bv));
      return sortDir === "asc" ? cmp : -cmp;
    });
    return sorted;
  }, [query, yearFilter, haulFilter, acFilter, sortBy, sortDir]);

  const totalPages = Math.ceil(filtered.length / PAGE_SIZE);
  const displayed = filtered.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE);

  const toggleSort = (col) => {
    if (sortBy === col) setSortDir(sortDir === "asc" ? "desc" : "asc");
    else { setSortBy(col); setSortDir(col === "date" || col === "fnr" || col === "route" || col === "ac" || col === "pos" || col === "dep" || col === "arr" ? "asc" : "desc"); }
  };
  const SortArrow = ({ col }) => sortBy === col ? <span className="ml-1 text-amber">{sortDir === "asc" ? "▲" : "▼"}</span> : <span className="ml-1 text-cream/20">⇅</span>;
  const SortableTh = ({ col, align = "left", children, className = "" }) => (
    <th
      onClick={() => toggleSort(col)}
      className={`${align === "right" ? "text-right" : "text-left"} px-3 cursor-pointer hover:text-amber transition-colors select-none ${className}`}
    >
      {children}<SortArrow col={col} />
    </th>
  );

  const years = ["all", "2001", "2002", "2003", "2004", "2005", "2006", "2007", "2008", "2009", "2010", "2011", "2012"];
  // Aircraft-typen, deduped, in einer schoenen reihenfolge (groesste zuerst)
  const acTypes = [
    { k: "all",     l: "Alle" },
    { k: "B742",    l: "B747-200" },
    { k: "B744",    l: "B747-400" },
    { k: "A340",    l: "A340" },
    { k: "A346",    l: "A346" },
    { k: "A333",    l: "A333" },
    { k: "A320",    l: "A320" },
    { k: "A300",    l: "A300" },
    { k: "B737",    l: "B737/B733" },
    { k: "unknown", l: "ohne Eintrag" },
  ];
  const acLabel = (a) => a ? a : <span className="text-cream/35">—</span>;
  const srcLabel = (s) => s === "L" ? "Logbuch" : (s === "S" ? "Spesen" : "Einsatzplan");

  return (
    <Section
      id="logbuch-suche"
      kicker="§ 10b"
      eyebrow="Logbuch-Suche"
      dark
      title={<>Alle 1.146 Flüge, <span className="text-amber italic font-light">durchsuchbar.</span></>}
      intro="Stadt, IATA-Code, LH-Nummer oder Jahr. Die komplette Karriere als Tabelle, gefiltert und gepaged. Für Statistik-Fans, Family-History und Beweise im Familien-Quiz."
    >
      <Reveal>
        <div className="ticket-dark border border-cream/15 rounded-sm p-5 mb-4">
          <input
            type="text"
            placeholder="Suchen: München, NYC, LH0791, A340, 1P1, Singapur…"
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            className="w-full bg-night/40 border border-cream/15 text-cream rounded-sm px-4 py-3 font-mono text-[13px] placeholder-cream/40 focus:border-amber focus:outline-none"
          />
          <div className="mt-3 flex flex-wrap gap-2 items-center">
            <span className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">Jahr:</span>
            {years.map((y) => (
              <button
                key={y}
                onClick={() => setYearFilter(y)}
                className={`px-2 py-1 rounded-sm border font-mono text-[10px] tracking-[0.12em] uppercase transition-colors ${
                  yearFilter === y ? "bg-amber text-night border-amber" : "bg-night/30 text-cream/70 border-cream/15 hover:border-amber/60"
                }`}
              >{y === "all" ? "Alle" : y}</button>
            ))}
            <span className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55 ml-3">Strecke:</span>
            {[
              { k: "all", l: "Alle" },
              { k: "short", l: "< 3500 km" },
              { k: "long", l: "≥ 3500 km" },
            ].map((c) => (
              <button
                key={c.k}
                onClick={() => setHaulFilter(c.k)}
                className={`px-2 py-1 rounded-sm border font-mono text-[10px] tracking-[0.12em] uppercase transition-colors ${
                  haulFilter === c.k ? "bg-amber text-night border-amber" : "bg-night/30 text-cream/70 border-cream/15 hover:border-amber/60"
                }`}
              >{c.l}</button>
            ))}
          </div>
          <div className="mt-3 flex flex-wrap gap-2 items-center">
            <span className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">Muster:</span>
            {acTypes.map((c) => (
              <button
                key={c.k}
                onClick={() => setAcFilter(c.k)}
                className={`px-2 py-1 rounded-sm border font-mono text-[10px] tracking-[0.12em] uppercase transition-colors ${
                  acFilter === c.k ? "bg-amber text-night border-amber" : "bg-night/30 text-cream/70 border-cream/15 hover:border-amber/60"
                }`}
              >{c.l}</button>
            ))}
            <span className="font-mono text-[9px] tracking-[0.18em] uppercase text-cream/40 ml-2">448 von 1.146 Flügen haben Muster-Eintrag (aus Einsatzplan)</span>
          </div>
        </div>

        <div className="ticket-dark border border-cream/15 rounded-sm overflow-hidden">
          <div className="px-4 py-3 border-b border-cream/15 flex items-baseline justify-between font-mono text-[10px] tracking-[0.2em] uppercase text-cream/65">
            <span>{filtered.length.toLocaleString("de-DE")} Treffer von {FLIGHTS_FULL.length.toLocaleString("de-DE")}</span>
            <span>Seite {page + 1} von {Math.max(1, totalPages)}</span>
          </div>
          <div className="overflow-x-auto">
            <table className="w-full text-sm min-w-[920px]">
              <thead>
                <tr className="font-mono text-[10px] tracking-[0.18em] uppercase text-cream/55 border-b border-cream/15">
                  <SortableTh col="date" className="py-2 pl-4">Datum</SortableTh>
                  <SortableTh col="fnr">LH-Nr</SortableTh>
                  <SortableTh col="route">Strecke</SortableTh>
                  <SortableTh col="km" align="right">km</SortableTh>
                  <SortableTh col="h" align="right">Block</SortableTh>
                  <SortableTh col="dep">Ab (LT)</SortableTh>
                  <SortableTh col="arr">An (LT)</SortableTh>
                  <SortableTh col="ac">Muster</SortableTh>
                  <SortableTh col="pos">Pos.</SortableTh>
                </tr>
              </thead>
              <tbody className="font-mono text-[12px] text-cream/85">
                {displayed.map((f, i) => {
                  const fromMeta = airportFull(f.von), toMeta = airportFull(f.nach);
                  return (
                    <tr key={i} className="border-b border-cream/5 hover:bg-amber/5">
                      <td className="py-1.5 px-4 text-cream/75">{f.date}</td>
                      <td className="px-3 text-amber/85">{f.fnr ? `LH${f.fnr}` : <span className="text-cream/35">—</span>}</td>
                      <td className="px-3">
                        <span className="text-cream">{f.von} → {f.nach}</span>
                        <span className="text-cream/45 ml-2 text-[11px]">{fromMeta?.stadt} → {toMeta?.stadt}</span>
                      </td>
                      <td className="px-3 text-right text-cream/75">{f.km.toLocaleString("de-DE")}</td>
                      <td className="px-3 text-right text-cream/75">{f.h.toFixed(2)}{f.est ? "*" : ""}</td>
                      <td className="px-3 text-cream/70">{f.dep || <span className="text-cream/30">—</span>}</td>
                      <td className="px-3 text-cream/70">{f.arr || <span className="text-cream/30">—</span>}</td>
                      <td className="px-3 text-cream/85">{acLabel(f.ac)}</td>
                      <td className="px-3 text-cream/60">{f.pos || <span className="text-cream/35">—</span>}</td>
                    </tr>
                  );
                })}
                {displayed.length === 0 && (
                  <tr><td colSpan="9" className="py-8 text-center text-cream/55 italic">Keine Flüge gefunden.</td></tr>
                )}
              </tbody>
            </table>
          </div>
          {totalPages > 1 && (
            <div className="px-4 py-3 border-t border-cream/15 flex items-center justify-between">
              <button
                onClick={() => setPage(Math.max(0, page - 1))}
                disabled={page === 0}
                className="font-mono text-[11px] tracking-[0.2em] uppercase text-amber border border-amber/40 rounded-sm px-3 py-1 hover:bg-amber hover:text-night disabled:opacity-30 disabled:cursor-not-allowed transition-colors"
              >◀ zurück</button>
              <span className="font-mono text-[10px] tracking-[0.18em] uppercase text-cream/55">
                {page * PAGE_SIZE + 1} – {Math.min((page + 1) * PAGE_SIZE, filtered.length)} von {filtered.length}
              </span>
              <button
                onClick={() => setPage(Math.min(totalPages - 1, page + 1))}
                disabled={page >= totalPages - 1}
                className="font-mono text-[11px] tracking-[0.2em] uppercase text-amber border border-amber/40 rounded-sm px-3 py-1 hover:bg-amber hover:text-night disabled:opacity-30 disabled:cursor-not-allowed transition-colors"
              >weiter ▶</button>
            </div>
          )}
        </div>
        <div className="mt-2 font-mono text-[9px] tracking-[0.2em] uppercase text-cream/45">
          * = Blockzeit geschätzt (typische Großkreis-Zeit, nicht aus Logbuch)
        </div>
      </Reveal>
    </Section>
  );
}

// ============================================================
// C3 · HEIMKEHR-PATTERN (Trips statt Flüge)
// ============================================================
const _trips = (() => {
  const hubs = new Set(["MUC", "FRA"]);
  const out = [];
  let cur = null;
  for (const f of FLIGHTS_FULL) {
    if (hubs.has(f.von) && !hubs.has(f.nach)) {
      // Trip start
      if (!cur) cur = { start: f.date, flights: [f], farthest: f };
      else cur.flights.push(f);
    } else if (cur) {
      cur.flights.push(f);
      if (hubs.has(f.nach)) {
        cur.end = f.date;
        const ms = new Date(cur.end).getTime() - new Date(cur.start).getTime();
        cur.days = Math.max(1, Math.round(ms / 86400000));
        // farthest is highest-km destination
        cur.farthest = cur.flights.reduce((a, b) => (b.km > a.km ? b : a));
        out.push(cur);
        cur = null;
      }
    }
  }
  // Filter unreasonable trip lengths (>30 days = Wehrdienst/Praktikum-Artefakt)
  return out.filter((t) => t.days <= 30);
})();

function SectionHeimkehr() {
  const stats = useMemo(() => {
    const days = _trips.map((t) => t.days).sort((a, b) => a - b);
    const buckets = [0, 0, 0, 0, 0]; // 1d, 2d, 3d, 4-7d, 8+d
    for (const d of days) {
      if (d === 1) buckets[0]++;
      else if (d === 2) buckets[1]++;
      else if (d === 3) buckets[2]++;
      else if (d <= 7) buckets[3]++;
      else buckets[4]++;
    }
    const longest = _trips.reduce((a, b) => (b.days > a.days ? b : a), { days: 0 });
    return {
      n: _trips.length,
      median: days[Math.floor(days.length / 2)],
      longest,
      buckets,
    };
  }, []);

  const total = stats.buckets.reduce((s, x) => s + x, 0);
  const bucketLabels = ["1 Tag", "2 Tage", "3 Tage", "4–7 Tage", "8+ Tage"];

  return (
    <Section
      id="heimkehr"
      kicker="§ 09d"
      eyebrow="Heimkehr-Pattern"
      dark
      title={<>{stats.n} Reisen — <span className="text-amber italic font-light">{stats.n} Heimkehren.</span></>}
      intro={`Ein Flugbegleiter-Leben in zwei Verben: wegfliegen, heimkehren. Hier sind alle Trips als zusammengehörige Hin-Rück-Paare gezählt — nicht einzelne Flüge. Der Median liegt bei ${stats.median} ${stats.median === 1 ? "Tag" : "Tagen"} draußen, die längste reale Reise dauerte ${stats.longest.days} Tage.`}
    >
      <Reveal>
        <div className="grid md:grid-cols-2 gap-5">
        {/* Big stats */}
        <div className="ticket-dark border border-cream/15 rounded-sm p-6">
          <div className="grid grid-cols-2 gap-4">
            <div>
              <div className="font-display text-5xl text-amber">{stats.n}</div>
              <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55 mt-1">Trips</div>
            </div>
            <div>
              <div className="font-display text-5xl text-cream">{stats.median}</div>
              <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55 mt-1">Median Tage draußen</div>
            </div>
            <div className="col-span-2 pt-3 border-t border-cream/15">
              <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55 mb-1">Längste Reise</div>
              <div className="font-display text-xl text-cream">{stats.longest.days} Tage</div>
              <div className="font-mono text-[10px] text-cream/55 mt-1">
                {stats.longest.start} → {stats.longest.end} · ziel: {stats.longest.farthest?.nach} {airportFull(stats.longest.farthest?.nach)?.stadt}
              </div>
            </div>
          </div>
        </div>

        {/* Distribution */}
        <div className="ticket-dark border border-cream/15 rounded-sm p-6">
          <div className="font-mono text-[10px] tracking-[0.25em] uppercase text-amber mb-4">Verteilung</div>
          <div className="space-y-3">
            {stats.buckets.map((n, i) => (
              <div key={i} className="flex items-center gap-3">
                <div className="font-mono text-[11px] tracking-[0.15em] uppercase text-cream/70 w-20">{bucketLabels[i]}</div>
                <div className="flex-1 h-3 bg-cream/10 rounded-sm overflow-hidden">
                  <div className="h-full bg-amber" style={{ width: `${(n / total) * 100}%` }} />
                </div>
                <div className="font-mono text-sm text-cream w-10 text-right">{n}</div>
              </div>
            ))}
          </div>
          <div className="mt-4 font-mono text-[10px] text-cream/55 italic leading-relaxed">
            Trips über 30 Tagen Dauer (Artefakte aus Wehrdienst/Praktikum-Lücken) sind ausgefiltert.
          </div>
        </div>
      </div>
      </Reveal>
    </Section>
  );
}

// ============================================================
// § 04 · DER WEG ZUM PURSER (Spanisch in Mexiko 2007+2008)
// ============================================================
function SectionWegZumPurser() {
  return (
    <Section
      id="weg-zum-purser"
      kicker="§ 04"
      eyebrow="Der Weg zum Purser"
      dark
      title={<>Aus einem Streifen <span className="text-amber italic font-light">werden zwei.</span></>}
      intro="Lufthansa setzte für die Beförderung zum Purser eine zweite Fremdsprache voraus. Spanisch erschloss zusätzlich den lateinamerikanischen Markt — und wurde in zwei Etappen am Karibischen Meer gelernt. Beides im Urlaub, beides anteilig von Lufthansa gefördert (Bildungsförderung + ID-Tickets)."
    >
      <Reveal>
        {/* Zwei Sprachreisen-Tickets nebeneinander */}
        <div className="grid md:grid-cols-2 gap-5">
          {/* CANCÚN 2007 */}
          <div className="ticket-dark border border-[#4ec5b8]/40 rounded-sm p-6 relative overflow-hidden">
            <div className="absolute top-0 left-0 right-0 h-1" style={{ background: "#4ec5b8" }} />
            <div className="font-mono text-[10px] tracking-[0.3em] uppercase" style={{ color: "#4ec5b8" }}>
              Reise 1 · 2007
            </div>
            <div className="mt-2 font-display text-3xl text-cream leading-tight">Cancún</div>
            <div className="font-mono text-[10px] tracking-[0.22em] uppercase text-cream/55 mt-1">
              03.03. – 06.04.2007 · 5 Wochen
            </div>

            <div className="mt-5 space-y-3 text-sm">
              <div>
                <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">Schule</div>
                <div className="text-cream/90 mt-0.5">El Bosque del Caribe</div>
                <div className="text-cream/55 text-[11px] font-mono">Avenida Náder · SM 3 · Cancún</div>
                <div className="text-cream/55 text-[11px] font-mono">Intensivkurs · 5 Std./Tag · 25 Std./Woche</div>
              </div>
              <div>
                <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">Unterkunft</div>
                <div className="text-cream/90 mt-0.5">Bei einer mexikanischen Familie</div>
                <div className="text-cream/55 text-[11px] font-mono">Einzelzimmer · Halbpension · 154 €/Woche</div>
              </div>
              <div>
                <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55" style={{ color: "#4ec5b8" }}>+ Parallel</div>
                <div className="text-cream/90 mt-0.5">PADI-Divemaster</div>
                <div className="text-cream/55 text-[11px] font-mono">Riffe · Cenoten · Isla Mujeres</div>
              </div>
            </div>

            <div className="mt-5 pt-4 border-t border-cream/15 flex items-baseline justify-between">
              <div>
                <div className="font-display text-2xl" style={{ color: "#4ec5b8" }}>1.620 €</div>
                <div className="font-mono text-[9px] tracking-[0.18em] uppercase text-cream/55">Sprachreise gesamt</div>
              </div>
              <div className="text-right font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">
                Anreise<br/>MUC–FRA–CUN
              </div>
            </div>
          </div>

          {/* PLAYA DEL CARMEN 2008 */}
          <div className="ticket-dark border border-[#4ec5b8]/40 rounded-sm p-6 relative overflow-hidden">
            <div className="absolute top-0 left-0 right-0 h-1" style={{ background: "#4ec5b8" }} />
            <div className="font-mono text-[10px] tracking-[0.3em] uppercase" style={{ color: "#4ec5b8" }}>
              Reise 2 · 2008
            </div>
            <div className="mt-2 font-display text-3xl text-cream leading-tight">Playa del Carmen</div>
            <div className="font-mono text-[10px] tracking-[0.22em] uppercase text-cream/55 mt-1">
              05.01. – 16.02.2008 · 6 Wochen
            </div>

            <div className="mt-5 space-y-3 text-sm">
              <div>
                <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">Schule</div>
                <div className="text-cream/90 mt-0.5">International House Riviera Maya</div>
                <div className="text-cream/55 text-[11px] font-mono">Playa del Carmen · Direktorin: Gina González</div>
                <div className="text-cream/55 text-[11px] font-mono">Standardkurs + 10 Einzellektionen · 90 Std.</div>
              </div>
              <div>
                <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">Unterkunft</div>
                <div className="text-cream/90 mt-0.5">Dorm der Sprachschule</div>
                <div className="text-cream/55 text-[11px] font-mono">Einzelzimmer · ohne Verpflegung</div>
              </div>
              <div>
                <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55" style={{ color: "#4ec5b8" }}>Ergebnis</div>
                <div className="text-cream/90 mt-0.5">Niveau A2 · PREINTERMEDIO</div>
                <div className="text-cream/55 text-[11px] font-mono">Zertifikat 15.02.2008</div>
              </div>
            </div>

            <div className="mt-5 pt-4 border-t border-cream/15 flex items-baseline justify-between">
              <div>
                <div className="font-display text-2xl" style={{ color: "#4ec5b8" }}>1.724 €</div>
                <div className="font-mono text-[9px] tracking-[0.18em] uppercase text-cream/55">Sprachreise gesamt</div>
              </div>
              <div className="text-right font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">
                Anreise<br/>MUC–FRA–CUN
              </div>
            </div>
          </div>
        </div>

        {/* Zwischenkurs */}
        <div className="mt-4 ticket-dark border border-cream/15 rounded-sm p-4 flex items-baseline justify-between gap-4">
          <div>
            <span className="font-mono text-[10px] tracking-[0.25em] uppercase text-cream/55">Zwischen den Reisen · </span>
            <span className="font-display text-lg text-cream">Spanisch Aktiv, München</span>
            <span className="text-cream/55"> — 10 Privatstunden bei Martin Polo, Juli 2007</span>
          </div>
          <div className="font-display text-lg text-amber whitespace-nowrap">310 €</div>
        </div>

        {/* Total + Beförderung */}
        <div className="mt-8 grid md:grid-cols-[1fr_auto_1fr] items-center gap-6">
          <div className="ticket-dark border border-cream/15 rounded-sm p-5 text-center">
            <div className="font-mono text-[10px] tracking-[0.25em] uppercase text-cream/55">11 Wochen Mexiko</div>
            <div className="font-display text-3xl text-cream mt-1">100+ Stunden Unterricht</div>
            <div className="font-mono text-[11px] tracking-[0.18em] uppercase mt-1" style={{ color: "#4ec5b8" }}>3.654 € · mit LH-Förderung</div>
          </div>

          <div className="font-display text-3xl text-amber whitespace-nowrap text-center">→</div>

          <div className="bg-amber/10 border-2 border-amber rounded-sm p-5 text-center">
            <div className="font-mono text-[10px] tracking-[0.25em] uppercase text-amber">Sechs Monate später</div>
            <div className="font-display text-3xl text-cream mt-1">29. August 2008</div>
            <div className="font-mono text-[11px] tracking-[0.18em] uppercase text-amber mt-1">P1-Ernennung im Einsatzplan</div>
            <div className="font-mono text-[10px] text-cream/55 mt-1">erste Tour: 1.9. MUC–CLT</div>
          </div>
        </div>

        {/* Detail-link */}
        <div className="mt-6 text-right">
          <a
            href="https://github.com/moritzschieder/lufthansa-erinnerung/blob/main/docs/mexiko-sprachreisen.md"
            target="_blank"
            rel="noopener noreferrer"
            className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55 hover:text-amber transition-colors"
          >
            Detail-Daten: docs/mexiko-sprachreisen.md →
          </a>
        </div>
      </Reveal>
    </Section>
  );
}

// ============================================================
// § 04b · BERUFSBEGLEITENDE BILDUNG (IUBH + MBA Ashridge)
// ============================================================
function SectionBerufsbegleitendeBildung() {
  return (
    <Section
      id="bildung"
      kicker="§ 04b"
      eyebrow="Berufsbegleitend"
      dark
      title={<>Drei Etappen <span className="text-amber italic font-light">parallel zum Fliegen.</span></>}
      intro="Bildungsbiografie in drei Stufen, alle während des aktiven Flugdiensts: Fachhochschulreife per Telekolleg von zuhause, danach Bachelor Luftverkehrsmanagement an der IUBH Bad Honnef mit Köln–München-Pendelei, dann Executive MBA in Ashridge. Der dünne Türkis-Streifen in der Saisonalitäts-Heatmap zeigt jeden Monat, in dem Lernen neben dem Fliegen lief."
    >
      <Reveal>
        <div className="grid md:grid-cols-3 gap-5">
          {/* TELEKOLLEG 2004-2007 */}
          <div className="ticket-dark border border-[#4ec5b8]/40 rounded-sm p-6 relative overflow-hidden">
            <div className="absolute top-0 left-0 right-0 h-1" style={{ background: "#4ec5b8" }} />
            <div className="font-mono text-[10px] tracking-[0.3em] uppercase" style={{ color: "#4ec5b8" }}>
              Vorqualifikation · 2004 – 2007
            </div>
            <div className="mt-2 font-display text-2xl text-cream leading-tight">Telekolleg</div>
            <div className="font-mono text-[10px] tracking-[0.22em] uppercase text-cream/55 mt-1">
              Fachhochschulreife · Wirtschaft · Fernunterricht
            </div>

            <div className="mt-5 space-y-3 text-sm">
              <div>
                <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">Format</div>
                <div className="text-cream/90 mt-0.5">Fachoberschule Freising · via Telekolleg</div>
                <div className="text-cream/55 text-[11px] font-mono">Lehrbriefe, BR-Sendungen, Begleit-Wochenenden</div>
              </div>
              <div>
                <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">Prüfung</div>
                <div className="text-cream/90 mt-0.5">FOS Freising · 3. März 2007</div>
                <div className="text-cream/55 text-[11px] font-mono">Schwerpunkt Wirtschaftslehre</div>
              </div>
              <div>
                <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55" style={{ color: "#4ec5b8" }}>Ergebnis</div>
                <div className="text-cream/90 mt-0.5">Fachhochschulreife</div>
                <div className="text-cream/55 text-[11px] font-mono">Zugang für späteres IUBH-Studium</div>
              </div>
            </div>

            <div className="mt-5 pt-4 border-t border-cream/15">
              <div className="font-display text-2xl" style={{ color: "#4ec5b8" }}>März 2004 – März 2007</div>
              <div className="font-mono text-[9px] tracking-[0.18em] uppercase text-cream/55 mt-0.5">
                36 Monate · parallel zu 354 Flügen
              </div>
            </div>
          </div>

          {/* IUBH 2008-2011 */}
          <div className="ticket-dark border border-[#4ec5b8]/40 rounded-sm p-6 relative overflow-hidden">
            <div className="absolute top-0 left-0 right-0 h-1" style={{ background: "#4ec5b8" }} />
            <div className="font-mono text-[10px] tracking-[0.3em] uppercase" style={{ color: "#4ec5b8" }}>
              Studium · 2008 – 2011
            </div>
            <div className="mt-2 font-display text-3xl text-cream leading-tight">IUBH Bad Honnef</div>
            <div className="font-mono text-[10px] tracking-[0.22em] uppercase text-cream/55 mt-1">
              Luftverkehrsmanagement · Bachelor · berufsbegleitend
            </div>

            <div className="mt-5 space-y-3 text-sm">
              <div>
                <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">Hochschule</div>
                <div className="text-cream/90 mt-0.5">Internationale Hochschule Bad Honnef</div>
                <div className="text-cream/55 text-[11px] font-mono">heute IU Internationale Hochschule · NRW</div>
              </div>
              <div>
                <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">Modell</div>
                <div className="text-cream/90 mt-0.5">3 Jahre · 6 Semester · berufsbegleitend</div>
                <div className="text-cream/55 text-[11px] font-mono">parallel zum aktiven Flugdienst</div>
              </div>
              <div>
                <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">Doppelleben</div>
                <div className="text-cream/90 mt-0.5">Wohnsitz Bad Honnef, stationiert München</div>
                <div className="text-cream/55 text-[11px] font-mono">der Schichtplan kannte keine Semesterferien · Bachelor-Arbeit komplett in einem 5-Tage-SIN-Layover (Januar 2011)</div>
              </div>
            </div>

            <div className="mt-5 pt-4 border-t border-cream/15">
              <div className="font-display text-2xl" style={{ color: "#4ec5b8" }}>Sep 2008 – Aug 2011</div>
              <div className="font-mono text-[9px] tracking-[0.18em] uppercase text-cream/55 mt-0.5">
                36 Monate parallel zu 257 Flügen
              </div>
            </div>
          </div>

          {/* MBA Ashridge 2011+ */}
          <div className="ticket-dark border border-[#4ec5b8]/40 rounded-sm p-6 relative overflow-hidden">
            <div className="absolute top-0 left-0 right-0 h-1" style={{ background: "#4ec5b8" }} />
            <div className="font-mono text-[10px] tracking-[0.3em] uppercase" style={{ color: "#4ec5b8" }}>
              Aufbaustudium · ab 2011
            </div>
            <div className="mt-2 font-display text-3xl text-cream leading-tight">Executive MBA</div>
            <div className="font-mono text-[10px] tracking-[0.22em] uppercase text-cream/55 mt-1">
              Ashridge Business School · London · LH-gefördert
            </div>

            <div className="mt-5 space-y-3 text-sm">
              <div>
                <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">Programm</div>
                <div className="text-cream/90 mt-0.5">Executive Master of Business Administration</div>
                <div className="text-cream/55 text-[11px] font-mono">Ashridge Business School · London, England</div>
              </div>
              <div>
                <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55">Modell</div>
                <div className="text-cream/90 mt-0.5">berufsbegleitend · 27 Monate · Modulkonzept</div>
                <div className="text-cream/55 text-[11px] font-mono">internationale Kohorte, Präsenzphasen UK · Auslandsmodul Beijing Foreign Studies University Juni 2013</div>
              </div>
              <div>
                <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-cream/55" style={{ color: "#4ec5b8" }}>+ LH-Förderung</div>
                <div className="text-cream/90 mt-0.5">Lufthansa-Bildungs-Stipendium</div>
                <div className="text-cream/55 text-[11px] font-mono">höchste Auszeichnung im LH-Sprach- und Bildungs-Programm</div>
              </div>
            </div>

            <div className="mt-5 pt-4 border-t border-cream/15">
              <div className="font-display text-2xl" style={{ color: "#4ec5b8" }}>Sep 2011 – Dez 2013</div>
              <div className="font-mono text-[9px] tracking-[0.18em] uppercase text-cream/55 mt-0.5">
                27 Monate · 44 Flüge im LH-Überlapp · Abschluss Master of Business Administration
              </div>
            </div>
          </div>
        </div>

        {/* Ehrenamt-Strang: Stadtrat Bad Honnef */}
        <div className="mt-6 ticket-dark border border-amber/30 rounded-sm p-5 relative overflow-hidden">
          <div className="absolute top-0 left-0 right-0 h-1 bg-amber" />
          <div className="grid md:grid-cols-[auto_1fr_auto] gap-4 items-baseline">
            <div className="font-mono text-[10px] tracking-[0.25em] uppercase text-amber">Ehrenamt parallel</div>
            <div>
              <div className="font-display text-xl text-cream leading-tight">Stadtrat der Stadt Bad Honnef</div>
              <div className="font-mono text-[10px] tracking-[0.18em] uppercase text-cream/55 mt-0.5">
                Stellvertretender Vorsitzender Ausschuss Bildung · Sport · Kultur
                <span className="text-cream/35"> · </span>
                Mitglied Aufsichtsrat Flugplatz Eudenbach GmbH
              </div>
            </div>
            <div className="text-right font-mono text-[11px] tracking-[0.18em] uppercase text-amber/85">
              10/2009 – 04/2012<br/>
              <span className="text-cream/55 text-[10px]">30 Monate</span>
            </div>
          </div>
        </div>

        {/* Mini-Summary */}
        <div className="mt-4 ticket-dark border border-cream/15 rounded-sm p-4 text-center">
          <div className="font-mono text-[10px] tracking-[0.25em] uppercase text-cream/55">Bilanz im Flugdienst</div>
          <div className="mt-1 font-display text-xl text-cream">
            Fachhochschulreife + Bachelor + MBA + Stadtrat-Mandat + 100+ Std Spanisch · neun Jahre Doppel- und Dreifachleben
          </div>
        </div>
      </Reveal>
    </Section>
  );
}

// Expose
Object.assign(window, {
  Section2005Spotlight,
  SectionWegZumPurser,
  SectionBerufsbegleitendeBildung,
  SectionRouteRanking,
  SectionCountryBreakdown,
  SectionFirstLastVisits,
  SectionMonthHeatmap,
  SectionContinentStories,
  SectionFlightDuration,
  SectionWeekday,
  SectionLogbookSearch,
  SectionHeimkehr,
  getMergedTimeline,
  _milestones,
});
