{"id":19103,"date":"2024-05-29T18:58:36","date_gmt":"2024-05-29T16:58:36","guid":{"rendered":"https:\/\/johanneum-lueneburg.de\/wordpress\/?page_id=19103"},"modified":"2026-02-15T14:25:17","modified_gmt":"2026-02-15T13:25:17","slug":"abiturrechner-z8whis2ns2lqx","status":"publish","type":"page","link":"https:\/\/johanneum-lueneburg.de\/wordpress\/abiturrechner-z8whis2ns2lqx\/","title":{"rendered":"Abiturrechner Lehrer"},"content":{"rendered":"<section class=\"l-section wpb_row height_auto\"><div class=\"l-section-h i-cf\"><div class=\"g-cols vc_row via_flex valign_top type_default stacking_default\"><div class=\"vc_col-sm-12 wpb_column vc_column_container\"><div class=\"vc_column-inner\"><div class=\"wpb_wrapper\"><div class=\"w-html\"><!doctype html>\n<html lang=\"de\">\n<head>\n  <meta charset=\"utf-8\" \/>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" \/>\n  <title>Abiturrechner Lehrer \u2013 Gymnasium Johanneum L\u00fcneburg (Offline)<\/title>\n  <meta name=\"theme-color\" content=\"#ffffff\" \/>\n  <style>\n    :root{\n      --bg: #ffffff;\n      --fg: #111827;\n      --muted: #6b7280;\n      --line: #e5e7eb;\n      --card: #ffffff;\n      --shadow: 0 8px 24px rgba(0,0,0,.08);\n      --good: #4CAF50;\n      --bad: #F44336;\n      --warn: #b45309;\n      --neutral: #374151;\n      --info: #60a5fa;\n      --chip: #111827;\n      --chipText: #ffffff;\n      --focus: #2563eb;\n      --radius: 12px;\n      --font: system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,Arial,\"Apple Color Emoji\",\"Segoe UI Emoji\";\n    }\n\n    *{ box-sizing: border-box; }\n\n    body{\n      margin: 0;\n      font-family: var(--font);\n      color: var(--fg);\n      background: var(--bg);\n    }\n\n    .wrap{\n      max-width: 640px;\n      margin: 28px auto 60px;\n      padding: 0 16px;\n    }\n\n    header{\n      text-align: center;\n      margin-bottom: 16px;\n    }\n\n    h1{\n      margin: 0;\n      font-size: clamp(26px, 3.2vw, 40px);\n      letter-spacing: .2px;\n    }\n\n    .subtitle{\n      margin-top: 4px;\n      color: var(--muted);\n      font-size: 16px;\n    }\n\n    .meta{\n      margin-top: 10px;\n      color: var(--muted);\n      font-size: 13px;\n    }\n\n    .toolbar{\n      display: flex;\n      gap: 10px;\n      justify-content: center;\n      flex-wrap: wrap;\n      margin: 18px 0 18px;\n    }\n\n    .savedmeta{\n      text-align: center;\n      color: var(--muted);\n      font-size: 13px;\n      margin: -10px 0 18px;\n    }\n\n    .savedmeta strong{\n      color: var(--fg);\n      font-weight: 700;\n    }\n\n    button{\n      border: 1px solid var(--line);\n      background: #f9fafb;\n      padding: 10px 14px;\n      border-radius: 10px;\n      cursor: pointer;\n      font-weight: 600;\n    }\n\n    button:hover{ background: #f3f4f6; }\n    button:active{ transform: translateY(1px); }\n\n    .card{\n      background: var(--card);\n      border: 1px solid var(--line);\n      border-radius: var(--radius);\n      box-shadow: var(--shadow);\n      overflow: hidden;\n    }\n\n    .grid{\n      display: grid;\n      \/* P-Fach soll klar die breiteste Spalte bleiben (ca. 1\/3 der Gesamtbreite) *\/\n      grid-template-columns: minmax(200px, 2fr) minmax(90px, 1fr) minmax(90px, 1fr) minmax(90px, 1fr) minmax(65px, 1fr);\n      \/* Wichtig: In manchen Browsern fuehrt align-items:center dazu, dass Zellen in einer Zeile unterschiedliche Hoehen annehmen.\n         Mit stretch bleiben die Zeilen sauber ausgerichtet; die vertikale Zentrierung passiert innerhalb der .cell per Flex. *\/\n      align-items: stretch;\n    }\n\n    .grid .head{\n      background: #f9fafb;\n      font-weight: 700;\n      border-bottom: 1px solid var(--line);\n    }\n\n    .cell{\n      padding: 8px 10px;\n      border-bottom: 1px solid var(--line);\n      min-height: 48px;\n      display: flex;\n      align-items: center;\n      gap: 8px;\n    }\n\n    .grid .cell:nth-child(5n){ justify-content: center; }\n\n    .grid .cell:nth-child(5n-1){ justify-content: center; }\n\n    .grid .cell:nth-child(5n-2),\n    .grid .cell:nth-child(5n-3){ justify-content: center; }\n\n    .grid .cell:nth-child(5n-4){ justify-content: flex-start; }\n\n    .grid .row:last-child .cell{ border-bottom: 0; }\n\n    .head .cell{ min-height: 44px; }\n\n    .field-label{\n      font-weight: 700;\n      font-size: 16px;\n    }\n\n    .p-tag{\n      display: inline-flex;\n      align-items: center;\n      justify-content: center;\n      min-width: 34px;\n      padding: 6px 8px;\n      border-radius: 999px;\n      background: #e0f2fe;\n      border: 1px solid #bae6fd;\n      color: #0f172a;\n      font-weight: 800;\n      font-size: 13px;\n      line-height: 1;\n      flex: 0 0 auto;\n    }\n\n    label.visually-hidden{\n      position: absolute;\n      width: 1px;\n      height: 1px;\n      padding: 0;\n      margin: -1px;\n      overflow: hidden;\n      clip: rect(0, 0, 0, 0);\n      white-space: nowrap;\n      border: 0;\n    }\n\n    input[type=\"text\"], input[type=\"number\"]{\n      width: 100%;\n      border: 1px solid var(--line);\n      border-radius: 10px;\n      padding: 6px 10px;\n      height: 34px;\n      line-height: 1.1;\n      font-size: 14px;\n      outline: none;\n      background: #ffffff;\n      -webkit-appearance: none;\n      appearance: none;\n    }\n\n    select.select{\n      border: 1px solid var(--line);\n      background: #ffffff;\n      border-radius: 10px;\n      padding: 6px 10px;\n      height: 40px;\n      font-size: 14px;\n      font-weight: 600;\n      color: var(--fg);\n      min-width: min(420px, 90vw);\n      max-width: 100%;\n    }\n\n    select.select:disabled{\n      color: var(--muted);\n      background: #f9fafb;\n    }\n\n    \/* Modal (Vornoten) *\/\n    .modal{\n      position: fixed;\n      inset: 0;\n      display: none;\n      align-items: center;\n      justify-content: center;\n      padding: 16px;\n      background: rgba(17,24,39,.55);\n      z-index: 50;\n    }\n\n    .modal.show{ display: flex; }\n\n    .modal-card{\n      width: min(520px, 96vw);\n      background: var(--card);\n      border: 1px solid var(--line);\n      border-radius: 16px;\n      box-shadow: var(--shadow);\n      overflow: hidden;\n    }\n\n    .modal-head{\n      display: flex;\n      align-items: center;\n      justify-content: space-between;\n      padding: 12px 14px;\n      background: #f9fafb;\n      border-bottom: 1px solid var(--line);\n      font-weight: 800;\n    }\n\n    .modal-body{\n      padding: 12px 14px;\n      font-size: 14px;\n      line-height: 1.5;\n    }\n\n    .modal-close{\n      border: 1px solid var(--line);\n      background: #ffffff;\n      border-radius: 10px;\n      padding: 6px 10px;\n      cursor: pointer;\n      font-weight: 800;\n    }\n\n    input[type=\"number\"]{\n      text-align: center;\n      max-width: 110px;\n    }\n\n    \/* Block I Eingabe kompakter (PB1) *\/\n    #PB1{\n      max-width: 140px;\n      height: 30px;\n      padding: 4px 10px;\n      line-height: 1.05;\n    }\n\n    input:focus{\n      border-color: var(--focus);\n      box-shadow: 0 0 0 3px rgba(37,99,235,.15);\n    }\n\n    input:invalid{\n      border-color: var(--bad);\n    }\n\n    .hint{\n      margin-top: 6px;\n      font-size: 12px;\n      color: var(--muted);\n    }\n\n    .error{\n      margin-top: 6px;\n      font-size: 12px;\n      color: var(--bad);\n      display: none;\n    }\n\n    .error.show{ display: block; }\n\n    .points{\n      display: inline-flex;\n      align-items: center;\n      justify-content: center;\n      min-width: 56px;\n      padding: 6px 10px;\n      border-radius: 10px;\n      color: var(--chipText);\n      background: var(--neutral);\n      font-weight: 800;\n      font-variant-numeric: tabular-nums;\n    }\n\n    .points.good{ background: var(--good); }\n    .points.bad{ background: var(--bad); }\n\n    .points.blank{\n      background: transparent;\n      color: transparent;\n      padding: 0;\n      min-width: 0;\n    }\n\n    .diff{\n      font-weight: 700;\n      font-variant-numeric: tabular-nums;\n    }\n\n    .diff.good{ color: var(--good); }\n    .diff.bad{ color: var(--bad); }\n\n    .summary{\n      display: flex;\n      gap: 16px;\n      justify-content: center;\n      flex-wrap: wrap;\n      margin: 18px 0 14px;\n    }\n\n    .chip{\n      display: inline-flex;\n      align-items: center;\n      gap: 8px;\n      padding: 10px 12px;\n      border: 1px solid var(--line);\n      border-radius: 999px;\n      background: #f9fafb;\n      font-weight: 700;\n    }\n\n    .chip .value{\n      display: inline-flex;\n      align-items: center;\n      justify-content: center;\n      min-width: 34px;\n      padding: 6px 10px;\n      border-radius: 999px;\n      color: var(--chipText);\n      background: var(--neutral);\n      font-variant-numeric: tabular-nums;\n    }\n\n    .value.good{ background: var(--good); }\n    .value.bad{ background: var(--bad); }\n\n    .divider{\n      height: 1px;\n      background: var(--line);\n      margin: 18px 0;\n    }\n\n    .final{\n      text-align: center;\n      padding: 18px 16px;\n    }\n\n    .status{\n      font-size: 22px;\n      font-weight: 900;\n      margin-bottom: 10px;\n    }\n\n    .status .badge{\n      display: inline-flex;\n      align-items: center;\n      justify-content: center;\n      width: 34px;\n      height: 34px;\n      border-radius: 10px;\n      margin-left: 10px;\n      color: #ffffff;\n      font-weight: 900;\n    }\n\n    .badge.good{ background: var(--good); }\n    .badge.bad{ background: var(--bad); }\n    .badge.neutral{ background: var(--info); }\n\n    .status.fhrline{\n      margin: 0;\n    }\n\n    .final-grid{\n      display: grid;\n      gap: 12px;\n      justify-content: center;\n      margin-top: 10px;\n    }\n\n    .final-row{\n      font-size: 18px;\n      font-weight: 700;\n    }\n\n    .final-row strong{\n      font-weight: 800;\n    }\n\n    .note{\n      font-weight: 700;\n    }\n\n    #abiturNote{\n      font-weight: 800;\n    }\n\n    #fhrNoteValue{\n      font-weight: 800;\n    }\n\n    \/* FHR-Zeile standardmaessig ausblenden (wird per JS bei Bedarf eingeblendet) *\/\n    .fhr{ display: none; }\n\n    .toast{\n      position: fixed;\n      left: 50%;\n      bottom: 18px;\n      transform: translateX(-50%);\n      background: rgba(17,24,39,.92);\n      color: #ffffff;\n      padding: 10px 14px;\n      border-radius: 999px;\n      font-weight: 700;\n      opacity: 0;\n      pointer-events: none;\n      transition: opacity .18s ease-in-out;\n    }\n\n    .toast.show{ opacity: 1; }\n\n    \/* Tablet\/kleinere Desktopbreiten: iPad (hochkant) liegt typischerweise bei 768 CSS-Pixeln.\n       Der vorherige Breakpoint 760px griff daher nicht zuverlaessig; dadurch konnte die Diff.-Spalte abgeschnitten sein. *\/\n    @media (max-width: 900px){\n      .grid{\n        \/* Auch auf Tablet: P-Fach bleibt dominanter, aber die letzte Spalte muss sicher sichtbar bleiben *\/\n        grid-template-columns: minmax(120px, 2fr) minmax(74px, 1fr) minmax(74px, 1fr) minmax(82px, 1fr) minmax(58px, 1fr);\n        column-gap: 0;\n      }\n      \/* Auf Tablet etwas kompakter, ohne Desktop-Layout zu veraendern *\/\n      input[type=\"text\"], input[type=\"number\"]{ padding: 5px 8px; height: 32px; line-height: 1.1; }\n      input[type=\"number\"]{ max-width: 84px; }\n      .cell{ padding: 8px 8px; min-height: 44px; }\n    }\n\n    @media (max-width: 560px){\n      #PB1{ height: 28px; padding: 3px 8px; }\n      \/* Mobile: Eingabefelder kompakter (verhindert uebergrosse Hoehe auf Smartphones) *\/\n      .cell{ min-height: 38px; }\n      .head .cell{ min-height: 36px; }\n      input[type=\"text\"], input[type=\"number\"]{ padding: 4px 8px; height: 30px; line-height: 1.05; }\n\n      \/* Mobile: schmale Spalten, Diff bleibt sichtbar *\/\n      .grid{\n        \/* Mobile: P-Fach bekommt den groessten Anteil der Restbreite *\/\n        grid-template-columns: minmax(86px, 2fr) minmax(62px, 1fr) minmax(62px, 1fr) minmax(68px, 1fr) minmax(46px, 1fr);\n      }\n      input[type=\"number\"]{ max-width: 72px; }\n      .cell{ padding: 6px 6px; }\n      .subtitle{ font-size: 14px; }\n      .meta{ font-size: 12px; }\n    }\n\n    @media print{\n      .toolbar, .toast{ display: none !important; }\n      .card{ box-shadow: none; }\n    }\n  <\/style>\n<\/head>\n<body>\n  <div class=\"wrap\">\n    <header>\n      <h1>Abiturrechner Lehrer<\/h1>\n      <div class=\"subtitle\">Gymnasium Johanneum L\u00fcneburg<\/div>\n      <div class=\"meta\">Version 3.07 (15.02.2026, F\u00fc)<\/div>\n    <\/header>\n\n    <div class=\"toolbar\">\n      <button type=\"button\" id=\"btnReset\">Reset<\/button>\n      <button type=\"button\" id=\"btnImport\">Import<\/button>\n      <input type=\"file\" id=\"fileInput\" accept=\".csv,text\/csv\" hidden>\n      <select id=\"csvValue\" class=\"select\" aria-label=\"Sch\u00fclerauswahl\" disabled><option value=\"\">(keine CSV geladen)<\/option><\/select>\n      <button type=\"button\" id=\"btnHistory\" disabled>Vornoten<\/button>\n      <button type=\"button\" id=\"btnPrev\" disabled>vorheriger Sch\u00fcler<\/button>\n      <button type=\"button\" id=\"btnNext\" disabled>n\u00e4chster Sch\u00fcler<\/button>\n    <\/div>\n\n    <div class=\"card\" aria-label=\"Eingabe der P-Ergebnisse\">\n      <div class=\"grid head\" role=\"rowgroup\">\n        <div class=\"cell\" role=\"columnheader\">P\u2011Fach<\/div>\n        <div class=\"cell\" role=\"columnheader\" style=\"justify-content:center;\">schr.<\/div>\n        <div class=\"cell\" role=\"columnheader\" style=\"justify-content:center;\">m\u00fcndl.<\/div>\n        <div class=\"cell\" role=\"columnheader\" style=\"justify-content:center;\">Punkte<\/div>\n        <div class=\"cell\" role=\"columnheader\" style=\"justify-content:center;\">Diff.<\/div>\n      <\/div>\n\n      <div id=\"rows\" role=\"rowgroup\"><\/div>\n    <\/div>\n\n    <div class=\"summary\" aria-label=\"Zwischenauswertung\">\n      <div class=\"chip\">Anz. Unterkurse = <span id=\"underCourses\" class=\"value\">0<\/span><\/div>\n      <div class=\"chip\">Abitur\u2011Punkte = <span id=\"totalPoints\" class=\"value\">0<\/span><\/div>\n    <\/div>\n\n    <div class=\"divider\" role=\"separator\"><\/div>\n\n    <div class=\"card final\" aria-label=\"Gesamtauswertung\">\n      <div id=\"abiturStatus\" class=\"status\">Abitur\u2011Pr\u00fcfung <span class=\"badge neutral\" aria-hidden=\"true\">?<\/span><\/div>\n\n      <div style=\"max-width: 360px; margin: 0 auto; text-align: left;\">\n        <label for=\"PB1\" class=\"field-label\">Punkte aus Block I = <\/label>\n        <input type=\"number\" id=\"PB1\" min=\"200\" max=\"600\" step=\"1\" inputmode=\"numeric\" placeholder=\"200\u2013600\" \/>\n        <div id=\"PB1Error\" class=\"error\">Bitte eine Zahl zwischen 200 und 600 eingeben.<\/div>\n      <\/div>\n\n      <div class=\"final-grid\">\n        <div class=\"final-row\">Gesamtpunkte = <strong id=\"finalPoints\">Block\u2011I\u2011Punkte fehlen<\/strong><\/div>\n        <div class=\"final-row\"><span class=\"note\">Abitur\u2011Note = <span id=\"abiturNote\">Block\u2011I\u2011Punkte fehlen<\/span><\/span><\/div>\n        <div id=\"fhrStatusDisplay\" class=\"status fhr fhrline\"><\/div>\n        <div id=\"fhrNoteDisplay\" class=\"final-row fhr\">\n          <span class=\"note\">FHR\u2011Note = <span id=\"fhrNoteValue\"><\/span><\/span>\n        <\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <div id=\"historyModal\" class=\"modal\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"historyTitle\">\n    <div class=\"modal-card\">\n      <div class=\"modal-head\">\n        <div id=\"historyTitle\">Vornoten<\/div>\n        <button type=\"button\" id=\"historyClose\" class=\"modal-close\" aria-label=\"Schlie\u00dfen\">\u00d7<\/button>\n      <\/div>\n      <div id=\"historyContent\" class=\"modal-body\"><\/div>\n    <\/div>\n  <\/div>\n\n  <div id=\"toast\" class=\"toast\" role=\"status\" aria-live=\"polite\"><\/div>\n\n  <script>\n    'use strict';\n\n    \/\/ =========================================================\n    \/\/ Abiturrechner \u2013 Version 3.07\n    \/\/ =========================================================\n\n    const CONFIG = Object.freeze({\n      rows: 5,\n      minGradePoints: 0,\n      maxGradePoints: 15,\n      blockI: { min: 200, max: 600 },\n      \/\/ Bestehen: (a) Summe P-Ergebnisse >= 100 und (b) mind. 3 Ergebnisse >= 20 Punkte\n      pass: { minTotalExamPoints: 100, minResultsAtLeast20: 3 },\n    });\n\n    \/\/ FHR-Parameter (werden ggf. aus der URL gelesen; Verhalten wie in V1.0)\n    let FHRNote = 0;\n    let FHRStatus = 0; \/\/ erwartet: 0,1,2\n\n    const elRows = document.getElementById('rows');\n    const elUnder = document.getElementById('underCourses');\n    const elTotal = document.getElementById('totalPoints');\n    const elStatus = document.getElementById('abiturStatus');\n    const elPB1 = document.getElementById('PB1');\n    const elPB1Error = document.getElementById('PB1Error');\n    const elFinalPoints = document.getElementById('finalPoints');\n    const elAbiturNote = document.getElementById('abiturNote');\n    const elFhrStatus = document.getElementById('fhrStatusDisplay');\n    const elFhrWrap = document.getElementById('fhrNoteDisplay');\n    const elFhrVal = document.getElementById('fhrNoteValue');\n    const elToast = document.getElementById('toast');\n\n    \/\/ Lehrerfunktionen: CSV-Import & Navigation\n    let csvData = [];\n\n    const btnReset = document.getElementById('btnReset');\n    const btnImport = document.getElementById('btnImport');\n    const fileInput = document.getElementById('fileInput');\n    const elStudentSelect = document.getElementById('csvValue');\n    const btnPrev = document.getElementById('btnPrev');\n    const btnNext = document.getElementById('btnNext');\n    const btnHistory = document.getElementById('btnHistory');\n    const modal = document.getElementById('historyModal');\n    const modalClose = document.getElementById('historyClose');\n    const modalContent = document.getElementById('historyContent');\n\n    \/\/ Build UI rows\n    function buildRows(){\n      const frag = document.createDocumentFragment();\n\n      for(let i=1;i<=CONFIG.rows;i++){\n        const row = document.createElement('div');\n        row.className = 'grid row';\n        row.setAttribute('role','row');\n        row.dataset.row = String(i);\n\n        \/\/ Fach (mit P-Label)\n        const cF = document.createElement('div');\n        cF.className = 'cell';\n\n        const tag = document.createElement('span');\n        tag.className = 'p-tag';\n        tag.textContent = `P${i}`;\n        tag.setAttribute('aria-hidden','true');\n\n        const inF = document.createElement('input');\n        inF.type = 'text';\n        inF.id = `P${i}f`;\n        inF.placeholder = `P${i}-Fach`;\n        inF.autocomplete = 'off';\n\n        cF.append(tag, inF);\n\n        \/\/ schriftlich (nur P1\u2013P4)\n        const cS = document.createElement('div');\n        cS.className = 'cell';\n        if(i < 5){\n          const inS = document.createElement('input');\n          inS.type = 'number';\n          inS.id = `P${i}s`;\n          inS.min = String(CONFIG.minGradePoints);\n          inS.max = String(CONFIG.maxGradePoints);\n          inS.step = '1';\n          inS.inputMode = 'numeric';\n          inS.placeholder = '0\u201315';\n          cS.appendChild(inS);\n        } else {\n          const dummy = document.createElement('div');\n          dummy.style.color = 'var(--muted)';\n          dummy.style.fontWeight = '700';\n          dummy.textContent = '\u2013';\n          cS.style.justifyContent = 'center';\n          cS.appendChild(dummy);\n        }\n\n        \/\/ m\u00fcndlich\n        const cM = document.createElement('div');\n        cM.className = 'cell';\n        const inM = document.createElement('input');\n        inM.type = 'number';\n        inM.id = `P${i}m`;\n        inM.min = String(CONFIG.minGradePoints);\n        inM.max = String(CONFIG.maxGradePoints);\n        inM.step = '1';\n        inM.inputMode = 'numeric';\n        inM.placeholder = (i === 5) ? '0\u201315' : '';\n        cM.appendChild(inM);\n\n        \/\/ Punkte\n        const cP = document.createElement('div');\n        cP.className = 'cell';\n        const spP = document.createElement('span');\n        spP.id = `P${i}Punkte`;\n        spP.className = 'points blank';\n        spP.textContent = '';\n        spP.setAttribute('aria-label','Punkte');\n        cP.appendChild(spP);\n\n        \/\/ Diff\n        const cD = document.createElement('div');\n        cD.className = 'cell';\n        const spD = document.createElement('span');\n        spD.id = `P${i}Diff`;\n        spD.className = 'diff';\n        spD.textContent = '';\n        cD.appendChild(spD);\n\n        row.append(cF,cS,cM,cP,cD);\n        frag.appendChild(row);\n      }\n\n      elRows.innerHTML = '';\n      elRows.appendChild(frag);\n    }\n\n    function numOrNull(value){\n      const x = Number(value);\n      return Number.isFinite(x) ? x : null;\n    }\n\n    function inRangeInt(x, min, max){\n      return Number.isInteger(x) && x >= min && x <= max;\n    }\n\n    function readGradeInput(id){\n      const el = document.getElementById(id);\n      if(!el) return null;\n      const raw = el.value.trim();\n      if(raw === '') return null;\n      const x = Number(raw);\n      if(!Number.isFinite(x)) return null;\n      \/\/ Wir akzeptieren nur ganze Punkte 0\u201315\n      const xi = Math.trunc(x);\n      if(String(xi) !== String(x)) {\n        \/\/ wenn jemand Dezimal eingibt, lassen wir es invalid aussehen, rechnen aber nicht.\n        return null;\n      }\n      if(!inRangeInt(xi, CONFIG.minGradePoints, CONFIG.maxGradePoints)) return null;\n      return xi;\n    }\n\n    function computeRowPoints(rowIndex, s, m){\n      \/\/ Liefert: { points: number|null, diff: number|null }\n      if(rowIndex < 5){\n        \/\/ Robust: schriftlich ist Pflicht, m\u00fcndlich optional.\n        if(s === null) return { points: null, diff: null };\n        if(m === null) return { points: 4 * s, diff: null };\n        const points = Math.round((8 * s + 4 * m) \/ 3);\n        const diff = points - 4 * s;\n        return { points, diff };\n      }\n      \/\/ P5: nur m\u00fcndlich\n      if(m === null) return { points: null, diff: null };\n      return { points: 4 * m, diff: null };\n    }\n\n    function renderRow(rowIndex){\n      const elPoints = document.getElementById(`P${rowIndex}Punkte`);\n      const elDiff = document.getElementById(`P${rowIndex}Diff`);\n\n      const s = rowIndex < 5 ? readGradeInput(`P${rowIndex}s`) : null;\n      const m = readGradeInput(`P${rowIndex}m`);\n\n      const res = computeRowPoints(rowIndex, s, m);\n\n      if(res.points === null){\n        elPoints.textContent = '';\n        elPoints.classList.remove('good','bad');\n        elPoints.classList.add('blank');\n        elDiff.textContent = '';\n        elDiff.classList.remove('good','bad');\n        return { hasResult: false, points: null };\n      }\n\n      elPoints.textContent = String(res.points);\n      elPoints.classList.remove('blank');\n      if(res.points < 20){\n        elPoints.classList.add('bad');\n        elPoints.classList.remove('good');\n      } else {\n        elPoints.classList.add('good');\n        elPoints.classList.remove('bad');\n      }\n\n      if(res.diff !== null){\n        const sign = res.diff >= 0 ? '+' : '';\n        elDiff.textContent = `${sign}${res.diff}`;\n        elDiff.classList.remove('good','bad');\n        if(res.diff < 0) elDiff.classList.add('bad');\n        if(res.diff > 0) elDiff.classList.add('good');\n      } else {\n        elDiff.textContent = '';\n        elDiff.classList.remove('good','bad');\n      }\n\n      return { hasResult: true, points: res.points };\n    }\n\n    function renderSummary(){\n      let under = 0;\n      let total = 0;\n      let atLeast20 = 0;\n      let count = 0;\n\n      for(let i=1;i<=CONFIG.rows;i++){\n        const out = renderRow(i);\n        if(!out.hasResult) continue;\n        count++;\n        total += out.points;\n        if(out.points < 20) under++;\n        if(out.points >= 20) atLeast20++;\n      }\n\n      elUnder.textContent = String(under);\n      elUnder.classList.remove('good','bad');\n      \/\/ V1.0-Logik: unter 3 = gut\n      if(under < 3) elUnder.classList.add('good');\n      else elUnder.classList.add('bad');\n\n      elTotal.textContent = String(total);\n      elTotal.classList.remove('good','bad');\n      if(total >= CONFIG.pass.minTotalExamPoints) elTotal.classList.add('good');\n      else elTotal.classList.add('bad');\n\n      return { under, total, atLeast20, count };\n    }\n\n    function setStatus(kind, text){\n      \/\/ kind: 'good' | 'bad' | 'neutral'\n      const badge = kind === 'good' ? '\u2713' : (kind === 'bad' ? '\u2717' : '?');\n      elStatus.innerHTML = `${text} <span class=\"badge ${kind}\" aria-hidden=\"true\">${badge}<\/span>`;\n    }\n\n    function setFhrStatus(kind, text){\n      \/\/ kind: 'good' | 'bad' | 'neutral'\n      const badge = kind === 'good' ? '\u2713' : (kind === 'bad' ? '\u2717' : '?');\n      elFhrStatus.innerHTML = `${text} <span class=\"badge ${kind}\" aria-hidden=\"true\">${badge}<\/span>`;\n    }\n\n    function renderFhr(){\n      \/\/ FHR-Status-Logik:\n      \/\/ FHR-Status-Logik (CSV wie bisher):\n      \/\/ 1 -> erreicht (gruenes \u2713) + FHR-Note\n      \/\/ 0 -> nicht erreicht (rotes \u2717)\n      \/\/ sonst -> unbekannt (blaues ?)\n      elFhrStatus.style.display = 'block';\n\n      if(FHRStatus === 1){\n        setFhrStatus('good', 'Fachhochschulreife erreicht');\n        elFhrVal.textContent = (FHRNote > 0) ? FHRNote.toFixed(1).replace('.', ',') : '\u2014';\n        elFhrWrap.style.display = 'block';\n      } else if(FHRStatus === 2){\n        setFhrStatus('bad', 'Fachhochschulreife nicht erreicht');\n        elFhrWrap.style.display = 'none';\n      } else {\n        setFhrStatus('neutral', 'Fachhochschulreife unbekannt');\n        elFhrWrap.style.display = 'none';\n      }\n    }\n\n    function parseBlockI(){\n      const raw = elPB1.value.trim();\n      if(raw === '') return null;\n      const x = Number(raw);\n      if(!Number.isFinite(x)) return null;\n      const xi = Math.trunc(x);\n      if(String(xi) !== String(x)) return null;\n      if(!inRangeInt(xi, CONFIG.blockI.min, CONFIG.blockI.max)) return null;\n      return xi;\n    }\n\n    function computeAbiturNote(finalPoints){\n      \/\/ V1.0-Formel: floor(40 - (P - 300)\/18) \/ 10\n      let note = Math.floor(40 - (finalPoints - 300) \/ 18) \/ 10;\n      \/\/ Format 1 Dezimal\n      note = Number(note.toFixed(1));\n\n      \/\/ Zusatzinfo wie V1.0\n      const z = (note - 0.1);\n      const y = (301 + (40 - note * 10) * 18 - finalPoints);\n\n      return {\n        note,\n        nextNote: Number(z.toFixed(1)),\n        neededPoints: Math.trunc(y)\n      };\n    }\n\n    function updateTotals(){\n      const s = renderSummary();\n\n      \/\/ Status\n      if(s.count < CONFIG.rows){\n        \/\/ Solange P-Ergebnisse fehlen (Abitur-Status: ?), keine FHR-Angaben anzeigen.\n        setStatus('neutral', 'Abitur\u2011Pr\u00fcfung');\n        elFhrStatus.style.display = 'none';\n        elFhrWrap.style.display = 'none';\n      } else {\n        const passed = (s.total >= CONFIG.pass.minTotalExamPoints) && (s.atLeast20 >= CONFIG.pass.minResultsAtLeast20);\n        if(passed){\n          setStatus('good', 'Abitur bestanden');\n          elFhrStatus.style.display = 'none';\n          elFhrWrap.style.display = 'none';\n        } else {\n          setStatus('bad', 'Abitur nicht bestanden');\n          renderFhr();\n        }\n      }\n\n\n      \/\/ Block I Validierung (nur anzeigen, wenn etwas eingegeben wurde)\n      const pb1 = parseBlockI();\n      const pb1Raw = elPB1.value.trim();\n      const pb1Invalid = (pb1Raw !== '' && pb1 === null);\n      elPB1Error.classList.toggle('show', pb1Invalid);\n\n      \/\/ Gesamtpunkte & Note\n      if(s.count < CONFIG.rows){\n        elFinalPoints.textContent = 'P\u2011Ergebnisse fehlen';\n        elAbiturNote.textContent = 'P\u2011Ergebnisse fehlen';\n        return;\n      }\n\n      if(pb1 === null){\n        elFinalPoints.textContent = 'Block\u2011I\u2011Punkte fehlen';\n        elAbiturNote.textContent = 'Block\u2011I\u2011Punkte fehlen';\n        return;\n      }\n\n      const finalPoints = pb1 + s.total;\n      elFinalPoints.textContent = String(finalPoints);\n\n      const passed = (s.total >= CONFIG.pass.minTotalExamPoints) && (s.atLeast20 >= CONFIG.pass.minResultsAtLeast20);\n      const note = computeAbiturNote(finalPoints);\n\n      if(!passed){\n        elAbiturNote.textContent = `--- (ca. ${note.note.toFixed(1).replace('.', ',')})`;\n        return;\n      }\n\n      const noteText = note.note.toFixed(1).replace('.', ',');\n      const zText = note.nextNote.toFixed(1).replace('.', ',');\n      const yText = String(note.neededPoints).replace('.', ',');\n      elAbiturNote.textContent = `${noteText} (+${yText} Punkte f\u00fcr ${zText})`;\n    }\n\n    function resetAll(){\n      document.querySelectorAll('input[type=\"text\"], input[type=\"number\"]').forEach(inp => inp.value = '');\n\n      \/\/ Auch FHR-Parameter zuruecksetzen\n      FHRNote = 0;\n      FHRStatus = 0;\n\n      \/\/ Outputs\n      for(let i=1;i<=CONFIG.rows;i++){\n        const elP = document.getElementById(`P${i}Punkte`);\n        const elD = document.getElementById(`P${i}Diff`);\n        elP.textContent = '';\n        elP.classList.remove('good','bad');\n        elP.classList.add('blank');\n        elD.textContent = '';\n        elD.classList.remove('good','bad');\n      }\n\n      elUnder.textContent = '0';\n      elUnder.classList.remove('good','bad');\n\n      elTotal.textContent = '0';\n      elTotal.classList.remove('good','bad');\n\n      setStatus('neutral','Abitur\u2011Pr\u00fcfung');\n      elPB1Error.classList.remove('show');\n\n      elFinalPoints.textContent = 'Block\u2011I\u2011Punkte fehlen';\n      elAbiturNote.textContent = 'Block\u2011I\u2011Punkte fehlen';\n\n      \/\/ FHR erst ausweisen, wenn die Abiturpruefung abschliessend negativ ist\n      elFhrStatus.style.display = 'none';\n      elFhrWrap.style.display = 'none';\n\n      \/\/ Lehrer: CSV-\/Navigation zuruecksetzen\n      csvData = [];\n      if(elStudentSelect){\n        elStudentSelect.innerHTML = '<option value=\"\">(keine CSV geladen)<\/option>';\n        elStudentSelect.disabled = true;\n      }\n      if(btnPrev) btnPrev.disabled = true;\n      if(btnNext) btnNext.disabled = true;\n      if(btnHistory) btnHistory.disabled = true;\n    }\n\n    function showToast(msg){\n      elToast.textContent = msg;\n      elToast.classList.add('show');\n      window.setTimeout(() => elToast.classList.remove('show'), 1300);\n    }\n\n    \/\/ =========================================================\n    \/\/ Lehrer: CSV-Import, Navigation, Vornoten\n    \/\/ =========================================================\n\n    function decodeCsvBuffer(buf){\n      const bytes = new Uint8Array(buf);\n      if(bytes.length >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE){\n        \/\/ UTF-16 LE mit BOM\n        return new TextDecoder('utf-16le').decode(bytes).replace(\/^\ufeff\/, '');\n      }\n      if(bytes.length >= 2 && bytes[0] == 0xFE && bytes[1] == 0xFF){\n        \/\/ UTF-16 BE mit BOM\n        return new TextDecoder('utf-16be').decode(bytes).replace(\/^\ufeff\/, '');\n      }\n      if(bytes.length >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF){\n        \/\/ UTF-8 mit BOM\n        return new TextDecoder('utf-8').decode(bytes).replace(\/^\ufeff\/, '');\n      }\n      \/\/ Fallback: UTF-8\n      return new TextDecoder('utf-8').decode(bytes);\n    }\n\n    function handleFileSelect(ev){\n      const file = ev.target.files && ev.target.files[0];\n      if(!file) return;\n\n      \/\/ Vorherige Eingaben und Anzeige zuruecksetzen (wie in Lehrer V1.0)\n      resetAll();\n\n      const reader = new FileReader();\n      reader.onload = () => {\n        try{\n          const csvText = decodeCsvBuffer(reader.result);\n          processCSV(csvText);\n          showToast('CSV eingelesen');\n        } catch {\n          showToast('CSV konnte nicht gelesen werden');\n        } finally {\n          \/\/ Input zuruecksetzen, damit die gleiche Datei erneut gewaehlt werden kann\n          ev.target.value = '';\n        }\n      };\n      reader.onerror = () => {\n        showToast('CSV konnte nicht gelesen werden');\n        ev.target.value = '';\n      };\n      reader.readAsArrayBuffer(file);\n    }\n\n    function processCSV(csv){\n      const lines = String(csv).split(\/\\r?\\n\/);\n      csvData = [];\n\n      for(const line of lines){\n        if(!line || !line.trim()) continue;\n        const values = line.split(';');\n        \/\/ Lehrer V1.0: mindestens 35 Spalten (Index bis 34)\n        if(values.length < 35) continue;\n\n        const get = (i) => (values[i] ?? '').toString().trim();\n\n        csvData.push({\n          id: get(0),\n          csvValue: get(1),\n          P1schr: get(2),\n          P2schr: get(3),\n          P3schr: get(4),\n          P4schr: get(5),\n          P5mdl: get(6),\n          blockIPoints: get(7),\n          P1Fach: get(8),\n          P2Fach: get(9),\n          P3Fach: get(10),\n          P4Fach: get(11),\n          P5Fach: get(12),\n          P1H1: get(13), P1H2: get(14), P1H3: get(15), P1H4: get(16),\n          P2H1: get(17), P2H2: get(18), P2H3: get(19), P2H4: get(20),\n          P3H1: get(21), P3H2: get(22), P3H3: get(23), P3H4: get(24),\n          P4H1: get(25), P4H2: get(26), P4H3: get(27), P4H4: get(28),\n          P5H1: get(29), P5H2: get(30), P5H3: get(31), P5H4: get(32),\n          FHRStatus: get(33),\n          FHRNote: get(34).replace(',', '.'),\n          extra: get(35) \/\/ vorhanden, wird aber bewusst nicht genutzt\n        });\n      }\n\n      populateDropdown();\n      if(csvData.length > 0){\n        updateFieldsFromSelection(0);\n      }\n    }\n\n    function populateDropdown(){\n      if(!elStudentSelect) return;\n      elStudentSelect.innerHTML = '';\n\n      if(csvData.length === 0){\n        elStudentSelect.disabled = true;\n        elStudentSelect.innerHTML = '<option value=\"\">(keine Daten)<\/option>';\n        btnPrev.disabled = true;\n        btnNext.disabled = true;\n        btnHistory.disabled = true;\n        return;\n      }\n\n      csvData.forEach((d, idx) => {\n        const opt = document.createElement('option');\n        opt.value = String(idx);\n        opt.textContent = d.csvValue || `(Datensatz ${idx+1})`;\n        elStudentSelect.appendChild(opt);\n      });\n\n      elStudentSelect.disabled = false;\n      elStudentSelect.selectedIndex = 0;\n      btnPrev.disabled = false;\n      btnNext.disabled = false;\n      btnHistory.disabled = false;\n    }\n\n    function selectPreviousStudent(){\n      if(!elStudentSelect || elStudentSelect.disabled) return;\n      if(elStudentSelect.selectedIndex > 0){\n        elStudentSelect.selectedIndex -= 1;\n        updateFieldsFromSelection(elStudentSelect.selectedIndex);\n      }\n    }\n\n    function selectNextStudent(){\n      if(!elStudentSelect || elStudentSelect.disabled) return;\n      if(elStudentSelect.selectedIndex < elStudentSelect.options.length - 1){\n        elStudentSelect.selectedIndex += 1;\n        updateFieldsFromSelection(elStudentSelect.selectedIndex);\n      }\n    }\n\n    function updateFieldsFromSelection(selectedIndex){\n      const idx = Number(selectedIndex);\n      if(!Number.isInteger(idx) || idx < 0 || idx >= csvData.length) return;\n\n      const d = csvData[idx];\n\n      \/\/ Faecher\n      const setText = (id, val) => {\n        const el = document.getElementById(id);\n        if(el) el.value = val ?? '';\n      };\n\n      setText('P1f', d.P1Fach);\n      setText('P2f', d.P2Fach);\n      setText('P3f', d.P3Fach);\n      setText('P4f', d.P4Fach);\n      setText('P5f', d.P5Fach);\n\n      \/\/ schriftlich (P1\u2013P4)\n      const setNum = (id, val) => {\n        const el = document.getElementById(id);\n        if(!el) return;\n        el.value = (val ?? '').toString().trim();\n      };\n\n      setNum('P1s', d.P1schr);\n      setNum('P2s', d.P2schr);\n      setNum('P3s', d.P3schr);\n      setNum('P4s', d.P4schr);\n\n      \/\/ muendlich (P1\u2013P4) immer zuruecksetzen\n      setNum('P1m', '');\n      setNum('P2m', '');\n      setNum('P3m', '');\n      setNum('P4m', '');\n\n      \/\/ P5 muendlich aus CSV\n      setNum('P5m', d.P5mdl);\n\n      \/\/ Block I\n      elPB1.value = (d.blockIPoints ?? '').toString().trim();\n\n      \/\/ FHR aus CSV (wie in Lehrer V1.0)\n      const s = Number((d.FHRStatus ?? '').toString().trim());\n      FHRStatus = Number.isInteger(s) ? s : 0;\n      const n = Number(String(d.FHRNote ?? '0').replace(',', '.'));\n      FHRNote = Number.isFinite(n) ? n : 0;\n\n      updateTotals();\n    }\n\n    function formatTwoDigits(value){\n      const s = (value ?? '').toString().trim();\n      if(s === '') return '\u2014';\n      return s.padStart(2, '0');\n    }\n\n    function openHistory(){\n      if(!modal) return;\n      modal.classList.add('show');\n    }\n\n    function closeHistory(){\n      if(!modal) return;\n      modal.classList.remove('show');\n    }\n\n    function showHistory(){\n      if(!elStudentSelect || elStudentSelect.disabled) return;\n      const idx = elStudentSelect.selectedIndex;\n      if(idx < 0 || idx >= csvData.length) return;\n      const d = csvData[idx];\n\n      const html = `\n        <div class=\"history-grid\">\n          <div class=\"history-row\"><strong>P1:<\/strong>&nbsp;&nbsp;&nbsp;<span>${formatTwoDigits(d.P1H1)} \u2013 ${formatTwoDigits(d.P1H2)} \u2013 ${formatTwoDigits(d.P1H3)} \u2013 ${formatTwoDigits(d.P1H4)}<\/span><\/div>\n          <div class=\"history-row\"><strong>P2:<\/strong>&nbsp;&nbsp;&nbsp;<span>${formatTwoDigits(d.P2H1)} \u2013 ${formatTwoDigits(d.P2H2)} \u2013 ${formatTwoDigits(d.P2H3)} \u2013 ${formatTwoDigits(d.P2H4)}<\/span><\/div>\n          <div class=\"history-row\"><strong>P3:<\/strong>&nbsp;&nbsp;&nbsp;<span>${formatTwoDigits(d.P3H1)} \u2013 ${formatTwoDigits(d.P3H2)} \u2013 ${formatTwoDigits(d.P3H3)} \u2013 ${formatTwoDigits(d.P3H4)}<\/span><\/div>\n          <div class=\"history-row\"><strong>P4:<\/strong>&nbsp;&nbsp;&nbsp;<span>${formatTwoDigits(d.P4H1)} \u2013 ${formatTwoDigits(d.P4H2)} \u2013 ${formatTwoDigits(d.P4H3)} \u2013 ${formatTwoDigits(d.P4H4)}<\/span><\/div>\n          <div class=\"history-row\"><strong>P5:<\/strong>&nbsp;&nbsp;&nbsp;<span>${formatTwoDigits(d.P5H1)} \u2013 ${formatTwoDigits(d.P5H2)} \u2013 ${formatTwoDigits(d.P5H3)} \u2013 ${formatTwoDigits(d.P5H4)}<\/span><\/div>\n        <\/div>\n      `;\n\n      if(modalContent) modalContent.innerHTML = html;\n      openHistory();\n    }\n\n    function fillFromUrl(){\n      const sp = new URLSearchParams(window.location.search);\n\n      \/\/ FHR\n      const fhrN = sp.get('FHRNote');\n      const fhrS = sp.get('FHRStatus');\n      if(fhrN !== null){\n        const x = Number(String(fhrN).replace(',', '.'));\n        if(Number.isFinite(x)) FHRNote = x;\n      }\n      if(fhrS !== null){\n        const x = Number(fhrS);\n        if(Number.isInteger(x)) FHRStatus = x;\n      }\n\n      for(let i=1;i<=CONFIG.rows;i++){\n        const f = sp.get(`P${i}f`);\n        const s = sp.get(`P${i}s`);\n        const m = sp.get(`P${i}m`);\n\n        const elF = document.getElementById(`P${i}f`);\n        if(elF && f !== null) elF.value = f;\n\n        if(i < 5){\n          const elS = document.getElementById(`P${i}s`);\n          if(elS && s !== null) elS.value = s;\n        }\n\n        const elM = document.getElementById(`P${i}m`);\n        if(elM && m !== null) elM.value = m;\n      }\n\n      const pb1 = sp.get('PB1');\n      if(pb1 !== null) elPB1.value = pb1;\n    }\n\n    function wireEvents(){\n      \/\/ Recalc on any relevant input\n      document.querySelectorAll('#rows input, #PB1').forEach(inp => {\n        inp.addEventListener('input', updateTotals);\n        inp.addEventListener('change', updateTotals);\n      });\n\n      \/\/ Reset\n      btnReset.addEventListener('click', () => {\n        resetAll();\n        updateTotals();\n      });\n\n      \/\/ CSV import\n      btnImport.addEventListener('click', () => fileInput.click());\n      fileInput.addEventListener('change', handleFileSelect);\n\n      \/\/ Student selection\n      elStudentSelect.addEventListener('change', () => {\n        updateFieldsFromSelection(elStudentSelect.selectedIndex);\n      });\n\n      \/\/ Prev\/Next\n      btnPrev.addEventListener('click', selectPreviousStudent);\n      btnNext.addEventListener('click', selectNextStudent);\n\n      \/\/ Vornoten modal\n      btnHistory.addEventListener('click', showHistory);\n      modalClose.addEventListener('click', closeHistory);\n      modal.addEventListener('click', (e) => {\n        if(e.target === modal) closeHistory();\n      });\n      document.addEventListener('keydown', (e) => {\n        if(e.key === 'Escape') closeHistory();\n      });\n    }\n\n\n    \/\/ Init\n    buildRows();\n    fillFromUrl();\n    wireEvents();\n    updateTotals();\n  <\/script>\n<\/body>\n<\/html><\/div><\/div><\/div><\/div><\/div><\/div><\/section>\n","protected":false},"excerpt":{"rendered":"Abiturrechner Lehrer \u2013 Gymnasium Johanneum L\u00fcneburg (Offline) Abiturrechner Lehrer Gymnasium Johanneum L\u00fcneburg Version 3.07 (15.02.2026, F\u00fc) Reset Import (keine CSV geladen) Vornoten vorheriger Sch\u00fcler n\u00e4chster Sch\u00fcler P\u2011Fach schr. m\u00fcndl. Punkte Diff. Anz. Unterkurse = 0 Abitur\u2011Punkte = 0 Abitur\u2011Pr\u00fcfung ? Punkte aus Block I = Bitte eine Zahl zwischen 200 und 600 eingeben. Gesamtpunkte =...","protected":false},"author":3,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-19103","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/johanneum-lueneburg.de\/wordpress\/wp-json\/wp\/v2\/pages\/19103","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/johanneum-lueneburg.de\/wordpress\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/johanneum-lueneburg.de\/wordpress\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/johanneum-lueneburg.de\/wordpress\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/johanneum-lueneburg.de\/wordpress\/wp-json\/wp\/v2\/comments?post=19103"}],"version-history":[{"count":15,"href":"https:\/\/johanneum-lueneburg.de\/wordpress\/wp-json\/wp\/v2\/pages\/19103\/revisions"}],"predecessor-version":[{"id":25413,"href":"https:\/\/johanneum-lueneburg.de\/wordpress\/wp-json\/wp\/v2\/pages\/19103\/revisions\/25413"}],"wp:attachment":[{"href":"https:\/\/johanneum-lueneburg.de\/wordpress\/wp-json\/wp\/v2\/media?parent=19103"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}