Testseite: Unterschied zwischen den Versionen

Aus Bündnis für Familie Tübingen.
Keine Bearbeitungszusammenfassung
Keine Bearbeitungszusammenfassung
Zeile 1: Zeile 1:
<!doctype html>
<html lang="de">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Viele Formen von Familie – Programm 2026</title>
  <style>
    :root {
      --bg: #f6f7fb;
      --surface: #ffffff;
      --surface-soft: #f8fafc;
      --text: #1f2937;
      --muted: #64748b;
      --line: #dbe1ea;
      --accent: #0f766e;
      --accent-dark: #115e59;
      --accent-soft: #ecfdf5;
      --radius: 16px;
    }
    *, *::before, *::after { box-sizing: border-box; }
    html { scroll-behavior: smooth; }
    body {
      margin: 0;
      color: var(--text);
      background: var(--bg);
      font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
      line-height: 1.5;
    }
    .wrap { width: min(100% - 2rem, 1200px); margin: 0 auto; padding: clamp(1rem, 3vw, 2rem) 0; }
    h1, h2, h3 { line-height: 1.2; overflow-wrap: anywhere; }
    h1 { margin: 0 0 .5rem; font-size: clamp(1.75rem, 1.2rem + 2vw, 2.75rem); }
    h2 { margin: 0 0 1rem; font-size: clamp(1.25rem, 1rem + .8vw, 1.6rem); }
    .intro { margin: 0 0 1.5rem; color: var(--muted); }
    .notice {
      margin: 0 0 1.5rem;
      padding: .85rem 1rem;
      border-left: 4px solid #d97706;
      border-radius: 10px;
      color: #78350f;
      background: #fffbeb;
    }
    .month {
      margin-bottom: 1.5rem;
      padding: clamp(.85rem, 2vw, 1.25rem);
      background: var(--surface);
      border: 1px solid var(--line);
      border-radius: var(--radius);
      box-shadow: 0 8px 24px rgb(15 23 42 / 5%);
    }
    .weekdays, .grid {
      display: grid;
      grid-template-columns: repeat(7, minmax(0, 1fr));
      gap: clamp(4px, 1vw, 10px);
    }
    .weekdays { margin-bottom: .5rem; }
    .weekdays div {
      padding: .25rem;
      color: var(--muted);
      font-size: .85rem;
      font-weight: 700;
      text-align: center;
    }
    .day {
      min-width: 0;
      min-height: 120px;
      padding: .6rem;
      background: var(--surface-soft);
      border: 1px solid var(--line);
      border-radius: 12px;
      display: flex;
      flex-direction: column;
      gap: .5rem;
    }
    .day.muted { opacity: .45; }
    .dn { color: #374151; font-size: .9rem; font-weight: 700; }
    .event {
      width: 100%;
      min-width: 0;
      padding: .65rem .7rem;
      color: var(--accent-dark);
      text-align: left;
      font: inherit;
      background: var(--accent-soft);
      border: 1px solid rgb(15 118 110 / 18%);
      border-left: 4px solid var(--accent);
      border-radius: 10px;
      cursor: pointer;
      overflow-wrap: anywhere;
      transition: background .2s ease, transform .2s ease;
    }
    .event:hover, .event:focus-visible { background: #d1fae5; transform: translateY(-1px); }
    .event:focus-visible, button:focus-visible {
      outline: 3px solid rgb(37 99 235 / 35%);
      outline-offset: 2px;
    }
    .event-time { display: block; margin-bottom: .15rem; font-size: .75rem; font-weight: 800; }
    .et { display: block; font-size: .84rem; font-weight: 700; line-height: 1.35; }
    .event-dialog {
      width: min(92vw, 680px);
      max-height: 88vh;
      padding: 0;
      color: var(--text);
      background: var(--surface);
      border: 0;
      border-radius: 18px;
      box-shadow: 0 20px 60px rgb(15 23 42 / 25%);
    }
    .event-dialog::backdrop { background: rgb(15 23 42 / 58%); }
    .dialog-inner { max-height: 88vh; overflow: auto; padding: 1.25rem; }
    .dialog-close {
      float: right;
      width: 2.75rem;
      height: 2.75rem;
      border: 0;
      border-radius: 50%;
      color: var(--text);
      background: #eef2f7;
      font-size: 1.5rem;
      line-height: 1;
      cursor: pointer;
    }
    .dialog-close:hover { background: #e2e8f0; }
    .dialog-title { margin: 0 3.25rem .9rem 0; color: var(--accent-dark); font-size: clamp(1.15rem, 1rem + .8vw, 1.5rem); }
    .dialog-date { margin: 0 0 1rem; color: var(--muted); font-weight: 700; }
    .dialog-details { display: grid; gap: .85rem; }
    .detail-row { padding-top: .85rem; border-top: 1px solid var(--line); overflow-wrap: anywhere; }
    .detail-row strong { display: block; margin-bottom: .2rem; color: var(--text); }
    .detail-description { white-space: pre-line; }
    .dialog-link { color: var(--accent-dark); font-weight: 700; overflow-wrap: anywhere; }
    @media (max-width: 700px) {
      .wrap { width: min(100% - 1rem, 600px); }
      .month { padding: .85rem; border-radius: 14px; }
      .weekdays, .grid { grid-template-columns: repeat(7, minmax(30px, 1fr)); gap: 3px; }
      .weekdays div { padding: .2rem 0; font-size: .7rem; }
      .day { min-height: 62px; padding: .35rem; border-radius: 9px; }
      .dn { font-size: .78rem; }
      .event { padding: .55rem .6rem; border-left-width: 3px; }
      .event-time { font-size: .65rem; }
      .et { font-size: .74rem; }
    }
    @media (max-width: 480px) {
      .grid { display: block; }
      .day { display: none; }
      .day.has-events {
        display: block;
        min-height: auto;
        margin-bottom: .7rem;
        padding: .7rem;
        background: var(--surface-soft);
      }
      .day.has-events .dn { margin-bottom: .55rem; font-size: 1rem; }
      .day.muted { display: none; }
      .event { margin-top: .5rem; }
      .event-time { font-size: .76rem; }
      .et { font-size: .92rem; }
    }
    @media (prefers-reduced-motion: reduce) {
      *, *::before, *::after { scroll-behavior: auto !important; transition: none !important; }
    }
  </style>
</head>
<body>
  <main class="wrap">
    <h1>Viele Formen von Familie</h1>
    <p class="intro">Hier findet ihr eine Übersicht über das gesamte Programm zur Ausstellung. Die Ausstellung ist von Anfang Oktober bis Dezember 2026 in der Hauptstelle der Stadtbibliothek (Kinder- und Jugendbereich sowie 1. Stock) und im Stadtmuseum zu sehen.</p>
    <p class="notice"><strong>In Kl&ruml;rung:</strong> Kino – drei Sonntagsmatineen mit Familienfilmen.</p>
    <div id="calendar"></div>
  </main>
  <dialog id="eventDialog" class="event-dialog" aria-labelledby="dialogTitle">
    <div class="dialog-inner">
      <button type="button" class="dialog-close" id="dialogClose" aria-label="Details schließen">&times;</button>
      <h2 id="dialogTitle" class="dialog-title"></h2>
      <p id="dialogDate" class="dialog-date"></p>
      <div class="dialog-details">
        <div class="detail-row"><strong>Uhrzeit</strong><span id="dialogTime"></span></div>
        <div class="detail-row"><strong>Zielgruppe</strong><span id="dialogAudience"></span></div>
        <div class="detail-row"><strong>Treffpunkt</strong><span id="dialogPlace"></span></div>
        <div class="detail-row"><strong>Beschreibung</strong><div id="dialogDescription" class="detail-description"></div></div>
        <div id="dialogLinkRow" class="detail-row" hidden><strong>Weitere Informationen / Anmeldung</strong><a id="dialogLink" class="dialog-link" target="_blank" rel="noopener"></a></div>
      </div>
    </div>
  </dialog>
  <script>
    /*
      =====================================================
      HIER ANGEBOTE ERGĨNZEN
      =====================================================
      Jedes Angebot ist ein Objekt mit diesen Feldern:
        - year: 2026
        - month: 10 (Oktober) oder 11 (November) oder 12 (Dezember)
        - day: 1 bis 31
        - time: z. B. "18:00 Uhr" oder "09:00–11:00 Uhr" oder "Ganzt䨤gig"
        - title: Kurzer Titel der Veranstaltung
        - audience: Zielgruppe
        - place: Ort / Treffpunkt
        - description: L&auml;ngere Beschreibung (mehrzeilig erlaubt)
        - link: Optional, z. B. "https://..." oder "mailto:..."
      Einfach ein neues Objekt in die passende Monatsliste einf&uuml;gen.
    */
    const events = [
      // Oktober 2026
      { year: 2026, month: 10, day: 6, time: "20:00–21:00 Uhr", title: "Hochsensible Kinder gut unterst&uuml;tzen und begleiten", audience: "Eltern", place: "Digitales Format", description: "Eltern sein in T&uuml;bingen – so geht's leichter! Welche konkreten Tipps haben T&uuml;binger Expert*innen f&uuml;r Eltern von j&uuml;ngeren und &auml;lteren Kindern – zu Erziehungsthemen, aber auch zu Herausforderungen, die das Elternsein mit sich bringt?\n\nDiese Online-Themenreihe ist eine Kooperation der Universit&auml;tsstadt T&uuml;bingen mit der Familien-Bildungsst&auml;tte (FBS) und dem B&uuml;ndnis f&uuml;r Familie (B&uuml;Fa).\n\nNach der Anmeldung erhalten Sie einen Zoom-Zugangscode. Ein eigener Zoom-Account ist nicht erforderlich.", link: "https://www.fbs-tuebingen.de/eltern-und-lebensthemen" },
      { year: 2026, month: 10, day: 8, time: "18:00 Uhr", title: "Filmvorf&uuml;hrung mit Gespr&auml;ch &uuml;ber den Film „All Inclusive“", audience: "Alle Generationen, f&uuml;r Kinder ab 7 Jahren", place: "Aula des Uhland-Gymnasiums, Uhlandstra&szlig;e 24, 72072 T&uuml;bingen", description: "Filmvorf&uuml;hrung mit Gespr&auml;ch &uuml;ber den Film „All Inclusive“. Die Veranstaltung wird vom Interkulturellen Mehrgenerationenhaus InF&ouml; e. V. und der Lebenshilfe veranstaltet." },
      { year: 2026, month: 10, day: 9, time: "15:00 Uhr", title: "Vorlesezeit in franz&ouml;sischer Sprache mit dem LESE-HAUS", audience: "Kinder von 4 bis 8 Jahren", place: "Stadtb&uuml;cherei T&uuml;bingen, Nonnengasse 19, Thekla-Waitz-Studio", description: "Geschichten und Lieder auf Franz&ouml;sisch laden Kinder spielerisch dazu ein, erste Eindr&uuml;cke der Sprache zu sammeln. Mitarbeitende des Institut Culturel Franco-Allemand (ICFA) gestalten diese besondere Vorlesezeit rund um das Thema Familie." },
      { year: 2026, month: 10, day: 9, time: "16:00 Uhr", title: "Vorlesezeit mit dem LESE-HAUS", audience: "Kinder von 4 bis 8 Jahren", place: "Stadtb&uuml;cherei T&uuml;bingen, Nonnengasse 19, Thekla-Waitz-Studio", description: "Bei dieser Vorlesezeit auf Deutsch stehen Geschichten rund um das Thema Familie im Mittelpunkt – mal lustig, mal spannend, mal ber&uuml;hrend." },
      { year: 2026, month: 10, day: 9, time: "17:00 Uhr", title: "Vorlesezeit in ukrainischer Sprache mit dem LESE-HAUS", audience: "Kinder von 4 bis 8 Jahren", place: "Stadtb&uuml;cherei T&uuml;bingen, Nonnengasse 19, Thekla-Waitz-Studio", description: "Lesepatinnen aus der Ukraine lesen Geschichten in ihrer Muttersprache und geben Einblicke in ukrainische Erz&auml;hltraditionen rund um das Thema Familie." },
      { year: 2026, month: 10, day: 21, time: "17:00 Uhr", title: "Familiensprachen und Sprachencaf&eacute;s im elkiko Familienzentrum", audience: "T&uuml;binger Familien und Interessierte", place: "Stadtb&uuml;cherei, Hauptstelle", description: "Mitmachangebote zum Thema Familiensprachen sowie Informationen zu den Sprachencaf&eacute;s im elkiko Familienzentrum." },
      { year: 2026, month: 10, day: 22, time: "18:00 Uhr", title: "Preisverleihung des 23. Schreibwettbewerbs f&uuml;r junge Menschen der Buchhandlung Wekenmann", audience: "Alle Interessierten", place: "Museumsgesellschaft Obere S&auml;le, Wilhelmstra&szlig;e 3, T&uuml;bingen", description: "Festliche Preisverleihung: Die sechs Preistr&auml;ger*innen des Schreibwettbewerbs „We are family“ lesen aus ihren Siegertexten. Dr. Daniela H&uuml;ttig, Erste Landesbeamtin und Schirmherrin des Wettbewerbs, spricht ein Gru&szlig;wort. Musikalisch umrahmt wird der Abend von Singer/Songwriter Jakob Longfield." },
      { year: 2026, month: 10, day: 31, time: "15:00 Uhr", title: "BamBam-Band &amp; Halloween Fair for Kids", audience: "Familien mit Kindern ab 3 Jahren", place: "d.a.i. T&uuml;bingen, Karlstra&szlig;e 3, T&uuml;bingen", description: "Konzert und Halloween f&uuml;r die ganze Familie. Ab 15 Uhr spielt die BamBam-Band. Ab 16 Uhr hei&szlig;t es Trick or Treat: Kinderdisco, Basteln, Gaming und jede Menge Spa&szlig; mit Popcorn und Snacks.\n\nAlles kostenfrei auf Spendenbasis. Kost&uuml;me sind sehr willkommen. Die Veranstaltung findet in deutscher und englischer Sprache statt.", link: "https://www.dai-tuebingen.de/bambam" },
      // November 2026
      { year: 2026, month: 11, day: 5, time: "09:00–11:00 Uhr", title: "„Der Widerspenstigen Z&auml;hmung?“ – Workshop", audience: "Eltern von Kindern im Vorschulalter", place: "Jugend- und Familienberatungszentrum T&uuml;bingen, Bismarckstra&szlig;e 110, 72072 T&uuml;bingen, 2. Stock", description: "Manche Kinder sind Weltmeister darin, Erwachsene an ihre Grenzen zu bringen. Gemeinsam soll ein Verst&auml;ndnis f&uuml;r als schwierig empfundene kindliche Verhaltensweisen entwickelt werden. Ziel ist au&szlig;erdem, neue Reaktionsweisen auszuprobieren, um unn&ouml;tige Machtk&auml;mpfe im Familienalltag zu vermeiden.", link: "mailto:jfbz-tue@kreis-tuebingen.de" },
      { year: 2026, month: 11, day: 5, time: "19:00 Uhr", title: "Nach der Elternzeit – welche Betreuung passt f&uuml;r mein Kind und unsere Familie?", audience: "(Werdende) Eltern, die sich &uuml;ber Kleinkindbetreuung informieren m&ouml;chten", place: "Online", description: "Um eine passende Betreuung nach der Elternzeit zu finden, braucht es einen &Uuml;berblick &uuml;ber die verschiedenen M&ouml;glichkeiten der Kinderbetreuung. Welches Modell passt f&uuml;r welches Kind und welche Familie, welche Kriterien k&ouml;nnen eine Rolle spielen und was sind die Besonderheiten der Kindertagespflege?", link: "mailto:info@tageselternverein.de" },
      { year: 2026, month: 11, day: 9, time: "18:00–20:00 Uhr", title: "Wassergew&ouml;hnung – wie unterst&uuml;tze ich mein Kind?", audience: "Alle Eltern und Erziehungsberechtigten", place: "Hirsch Begegnungsst&auml;tte, Hirschgasse 9, 72070 T&uuml;bingen", description: "Schwimmen f&uuml;r alle Kinder informiert zu einem besseren Verst&auml;ndnis von Wassergew&ouml;hnung. Enthalten sind alltagspraktische &Uuml;bungen f&uuml;r Kinder vor dem Schwimmkurs. Eintritt frei, Getr&auml;nke gegen Spende." },
      { year: 2026, month: 11, day: 10, time: "18:00 Uhr", title: "Live-Show mit Miriam Davoudvandi: Das k&ouml;nnen wir uns nicht leisten", audience: "Alle Interessierten", place: "Gemeindehaus Lamm, Gro&szlig;er Saal 1, Am Markt 7, T&uuml;bingen", description: "Was es bedeutet, in Deutschland arm aufzuwachsen: Journalistin und Podcasterin Miriam Davoudvandi (aka Cashmiri) bringt mit dem aktuellen Buch „Das k&ouml;nnen wir uns nicht leisten“ ihre Geschichte mit Texten, pers&ouml;nlichen Geschichten, Humor und Pop auf die B&uuml;hne." },
      { year: 2026, month: 11, day: 10, time: "17:00–19:00 Uhr", title: "Das Geheimnis des Bildungserfolgs, Teil 1: Entwicklungsaufgaben", audience: "Eltern von Kindern der 1. bis 6. Klasse", place: "Br&uuml;ckenhaus, Werkstra&szlig;e 8, 72074 T&uuml;bingen", description: "Teil 1: Entwicklungsaufgaben, Eltern-Kind-Bindung, Mutter und Vater als Unterst&uuml;tzer*innen, Lernen und Selbstwirksamkeit. Wie k&ouml;nnen Eltern ihr Kind unterst&uuml;tzen, damit es in der Schule gut zurechtkommt? Zu diesen Fragen gibt es jeweils einen kurzen Vortrag auf Deutsch, Englisch, Arabisch und Persisch." },
      { year: 2026, month: 11, day: 11, time: "19:00–21:00 Uhr", title: "„Zu Hause in einer Erziehungsstelle“ – Podiumsdiskussion", audience: "Eltern", place: "Lorettoplatz 30, T&uuml;bingen", description: "Vorstellung von kit jugendhilfe mit dem Schwerpunkt auf professionellen Pflegefamilien und anschlie&szlig;ender Diskussionsrunde. Eine kurze R&uuml;ckmeldung zur Teilnahme w&auml;re sch&ouml;n, ist aber nicht zwingend notwendig.", link: "mailto:andrea.leitgeb@kit-jugendhilfe.de" },
      { year: 2026, month: 11, day: 15, time: "14:00–17:00 Uhr", title: "Familienbasteln im Caf&eacute; frieDa", audience: "Familien mit Kindern, 0 bis 99 Jahre", place: "Caf&eacute; frieDa, Friedrich-Dannenmann-Stra&szlig;e 69, 72070 T&uuml;bingen", description: "Bunte Bastelstunde im Caf&eacute; frieDa mit Angeboten f&uuml;r die ganz Kleinen und Gr&ouml;&szlig;eren. Unter anderem werden Leinenbeutel bemalt und bedruckt. F&uuml;r die gro&szlig;en G&auml;ste gibt es Kaffee und Kuchen auf Spendenbasis. Keine Anmeldung erforderlich." },
      { year: 2026, month: 11, day: 17, time: "17:00–19:00 Uhr", title: "Das Geheimnis des Bildungserfolgs, Teil 2: Erfolg in Schule, Bildung und Teilhabe", audience: "Eltern von Kindern der 1. bis 6. Klasse", place: "Br&uuml;ckenhaus, Werkstra&szlig;e 8, 72074 T&uuml;bingen", description: "Das Geheimnis des Bildungserfolgs – was Ihr Kind von Ihnen als Mutter und Vater braucht. Drei internationale Pr&auml;senz-Elternabende auf Deutsch, Englisch, Arabisch und Persisch f&uuml;r Eltern von Kindern der 1. bis 6. Klasse." },
      { year: 2026, month: 11, day: 19, time: "09:30–11:30 Uhr", title: "Zutaten f&uuml;r ein starkes Familienleben", audience: "Eltern mit Kindern im Alter von 0 bis 3 Jahren", place: "Jugend- und Familienberatungszentrum T&uuml;bingen, Bismarckstra&szlig;e 110, 72072 T&uuml;bingen, 2. Stock", description: "Rezeptideen f&uuml;r ein starkes Familienleben – Zutaten: Feinf&uuml;hligkeit, Zeit, Vertrauen und mehr. In gem&uuml;tlicher und kleinkindorientierter Atmosph&auml;re wird gemeinsam dar&uuml;ber gesprochen, was f&uuml;r ein starkes Familienleben sorgt.", link: "mailto:fruehehilfen@kreis-tuebingen.de" },
      { year: 2026, month: 11, day: 21, time: "10:00–15:00 Uhr", title: "Bastelspa&szlig; f&uuml;r die ganze Familie – Thanksgiving Edition", audience: "Familien mit Kindern aller Altersgruppen", place: "d.a.i. T&uuml;bingen, Karlstra&szlig;e 3, Bibliothek", description: "Kommen Sie ins d.a.i. T&uuml;bingen und basteln Sie gemeinsam wundersch&ouml;ne Thanksgiving-Dekorationen. Feiern Sie den Tag der Dankbarkeit bei einer gem&uuml;tlichen Bastelrunde in der d.a.i.-Bibliothek." },
      { year: 2026, month: 11, day: 24, time: "17:00–19:00 Uhr", title: "Das Geheimnis des Bildungserfolgs, Teil 3: Medienerziehung", audience: "Eltern von Kindern der 1. bis 6. Klasse", place: "Br&uuml;ckenhaus, Werkstra&szlig;e 8, 72074 T&uuml;bingen", description: "Das Geheimnis des Bildungserfolgs – was Ihr Kind von Ihnen als Mutter und Vater braucht. Drei internationale Pr&auml;senz-Elternabende auf Deutsch, Englisch, Arabisch und Persisch." },
      { year: 2026, month: 11, day: 24, time: "Ganzt&auml;gig", title: "Gemeinsam gegen Abfall – Papierrecycling in der Stadtb&uuml;cherei", audience: "Familien", place: "Stadtb&uuml;cherei T&uuml;bingen, Nonnengasse 19, T&uuml;bingen", description: "Im Rahmen der Europ&auml;ischen Woche der Abfallvermeidung bietet die Stadtb&uuml;cherei T&uuml;bingen eine besondere Mitmachaktion an: An einem Papierbindetisch k&ouml;nnen aus einseitig bedrucktem Altpapier Spiralbl&ouml;cke hergestellt werden. Papier ist vor Ort verf&uuml;gbar, kann aber auch gerne mitgebracht werden." },
      { year: 2026, month: 11, day: 25, time: "Ganzt&auml;gig", title: "Gemeinsam gegen Abfall – Papierrecycling in der Stadtb&uuml;cherei", audience: "Familien", place: "Stadtb&uuml;cherei T&uuml;bingen, Nonnengasse 19, T&uuml;bingen", description: "Im Rahmen der Europ&auml;ischen Woche der Abfallvermeidung bietet die Stadtb&uuml;cherei T&uuml;bingen eine besondere Mitmachaktion an: An einem Papierbindetisch k&ouml;nnen aus einseitig bedrucktem Altpapier Spiralbl&ouml;cke hergestellt werden. Papier ist vor Ort verf&uuml;gbar, kann aber auch gerne mitgebracht werden." },
      { year: 2026, month: 11, day: 26, time: "Ganzt&auml;gig", title: "Gemeinsam gegen Abfall – Papierrecycling in der Stadtb&uuml;cherei", audience: "Familien", place: "Stadtb&uuml;cherei T&uuml;bingen, Nonnengasse 19, T&uuml;bingen", description: "Im Rahmen der Europ&auml;ischen Woche der Abfallvermeidung bietet die Stadtb&uuml;cherei T&uuml;bingen eine besondere Mitmachaktion an: An einem Papierbindetisch k&ouml;nnen aus einseitig bedrucktem Altpapier Spiralbl&ouml;cke hergestellt werden. Papier ist vor Ort verf&uuml;gbar, kann aber auch gerne mitgebracht werden." }
    ];
    /*
      =====================================================
      AB HIER NICHTS MEHR ÄNDERN (au&szlig;er bei Bedarf)
      =====================================================
    */
    const monthNames = { 10: "Oktober", 11: "November", 12: "Dezember" };
    const weekdayNames = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"];
    function getDaysInMonth(year, month) {
      return new Date(year, month, 0).getDate();
    }
    function getWeekday(year, month, day) {
      const date = new Date(year, month - 1, day);
      let wd = date.getDay(); // 0 = Sonntag
      return wd === 0 ? 6 : wd - 1; // 0 = Montag
    }
    function escapeHtml(str) {
      return str
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;");
    }
    function renderCalendar() {
      const container = document.getElementById("calendar");
      const monthsToRender = [10, 11, 12];
      monthsToRender.forEach(month => {
        const monthEvents = events.filter(e => e.month === month && e.year === 2026);
        if (monthEvents.length === 0) return;
        const section = document.createElement("section");
        section.className = "month";
        section.setAttribute("aria-labelledby", `month-${month}`);
        const heading = document.createElement("h2");
        heading.id = `month-${month}`;
        heading.textContent = `${monthNames[month]} 2026`;
        section.appendChild(heading);
        const weekdays = document.createElement("div");
        weekdays.className = "weekdays";
        weekdays.setAttribute("aria-hidden", "true");
        weekdayNames.forEach(name => {
          const div = document.createElement("div");
          div.textContent = name;
          weekdays.appendChild(div);
        });
        section.appendChild(weekdays);
        const grid = document.createElement("div");
        grid.className = "grid";
        const daysInMonth = getDaysInMonth(2026, month);
        const firstWeekday = getWeekday(2026, month, 1);
        // Leere Tage vor dem 1. des Monats
        for (let i = 0; i < firstWeekday; i++) {
          const prevDay = getDaysInMonth(2026, month - 1) - firstWeekday + 1 + i;
          const dayDiv = document.createElement("div");
          dayDiv.className = "day muted";
          const dn = document.createElement("div");
          dn.className = "dn";
          dn.textContent = prevDay;
          dayDiv.appendChild(dn);
          grid.appendChild(dayDiv);
        }
        // Eigentliche Tage
        for (let day = 1; day <= daysInMonth; day++) {
          const dayEvents = monthEvents.filter(e => e.day === day);
          const dayDiv = document.createElement("div");
          dayDiv.className = "day";
          if (dayEvents.length > 0) dayDiv.classList.add("has-events");
          const dn = document.createElement("div");
          dn.className = "dn";
          dn.textContent = day;
          dayDiv.appendChild(dn);
          dayEvents.forEach(ev => {
            const btn = document.createElement("button");
            btn.type = "button";
            btn.className = "event";
            btn.setAttribute("data-date", `${day}. ${monthNames[month]} 2026`);
            btn.setAttribute("data-time", ev.time);
            btn.setAttribute("data-audience", ev.audience);
            btn.setAttribute("data-place", ev.place);
            btn.setAttribute("data-description", ev.description);
            if (ev.link) btn.setAttribute("data-link", ev.link);
            const timeSpan = document.createElement("span");
            timeSpan.className = "event-time";
            timeSpan.textContent = ev.time;
            const titleSpan = document.createElement("span");
            titleSpan.className = "et";
            titleSpan.textContent = ev.title;
            btn.appendChild(timeSpan);
            btn.appendChild(titleSpan);
            dayDiv.appendChild(btn);
          });
          grid.appendChild(dayDiv);
        }
        // Leere Tage nach dem letzten Tag des Monats (auff&uuml;llen auf volle Wochen)
        const totalCells = firstWeekday + daysInMonth;
        const remaining = (7 - (totalCells % 7)) % 7;
        for (let i = 1; i <= remaining; i++) {
          const dayDiv = document.createElement("div");
          dayDiv.className = "day muted";
          const dn = document.createElement("div");
          dn.className = "dn";
          dn.textContent = i;
          dayDiv.appendChild(dn);
          grid.appendChild(dayDiv);
        }
        section.appendChild(grid);
        container.appendChild(section);
      });
    }
    // Dialog-Logik
    (() => {
      const dialog = document.getElementById("eventDialog");
      const closeButton = document.getElementById("dialogClose");
      const title = document.getElementById("dialogTitle");
      const date = document.getElementById("dialogDate");
      const time = document.getElementById("dialogTime");
      const audience = document.getElementById("dialogAudience");
      const place = document.getElementById("dialogPlace");
      const description = document.getElementById("dialogDescription");
      const linkRow = document.getElementById("dialogLinkRow");
      const link = document.getElementById("dialogLink");
      document.addEventListener("click", (event) => {
        if (!event.target.classList.contains("event")) return;
        const btn = event.target;
        title.textContent = btn.querySelector(".et").textContent.trim();
        date.textContent = btn.dataset.date || "";
        time.textContent = btn.dataset.time || "Nicht angegeben";
        audience.textContent = btn.dataset.audience || "Nicht angegeben";
        place.textContent = btn.dataset.place || "Nicht angegeben";
        description.textContent = btn.dataset.description || "Keine Beschreibung vorhanden.";
        const url = btn.dataset.link;
        if (url) {
          linkRow.hidden = false;
          link.href = url;
          link.textContent = url.startsWith("mailto:") ? url.replace("mailto:", "") : url;
          link.target = url.startsWith("mailto:") ? "_self" : "_blank";
        } else {
          linkRow.hidden = true;
          link.removeAttribute("href");
          link.textContent = "";
        }
        dialog.showModal();
      });
      closeButton.addEventListener("click", () => dialog.close());
      dialog.addEventListener("click", (event) => {
        if (event.target === dialog) dialog.close();
      });
    })();
    // Kalender beim Laden rendern
    renderCalendar();
  </script>
</body>
</html>


<!doctype html>
<!doctype html>

Version vom 7. September 2026, 12:20 Uhr

<!doctype html> Viele Formen von Familie – Programm 2026

Viele Formen von Familie

Hier findet ihr eine Übersicht über das gesamte Programm zur Ausstellung. Die Ausstellung ist von Anfang Oktober bis Dezember 2026 in der Hauptstelle der Stadtbibliothek (Kinder- und Jugendbereich sowie 1. Stock) und im Stadtmuseum zu sehen.

In Kl&ruml;rung: Kino – drei Sonntagsmatineen mit Familienfilmen.

Uhrzeit
Zielgruppe
Treffpunkt
Beschreibung





<!doctype html> Viele Formen von Familie – Programm 2026

Viele Formen von Familie

Hier findet ihr eine Übersicht über das gesamte Programm zur Ausstellung. Die Ausstellung ist von Anfang Oktober bis Dezember 2026 in der Hauptstelle der Stadtbücherei (Kinder- und Jugendbereich sowie 1. Stock) und im Stadtmuseum zu sehen.

In Klärung: Kino – drei Sonntagsmatineen mit Familienfilmen.

Oktober 2026

28
29
30
1
2
3
4
5
6. Oktober
7
8. Oktober
9. Oktober
10
11
12
13
14
15
16
17
18
19
20
21. Oktober
22. Oktober
23
24
25
26
27
28
29
30
31. Oktober
1

November 2026

26
27
28
29
30
31
1
2
3
4
5. November
6
7
8
9. November
10. November
11. November
12
13
14
15. November
16
17. November
18
19. November
20
21. November
22
23
24. November
25. November
26. November
27
28
29
30
1
2
3
4
5
6

Uhrzeit
Zielgruppe
Treffpunkt
Beschreibung