<?php
/**
 * Shared owner-page UX: instant [data-tip] tooltips (always) + an optional
 * self-contained Guide modal (vanilla, no Bootstrap JS needed).
 *
 * Usage: set $ownerGuide before including for the guide; a button with
 * data-guide-open opens it. Include once per page.
 *
 * @var array{id?:string,eyebrow?:string,title?:string,subtitle?:string,icon?:string,
 *            topics?:array<int,array{id:string,label:string,icon?:string,keywords?:string,body:string}>}|null $ownerGuide
 */
$og = isset($ownerGuide) && is_array($ownerGuide) ? $ownerGuide : null;
$ogId = $og ? (string) ($og['id'] ?? 'ownerGuide') : '';
$ogTopics = $og && is_array($og['topics'] ?? null) ? $og['topics'] : [];
?>
<style>
/* ── instant, theme-aware tooltip ──
   Rendered as ONE fixed-position node on <body>, not a ::after on the trigger.
   That is deliberate: a ::after lives inside its ancestors, so any card with
   `overflow:hidden` (needed for rounded corners) or any scroll container
   silently clips it. That produced the same class of bug three times — a
   tooltip cut off at a card edge, then off the top of the viewport, then at
   risk again inside the table's scroll wrapper. Fixed positioning cannot be
   clipped by an ancestor, and the placement below auto-flips to stay on screen,
   so callers no longer have to reason about where a control sits.
   `.tip-left` / `.tip-up` remain honoured as PREFERENCES, overridden when they
   would push the tooltip off screen. */
#ppuxTip {
    position: fixed; z-index: 2147483000; pointer-events: none;
    background: var(--pp-navbar-bg, #1f2328); color: #f4f6fb;
    font-size: .72rem; font-weight: 600; line-height: 1.35; text-align: center;
    white-space: normal; width: max-content; max-width: 240px;
    padding: 6px 10px; border-radius: 8px;
    box-shadow: 0 8px 22px rgba(0, 0, 0, .28); border: 1px solid var(--pp-border-strong, #3b4b65);
    opacity: 0; transition: opacity .1s ease-out; left: 0; top: 0;
}
#ppuxTip.on { opacity: 1; }
@media (prefers-reduced-motion: reduce) { #ppuxTip { transition: none; } }
</style>
<script>
/* Tooltip driver for [data-tip]. Deliberately defensive: any failure must leave
   the page working, never throw on hover. */
(function () {
    if (window.__ppuxTip) return;
    window.__ppuxTip = true;

    var GAP = 8, tip = null, current = null;

    function node() {
        if (!tip) {
            tip = document.createElement('div');
            tip.id = 'ppuxTip';
            tip.setAttribute('role', 'tooltip');
            document.body.appendChild(tip);
        }
        return tip;
    }

    function place(el) {
        var t = node(), text = el.getAttribute('data-tip') || '';
        if (!text) return hide();
        t.textContent = text;
        t.classList.add('on');

        var r = el.getBoundingClientRect(), b = t.getBoundingClientRect();
        var vw = document.documentElement.clientWidth, vh = document.documentElement.clientHeight;

        // Vertical: honour tip-up when it fits, otherwise put it wherever there is room.
        var wantUp = el.classList.contains('tip-up');
        var fitsUp = r.top - b.height - GAP >= 0;
        var fitsDown = r.bottom + b.height + GAP <= vh;
        var up = wantUp ? (fitsUp || !fitsDown) : (!fitsDown && fitsUp);
        var top = up ? r.top - b.height - GAP : r.bottom + GAP;

        // Horizontal: tip-left right-aligns; then clamp so it can never leave the viewport.
        var left = el.classList.contains('tip-left')
            ? r.right - b.width
            : r.left + (r.width - b.width) / 2;
        left = Math.max(GAP, Math.min(left, vw - b.width - GAP));
        top  = Math.max(GAP, Math.min(top, vh - b.height - GAP));

        t.style.left = Math.round(left) + 'px';
        t.style.top  = Math.round(top) + 'px';
    }

    function hide() {
        current = null;
        if (tip) { tip.classList.remove('on'); tip.style.left = '-9999px'; }
    }

    function enter(e) {
        var el = e.target && e.target.closest ? e.target.closest('[data-tip]') : null;
        if (!el || el === current) return;
        current = el;
        try { place(el); } catch (err) { hide(); }
    }

    document.addEventListener('mouseover', enter, true);
    document.addEventListener('focusin', enter, true);
    document.addEventListener('mouseout', function (e) {
        var el = e.target && e.target.closest ? e.target.closest('[data-tip]') : null;
        if (el && el === current) hide();
    }, true);
    document.addEventListener('focusout', hide, true);
    document.addEventListener('scroll', hide, true);
    window.addEventListener('resize', hide);
    document.addEventListener('keydown', function (e) { if (e.key === 'Escape') hide(); });
})();
</script>
<style>
<?php if ($og): ?>
/* ── owner guide modal ── */
.og-ov { position: fixed; inset: 0; z-index: 110000; display: none; background: color-mix(in srgb, var(--pp-navbar-bg, #0f172a) 58%, transparent); backdrop-filter: blur(3px); }
.og-ov.open { display: block; }
.og-panel { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: min(980px, calc(100vw - 2rem)); height: min(680px, calc(100vh - 2rem));
    display: flex; flex-direction: column; background: var(--pp-surface, #fff); color: var(--pp-text, #172033); border: 1px solid var(--pp-border, #e2e8f0); border-radius: 16px; overflow: hidden; box-shadow: 0 30px 80px rgba(0,0,0,.4); }
.og-h { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; padding: 1rem 1.2rem; border-bottom: 1px solid var(--pp-border, #e2e8f0);
    background: linear-gradient(120deg, color-mix(in srgb, var(--pp-accent, #2563eb) 16%, var(--pp-surface)), var(--pp-surface)); flex: 0 0 auto; }
.og-head { display: flex; gap: .8rem; align-items: flex-start; }
.og-ic { flex: 0 0 auto; width: 2.6rem; height: 2.6rem; display: grid; place-items: center; border-radius: 12px; background: var(--pp-accent, #2563eb); color: var(--pp-accent-contrast, #fff); font-size: 1.15rem; }
.og-kicker { font-size: .62rem; font-weight: 850; letter-spacing: .08em; text-transform: uppercase; color: var(--pp-accent, #2563eb); }
.og-h h2 { font-size: 1.15rem; font-weight: 800; margin: .05rem 0; letter-spacing: -.02em; }
.og-h p { margin: 0; font-size: .8rem; color: var(--pp-text-muted, #64748b); }
.og-x { flex: 0 0 auto; border: 0; background: transparent; color: var(--pp-text-muted, #64748b); font-size: 1.05rem; cursor: pointer; width: 34px; height: 34px; border-radius: 8px; }
.og-x:hover { background: var(--pp-surface-muted, #f1f5f9); color: var(--pp-text); }
.og-search { position: relative; padding: .65rem 1.2rem; border-bottom: 1px solid var(--pp-border, #e2e8f0); background: var(--pp-surface, #fff); flex: 0 0 auto; }
.og-search i { position: absolute; left: 1.55rem; top: 50%; transform: translateY(-50%); color: var(--pp-text-muted, #94a3b8); }
.og-search input { width: 100%; height: 38px; border: 1px solid var(--pp-border, #e2e8f0); border-radius: 9px; padding: 0 .8rem 0 2.1rem; background: var(--pp-input-bg, #fff); color: var(--pp-text); font: inherit; font-size: .86rem; }
.og-search input:focus { outline: none; border-color: var(--pp-accent, #2563eb); box-shadow: 0 0 0 3px var(--pp-accent-soft, #eaf2ff); }
.og-body { display: grid; grid-template-columns: 240px minmax(0, 1fr); min-height: 0; flex: 1 1 auto; }
.og-nav { border-right: 1px solid var(--pp-border, #e2e8f0); background: var(--pp-surface-soft, #f8fafc); padding: .7rem; overflow-y: auto; }
.og-topic { display: flex; align-items: center; gap: .55rem; width: 100%; text-align: left; border: 0; background: transparent; color: var(--pp-text-muted, #475569); padding: .55rem .65rem; border-radius: 9px; cursor: pointer; font-size: .82rem; font-weight: 650; }
.og-topic:hover { background: var(--pp-surface-muted, #f1f5f9); color: var(--pp-text); }
.og-topic.active { background: var(--pp-accent-soft, #eaf2ff); color: var(--pp-accent, #2563eb); }
.og-content { min-width: 0; overflow-y: auto; padding: 1.25rem 1.4rem; }
.og-pane { display: none; max-width: 780px; }
.og-pane.active { display: block; animation: ppuxFade .16s ease; }
@keyframes ppuxFade { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
.og-empty { display: none; text-align: center; color: var(--pp-text-muted, #94a3b8); padding: 3rem 1rem; }
.og-empty.show { display: block; }
.og-pane h3 { font-size: 1.05rem; font-weight: 800; margin: 0 0 .4rem; }
.og-pane h4 { font-size: .9rem; font-weight: 750; margin: 1rem 0 .35rem; }
.og-pane p, .og-pane li { font-size: .84rem; line-height: 1.6; color: var(--pp-text, #334155); }
.og-pane ul { margin: .3rem 0 .6rem; padding-left: 1.15rem; }
.og-pane ol, .og-pane ol.og-list, .og-pane ul.og-list { margin: .3rem 0 .7rem; padding-left: 1.35rem; }
.og-pane ol.og-list li, .og-pane ul.og-list li { margin: .3rem 0; }
.og-pane li b, .og-pane li strong { color: var(--pp-text, #0f172a); font-weight: 700; }
.og-pane code { background: var(--pp-surface-muted, #f1f5f9); border-radius: 5px; padding: 1px 6px; font-size: .82rem; }
.og-lead { display: flex; gap: .7rem; align-items: flex-start; background: var(--pp-surface-soft, #f8fafc); border: 1px solid var(--pp-border, #e2e8f0); border-radius: 12px; padding: .85rem 1rem; margin-bottom: .9rem; }
.og-lead i { color: var(--pp-accent, #2563eb); font-size: 1.15rem; margin-top: 1px; }
.og-steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: .55rem; margin: .6rem 0; }
.og-step { border: 1px solid var(--pp-border, #e2e8f0); border-radius: 10px; padding: .7rem; }
.og-step b { display: grid; width: 1.4rem; height: 1.4rem; place-items: center; border-radius: 50%; background: var(--pp-accent-soft, #eaf2ff); color: var(--pp-accent, #2563eb); font-size: .72rem; margin-bottom: .35rem; }
.og-step strong { font-size: .8rem; } .og-step span { font-size: .74rem; color: var(--pp-text-muted, #64748b); }
.og-tip { display: flex; gap: .55rem; margin-top: .7rem; padding: .6rem .75rem; border-radius: 9px; font-size: .78rem;
    background: color-mix(in srgb, #16a34a 12%, var(--pp-surface)); color: color-mix(in srgb, #16a34a 78%, var(--pp-text)); }
.og-warn { display: flex; gap: .55rem; margin-top: .7rem; padding: .6rem .75rem; border-radius: 9px; font-size: .78rem;
    background: color-mix(in srgb, #f59e0b 14%, var(--pp-surface)); color: color-mix(in srgb, #f59e0b 82%, var(--pp-text)); }
@media (max-width: 720px) { .og-body { grid-template-columns: 1fr; } .og-nav { display: flex; overflow-x: auto; border-right: 0; border-bottom: 1px solid var(--pp-border); } .og-topic { flex: 0 0 auto; } }
<?php endif; ?>
</style>
<?php if ($og): ?>
<div class="og-ov" id="<?= htmlspecialchars($ogId) ?>">
    <div class="og-panel">
        <div class="og-h">
            <div class="og-head">
                <span class="og-ic"><i class="bi bi-<?= htmlspecialchars((string) ($og['icon'] ?? 'book')) ?>"></i></span>
                <div>
                    <div class="og-kicker"><?= htmlspecialchars((string) ($og['eyebrow'] ?? 'Guide')) ?></div>
                    <h2><?= htmlspecialchars((string) ($og['title'] ?? 'Guide')) ?></h2>
                    <p><?= htmlspecialchars((string) ($og['subtitle'] ?? '')) ?></p>
                </div>
            </div>
            <button type="button" class="og-x" data-og-close aria-label="Close guide"><i class="bi bi-x-lg"></i></button>
        </div>
        <div class="og-search"><i class="bi bi-search"></i><input type="search" placeholder="Search this guide…" data-og-search autocomplete="off"></div>
        <div class="og-body">
            <nav class="og-nav">
                <?php foreach ($ogTopics as $i => $t): ?>
                    <button type="button" class="og-topic<?= $i === 0 ? ' active' : '' ?>" data-og-topic="<?= htmlspecialchars((string) $t['id']) ?>">
                        <i class="bi bi-<?= htmlspecialchars((string) ($t['icon'] ?? 'circle')) ?>"></i><span><?= htmlspecialchars((string) $t['label']) ?></span>
                    </button>
                <?php endforeach; ?>
            </nav>
            <div class="og-content">
                <div class="og-empty" data-og-empty><i class="bi bi-search d-block" style="font-size:1.6rem;margin-bottom:.4rem"></i>No matching topic. Try another term.</div>
                <?php foreach ($ogTopics as $i => $t): ?>
                    <section class="og-pane<?= $i === 0 ? ' active' : '' ?>" data-og-pane="<?= htmlspecialchars((string) $t['id']) ?>" data-og-keywords="<?= htmlspecialchars((string) ($t['keywords'] ?? '')) ?>">
                        <?= (string) $t['body'] ?>
                    </section>
                <?php endforeach; ?>
            </div>
        </div>
    </div>
</div>
<script>
(function () {
    var ov = document.getElementById(<?= json_encode($ogId) ?>);
    if (!ov) return;
    function open() { ov.classList.add('open'); var s = ov.querySelector('[data-og-search]'); if (s) setTimeout(function () { s.focus(); }, 50); }
    function close() { ov.classList.remove('open'); }
    function show(id) {
        ov.querySelectorAll('[data-og-topic]').forEach(function (b) { b.classList.toggle('active', b.getAttribute('data-og-topic') === id); });
        ov.querySelectorAll('[data-og-pane]').forEach(function (p) { p.classList.toggle('active', p.getAttribute('data-og-pane') === id); });
    }
    document.addEventListener('click', function (e) {
        var o = e.target.closest('[data-guide-open]');
        if (o && o.getAttribute('data-guide-open') === <?= json_encode($ogId) ?>) { e.preventDefault(); open(); return; }
        if (e.target.closest('[data-og-close]') || e.target === ov) { close(); return; }
        var t = e.target.closest('[data-og-topic]'); if (t && ov.contains(t)) show(t.getAttribute('data-og-topic'));
    });
    document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && ov.classList.contains('open')) close(); });
    var search = ov.querySelector('[data-og-search]');
    if (search) search.addEventListener('input', function () {
        var q = search.value.trim().toLowerCase();
        var panes = ov.querySelectorAll('[data-og-pane]'), topics = ov.querySelectorAll('[data-og-topic]'), first = null;
        panes.forEach(function (p, i) {
            var hay = ((p.getAttribute('data-og-keywords') || '') + ' ' + p.textContent).toLowerCase();
            var hit = q === '' || hay.indexOf(q) > -1;
            topics[i].style.display = hit ? '' : 'none';
            if (hit && first === null) first = p.getAttribute('data-og-pane');
        });
        ov.querySelector('[data-og-empty]').classList.toggle('show', first === null);
        if (q !== '' && first) show(first);
    });
})();
</script>
<?php endif; ?>
