| Server IP : 202.61.199.114 / Your IP : 216.73.217.139 Web Server : nginx/1.22.1 System : Linux de.arni-solutions.de 6.1.0-49-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.174-1 (2026-05-26) x86_64 User : web20 ( 1018) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /proc/3951573/root/tmp/ |
Upload File : |
<!DOCTYPE html><html lang="de"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, viewport-fit=cover"><meta name="robots" content="noindex,nofollow"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"><meta name="theme-color" content="#0d2c44"><link rel="manifest" href="https://seaside.pacim.de/manifest.php?t=63755ecedbb6c1c962cf0dd2"><link rel="apple-touch-icon" href="https://seaside.pacim.de/assets/webapp/icon-180.png"><meta name="apple-mobile-web-app-title" content="Seaside Parking"><title>Abreisen Sa, 10.10.2026</title><style>
@font-face{font-family:"EuroPlate";src:url("https://seaside.pacim.de/assets/fonts/EuroPlate.ttf") format("truetype");font-display:swap;}
:root{--navy:#0d2c44;--navy2:#11436b;--blue:#1b6ca8;--green:#2fb344;--red:#d63939;--gold:#f3b53f;--ink:#16384f;--muted:#5b7488;--line:#e6eef4;}
*{box-sizing:border-box;margin:0;padding:0;}
html{-webkit-text-size-adjust:100%;text-size-adjust:100%;}
body{font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:#eef3f7;color:var(--ink);padding:0 0 40px;touch-action:manipulation;}
.wrap{max-width:560px;margin:0 auto;}
.topbar{position:sticky;top:0;z-index:6;}
/* iOS: Inhalt nicht unter Dynamic Island / Statusleiste schieben (viewport-fit=cover + Safe-Area). */
.hd{position:relative;background:linear-gradient(135deg,var(--navy) 0%,var(--navy2) 65%,var(--blue) 130%);color:#fff;
padding:calc(20px + env(safe-area-inset-top,0px)) calc(18px + env(safe-area-inset-right,0px)) 18px calc(18px + env(safe-area-inset-left,0px));}
.term-back{position:absolute;top:calc(14px + env(safe-area-inset-top,0px));right:calc(14px + env(safe-area-inset-right,0px));
display:inline-flex;align-items:center;gap:5px;background:rgba(255,255,255,.16);color:#fff;text-decoration:none;
border-radius:9px;padding:7px 12px;font-size:13px;font-weight:700;-webkit-tap-highlight-color:transparent;}
.term-back:active{background:rgba(255,255,255,.3);}
.term-back svg{width:16px;height:16px;}
.hd .k{font-size:11px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;opacity:.8;}
.hd .t{font-size:21px;font-weight:800;margin-top:2px;}
.hd .sub{font-size:12.5px;opacity:.85;margin-top:3px;}
.stats{display:flex;gap:8px;margin-top:9px;}
.stat{background:rgba(255,255,255,.14);border-radius:10px;padding:6px 11px;font-size:12.5px;font-weight:600;}
.stat b{font-size:15px;} .stat.open b{color:#ffd0cf;}
.stat.filt{cursor:pointer;user-select:none;-webkit-tap-highlight-color:transparent;transition:background .15s ease,box-shadow .15s ease;}
.stat.filt.active{background:rgba(255,255,255,.34);box-shadow:inset 0 0 0 1.5px rgba(255,255,255,.6);}
.stat.live{display:inline-flex;align-items:center;gap:6px;}
.live-dot{width:8px;height:8px;border-radius:50%;background:#34d058;box-shadow:0 0 0 0 rgba(52,208,88,.6);animation:dlpulse 2s infinite;}
.stat.live.stale .live-dot{background:#ffb84d;animation:none;}
@keyframes dlpulse{0%{box-shadow:0 0 0 0 rgba(52,208,88,.6);}70%{box-shadow:0 0 0 7px rgba(52,208,88,0);}100%{box-shadow:0 0 0 0 rgba(52,208,88,0);}}
@media (prefers-reduced-motion: reduce){ .live-dot{animation:none;} }
/* Check-in-Overlay (Iframe, kein Seitenreload) */
.ov{position:fixed;inset:0;z-index:50;background:#eef3f7;display:flex;flex-direction:column;}
.ov-frame{flex:1 1 auto;width:100%;border:0;background:#eef3f7;}
.row-flash{animation:dlflash 1.1s ease-out;}
@keyframes dlflash{0%{background:#fff6cc;}100%{}}
/* Sitzungsende-Bildschirm */
.ended{position:fixed;inset:0;z-index:60;background:linear-gradient(160deg,var(--navy) 0%,var(--navy2) 70%,var(--blue) 140%);display:flex;align-items:center;justify-content:center;padding:24px;}
.ended-box{text-align:center;color:#fff;max-width:340px;}
.ended-ic{width:64px;height:64px;color:#cfe2f2;margin:0 auto 16px;}
.ended-t{font-size:24px;font-weight:800;}
.ended-x{font-size:14.5px;opacity:.85;margin-top:10px;line-height:1.5;}
.flt{display:flex;flex-wrap:wrap;gap:6px;margin-top:9px;}
.chip{background:rgba(255,255,255,.16);border-radius:999px;padding:3px 9px;font-size:11.5px;font-weight:600;}
.search{background:#fff;padding:10px 14px;border-bottom:1px solid var(--line);}
.search-box{position:relative;}
.search input{width:100%;padding:11px 40px 11px 14px;border:1px solid var(--line);border-radius:10px;font-size:15px;background:#f6f9fc;}
.search input[type=search]::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none;}
.search-clear{position:absolute;right:6px;top:50%;transform:translateY(-50%);width:30px;height:30px;border:0;border-radius:50%;
background:#e6eef4;color:var(--navy);font-size:20px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;}
.search-clear:active{filter:brightness(.94);}
.d-none{display:none!important;}
/* Meinten Sie? – Vorschläge bei ähnlichem Kennzeichen */
.suggest{background:#fff8e6;border-bottom:1px solid #f3e3b0;padding:10px 14px;display:flex;flex-wrap:wrap;align-items:center;gap:8px;}
.suggest .sg-l{font-size:12.5px;font-weight:700;color:#8a6400;}
.suggest .sg-chip{background:#fff;border:1.5px solid #e8c766;border-radius:8px;padding:6px 12px;font-size:14px;font-weight:800;color:var(--navy);cursor:pointer;font-family:inherit;}
.suggest .sg-chip:active{filter:brightness(.95);}
.tblwrap{overflow-x:auto;-webkit-overflow-scrolling:touch;background:#fff;}
.tbl{width:100%;border-collapse:collapse;background:#fff;}
.tbl th{font-size:10px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);text-align:left;
padding:9px 10px;border-bottom:2px solid var(--line);background:#f6f9fc;}
.tbl td{padding:12px 10px;border-bottom:1px solid var(--line);font-size:14px;vertical-align:middle;}
.tbl tbody tr{cursor:pointer;}
/* Zeilen-Status: eingecheckt grün, Zahlung offen orange, bezahlt aber nicht eingecheckt leicht blau */
.tbl tbody tr.st-in{background:rgba(47,179,68,.20);box-shadow:inset 3px 0 0 #2fb344;}
.tbl tbody tr.st-open{background:rgba(245,159,0,.20);box-shadow:inset 3px 0 0 #f59f00;}
.tbl tbody tr.st-paid{background:rgba(27,108,168,.13);box-shadow:inset 3px 0 0 #1b6ca8;}
.tbl tbody tr.st-storno{background:rgba(214,57,57,.12);box-shadow:inset 3px 0 0 #d63939;}
.tbl tbody tr.st-storno .plate{text-decoration:line-through;color:#a23030;}
/* außerhalb des Filters – nur per Suche sichtbar, dezent + Markierung */
.tbl tbody tr.row-out{display:none;background:#f4f6f9;box-shadow:inset 3px 0 0 #b6c2cf;opacity:.92;}
.outflag{display:inline-block;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:#6b7a89;background:#e4e9ef;border-radius:5px;padding:1px 6px;margin-left:6px;vertical-align:middle;}
.pcam{display:inline-flex;align-items:center;gap:2px;font-size:11px;font-weight:700;color:var(--blue);background:rgba(27,108,168,.10);border-radius:5px;padding:1px 5px;margin-left:6px;vertical-align:middle;white-space:nowrap;}
.pcam svg{width:12px;height:12px;}
.tbl tbody tr:active{filter:brightness(.96);}
/* Kennzeichen in der FE-Schrift (EuroPlate). Nur ein Schnitt -> „fett" per Kontur. */
.plate{font-family:"EuroPlate",-apple-system,Segoe UI,Roboto,sans-serif;font-weight:800;letter-spacing:.5px;color:var(--navy);white-space:nowrap;-webkit-text-stroke:.45px currentColor;}
.nm{font-weight:600;}
.mut{color:var(--muted);}
mark{background:#ffd54a;color:#3a2c00;border-radius:2px;padding:0 1px;}
.empty{padding:40px 20px;text-align:center;color:var(--muted);}
.foot{font-size:12px;color:var(--muted);text-align:center;padding:16px;}
.err{max-width:440px;margin:60px auto;background:#fff;border-radius:18px;padding:34px 26px;text-align:center;box-shadow:0 14px 40px rgba(13,44,68,.16);}
.err h1{font-size:40px;color:var(--navy);} .err p{color:var(--muted);margin-top:8px;}
/* PIN-Sperrbildschirm: gemeinsame Komponente (pacim_pin_css), siehe dl_pin_screen(). */
/* ---- ressourcenarme Animationen: nur opacity/transform (Compositor), keine Layout-Props ---- */
@keyframes dlFadeUp{from{opacity:0;transform:translateY(8px);}to{opacity:1;transform:none;}}
@keyframes dlFade{from{opacity:0;}to{opacity:1;}}
@keyframes dlSheet{from{opacity:.3;transform:translateY(16px);}to{opacity:1;transform:none;}}
@keyframes dlPop{from{opacity:0;transform:scale(.92);}to{opacity:1;transform:none;}}
.tblwrap{animation:dlFadeUp .28s ease both;}
.suggest:not(.d-none){animation:dlFadeUp .18s ease;}
.ov:not(.d-none){animation:dlFade .18s ease;}
.ov-frame{animation:dlSheet .24s cubic-bezier(.22,.61,.36,1);}
.ended:not(.d-none){animation:dlFade .25s ease;}
.ended-box{animation:dlPop .32s cubic-bezier(.22,.61,.36,1) both;}
.tbl tbody tr{transition:background .25s ease;}
@media (prefers-reduced-motion: reduce){
.tblwrap,.suggest,.ov,.ov-frame,.ended,.ended-box,.row-flash,.pin-card.shake{animation:none!important;}
.tbl tbody tr{transition:none;} .pin-key.lit,.pin-key:active{transform:none;}
}
</style></head><body><div class="wrap">
<div class="topbar">
<div class="hd">
<div class="k">Abreisen · Check-out</div>
<div class="t">Sa, 10.10.2026</div>
<div class="stats">
<span class="stat filt" id="filt-arr" role="button" tabindex="0"><b id="st-arr">0</b> / <span id="st-tot">5</span> abgereist</span>
<span class="stat open filt" id="filt-open" role="button" tabindex="0"><b id="st-open">5</b> offen</span>
<span class="stat live" id="dl-live" title="Automatische Aktualisierung"><span class="live-dot"></span> live</span>
</div>
<div class="flt"><span class="chip">ZZ_TEST_ABREISE</span><span class="chip">Abreisen</span><span class="chip">offen/abgereist</span></div> </div>
<div class="search">
<div class="search-box">
<input type="search" id="dl-search" placeholder="Suche: Kennzeichen, Name, Buchungs-/Rechnungsnr. …" autocomplete="off" autocapitalize="characters">
<button type="button" class="search-clear d-none" id="dl-clear" aria-label="Suche löschen">×</button>
</div>
</div>
</div><!-- /topbar -->
<div class="suggest d-none" id="dl-suggest"></div>
<div class="tblwrap"><table class="tbl"><thead><tr><th>Kennz.</th><th>Name</th><th>Typ</th><th>Zeit</th><th>Tage</th></tr></thead><tbody id="dl-body"><tr class="st-open" data-id="112027" data-cin="" data-href="https://seaside.pacim.de/checkin.php?t=63755ecedbb6c1c962cf0dd2&b=112027" data-s="akjr441körner2617191" data-plate="AK - JR 441" data-out="0"><td class="plate">AK - JR 441</td><td class="nm">Körner</td><td class="mut">A</td><td class="mut tcell">—</td><td class="mut">14</td></tr><tr class="st-open" data-id="112026" data-cin="" data-href="https://seaside.pacim.de/checkin.php?t=63755ecedbb6c1c962cf0dd2&b=112026" data-s="akjr441körner2617190" data-plate="AK - JR 441" data-out="0"><td class="plate">AK - JR 441</td><td class="nm">Körner</td><td class="mut">A</td><td class="mut tcell">—</td><td class="mut">14</td></tr><tr class="st-open" data-id="106356" data-cin="" data-href="https://seaside.pacim.de/checkin.php?t=63755ecedbb6c1c962cf0dd2&b=106356" data-s="bar8380caspari2616861" data-plate="B - AR 8380" data-out="0"><td class="plate">B - AR 8380</td><td class="nm">Caspari</td><td class="mut">H</td><td class="mut tcell">—</td><td class="mut">14</td></tr><tr class="st-open" data-id="106225" data-cin="" data-href="https://seaside.pacim.de/checkin.php?t=63755ecedbb6c1c962cf0dd2&b=106225" data-s="fbtk1507fischer2616730" data-plate="FB - TK 1507" data-out="0"><td class="plate">FB - TK 1507</td><td class="nm">Fischer</td><td class="mut">A</td><td class="mut tcell">—</td><td class="mut">7</td></tr><tr class="st-open" data-id="108183" data-cin="" data-href="https://seaside.pacim.de/checkin.php?t=63755ecedbb6c1c962cf0dd2&b=108183" data-s="lsr8515kleine2616989" data-plate="L - SR 8515" data-out="0"><td class="plate">L - SR 8515</td><td class="nm">Kleine</td><td class="mut">H</td><td class="mut tcell">—</td><td class="mut">14</td></tr></tbody></table></div>
<div class="empty d-none" id="dl-none">Kein Treffer.</div>
</div>
<!-- Sitzungsende: Zugang nicht mehr verfügbar (vom Autorefresh erkannt) -->
<div class="ended d-none" id="dl-ended">
<div class="ended-box">
<svg class="ended-ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><rect x="5" y="11" width="14" height="9" rx="2"/><path d="M8 11V8a4 4 0 0 1 8 0v3"/></svg>
<div class="ended-t">Zugang beendet</div>
<div class="ended-x">Dieser mobile Zugang ist nicht mehr verfügbar (widerrufen oder abgelaufen).<br>Bitte wende dich an das Büro.</div>
</div>
</div>
<!-- Check-in/Zahlung im Overlay (kein Seitenwechsel) -->
<div class="ov d-none" id="dl-ov">
<!-- Kein eigener Zurück-Balken mehr: die Buchungseinzelansicht (checkin.php) hat ihren eigenen Zurück-Button oben rechts im Header (postMessage {close:1}). -->
<iframe class="ov-frame" id="dl-ov-frame" title="Check-in" src="about:blank" allowfullscreen></iframe>
</div>
<script>
(function(){
// Browser-Zoom unterbinden (iOS Safari ignoriert user-scalable=no)
['gesturestart','gesturechange','gestureend'].forEach(function(ev){ document.addEventListener(ev, function(e){ e.preventDefault(); }, {passive:false}); });
document.addEventListener('touchmove', function(e){ if(e.touches && e.touches.length>1) e.preventDefault(); }, {passive:false});
var inp=document.getElementById('dl-search'), body=document.getElementById('dl-body'), none=document.getElementById('dl-none');
if(!body) return;
var DL_FP = "031d79c3f62d";
function esc(s){ return String(s==null?'':s).replace(/[&<>"]/g,function(c){return {'&':'&','<':'<','>':'>','"':'"'}[c];}); }
function norm(s){ return (s||'').toLowerCase().replace(/[^0-9a-zäöüß]/g,''); }
function subseq(q,s){ var i=0; for(var j=0;j<s.length && i<q.length;j++) if(s[j]===q[i]) i++; return i===q.length; }
// Damerau-Levenshtein (OSA) – erlaubt Buchstaben-/Zahlendreher als 1 Edit.
function dlev(a,b){
var al=a.length, bl=b.length; if(!al) return bl; if(!bl) return al;
var d=[]; for(var i=0;i<=al;i++){ d[i]=[i]; } for(var j=0;j<=bl;j++){ d[0][j]=j; }
for(i=1;i<=al;i++) for(j=1;j<=bl;j++){
var c=a[i-1]===b[j-1]?0:1;
d[i][j]=Math.min(d[i-1][j]+1, d[i][j-1]+1, d[i-1][j-1]+c);
if(i>1&&j>1&&a[i-1]===b[j-2]&&a[i-2]===b[j-1]) d[i][j]=Math.min(d[i][j], d[i-2][j-2]+1);
}
return d[al][bl];
}
// Kennzeichen ohne Ortskürzel (erstes Segment), normalisiert – für „anderer Ort, Rest passt".
function plateCore(plate){ var p=(plate||'').split(/[^0-9a-zA-ZäöüÄÖÜß]+/).filter(Boolean); return norm(p.length>1?p.slice(1).join(''):plate); }
// Kennzeichen mit Treffer-Hervorhebung (Subsequenz, Leer-/Sonderzeichen werden übersprungen).
function hl(plate,q){
if(!q) return esc(plate);
var qi=0,out='';
for(var i=0;i<plate.length;i++){ var ch=plate[i], n=norm(ch);
if(qi<q.length && n && n===q[qi]){ out+='<mark>'+esc(ch)+'</mark>'; qi++; } else out+=esc(ch); }
return qi===q.length ? out : esc(plate);
}
var orig=[].slice.call(body.querySelectorAll('tr')); // Ausgangsreihenfolge merken
var sgBox=document.getElementById('dl-suggest');
// „Meinten Sie?": ähnliche Kennzeichen (Tippfehler, Dreher, falsches Ortskürzel), die NICHT bereits Treffer sind.
function suggest(q){
if(q.length<2) return [];
var maxd = q.length<=3?1:(q.length<=6?2:3), res=[], seen={};
for(var i=0;i<orig.length;i++){ var r=orig[i], plate=r.getAttribute('data-plate')||'';
if(plate===''||plate==='—') continue;
if(subseq(q, norm(plate))) continue; // bereits exakter Kennzeichen-Treffer → nicht vorschlagen
var dist=Math.min(dlev(q, norm(plate)), dlev(q, plateCore(plate)));
if(dist<=maxd && !seen[plate]){ seen[plate]=1; res.push({plate:plate, href:r.dataset.href, d:dist}); }
}
res.sort(function(a,b){return a.d-b.d;});
return res.slice(0,4);
}
function renderSuggest(q, plateHits){
if(!sgBox) return;
if(!q || q.length<2 || plateHits>0){ sgBox.classList.add('d-none'); sgBox.innerHTML=''; return; }
var s=suggest(q);
if(!s.length){ sgBox.classList.add('d-none'); sgBox.innerHTML=''; return; }
var html='<span class="sg-l">Meinten Sie?</span>';
for(var i=0;i<s.length;i++) html+='<button type="button" class="sg-chip" data-plate="'+esc(s[i].plate)+'">'+esc(s[i].plate)+'</button>';
sgBox.innerHTML=html; sgBox.classList.remove('d-none');
}
if(sgBox) sgBox.addEventListener('click', function(e){ var b=e.target.closest('.sg-chip'); if(!b) return; inp.value=b.getAttribute('data-plate'); runSearch(); inp.focus(); });
// ---- Check-in/Zahlung im Overlay öffnen (kein Seitenwechsel)
var ov=document.getElementById('dl-ov'), ovFrame=document.getElementById('dl-ov-frame');
function openCheckin(href){ if(!ov||!href) return; ovFrame.src = href + (href.indexOf('?')>=0?'&':'?') + 'embed=1'; ov.classList.remove('d-none'); document.documentElement.style.overflow='hidden'; }
function closeCheckin(){ if(!ov) return; ov.classList.add('d-none'); ovFrame.src='about:blank'; document.documentElement.style.overflow='';
if(inp) inp.value=''; // nach Öffnen+Zurück: Suche leeren
runSearch(); // Status-Filter neu anwenden -> Fahrzeug erscheint/verschwindet entsprechend
poll(); }
// ganze Zeile klickbar
body.addEventListener('click', function(e){ var tr=e.target.closest('tr'); if(tr && tr.dataset.href) openCheckin(tr.dataset.href); });
// Live-Suche: filtern, Kennzeichen-Treffer nach oben, Hervorhebung, Vorschläge.
function runSearch(){
var q=norm(inp?inp.value:''), shown=0, plateHits=0, hitsP=[], hitsO=[];
for(var i=0;i<orig.length;i++){ var r=orig[i];
var isOut = r.getAttribute('data-out')==='1';
// ohne Suche: nur Fahrzeuge im Filter; mit Suche: über alle (auch außerhalb) suchen.
var full = q ? subseq(q, r.getAttribute('data-s')||'') : (!isOut && passStatus(r));
var pl = q && subseq(q, norm(r.getAttribute('data-plate')||''));
r.style.display = full?'table-row':'none';
var cell=r.querySelector('td.plate'); if(cell){ var p=r.getAttribute('data-plate')||''; cell.innerHTML = full ? hl(p,q) : esc(p); }
if(full){ shown++; if(pl){ plateHits++; hitsP.push(r); } else hitsO.push(r); }
}
// Reihenfolge: bei Suche Kennzeichen-Treffer zuerst; im „angereist"-Filter nach tatsächlicher
// Check-in-Zeit absteigend (neueste zuerst); sonst Ausgangsreihenfolge (nach Kennzeichen).
var order;
if(q){ order = hitsP.concat(hitsO); }
else if(statusFilter==='arrived'){
var vis=[], hid=[];
for(var z=0;z<orig.length;z++){ (orig[z].style.display!=='none'?vis:hid).push(orig[z]); }
vis.sort(function(a,b){ return (b.getAttribute('data-cin')||'').localeCompare(a.getAttribute('data-cin')||''); });
order = vis.concat(hid);
}
else { order = orig; }
for(var k=0;k<order.length;k++) body.appendChild(order[k]);
if(none) none.classList.toggle('d-none', shown!==0);
if(clearBtn) clearBtn.classList.toggle('d-none', !(inp && inp.value));
renderSuggest(q, plateHits);
if(q){ var fh=hitsP[0]||hitsO[0]; if(fh) scrollToFirst(fh); } // bei Suche zum ersten Treffer springen
}
function scrollToFirst(el){
var tb=document.querySelector('.topbar'); var off=(tb?tb.offsetHeight:0)+6;
var y=el.getBoundingClientRect().top + (window.pageYOffset||document.documentElement.scrollTop||0) - off;
window.scrollTo({top:(y<0?0:y), behavior:'smooth'});
}
var clearBtn=document.getElementById('dl-clear');
function clearSearch(){ if(inp) inp.value=''; runSearch(); }
if(clearBtn) clearBtn.addEventListener('click', function(){ clearSearch(); if(inp) inp.focus(); });
if(inp) inp.addEventListener('input', runSearch);
// Status-Schnellfilter über die Kopf-Statistik (angereist / offen). Beide schließen sich aus.
var statusFilter=''; // '' | 'arrived' | 'open'
function passStatus(r){ if(!statusFilter) return true; var arr=r.classList.contains('st-in'); return statusFilter==='arrived'?arr:!arr; }
function updateFilt(){ var fa=document.getElementById('filt-arr'), fo=document.getElementById('filt-open'); if(fa) fa.classList.toggle('active',statusFilter==='arrived'); if(fo) fo.classList.toggle('active',statusFilter==='open'); }
(function(){
var fa=document.getElementById('filt-arr'), fo=document.getElementById('filt-open');
function tog(which){ statusFilter=(statusFilter===which)?'':which; updateFilt(); if(inp) inp.value=''; runSearch(); }
if(fa){ fa.addEventListener('click',function(){tog('arrived');}); fa.addEventListener('keydown',function(e){if(e.key==='Enter'||e.key===' '){e.preventDefault();tog('arrived');}}); }
if(fo){ fo.addEventListener('click',function(){tog('open');}); fo.addEventListener('keydown',function(e){if(e.key==='Enter'||e.key===' '){e.preventDefault();tog('open');}}); }
})();
// ================= Autorefresh (ressourcenschonend) =================
// Poll schickt den letzten Fingerprint; ist nichts passiert, kommt nur {"v":…} zurück
// (kein Datentransfer, kein Render). Pausiert bei verstecktem Tab und offenem Overlay.
var fp = DL_FP, INT = 20000, live=document.getElementById('dl-live'), ended=false, pollTimer=null;
// Zugang nicht mehr verfügbar -> App „beenden": Sitzungsende-Bildschirm, Polling stoppen.
function endSession(){
if(ended) return; ended=true;
if(pollTimer){ clearInterval(pollTimer); pollTimer=null; }
if(ov) ov.classList.add('d-none');
var el=document.getElementById('dl-ended'); if(el) el.classList.remove('d-none');
document.documentElement.style.overflow='hidden';
}
function base(){ return location.pathname + location.search; }
function pollUrl(extra){ var b=base(); return b + (b.indexOf('?')>=0?'&':'?') + extra; }
function setText(id,v){ var el=document.getElementById(id); if(el) el.textContent=v; }
function stOf(tr){ return tr.classList.contains('st-in')?'i':tr.classList.contains('st-paid')?'p':tr.classList.contains('st-storno')?'x':'o'; }
function applyState(tr, st){ tr.classList.remove('st-in','st-open','st-paid','st-storno'); tr.classList.add(st==='i'?'st-in':st==='p'?'st-paid':st==='x'?'st-storno':'st-open'); }
function flash(tr){ tr.classList.remove('row-flash'); void tr.offsetWidth; tr.classList.add('row-flash'); }
function sameSet(a,b){ if(a.length!==b.length) return false; var m={},i; for(i=0;i<a.length;i++) m[a[i]]=1; for(i=0;i<b.length;i++) if(!m[b[i]]) return false; return true; }
function applyPoll(items){
var map={}, tmap={}, ids=[], i;
for(i=0;i<items.length;i++){ map[items[i][0]]=items[i][1]; tmap[items[i][0]]=items[i][2]; ids.push(String(items[i][0])); }
var domRows=body.querySelectorAll('tr[data-out="0"]'), domIds=[];
for(i=0;i<domRows.length;i++) domIds.push(domRows[i].getAttribute('data-id'));
if(!sameSet(ids, domIds)){ fetchRows(); return; } // Zusammensetzung geändert -> einmalig Zeilen neu laden
var dirty=false;
for(i=0;i<domRows.length;i++){ var tr=domRows[i], id=tr.getAttribute('data-id'), st=map[id], tm=tmap[id], chg=false;
if(st && st!==stOf(tr)){ applyState(tr, st); chg=true; }
var tc=tr.querySelector('.tcell'); if(tc && tm!==undefined){ var nv=tm||'—'; if(tc.textContent!==nv){ tc.textContent=nv; chg=true; } }
if(st!==undefined) tr.setAttribute('data-cin', (st==='i' && tm) ? tm.replace(/\D/g,'') : ''); // Sortierzeit aktuell halten
if(chg){ flash(tr); dirty=true; }
}
var arr=0; for(i=0;i<items.length;i++) if(items[i][1]==='i') arr++;
setText('st-arr',arr); setText('st-tot',items.length); setText('st-open',items.length-arr);
if(dirty && (statusFilter || (inp&&inp.value))) runSearch(); // nur bei aktivem Filter/Suche neu anwenden (sonst Flash erhalten)
}
function fetchRows(){
fetch(pollUrl('action=rows'), {headers:{'X-Requested-With':'XMLHttpRequest'}}).then(function(r){return r.json();}).then(function(res){
if(res && res.gone){ endSession(); return; }
if(res && res.locked){ location.reload(); return; }
if(!res||typeof res.html!=='string') return;
fp=res.v; body.innerHTML=res.html; orig=[].slice.call(body.querySelectorAll('tr'));
setText('st-arr',res.arr); setText('st-open',res.open); setText('st-tot',res.total); runSearch();
}).catch(function(){});
}
function poll(){
if(ended || document.hidden) return;
if(ov && !ov.classList.contains('d-none')) return; // während Overlay offen nicht pollen
fetch(pollUrl('action=poll&v='+encodeURIComponent(fp)), {headers:{'X-Requested-With':'XMLHttpRequest'}})
.then(function(r){return r.json();}).then(function(res){
if(res && res.gone){ endSession(); return; } // Zugang widerrufen/abgelaufen
if(res && res.locked){ location.reload(); return; } // PIN-Sperre erneut aktiv (Cookie abgelaufen)
if(live) live.classList.remove('stale');
if(res && res.v && res.v!==fp){ fp=res.v; if(res.items) applyPoll(res.items); }
}).catch(function(){ if(live) live.classList.add('stale'); });
}
pollTimer=setInterval(poll, INT);
document.addEventListener('visibilitychange', function(){ if(!document.hidden) poll(); });
// Sofort-Updates aus dem Check-in-Overlay (ohne auf den Poll zu warten)
window.addEventListener('message', function(ev){
var d=ev.data; if(!d || !d.pacimCi) return;
if(d.rows){ fetchRows(); }
else if(d.b && d.state){ var tr=body.querySelector('tr[data-id="'+d.b+'"]'); if(tr){ applyState(tr,d.state); flash(tr); if(statusFilter||(inp&&inp.value)) runSearch(); } }
if(d.close){ closeCheckin(); }
});
})();
</script>
<script>(function(){function go(){var e=document.documentElement,r=e.requestFullscreen||e.webkitRequestFullscreen;if(r){try{r.call(e);}catch(_){}}}document.addEventListener("pointerdown",function f(){document.removeEventListener("pointerdown",f);go();},{passive:true});})();</script><style>
#pwi-ov{position:fixed;inset:0;z-index:9000;display:none;flex-direction:column;align-items:center;justify-content:flex-start;
background:linear-gradient(160deg,#0d2c44 0%,#11436b 60%,#1b6ca8 130%);color:#fff;overflow-y:auto;-webkit-overflow-scrolling:touch;
padding:calc(34px + env(safe-area-inset-top,0px)) 22px calc(28px + env(safe-area-inset-bottom,0px));
font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;}
#pwi-ov.pwi-show{display:flex;}
.pwi-card{width:100%;max-width:430px;margin:auto 0;text-align:center;}
.pwi-icon{width:96px;height:96px;border-radius:22px;box-shadow:0 12px 34px rgba(0,0,0,.32);margin:0 auto 18px;display:block;background:#fff;}
.pwi-name{font-size:25px;font-weight:800;letter-spacing:.2px;}
.pwi-sub{font-size:14.5px;opacity:.82;margin-top:6px;line-height:1.5;}
.pwi-steps{margin:22px 0 6px;text-align:left;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.14);border-radius:16px;padding:16px 16px 6px;}
.pwi-step{display:flex;align-items:flex-start;gap:12px;padding:0 0 14px;}
.pwi-num{flex:0 0 auto;width:26px;height:26px;border-radius:50%;background:#fff;color:#0d2c44;font-weight:800;font-size:14px;display:flex;align-items:center;justify-content:center;margin-top:1px;}
.pwi-txt{font-size:14.5px;line-height:1.5;}
.pwi-txt b{font-weight:800;}
.pwi-ic{display:inline-flex;vertical-align:-5px;margin:0 1px;}
.pwi-ic svg{width:19px;height:19px;}
.pwi-install{display:none;width:100%;margin-top:18px;background:#fff;color:#0d2c44;border:0;border-radius:13px;padding:15px;font-size:16px;font-weight:800;cursor:pointer;-webkit-tap-highlight-color:transparent;}
.pwi-install:active{transform:scale(.98);}
.pwi-install.pwi-show{display:block;}
.pwi-cont{display:inline-block;margin-top:18px;color:#cfe2f2;font-size:14px;font-weight:600;text-decoration:underline;cursor:pointer;background:none;border:0;-webkit-tap-highlight-color:transparent;}
.pwi-hint{font-size:12px;opacity:.6;margin-top:14px;}
</style>
<div id="pwi-ov" role="dialog" aria-label="App installieren">
<div class="pwi-card">
<img class="pwi-icon" src="https://seaside.pacim.de/assets/webapp/icon-192.png" alt="" width="96" height="96">
<div class="pwi-name">Seaside Parking</div>
<div class="pwi-sub">Für den schnellsten Zugriff diese App zum Startbildschirm hinzufügen – sie öffnet dann im Vollbild wie eine echte App.</div>
<div class="pwi-steps" id="pwi-ios" style="display:none">
<div class="pwi-step"><span class="pwi-num">1</span><span class="pwi-txt">Unten in der Safari-Leiste auf das <b>Teilen-Symbol</b>
<span class="pwi-ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M12 16V4M8 8l4-4 4 4"/><path d="M5 12v6a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-6"/></svg></span>
tippen.</span></div>
<div class="pwi-step"><span class="pwi-num">2</span><span class="pwi-txt">Im Menü <b>„Zum Home-Bildschirm"</b> wählen.</span></div>
<div class="pwi-step"><span class="pwi-num">3</span><span class="pwi-txt">Oben rechts auf <b>„Hinzufügen"</b> tippen – fertig.</span></div>
</div>
<div class="pwi-steps" id="pwi-android" style="display:none">
<div class="pwi-step"><span class="pwi-num">1</span><span class="pwi-txt">Oben rechts im Browser auf das <b>Menü</b>
<span class="pwi-ic"><svg viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="5" r="2"/><circle cx="12" cy="12" r="2"/><circle cx="12" cy="19" r="2"/></svg></span>
tippen.</span></div>
<div class="pwi-step"><span class="pwi-num">2</span><span class="pwi-txt"><b>„App installieren"</b> bzw. <b>„Zum Startbildschirm hinzufügen"</b> wählen.</span></div>
<div class="pwi-step"><span class="pwi-num">3</span><span class="pwi-txt">Mit <b>„Installieren"</b> bestätigen – fertig.</span></div>
</div>
<div class="pwi-steps" id="pwi-other" style="display:none">
<div class="pwi-step"><span class="pwi-num">1</span><span class="pwi-txt">Im Browser-Menü <b>„Zum Startbildschirm hinzufügen"</b> bzw. <b>„App installieren"</b> wählen.</span></div>
</div>
<button type="button" class="pwi-install" id="pwi-install">App installieren</button>
<br><button type="button" class="pwi-cont" id="pwi-cont">Im Browser fortfahren</button>
<div class="pwi-hint">Diese Anleitung erscheint nur im Browser, nicht in der installierten App.</div>
</div>
</div>
<script>(function(){
var ov=document.getElementById('pwi-ov'); if(!ov) return;
var KEY='pwiSkip';
function standalone(){ return (window.matchMedia&&window.matchMedia('(display-mode: standalone)').matches) || window.navigator.standalone===true; }
function embedded(){ try{ return window.self!==window.top; }catch(e){ return true; } }
var skipped=false; try{ skipped=sessionStorage.getItem(KEY)==='1'; }catch(e){}
if(standalone()||embedded()||skipped) return;
var ua=navigator.userAgent||'';
var isIOS=/iphone|ipad|ipod/i.test(ua) || (navigator.platform==='MacIntel' && navigator.maxTouchPoints>1);
var isAndroid=/android/i.test(ua);
document.getElementById( isIOS?'pwi-ios':(isAndroid?'pwi-android':'pwi-other') ).style.display='block';
// Ein-Tipp-Installation, falls der Browser sie anbietet (Chrome/Edge mit erfüllten Kriterien).
var deferred=null, btn=document.getElementById('pwi-install');
window.addEventListener('beforeinstallprompt', function(e){ e.preventDefault(); deferred=e; if(btn) btn.classList.add('pwi-show'); });
if(btn) btn.addEventListener('click', function(){ if(!deferred) return; deferred.prompt(); deferred.userChoice.then(function(c){ deferred=null; btn.classList.remove('pwi-show'); if(c&&c.outcome==='accepted') hide(); }); });
// Wurde die App während der Sitzung installiert → Overlay weg.
window.addEventListener('appinstalled', function(){ hide(); });
function hide(){ ov.classList.remove('pwi-show'); }
document.getElementById('pwi-cont').addEventListener('click', function(){ try{ sessionStorage.setItem(KEY,'1'); }catch(e){} hide(); });
ov.classList.add('pwi-show');
})();</script>
</body></html>