<?php $this->start('body'); ?>

<!-- ── Slice 8Z-z-C · Dedicated per-site page · Vue 3 Options API root ── -->
<style>[v-cloak]{display:none !important;}</style>

<div id="site_view_root" v-cloak>

    <!-- Breadcrumb + primary actions -->
    <div class="d-flex align-items-center mb-3">
        <a href="<?= PROOT; ?>Projects/sites/<?= (int)$p['c0d']; ?>" class="btn btn-sm" style="background:transparent;border:1px solid #E2E8F0;color:#64748B;font-weight:500;">
            <i class="fas fa-arrow-left me-1"></i>All sites
        </a>
        <div class="ms-3">
            <div class="small text-muted" style="color:#94A3B8;font-size:.66rem;letter-spacing:.4px;text-transform:uppercase;">
                <?= htmlspecialchars((string)($p['project_no'] ?? '')); ?> · Site
            </div>
            <div style="color:#334155;font-weight:700;font-size:1.02rem;">
                <code style="color:#7A8BA1;background:#EEF2F6;padding:.15rem .4rem;border-radius:3px;font-size:.9rem;">{{ site.site_code || '—' }}</code>
                <span class="ms-2">{{ site.name }}</span>
                <span v-if="site.poc_confirmed_on" class="badge ms-2" style="background:#7A9E7E;color:#fff;font-size:.62rem;font-weight:600;padding:.2rem .55rem;">
                    <i class="fas fa-check-circle me-1"></i>POC confirmed
                </span>
            </div>
        </div>
        <?php if ($can_edit): ?>
        <div class="ms-auto">
            <button type="button" @click="openSiteEdit" class="btn btn-sm" style="background:transparent;border:1px solid #7A8BA1;color:#7A8BA1;font-weight:500;">
                <i class="fas fa-pencil-alt me-1"></i>Edit site
            </button>
            <button type="button" @click="removeSite" class="btn btn-sm" style="background:transparent;border:1px solid #C48579;color:#C48579;font-weight:500;">
                <i class="fas fa-times me-1"></i>Delete site
            </button>
        </div>
        <?php endif; ?>
    </div>

    <!-- Site details -->
    <div class="card border-0 shadow-sm mb-3">
        <div class="card-header bg-white" style="border-bottom:1px solid #E2E8F0;">
            <strong style="color:#334155;"><i class="fas fa-map-pin me-2" style="color:#7A8BA1;"></i>Site details</strong>
        </div>
        <div class="card-body p-3">
            <div class="row g-3">
                <div class="col-md-3">
                    <div class="small" style="color:#94A3B8;font-size:.6rem;letter-spacing:.4px;text-transform:uppercase;font-weight:600;">Region</div>
                    <div style="color:#334155;font-weight:500;">{{ site.region || '—' }}</div>
                </div>
                <div class="col-md-3">
                    <div class="small" style="color:#94A3B8;font-size:.6rem;letter-spacing:.4px;text-transform:uppercase;font-weight:600;">District</div>
                    <div style="color:#334155;font-weight:500;">{{ site.district || '—' }}</div>
                </div>
                <div class="col-md-3">
                    <div class="small" style="color:#94A3B8;font-size:.6rem;letter-spacing:.4px;text-transform:uppercase;font-weight:600;">GPS</div>
                    <div v-if="site.gps_lat && site.gps_lng" style="color:#334155;font-family:monospace;">
                        <a :href="'https://maps.google.com/?q=' + site.gps_lat + ',' + site.gps_lng" target="_blank" style="color:#7A8BA1;text-decoration:none;">
                            {{ site.gps_lat }}, {{ site.gps_lng }} <i class="fas fa-external-link-alt" style="font-size:.62rem;"></i>
                        </a>
                    </div>
                    <div v-else style="color:#94A3B8;">—</div>
                </div>
                <div class="col-md-3">
                    <div class="small" style="color:#94A3B8;font-size:.6rem;letter-spacing:.4px;text-transform:uppercase;font-weight:600;">Active</div>
                    <div style="color:#334155;font-weight:500;">
                        <span v-if="(site.is_active | 0) === 1" style="color:#7A9E7E;"><i class="fas fa-check-circle me-1"></i>Yes</span>
                        <span v-else style="color:#C48579;"><i class="fas fa-times-circle me-1"></i>No</span>
                    </div>
                </div>
                <div class="col-md-12">
                    <div class="small" style="color:#94A3B8;font-size:.6rem;letter-spacing:.4px;text-transform:uppercase;font-weight:600;">Physical address</div>
                    <div style="color:#334155;">{{ site.physical_address || '—' }}</div>
                </div>
                <div v-if="site.access_notes" class="col-md-12">
                    <div class="small" style="color:#94A3B8;font-size:.6rem;letter-spacing:.4px;text-transform:uppercase;font-weight:600;">Access notes</div>
                    <div style="color:#64748B;font-size:.86rem;">{{ site.access_notes }}</div>
                </div>
            </div>
        </div>
    </div>

    <!-- POCs -->
    <div class="card border-0 shadow-sm mb-3" style="border-left:3px solid #7A9E7E !important;">
        <div class="card-header bg-white d-flex align-items-center justify-content-between" style="border-bottom:1px solid #E2E8F0;">
            <div>
                <strong style="color:#334155;"><i class="fas fa-address-card me-2" style="color:#7A9E7E;"></i>Points of contact</strong>
            </div>
            <?php if ($can_edit): ?>
            <div>
                <button v-if="!site.poc_confirmed_on" type="button" @click="confirmPocs"
                        :disabled="!sitePocs.length && !projectPocs.length"
                        :title="!sitePocs.length && !projectPocs.length ? 'Add a POC first' : ''"
                        class="btn btn-sm" style="background:#7A9E7E;border-color:#7A9E7E;color:#fff;font-weight:600;">
                    <i class="fas fa-check me-1"></i>Confirm POCs
                </button>
                <button v-else type="button" @click="unconfirmPocs"
                        class="btn btn-sm" style="background:transparent;border:1px solid #C48579;color:#C48579;font-weight:500;">
                    <i class="fas fa-undo me-1"></i>Un-confirm
                </button>
                <button type="button" @click="openPocAdd" class="btn btn-sm ms-1" style="background:#7A8BA1;border-color:#7A8BA1;color:#fff;font-weight:600;">
                    <i class="fas fa-plus me-1"></i>Add POC
                </button>
            </div>
            <?php endif; ?>
        </div>
        <div class="card-body p-3">
            <!-- Inherited from project -->
            <div v-if="projectPocs.length" class="mb-3 p-2" style="background:#F1F5F9;border:1px dashed #CBD5E1;border-radius:4px;">
                <div class="small mb-2" style="color:#64748B;font-size:.62rem;text-transform:uppercase;letter-spacing:.4px;font-weight:600;">
                    <i class="fas fa-arrow-turn-down me-1"></i>Inherited from project · {{ projectPocs.length }} default POC{{ projectPocs.length === 1 ? '' : 's' }}
                </div>
                <div class="d-flex flex-wrap gap-2">
                    <span v-for="pc in projectPocs" :key="'ipc_'+pc.c0d"
                          class="badge d-inline-flex align-items-center gap-1" style="background:#fff;color:#334155;border:1px solid #CBD5E1;font-size:.7rem;font-weight:500;padding:.3rem .55rem;">
                        <span class="badge" :style="'background:'+projectRoleColor(pc.poc_role)+';color:#fff;font-size:.55rem;font-weight:600;padding:.1rem .3rem;text-transform:capitalize;'">{{ projectRoleLabel(pc.poc_role) }}</span>
                        <strong style="color:#334155;font-weight:600;">{{ pc.poc_name }}</strong>
                        <span v-if="pc.poc_phone" style="color:#64748B;font-size:.66rem;">· {{ pc.poc_phone }}</span>
                    </span>
                </div>
            </div>

            <div v-if="!sitePocs.length" class="p-2 text-center" style="color:#94A3B8;font-size:.82rem;">
                <span v-if="projectPocs.length">No site-specific POC. The project defaults above apply.</span>
                <span v-else>No POCs on file. PM / PC needs at least one to confirm the site.</span>
            </div>
            <table v-else class="table table-sm mb-0" style="font-size:.82rem;">
                <tbody>
                    <tr v-for="pc in sitePocs" :key="'p_'+pc.c0d">
                        <td style="width:140px;">
                            <span class="badge" :style="'background:'+roleColor(pc.poc_role)+';color:#fff;font-size:.6rem;font-weight:600;padding:.2rem .5rem;text-transform:capitalize;'">
                                {{ roleLabel(pc.poc_role) }}
                            </span>
                            <span v-if="pc.is_primary == 1" class="badge ms-1" style="background:#7A9E7E;color:#fff;font-size:.55rem;font-weight:600;padding:.15rem .3rem;">PRIMARY</span>
                        </td>
                        <td style="color:#334155;font-weight:500;">{{ pc.poc_name }}</td>
                        <td style="color:#64748B;">
                            <a v-if="pc.poc_phone" :href="'tel:' + pc.poc_phone" style="color:#64748B;text-decoration:none;"><i class="fas fa-phone me-1" style="font-size:.66rem;"></i>{{ pc.poc_phone }}</a>
                        </td>
                        <td style="color:#64748B;">
                            <a v-if="pc.poc_email" :href="'mailto:' + pc.poc_email" style="color:#64748B;text-decoration:none;"><i class="fas fa-envelope me-1" style="font-size:.66rem;"></i>{{ pc.poc_email }}</a>
                        </td>
                        <?php if ($can_edit): ?>
                        <td style="width:80px;text-align:right;">
                            <button type="button" @click="openPocEdit(pc)" class="btn btn-sm" style="background:transparent;border:none;color:#7A8BA1;padding:0 .3rem;"><i class="fas fa-pencil-alt"></i></button>
                            <button type="button" @click="removePoc(pc)" class="btn btn-sm" style="background:transparent;border:none;color:#C48579;padding:0 .3rem;"><i class="fas fa-times"></i></button>
                        </td>
                        <?php endif; ?>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>

    <!-- Equipment -->
    <div class="card border-0 shadow-sm mb-3" style="border-left:3px solid #D4A574 !important;">
        <div class="card-header bg-white d-flex align-items-center justify-content-between" style="border-bottom:1px solid #E2E8F0;">
            <div>
                <strong style="color:#334155;"><i class="fas fa-cubes me-2" style="color:#D4A574;"></i>Installed equipment</strong>
                <span class="text-muted ms-2" style="color:#94A3B8;font-size:.72rem;">{{ assets.length }} unit{{ assets.length === 1 ? '' : 's' }} on file at this site</span>
            </div>
            <?php if ($can_edit): ?>
            <button v-if="!assetFormOpen" type="button" @click="openAssetAdd" class="btn btn-sm" style="background:#D4A574;border-color:#D4A574;color:#fff;font-weight:600;">
                <i class="fas fa-plus me-1"></i>Add equipment
            </button>
            <?php endif; ?>
        </div>

        <!-- Inline add / edit form · Vue3 · replaces the earlier jquery-confirm modal -->
        <div v-if="assetFormOpen" class="p-3" style="background:#F8FAFC;border-bottom:1px solid #E2E8F0;border-left:3px solid #D4A574;">
            <div class="d-flex align-items-center justify-content-between mb-2">
                <div>
                    <strong style="color:#334155;font-size:.86rem;">
                        <i class="fas" :class="assetFormMode === 'edit' ? 'fa-pencil-alt' : 'fa-plus'" style="color:#D4A574;"></i>
                        {{ assetFormMode === 'edit' ? 'Edit equipment' : 'Add equipment' }}
                    </strong>
                    <span class="text-muted ms-2" style="color:#94A3B8;font-size:.68rem;">Blueprint pre-fills type / make / model / size — override any field manually.</span>
                </div>
                <button type="button" @click="closeAssetForm" class="btn btn-sm" style="background:transparent;border:none;color:#94A3B8;">
                    <i class="fas fa-times"></i>
                </button>
            </div>

            <div class="row g-2">
                <div class="col-md-12">
                    <label class="form-label small text-muted" style="font-size:.66rem;text-transform:uppercase;letter-spacing:.3px;font-weight:600;">Blueprint (optional — prefills fields)</label>
                    <select v-model="assetForm.spec_id" @change="onSpecChange" class="form-select form-select-sm">
                        <option value="">— none · manual —</option>
                        <option v-for="sp in assetSpecs" :key="'sp_'+sp.c0d" :value="sp.c0d">{{ sp.spec_code }} · {{ sp.spec_name }}</option>
                    </select>
                </div>

                <div class="col-md-4">
                    <label class="form-label small text-muted" style="font-size:.66rem;text-transform:uppercase;letter-spacing:.3px;font-weight:600;">Asset type *</label>
                    <select v-model="assetForm.asset_type_id" @change="onTypeChange" class="form-select form-select-sm">
                        <option value="">—</option>
                        <option v-for="t in assetTypes" :key="'t_'+t.c0d" :value="t.c0d">{{ t.name }} ({{ t.code }})</option>
                    </select>
                </div>
                <div class="col-md-4">
                    <label class="form-label small text-muted d-flex align-items-center justify-content-between" style="font-size:.66rem;text-transform:uppercase;letter-spacing:.3px;font-weight:600;">
                        Asset no
                        <a href="#" v-if="suggestedAssetNo && suggestedAssetNo !== assetForm.asset_no"
                           @click.prevent="assetForm.asset_no = suggestedAssetNo"
                           style="color:#7A9E7E;text-decoration:none;font-size:.62rem;font-weight:500;text-transform:none;letter-spacing:0;">
                            <i class="fas fa-magic me-1"></i>Use suggestion
                        </a>
                    </label>
                    <input type="text" v-model="assetForm.asset_no" :placeholder="suggestedAssetNo || '—'" class="form-control form-control-sm">
                    <div v-if="suggestedAssetNo && !assetForm.asset_no" class="small mt-1" style="color:#94A3B8;font-size:.62rem;">Suggested: <code style="color:#7A9E7E;">{{ suggestedAssetNo }}</code></div>
                </div>
                <div class="col-md-4">
                    <label class="form-label small text-muted" style="font-size:.66rem;text-transform:uppercase;letter-spacing:.3px;font-weight:600;">Serial no</label>
                    <input type="text" v-model="assetForm.serial_no" class="form-control form-control-sm">
                </div>

                <div class="col-md-3">
                    <label class="form-label small text-muted" style="font-size:.66rem;text-transform:uppercase;letter-spacing:.3px;font-weight:600;">Make</label>
                    <input type="text" v-model="assetForm.make" class="form-control form-control-sm">
                </div>
                <div class="col-md-3">
                    <label class="form-label small text-muted" style="font-size:.66rem;text-transform:uppercase;letter-spacing:.3px;font-weight:600;">Model</label>
                    <input type="text" v-model="assetForm.model" class="form-control form-control-sm">
                </div>
                <div class="col-md-2">
                    <label class="form-label small text-muted" style="font-size:.66rem;text-transform:uppercase;letter-spacing:.3px;font-weight:600;">Size value</label>
                    <input type="number" v-model.number="assetForm.size_value" class="form-control form-control-sm">
                </div>
                <div class="col-md-2">
                    <label class="form-label small text-muted" style="font-size:.66rem;text-transform:uppercase;letter-spacing:.3px;font-weight:600;">Size unit</label>
                    <input type="text" v-model="assetForm.size_unit" class="form-control form-control-sm" placeholder="kVA · kBTU · kW">
                </div>
                <div class="col-md-2">
                    <label class="form-label small text-muted" style="font-size:.66rem;text-transform:uppercase;letter-spacing:.3px;font-weight:600;">Install date</label>
                    <input type="date" v-model="assetForm.install_date" class="form-control form-control-sm">
                </div>

                <div class="col-md-3">
                    <label class="form-label small text-muted" style="font-size:.66rem;text-transform:uppercase;letter-spacing:.3px;font-weight:600;">Warranty end</label>
                    <input type="date" v-model="assetForm.warranty_end" class="form-control form-control-sm">
                </div>
            </div>

            <div class="mt-3 d-flex align-items-center justify-content-end gap-2">
                <button type="button" @click="closeAssetForm" class="btn btn-sm" style="background:transparent;border:1px solid #E2E8F0;color:#64748B;font-weight:500;">Cancel</button>
                <button type="button" @click="saveAssetForm" :disabled="assetFormBusy || !assetForm.asset_type_id"
                        class="btn btn-sm" style="background:#D4A574;border-color:#D4A574;color:#fff;font-weight:600;">
                    <i class="fas" :class="assetFormBusy ? 'fa-spinner fa-spin' : (assetFormMode === 'edit' ? 'fa-save' : 'fa-plus')"></i>
                    <span class="ms-1">{{ assetFormBusy ? 'Saving…' : (assetFormMode === 'edit' ? 'Save changes' : 'Add equipment') }}</span>
                </button>
            </div>
        </div>

        <div class="card-body p-0">
            <div v-if="!assets.length" class="p-4 text-center" style="color:#94A3B8;font-size:.82rem;">
                No equipment logged at this site yet. Use <strong>Add equipment</strong>, or apply a blueprint from the project sites page.
            </div>
            <table v-else class="table table-sm mb-0" style="font-size:.82rem;">
                <thead style="background:#F8FAFC;">
                    <tr style="color:#64748B;font-size:.66rem;text-transform:uppercase;letter-spacing:.4px;">
                        <th>Type</th>
                        <th>Asset no</th>
                        <th>Serial</th>
                        <th>Make / model</th>
                        <th style="text-align:right;">Size</th>
                        <th>Install date</th>
                        <th>Warranty</th>
                        <th>Spec</th>
                        <?php if ($can_edit): ?><th style="width:80px;"></th><?php endif; ?>
                    </tr>
                </thead>
                <tbody>
                    <tr v-for="a in assets" :key="'a_'+a.c0d">
                        <td><i :class="'fas ' + (a.asset_icon || 'fa-cube')" style="color:#D4A574;"></i> <span style="color:#334155;">{{ a.asset_type_name }}</span></td>
                        <td><code style="color:#7A8BA1;font-size:.76rem;">{{ a.asset_no || '—' }}</code></td>
                        <td style="color:#64748B;">{{ a.serial_no }}</td>
                        <td style="color:#64748B;">{{ [a.make, a.model].filter(Boolean).join(' ') }}</td>
                        <td style="text-align:right;color:#334155;font-family:monospace;">{{ a.size_value }} <span style="color:#94A3B8;font-size:.72rem;">{{ a.size_unit }}</span></td>
                        <td style="color:#64748B;">{{ a.install_date }}</td>
                        <td style="color:#64748B;">{{ a.warranty_end }}</td>
                        <td>
                            <code v-if="a.spec_id" style="color:#7A9E7E;font-size:.68rem;background:#EAF2E8;padding:.1rem .3rem;border-radius:2px;">{{ specCodeFor(a.spec_id) }}</code>
                            <span v-else style="color:#94A3B8;font-size:.68rem;">—</span>
                        </td>
                        <?php if ($can_edit): ?>
                        <td style="text-align:right;">
                            <button type="button" @click="openAssetEdit(a)" class="btn btn-sm" style="background:transparent;border:none;color:#7A8BA1;padding:0 .3rem;"><i class="fas fa-pencil-alt"></i></button>
                            <button type="button" @click="removeAsset(a)" class="btn btn-sm" style="background:transparent;border:none;color:#C48579;padding:0 .3rem;"><i class="fas fa-times"></i></button>
                        </td>
                        <?php endif; ?>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>

    <!-- Callouts & incidents at this site · 8Z-z-E -->
    <div class="card border-0 shadow-sm mb-3" style="border-left:3px solid #C48579 !important;">
        <div class="card-header bg-white d-flex align-items-center justify-content-between" style="border-bottom:1px solid #E2E8F0;">
            <div>
                <strong style="color:#334155;"><i class="fas fa-fire-extinguisher me-2" style="color:#C48579;"></i>Callouts &amp; incidents</strong>
                <span class="text-muted ms-2" style="color:#94A3B8;font-size:.72rem;">{{ callouts.length }} record{{ callouts.length === 1 ? '' : 's' }} at this site</span>
            </div>
            <?php if ($can_edit): ?>
            <button v-if="!calloutFormOpen" type="button" @click="openCalloutAdd" class="btn btn-sm" style="background:#C48579;border-color:#C48579;color:#fff;font-weight:600;">
                <i class="fas fa-plus me-1"></i>Raise callout
            </button>
            <?php endif; ?>
        </div>

        <!-- Inline raise-callout form -->
        <div v-if="calloutFormOpen" class="p-3" style="background:#F8FAFC;border-bottom:1px solid #E2E8F0;border-left:3px solid #C48579;">
            <div class="d-flex align-items-center justify-content-between mb-2">
                <strong style="color:#334155;font-size:.86rem;"><i class="fas fa-plus" style="color:#C48579;"></i> Raise a new callout</strong>
                <button type="button" @click="closeCalloutForm" class="btn btn-sm" style="background:transparent;border:none;color:#94A3B8;"><i class="fas fa-times"></i></button>
            </div>
            <div class="row g-2">
                <div class="col-md-3">
                    <label class="form-label small text-muted" style="font-size:.66rem;text-transform:uppercase;letter-spacing:.3px;font-weight:600;">Category *</label>
                    <select v-model="calloutForm.category_id" class="form-select form-select-sm">
                        <option value="">—</option>
                        <option v-for="c in calloutCategories" :key="'cat_'+c.c0d" :value="c.c0d">{{ c.name }} · {{ c.sla_response_hrs }}h response</option>
                    </select>
                </div>
                <div class="col-md-3">
                    <label class="form-label small text-muted" style="font-size:.66rem;text-transform:uppercase;letter-spacing:.3px;font-weight:600;">Affected asset (optional)</label>
                    <select v-model="calloutForm.asset_id" class="form-select form-select-sm">
                        <option value="">— site-wide —</option>
                        <option v-for="a in assets" :key="'ca_'+a.c0d" :value="a.c0d">{{ a.asset_type_name }} · {{ a.asset_no || a.serial_no || '#'+a.c0d }}</option>
                    </select>
                </div>
                <div class="col-md-3">
                    <label class="form-label small text-muted" style="font-size:.66rem;text-transform:uppercase;letter-spacing:.3px;font-weight:600;">Reported by (name)</label>
                    <input type="text" v-model="calloutForm.reported_by_name" class="form-control form-control-sm" placeholder="Client site POC name">
                </div>
                <div class="col-md-3">
                    <label class="form-label small text-muted" style="font-size:.66rem;text-transform:uppercase;letter-spacing:.3px;font-weight:600;">Reported by (phone)</label>
                    <input type="text" v-model="calloutForm.reported_by_phone" class="form-control form-control-sm" placeholder="+255…">
                </div>
                <div class="col-md-12">
                    <label class="form-label small text-muted" style="font-size:.66rem;text-transform:uppercase;letter-spacing:.3px;font-weight:600;">Fault description *</label>
                    <textarea v-model="calloutForm.description" rows="2" class="form-control form-control-sm" placeholder="Genset failed to start after grid outage · smell of burnt insulation on transfer switch"></textarea>
                </div>
                <div class="col-md-12">
                    <div class="form-check">
                        <input type="checkbox" v-model="calloutForm.is_emergency" id="cf_em" class="form-check-input">
                        <label for="cf_em" class="form-check-label small">Mark as EMERGENCY — bypasses SLA queue, ops paged immediately</label>
                    </div>
                </div>
            </div>
            <div class="mt-3 d-flex align-items-center justify-content-end gap-2">
                <button type="button" @click="closeCalloutForm" class="btn btn-sm" style="background:transparent;border:1px solid #E2E8F0;color:#64748B;font-weight:500;">Cancel</button>
                <button type="button" @click="saveCalloutForm" :disabled="calloutFormBusy || !calloutForm.category_id || !calloutForm.description"
                        class="btn btn-sm" style="background:#C48579;border-color:#C48579;color:#fff;font-weight:600;">
                    <i class="fas" :class="calloutFormBusy ? 'fa-spinner fa-spin' : 'fa-fire-extinguisher'"></i>
                    <span class="ms-1">{{ calloutFormBusy ? 'Raising…' : 'Raise callout' }}</span>
                </button>
            </div>
        </div>

        <div class="card-body p-0">
            <div v-if="!callouts.length" class="p-4 text-center" style="color:#94A3B8;font-size:.82rem;">
                No callouts recorded at this site yet. Use <strong>Raise callout</strong> when the client reports a fault.
            </div>
            <table v-else class="table table-sm mb-0" style="font-size:.78rem;">
                <thead style="background:#F8FAFC;">
                    <tr style="color:#64748B;font-size:.62rem;text-transform:uppercase;letter-spacing:.4px;">
                        <th>Callout no</th>
                        <th>Category</th>
                        <th>Reported</th>
                        <th>Description</th>
                        <th>Status</th>
                        <th>SLA</th>
                        <?php if ($can_edit): ?><th style="width:140px;"></th><?php endif; ?>
                    </tr>
                </thead>
                <tbody>
                    <tr v-for="co in callouts" :key="'co_'+co.c0d">
                        <td>
                            <a :href="PROOT + 'Callouts/view/' + co.c0d" style="color:#7A8BA1;text-decoration:none;font-weight:600;">
                                <code style="color:#7A8BA1;font-size:.72rem;background:#EEF2F6;padding:.1rem .35rem;border-radius:2px;">{{ co.callout_no }}</code>
                            </a>
                            <span v-if="(co.is_emergency | 0) === 1" class="badge ms-1" style="background:#C48579;color:#fff;font-size:.55rem;font-weight:700;padding:.1rem .35rem;">EMERGENCY</span>
                        </td>
                        <td>
                            <span class="badge" :style="'background:'+(co.color_hex || '#94A3B8')+';color:#fff;font-size:.6rem;font-weight:600;padding:.15rem .4rem;text-transform:capitalize;'">{{ co.cat_name }}</span>
                        </td>
                        <td style="color:#64748B;font-size:.72rem;">{{ fmtWhen(co.reported_at) }}</td>
                        <td style="color:#334155;max-width:280px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" :title="co.description">{{ co.description }}</td>
                        <td>
                            <span class="badge" :style="'background:'+calloutStatusColor(co.status)+';color:#fff;font-size:.58rem;font-weight:600;padding:.2rem .5rem;'">{{ co.status }}</span>
                        </td>
                        <td style="font-size:.7rem;">
                            <span v-if="co.sla_response_breached_hrs" style="color:#C48579;font-weight:600;"><i class="fas fa-clock me-1"></i>+{{ co.sla_response_breached_hrs }}h</span>
                            <span v-else style="color:#7A9E7E;"><i class="fas fa-check me-1"></i>on target</span>
                        </td>
                        <?php if ($can_edit): ?>
                        <td class="text-end">
                            <button type="button" @click="requestSpareFromCallout(co)" class="btn btn-sm" style="background:transparent;border:1px solid #7A8BA1;color:#7A8BA1;font-weight:500;font-size:.66rem;padding:.15rem .45rem;">
                                <i class="fas fa-hand-holding-medical me-1"></i>Request spare
                            </button>
                        </td>
                        <?php endif; ?>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>

    <!-- Spare-part requests · 8Z-z-D · POC-approved change requests -->
    <div class="card border-0 shadow-sm mb-3" style="border-left:3px solid #7A8BA1 !important;">
        <div class="card-header bg-white d-flex align-items-center justify-content-between" style="border-bottom:1px solid #E2E8F0;">
            <div>
                <strong style="color:#334155;"><i class="fas fa-hand-holding-medical me-2" style="color:#7A8BA1;"></i>Spare-part requests</strong>
                <span class="text-muted ms-2" style="color:#94A3B8;font-size:.72rem;">{{ spareRequests.length }} request{{ spareRequests.length === 1 ? '' : 's' }} · POC → PM → Procurement</span>
            </div>
            <?php if ($can_edit): ?>
            <button v-if="!spareFormOpen" type="button" @click="openSpareAdd" class="btn btn-sm" style="background:#7A8BA1;border-color:#7A8BA1;color:#fff;font-weight:600;">
                <i class="fas fa-plus me-1"></i>Request spare change
            </button>
            <?php endif; ?>
        </div>

        <!-- Inline add-request form -->
        <div v-if="spareFormOpen" class="p-3" style="background:#F8FAFC;border-bottom:1px solid #E2E8F0;border-left:3px solid #7A8BA1;">
            <div class="d-flex align-items-center justify-content-between mb-2">
                <div>
                    <strong style="color:#334155;font-size:.86rem;">
                        <i class="fas fa-plus" style="color:#7A8BA1;"></i>
                        {{ spareForm.c0d ? 'Edit request' : 'Request spare change' }}
                    </strong>
                    <span class="text-muted ms-2" style="color:#94A3B8;font-size:.68rem;">Header first; add line items after saving as draft.</span>
                </div>
                <button type="button" @click="closeSpareForm" class="btn btn-sm" style="background:transparent;border:none;color:#94A3B8;">
                    <i class="fas fa-times"></i>
                </button>
            </div>
            <div class="row g-2">
                <div class="col-md-3">
                    <label class="form-label small text-muted" style="font-size:.66rem;text-transform:uppercase;letter-spacing:.3px;font-weight:600;">Urgency</label>
                    <select v-model="spareForm.urgency" class="form-select form-select-sm">
                        <option value="ROUTINE">Routine</option>
                        <option value="URGENT">Urgent</option>
                        <option value="EMERGENCY">Emergency</option>
                    </select>
                </div>
                <div class="col-md-3">
                    <label class="form-label small text-muted" style="font-size:.66rem;text-transform:uppercase;letter-spacing:.3px;font-weight:600;">Who pays</label>
                    <select v-model="spareForm.who_pays" class="form-select form-select-sm">
                        <option value="TBD">TBD · decide later</option>
                        <option value="CLIENT">Client</option>
                        <option value="INTERNAL">Internal (absorbed)</option>
                        <option value="MIXED">Mixed · per line</option>
                    </select>
                </div>
                <div class="col-md-6">
                    <label class="form-label small text-muted" style="font-size:.66rem;text-transform:uppercase;letter-spacing:.3px;font-weight:600;">Reason</label>
                    <input type="text" v-model="spareForm.reason" class="form-control form-control-sm" placeholder="Battery failed load test · Genset AVR blown · …">
                </div>
                <div class="col-md-12">
                    <label class="form-label small text-muted" style="font-size:.66rem;text-transform:uppercase;letter-spacing:.3px;font-weight:600;">Notes (optional)</label>
                    <textarea v-model="spareForm.notes" rows="2" class="form-control form-control-sm"></textarea>
                </div>
            </div>
            <div class="mt-3 d-flex align-items-center justify-content-end gap-2">
                <button type="button" @click="closeSpareForm" class="btn btn-sm" style="background:transparent;border:1px solid #E2E8F0;color:#64748B;font-weight:500;">Cancel</button>
                <button type="button" @click="saveSpareForm" :disabled="spareFormBusy"
                        class="btn btn-sm" style="background:#7A8BA1;border-color:#7A8BA1;color:#fff;font-weight:600;">
                    <i class="fas" :class="spareFormBusy ? 'fa-spinner fa-spin' : 'fa-save'"></i>
                    <span class="ms-1">{{ spareFormBusy ? 'Saving…' : (spareForm.c0d ? 'Save changes' : 'Create draft') }}</span>
                </button>
            </div>
        </div>

        <div class="card-body p-0">
            <div v-if="!spareRequests.length" class="p-4 text-center" style="color:#94A3B8;font-size:.82rem;">
                No spare-part requests yet. Use <strong>Request spare change</strong> when a callout or PM visit turns up a faulty part.
            </div>
            <div v-else>
                <div v-for="r in spareRequests" :key="'sr_'+r.c0d" class="p-3" style="border-bottom:1px solid #E2E8F0;">
                    <div class="d-flex align-items-start justify-content-between mb-1">
                        <div>
                            <code style="color:#7A8BA1;font-size:.78rem;background:#EEF2F6;padding:.15rem .4rem;border-radius:3px;">{{ r.request_no }}</code>
                            <span class="badge ms-2" :style="'background:'+urgencyColor(r.urgency)+';color:#fff;font-size:.6rem;font-weight:600;padding:.2rem .5rem;text-transform:capitalize;'">{{ r.urgency }}</span>
                            <span class="badge ms-1" :style="'background:'+statusColor(r.status)+';color:#fff;font-size:.6rem;font-weight:600;padding:.2rem .5rem;'">{{ r.status }}</span>
                            <span class="badge ms-1" style="background:#EEF2F6;color:#64748B;font-size:.6rem;font-weight:500;padding:.2rem .5rem;text-transform:capitalize;"><i class="fas fa-coins me-1"></i>{{ whoPaysLabel(r.who_pays) }}</span>
                            <span class="ms-2 small" style="color:#334155;font-weight:500;font-size:.78rem;">{{ r.reason || 'no reason given' }}</span>
                        </div>
                        <div v-if="canEdit" class="text-nowrap">
                            <button v-if="r.status === 'DRAFT'" type="button" @click="submitSpare(r)" class="btn btn-sm" style="background:#7A9E7E;border-color:#7A9E7E;color:#fff;font-weight:600;font-size:.68rem;padding:.2rem .5rem;">
                                <i class="fas fa-paper-plane me-1"></i>Submit
                            </button>
                            <button v-if="r.status === 'AWAITING_POC'" type="button" @click="openPocConfirm(r)" class="btn btn-sm" style="background:#D4A574;border-color:#D4A574;color:#fff;font-weight:600;font-size:.68rem;padding:.2rem .5rem;">
                                <i class="fas fa-comments me-1"></i>Record POC
                            </button>
                            <button v-if="r.status === 'WITH_PROCUREMENT' || r.status === 'PM_APPROVED'" type="button" @click="openProcDecide(r)" class="btn btn-sm" style="background:#5B7A9E;border-color:#5B7A9E;color:#fff;font-weight:600;font-size:.68rem;padding:.2rem .5rem;">
                                <i class="fas fa-truck-loading me-1"></i>Procurement decide
                            </button>
                            <button v-if="r.status === 'DRAFT' || r.status === 'AWAITING_POC'" type="button" @click="openSpareEdit(r)" class="btn btn-sm ms-1" style="background:transparent;border:none;color:#7A8BA1;padding:0 .3rem;"><i class="fas fa-pencil-alt"></i></button>
                            <button v-if="r.status === 'DRAFT'" type="button" @click="removeSpare(r)" class="btn btn-sm" style="background:transparent;border:none;color:#C48579;padding:0 .3rem;"><i class="fas fa-times"></i></button>
                        </div>
                    </div>

                    <!-- Items -->
                    <div class="mt-2 ps-3">
                        <div v-if="!r.items || !r.items.length" class="small" style="color:#94A3B8;font-size:.72rem;">
                            No line items yet.
                            <a v-if="canEdit && (r.status === 'DRAFT' || r.status === 'AWAITING_POC')" href="#" @click.prevent="openItemAdd(r)" style="color:#7A9E7E;text-decoration:none;font-weight:500;">Add first item →</a>
                        </div>
                        <table v-else class="table table-sm mb-0" style="font-size:.76rem;">
                            <thead>
                                <tr style="color:#64748B;font-size:.62rem;text-transform:uppercase;letter-spacing:.3px;">
                                    <th>Part</th>
                                    <th style="width:80px;text-align:right;">Qty</th>
                                    <th style="width:60px;">UOM</th>
                                    <th style="width:120px;text-align:right;">Est cost</th>
                                    <th style="width:100px;">Who pays</th>
                                    <th style="width:60px;"></th>
                                </tr>
                            </thead>
                            <tbody>
                                <tr v-for="it in r.items" :key="'sri_'+it.c0d">
                                    <td style="color:#334155;">
                                        <span v-if="it.part_id"><code style="color:#7A8BA1;font-size:.68rem;">{{ it.sku }}</code> {{ it.part_name_catalog }}</span>
                                        <span v-else style="font-style:italic;color:#64748B;">{{ it.part_name_free }}</span>
                                    </td>
                                    <td style="text-align:right;color:#334155;font-family:monospace;">{{ fmtQty(it.qty) }}</td>
                                    <td style="color:#64748B;">{{ it.uom }}</td>
                                    <td style="text-align:right;color:#334155;font-family:monospace;">{{ fmtCost(it.unit_cost_estimate) }}</td>
                                    <td>
                                        <span v-if="it.who_pays_line" class="badge" :style="'background:'+(it.who_pays_line === 'CLIENT' ? '#7A9E7E' : '#7A8BA1')+';color:#fff;font-size:.55rem;font-weight:600;padding:.1rem .3rem;text-transform:capitalize;'">{{ it.who_pays_line }}</span>
                                        <span v-else style="color:#94A3B8;font-size:.66rem;">inherit</span>
                                    </td>
                                    <td style="text-align:right;">
                                        <button v-if="canEdit && (r.status === 'DRAFT' || r.status === 'AWAITING_POC')" type="button" @click="openItemEdit(r, it)" class="btn btn-sm" style="background:transparent;border:none;color:#7A8BA1;padding:0 .2rem;font-size:.66rem;"><i class="fas fa-pencil-alt"></i></button>
                                        <button v-if="canEdit && (r.status === 'DRAFT' || r.status === 'AWAITING_POC')" type="button" @click="removeItem(r, it)" class="btn btn-sm" style="background:transparent;border:none;color:#C48579;padding:0 .2rem;font-size:.66rem;"><i class="fas fa-times"></i></button>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        <div v-if="canEdit && (r.status === 'DRAFT' || r.status === 'AWAITING_POC') && r.items && r.items.length" class="mt-1">
                            <a href="#" @click.prevent="openItemAdd(r)" style="color:#7A9E7E;text-decoration:none;font-size:.7rem;font-weight:500;"><i class="fas fa-plus me-1"></i>Add another item</a>
                        </div>

                        <!-- POC confirmation trail -->
                        <div v-if="r.poc_approved_on" class="mt-2 p-2" style="background:#EAF2E8;border:1px solid #7A9E7E;border-radius:4px;">
                            <div class="small" style="color:#334155;font-size:.72rem;">
                                <i class="fas fa-check-circle me-1" style="color:#7A9E7E;"></i>
                                <strong>POC confirmed</strong> via {{ (r.poc_confirmation_method || 'other').toLowerCase() }}
                                on {{ fmtWhen(r.poc_approved_on) }}
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<script src="<?= PROOT ?>js/Olibs/vue.global.prod.js"></script>
<script>
(function () {
    var SID       = <?= (int)$site['c0d']; ?>;
    var CID       = <?= (int)$contract_id; ?>;
    var PID       = <?= (int)$p['c0d']; ?>;
    var PROOT     = <?= json_encode(PROOT); ?>;
    var canEdit   = <?= !empty($can_edit) ? 'true' : 'false'; ?>;
    var bootSite        = <?= json_encode($site         ?? [], JSON_UNESCAPED_SLASHES); ?>;
    var bootSitePocs    = <?= json_encode($site_pocs    ?? [], JSON_UNESCAPED_SLASHES); ?>;
    var bootProjectPocs = <?= json_encode($project_pocs ?? [], JSON_UNESCAPED_SLASHES); ?>;
    var bootAssets      = <?= json_encode($assets       ?? [], JSON_UNESCAPED_SLASHES); ?>;
    var bootAssetSpecs  = <?= json_encode($asset_specs  ?? [], JSON_UNESCAPED_SLASHES); ?>;
    var bootSpareReqs   = <?= json_encode($spare_requests ?? [], JSON_UNESCAPED_SLASHES); ?>;
    var bootCallouts    = <?= json_encode($callouts ?? [], JSON_UNESCAPED_SLASHES); ?>;
    var CALLOUT_CATS    = <?= json_encode($callout_categories ?? [], JSON_UNESCAPED_SLASHES); ?>;
    var SPARE_PARTS     = <?= json_encode(array_map(fn($p) => ['c0d'=>(int)$p['c0d'],'sku'=>$p['sku'],'name'=>$p['name'],'uom'=>$p['uom'],'unit_cost'=>$p['unit_cost']], $spare_parts ?? []), JSON_UNESCAPED_SLASHES); ?>;
    var ASSET_TYPES     = <?= json_encode(array_map(fn($t) => ['c0d'=>(int)$t['c0d'],'code'=>$t['code'],'name'=>$t['name'],'icon'=>$t['icon'] ?? 'fa-cube'], $asset_types ?? []), JSON_UNESCAPED_SLASHES); ?>;
    var SITES_URL       = <?= json_encode(PROOT . 'Projects/sites/' . (int)$p['c0d']); ?>;

    if (typeof Vue === 'undefined') {
        $('#site_view_root').removeAttr('v-cloak').css('display','block');
        $('#site_view_root').prepend('<div class="alert alert-warning small">Site UI could not initialise (Vue failed to load).</div>');
        return;
    }

    var esc = function (s) { return String(s == null ? '' : s).replace(/&/g,'&amp;').replace(/"/g,'&quot;').replace(/</g,'&lt;'); };
    var ROLE_LABEL   = { SITE_MANAGER:'Site manager', TECH:'Tech', SECURITY:'Security', OPS:'Ops', OTHER:'Other' };
    var ROLE_COLOR   = { SITE_MANAGER:'#7A9E7E', TECH:'#7A8BA1', SECURITY:'#94A3B8', OPS:'#D4A574', OTHER:'#64748B' };
    var P_ROLE_LABEL = { ACCOUNT_MANAGER:'Account manager', REGIONAL_OPS:'Regional ops', TECH_LEAD:'Tech lead', FINANCE:'Finance', ESCALATION:'Escalation', OTHER:'Other' };
    var P_ROLE_COLOR = { ACCOUNT_MANAGER:'#7A8BA1', REGIONAL_OPS:'#7A9E7E', TECH_LEAD:'#5B7A9E', FINANCE:'#D4A574', ESCALATION:'#C48579', OTHER:'#64748B' };

    var app = Vue.createApp({
        data: function () {
            return {
                SID: SID, CID: CID, PID: PID, PROOT: PROOT, canEdit: canEdit,
                site:        bootSite,
                sitePocs:    (bootSitePocs    || []).slice(),
                projectPocs: (bootProjectPocs || []).slice(),
                assets:      (bootAssets      || []).slice(),
                assetSpecs:    (bootAssetSpecs  || []).slice(),
                assetTypes:    (ASSET_TYPES     || []).slice(),
                spareRequests: (bootSpareReqs   || []).slice(),
                spareParts:    (SPARE_PARTS     || []).slice(),
                callouts:          (bootCallouts     || []).slice(),
                calloutCategories: (CALLOUT_CATS     || []).slice(),
                // Inline spare-request form state.
                spareFormOpen: false,
                spareFormBusy: false,
                spareForm: { c0d: null, urgency: 'ROUTINE', who_pays: 'TBD', reason: '', notes: '' },
                // Inline callout raise form state.
                calloutFormOpen: false,
                calloutFormBusy: false,
                calloutForm: {
                    category_id: '', asset_id: '',
                    reported_by_name: '', reported_by_phone: '',
                    description: '', is_emergency: false, reported_at: ''
                },
                // Inline equipment form state (replaces the earlier jquery-confirm modal).
                assetFormOpen: false,
                assetFormMode: 'add',
                assetFormEditingId: null,
                assetFormBusy: false,
                assetForm: {
                    c0d: null, spec_id: '', asset_type_id: '',
                    asset_no: '', serial_no: '',
                    make: '', model: '',
                    size_value: '', size_unit: '',
                    install_date: '', warranty_end: ''
                }
            };
        },
        computed: {
            /**
             * Suggested asset_no format: {site_code}-{asset_type_code}-{NN}
             * where NN is the next available integer given existing assets at this
             * site of the same type. Users can override the value freely.
             */
            suggestedAssetNo: function () {
                if (this.assetFormMode === 'edit') return '';
                var siteCode = (this.site && this.site.site_code) ? String(this.site.site_code) : '';
                var typeId   = this.assetForm.asset_type_id | 0;
                if (!siteCode || !typeId) return '';
                var type = this.assetTypes.find(function (t) { return (t.c0d | 0) === typeId; });
                if (!type || !type.code) return '';
                var prefix = siteCode + '-' + String(type.code).toUpperCase() + '-';
                // Find max NN among existing asset_no values that match this prefix.
                var maxN = 0;
                this.assets.forEach(function (a) {
                    var no = (a.asset_no || '').toString();
                    if (no.length > prefix.length && no.substring(0, prefix.length).toUpperCase() === prefix.toUpperCase()) {
                        var tail = no.substring(prefix.length);
                        var n = parseInt(tail, 10);
                        if (isFinite(n) && n > maxN) maxN = n;
                    }
                });
                var next = maxN + 1;
                return prefix + (next < 10 ? '0' + next : String(next));
            }
        },
        methods: {
            emptyAssetForm: function () {
                return {
                    c0d: null, spec_id: '', asset_type_id: '',
                    asset_no: '', serial_no: '',
                    make: '', model: '',
                    size_value: '', size_unit: '',
                    install_date: '', warranty_end: ''
                };
            },
            roleLabel: function (r) { return ROLE_LABEL[r] || r; },
            roleColor: function (r) { return ROLE_COLOR[r] || '#64748B'; },
            projectRoleLabel: function (r) { return P_ROLE_LABEL[r] || r; },
            projectRoleColor: function (r) { return P_ROLE_COLOR[r] || '#64748B'; },
            specCodeFor: function (specId) {
                var sp = this.assetSpecs.find(function (s) { return (s.c0d | 0) === (specId | 0); });
                return sp ? sp.spec_code : '';
            },

            refresh: function () {
                var self = this;
                $.post(PROOT + 'Projects/siteSnapshotAj', { site_id: self.SID }, 'json').done(function (res) {
                    if (!res || !res.ok) return;
                    self.site          = res.site          || self.site;
                    self.sitePocs      = res.site_pocs     || [];
                    self.projectPocs   = res.project_pocs  || [];
                    self.assets        = res.assets        || [];
                    self.assetSpecs    = res.asset_specs   || [];
                    self.spareRequests = res.spare_requests|| [];
                    self.callouts      = res.callouts      || [];
                });
            },

            /* ── Callouts (8Z-z-E) ─────────────────────────────────── */
            calloutStatusColor: function (s) {
                return {
                    OPEN:'#C48579', DISPATCHED:'#D4A574', ON_SITE:'#5B7A9E',
                    RESOLVED:'#7A9E7E', CLIENT_ACCEPTED:'#7A9E7E', CLOSED:'#64748B'
                }[s] || '#64748B';
            },
            openCalloutAdd: function () {
                var now = new Date();
                var iso = now.getFullYear() + '-' +
                          String(now.getMonth()+1).padStart(2,'0') + '-' +
                          String(now.getDate()).padStart(2,'0') + ' ' +
                          String(now.getHours()).padStart(2,'0') + ':' +
                          String(now.getMinutes()).padStart(2,'0') + ':00';
                this.calloutForm = {
                    category_id: '', asset_id: '',
                    reported_by_name: this.sitePocs.length ? this.sitePocs[0].poc_name : '',
                    reported_by_phone: this.sitePocs.length ? (this.sitePocs[0].poc_phone || '') : '',
                    description: '', is_emergency: false,
                    reported_at: iso
                };
                this.calloutFormOpen = true;
                this.calloutFormBusy = false;
            },
            closeCalloutForm: function () {
                this.calloutFormOpen = false;
                this.calloutFormBusy = false;
            },
            saveCalloutForm: function () {
                var self = this;
                if (self.calloutFormBusy) return;
                if (!self.calloutForm.category_id || !self.calloutForm.description) {
                    if (window.rsToast) rsToast('Category + description are required.', 'warning');
                    return;
                }
                self.calloutFormBusy = true;
                var payload = Object.assign({}, self.calloutForm);
                payload.site_id = self.SID;
                payload.is_emergency = payload.is_emergency ? 1 : 0;
                $.post(PROOT + 'Projects/siteCalloutRaiseAj', payload, 'json').done(function (res) {
                    self.calloutFormBusy = false;
                    if (res && res.ok) {
                        if (window.rsToast) rsToast('Callout raised — id ' + res.id, 'success');
                        self.closeCalloutForm();
                        self.refresh();
                    } else if (window.rsToast) rsToast('Failed: ' + (res && res.msg || 'unknown'), 'error');
                }).fail(function (xhr) { self.calloutFormBusy = false; if (window.rsToast) rsToast('Network error (HTTP ' + xhr.status + ').', 'error'); });
            },
            /** Bridge: open the spare-request form pre-scoped to this callout. */
            requestSpareFromCallout: function (co) {
                this.spareForm = {
                    c0d: null,
                    callout_id: co.c0d,
                    urgency: (co.is_emergency | 0) === 1 ? 'EMERGENCY' : 'ROUTINE',
                    who_pays: 'TBD',
                    reason: 'From callout ' + co.callout_no + ' · ' + (co.description || '').substring(0, 120),
                    notes: ''
                };
                this.spareFormOpen = true;
                this.spareFormBusy = false;
                var self = this;
                this.$nextTick(function () {
                    // Scroll the spare-request card into view.
                    var el = document.querySelector('.card [style*="7A8BA1"]');
                    if (el && el.scrollIntoView) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
                    void self;
                });
            },

            /* ── Spare requests (8Z-z-D) ───────────────────────────── */
            urgencyColor: function (u) {
                return { EMERGENCY: '#C48579', URGENT: '#D4A574', ROUTINE: '#94A3B8' }[u] || '#64748B';
            },
            statusColor: function (s) {
                var map = {
                    DRAFT:'#94A3B8', AWAITING_POC:'#D4A574', POC_APPROVED:'#7A9E7E',
                    POC_DECLINED:'#C48579', AWAITING_PM:'#D4A574', PM_APPROVED:'#7A9E7E',
                    PM_DECLINED:'#C48579', WITH_PROCUREMENT:'#5B7A9E', STOCK_ISSUED:'#7A9E7E',
                    SOURCING:'#5B7A9E', ORDERED:'#5B7A9E', DELIVERED:'#7A8BA1',
                    INSTALLED:'#7A9E7E', CLOSED:'#64748B', CANCELLED:'#C48579'
                };
                return map[s] || '#64748B';
            },
            whoPaysLabel: function (w) {
                return { CLIENT:'Client', INTERNAL:'Internal', MIXED:'Mixed', TBD:'TBD' }[w] || w;
            },
            fmtCost: function (c) {
                var v = parseFloat(c); if (!isFinite(v) || v === 0) return '—';
                return v.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 2 });
            },
            fmtWhen: function (iso) {
                if (!iso) return '';
                var d = new Date(String(iso).replace(' ', 'T'));
                if (isNaN(d.getTime())) return iso;
                var M = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
                return d.getDate() + ' ' + M[d.getMonth()] + ' · ' + String(d.getHours()).padStart(2,'0') + ':' + String(d.getMinutes()).padStart(2,'0');
            },
            fmtQty: function (q) {
                var v = parseFloat(q); if (!isFinite(v)) return '';
                var s = v.toFixed(4); return s.replace(/\.?0+$/, '');
            },
            openSpareAdd: function () {
                this.spareForm = { c0d: null, urgency: 'ROUTINE', who_pays: 'TBD', reason: '', notes: '' };
                this.spareFormOpen = true;
                this.spareFormBusy = false;
            },
            openSpareEdit: function (r) {
                this.spareForm = {
                    c0d: r.c0d,
                    urgency: r.urgency || 'ROUTINE',
                    who_pays: r.who_pays || 'TBD',
                    reason: r.reason || '',
                    notes: r.notes || ''
                };
                this.spareFormOpen = true;
                this.spareFormBusy = false;
            },
            closeSpareForm: function () {
                this.spareFormOpen = false;
                this.spareFormBusy = false;
            },
            saveSpareForm: function () {
                var self = this;
                if (self.spareFormBusy) return;
                self.spareFormBusy = true;
                var payload = Object.assign({}, self.spareForm);
                payload.contract_id = self.CID;
                payload.site_id     = self.SID;
                if (!payload.c0d) delete payload.c0d;
                $.post(PROOT + 'Projects/spareRequestSaveAj', payload, 'json').done(function (res) {
                    self.spareFormBusy = false;
                    if (res && res.ok) {
                        if (window.rsToast) rsToast(payload.c0d ? 'Saved.' : 'Draft created — add line items next.', 'success');
                        self.closeSpareForm();
                        self.refresh();
                    } else if (window.rsToast) rsToast('Failed: ' + (res && res.msg || 'unknown'), 'error');
                }).fail(function (xhr) { self.spareFormBusy = false; if (window.rsToast) rsToast('Network error (HTTP ' + xhr.status + ').', 'error'); });
            },
            removeSpare: function (r) {
                var self = this;
                $.confirm({
                    title: 'Remove this draft request?', type: 'orange',
                    content: 'Request ' + esc(r.request_no) + ' will be soft-deleted. Only DRAFT requests can be removed.',
                    buttons: {
                        yes: { text: 'Remove', btnClass: 'btn-warning', action: function () {
                            $.post(PROOT + 'Projects/spareRequestRemoveAj', { request_id: r.c0d }, 'json').done(function (res) {
                                if (res && res.ok) { if (window.rsToast) rsToast('Removed.', 'success'); self.refresh(); }
                                else if (window.rsToast) rsToast('Failed: ' + (res && res.msg || 'unknown'), 'error');
                            });
                        }},
                        cancel: { text: 'Cancel', btnClass: 'btn-light' }
                    }
                });
            },
            submitSpare: function (r) {
                var self = this;
                $.confirm({
                    title: 'Submit for approval?', type: 'blue',
                    content: 'Request ' + esc(r.request_no) + ' will start the POC → PM → Procurement chain. You cannot edit the header once submitted.',
                    buttons: {
                        yes: { text: 'Submit', btnClass: 'btn-primary', action: function () {
                            $.post(PROOT + 'Projects/spareRequestSubmitAj', { request_id: r.c0d }, 'json').done(function (res) {
                                if (res && res.ok) { if (window.rsToast) rsToast('Submitted — awaiting POC confirmation.', 'success'); self.refresh(); }
                                else if (window.rsToast) rsToast('Failed: ' + (res && res.msg || 'unknown'), 'error');
                            });
                        }},
                        cancel: { text: 'Cancel', btnClass: 'btn-light' }
                    }
                });
            },
            openPocConfirm: function (r) {
                var self = this;
                var pocOpts = '<option value="">— none / unlinked —</option>' + self.sitePocs.map(function (pc) {
                    return '<option value="' + pc.c0d + '">' + esc(pc.poc_role) + ' · ' + esc(pc.poc_name) + '</option>';
                }).join('') + self.projectPocs.map(function (pc) {
                    return '<option value="' + pc.c0d + '">[Project] ' + esc(pc.poc_role) + ' · ' + esc(pc.poc_name) + '</option>';
                }).join('');
                var methodOpts = ['EMAIL','WHATSAPP','PHONE','ONSITE_SIGNATURE','OTHER'].map(function (m) {
                    return '<option value="' + m + '">' + m.replace('_',' ').toLowerCase() + '</option>';
                }).join('');
                $.confirm({
                    title: 'Record POC decision', columnClass: 'medium', type: 'blue',
                    content: ''
                        + '<div class="row g-2">'
                        +   '<div class="col-6"><label class="form-label small text-muted">POC</label><select id="poc_id" class="form-select form-select-sm">' + pocOpts + '</select></div>'
                        +   '<div class="col-6"><label class="form-label small text-muted">Method *</label><select id="poc_m" class="form-select form-select-sm">' + methodOpts + '</select></div>'
                        + '</div>'
                        + '<div class="mt-2"><label class="form-label small text-muted">Attachment path (optional)</label><input type="text" id="poc_a" class="form-control form-control-sm" placeholder="uploads/poc/screenshot.jpg"></div>'
                        + '<div class="mt-2"><label class="form-label small text-muted">Comment (what did POC say?)</label><textarea id="poc_c" class="form-control form-control-sm" rows="2"></textarea></div>',
                    buttons: {
                        approve: { text: 'POC approved', btnClass: 'btn-success', action: function () {
                            self.postPocDecision(r.c0d, 'APPROVE');
                        }},
                        decline: { text: 'POC declined', btnClass: 'btn-warning', action: function () {
                            self.postPocDecision(r.c0d, 'DECLINE');
                        }},
                        cancel: { text: 'Cancel', btnClass: 'btn-light' }
                    }
                });
            },
            postPocDecision: function (requestId, decision) {
                var self = this;
                $.post(PROOT + 'Projects/spareRequestPocConfirmAj', {
                    request_id: requestId, decision: decision,
                    poc_id: $('#poc_id').val(),
                    method: $('#poc_m').val(),
                    attachment_path: $('#poc_a').val(),
                    comment: $('#poc_c').val()
                }, 'json').done(function (res) {
                    if (res && res.ok) { if (window.rsToast) rsToast('POC decision recorded.', 'success'); self.refresh(); }
                    else if (window.rsToast) rsToast('Failed: ' + (res && res.msg || 'unknown'), 'error');
                });
            },
            openProcDecide: function (r) {
                var self = this;
                $.confirm({
                    title: 'Procurement triage for ' + esc(r.request_no), type: 'blue', columnClass: 'medium',
                    content: ''
                        + '<p class="small text-muted mb-3">Choose how the spare will be sourced. All three options advance the request to the next state.</p>'
                        + '<div class="mb-2"><label class="form-label small text-muted">Notes (visible on request)</label><textarea id="pd_notes" class="form-control form-control-sm" rows="2" placeholder="e.g. Awarded to Reliable Motors — 5 day lead time"></textarea></div>',
                    buttons: {
                        stock:  { text: '<i class="fas fa-warehouse me-1"></i>From stock', btnClass: 'btn-outline-success', action: function () {
                            self.postProcDecision(r.c0d, 'FROM_STOCK');
                        }},
                        proc:   { text: '<i class="fas fa-shopping-cart me-1"></i>Procure', btnClass: 'btn-outline-primary', action: function () {
                            self.postProcDecision(r.c0d, 'PROCURE');
                        }},
                        cash:   { text: '<i class="fas fa-money-bill-wave me-1"></i>Emergency cash', btnClass: 'btn-outline-danger', action: function () {
                            self.postProcDecision(r.c0d, 'EMERGENCY_CASH');
                        }},
                        cancel: { text: 'Cancel', btnClass: 'btn-light' }
                    }
                });
            },
            postProcDecision: function (requestId, decision) {
                var self = this;
                $.post(PROOT + 'Projects/spareRequestDecideAj', {
                    request_id: requestId, decision: decision, notes: $('#pd_notes').val()
                }, 'json').done(function (res) {
                    if (res && res.ok) { if (window.rsToast) rsToast('Triage: ' + decision.replace('_',' ').toLowerCase(), 'success'); self.refresh(); }
                    else if (window.rsToast) rsToast('Failed: ' + (res && res.msg || 'unknown'), 'error');
                });
            },
            openItemAdd: function (r) { this.openItemForm(r, {}); },
            openItemEdit: function (r, it) { this.openItemForm(r, it); },
            openItemForm: function (r, it) {
                var self = this;
                it = it || {};
                var partOpts = '<option value="">— free-text below —</option>' + self.spareParts.map(function (sp) {
                    return '<option value="' + sp.c0d + '"' + ((it.part_id | 0) === (sp.c0d | 0) ? ' selected' : '')
                         + ' data-uom="' + esc(sp.uom) + '" data-cost="' + esc(sp.unit_cost) + '">' + esc(sp.sku) + ' · ' + esc(sp.name) + '</option>';
                }).join('');
                var payerOpts = '<option value="">inherit from request (' + esc(r.who_pays) + ')</option>'
                    + '<option value="CLIENT"'   + (it.who_pays_line === 'CLIENT'   ? ' selected' : '') + '>Client</option>'
                    + '<option value="INTERNAL"' + (it.who_pays_line === 'INTERNAL' ? ' selected' : '') + '>Internal</option>';
                $.confirm({
                    title: it.c0d ? 'Edit line item' : 'Add line item', columnClass: 'medium', type: 'blue',
                    content: ''
                        + '<div class="mb-2"><label class="form-label small text-muted">Part (from catalogue)</label><select id="i_part" class="form-select form-select-sm">' + partOpts + '</select></div>'
                        + '<div class="mb-2"><label class="form-label small text-muted">Or type a part name (used when no catalogue entry)</label><input type="text" id="i_free" class="form-control form-control-sm" value="' + esc(it.part_name_free) + '"></div>'
                        + '<div class="row g-2">'
                        +   '<div class="col-3"><label class="form-label small text-muted">Qty *</label><input type="number" step="0.0001" id="i_qty" class="form-control form-control-sm" value="' + esc(it.qty || 1) + '"></div>'
                        +   '<div class="col-3"><label class="form-label small text-muted">UOM</label><input type="text" id="i_uom" class="form-control form-control-sm" value="' + esc(it.uom) + '"></div>'
                        +   '<div class="col-3"><label class="form-label small text-muted">Est unit cost</label><input type="number" step="0.01" id="i_cost" class="form-control form-control-sm" value="' + esc(it.unit_cost_estimate) + '"></div>'
                        +   '<div class="col-3"><label class="form-label small text-muted">Who pays (line)</label><select id="i_pay" class="form-select form-select-sm">' + payerOpts + '</select></div>'
                        + '</div>'
                        + '<div class="mt-2"><label class="form-label small text-muted">Notes</label><input type="text" id="i_notes" class="form-control form-control-sm" value="' + esc(it.notes) + '"></div>',
                    onContentReady: function () {
                        $('#i_part').on('change', function () {
                            var opt = this.options[this.selectedIndex];
                            if (!opt || !opt.value) return;
                            if (opt.dataset.uom && !$('#i_uom').val())  $('#i_uom').val(opt.dataset.uom);
                            if (opt.dataset.cost && !$('#i_cost').val()) $('#i_cost').val(opt.dataset.cost);
                        });
                    },
                    buttons: {
                        save: { text: it.c0d ? 'Save' : 'Add', btnClass: 'btn-primary', action: function () {
                            var payload = {
                                request_id: r.c0d,
                                part_id: $('#i_part').val() || '',
                                part_name_free: $('#i_free').val(),
                                qty: $('#i_qty').val(),
                                uom: $('#i_uom').val(),
                                unit_cost_estimate: $('#i_cost').val(),
                                who_pays_line: $('#i_pay').val(),
                                notes: $('#i_notes').val()
                            };
                            if (it.c0d) payload.c0d = it.c0d;
                            $.post(PROOT + 'Projects/spareRequestItemSaveAj', payload, 'json').done(function (res) {
                                if (res && res.ok) { if (window.rsToast) rsToast('Item saved.', 'success'); self.refresh(); }
                                else if (window.rsToast) rsToast('Failed: ' + (res && res.msg || 'unknown'), 'error');
                            });
                        }},
                        cancel: { text: 'Cancel', btnClass: 'btn-light' }
                    }
                });
            },
            removeItem: function (r, it) {
                var self = this;
                $.confirm({
                    title: 'Remove this line item?', type: 'orange',
                    content: 'This will be soft-deleted from the request.',
                    buttons: {
                        yes: { text: 'Remove', btnClass: 'btn-warning', action: function () {
                            $.post(PROOT + 'Projects/spareRequestItemRemoveAj', { item_id: it.c0d }, 'json').done(function (res) {
                                if (res && res.ok) { if (window.rsToast) rsToast('Removed.', 'success'); self.refresh(); }
                                else if (window.rsToast) rsToast('Failed: ' + (res && res.msg || 'unknown'), 'error');
                            });
                        }},
                        cancel: { text: 'Cancel', btnClass: 'btn-light' }
                    }
                });
            },

            /* ── Site details ──────────────────────────────────────── */
            siteFormHtml: function (s) {
                s = s || {};
                return ''
                     + '<div class="row g-2"><div class="col-4"><label class="form-label small text-muted">Site code</label><input type="text" id="s_code" class="form-control form-control-sm" value="' + esc(s.site_code) + '"></div>'
                     + '<div class="col-8"><label class="form-label small text-muted">Site name *</label><input type="text" id="s_name" class="form-control form-control-sm" value="' + esc(s.name) + '"></div></div>'
                     + '<div class="row g-2 mt-1"><div class="col-6"><label class="form-label small text-muted">Region</label><input type="text" id="s_reg" class="form-control form-control-sm" value="' + esc(s.region) + '"></div>'
                     + '<div class="col-6"><label class="form-label small text-muted">District</label><input type="text" id="s_dis" class="form-control form-control-sm" value="' + esc(s.district) + '"></div></div>'
                     + '<div class="mt-1"><label class="form-label small text-muted">Physical address</label><input type="text" id="s_addr" class="form-control form-control-sm" value="' + esc(s.physical_address) + '"></div>'
                     + '<div class="row g-2 mt-1"><div class="col-6"><label class="form-label small text-muted">GPS lat</label><input type="number" step="0.0000001" id="s_lat" class="form-control form-control-sm" value="' + esc(s.gps_lat) + '"></div>'
                     + '<div class="col-6"><label class="form-label small text-muted">GPS lng</label><input type="number" step="0.0000001" id="s_lng" class="form-control form-control-sm" value="' + esc(s.gps_lng) + '"></div></div>'
                     + '<div class="mt-1"><label class="form-label small text-muted">Access notes</label><textarea id="s_notes" class="form-control form-control-sm" rows="2">' + esc(s.access_notes) + '</textarea></div>';
            },
            collectSitePayload: function () {
                return { site_code: $('#s_code').val(), name: $('#s_name').val(),
                         region: $('#s_reg').val(), district: $('#s_dis').val(),
                         physical_address: $('#s_addr').val(),
                         gps_lat: $('#s_lat').val(), gps_lng: $('#s_lng').val(),
                         access_notes: $('#s_notes').val(), is_active: 1 };
            },
            openSiteEdit: function () {
                var self = this;
                $.confirm({
                    title: 'Edit site', content: self.siteFormHtml(self.site), columnClass: 'medium', type: 'blue',
                    buttons: {
                        save: { text: 'Save', btnClass: 'btn-primary', action: function () {
                            var p = self.collectSitePayload(); p.contract_id = self.CID; p.c0d = self.site.c0d;
                            $.post(PROOT + 'Projects/svcSiteSaveAj', p, 'json').done(function (res) {
                                if (res && res.ok) { if (window.rsToast) rsToast('Site saved.', 'success'); self.refresh(); }
                                else if (window.rsToast) rsToast('Failed: ' + (res && res.msg || 'unknown'), 'error');
                            });
                        }},
                        cancel: { text: 'Cancel', btnClass: 'btn-light' }
                    }
                });
            },
            removeSite: function () {
                var self = this;
                $.confirm({
                    title: 'Delete this site?', type: 'orange',
                    content: 'Site "' + esc(self.site.name) + '" will be removed along with its POCs and any equipment. This cannot be undone.',
                    buttons: {
                        yes: { text: 'Delete', btnClass: 'btn-warning', action: function () {
                            $.post(PROOT + 'Projects/svcSiteRemoveAj', { site_id: self.site.c0d }, 'json').done(function (res) {
                                if (res && res.ok) {
                                    if (window.rsToast) rsToast('Deleted.', 'success');
                                    window.location.href = SITES_URL;
                                } else if (window.rsToast) rsToast(res && res.msg || 'Delete failed', 'error');
                            });
                        }},
                        cancel: { text: 'Cancel', btnClass: 'btn-light' }
                    }
                });
            },

            /* ── POCs ──────────────────────────────────────────────── */
            pocFormHtml: function (pc) {
                pc = pc || {};
                var roles = ['SITE_MANAGER','TECH','SECURITY','OPS','OTHER'];
                var opts = roles.map(function (r) { return '<option value="' + r + '"' + ((pc.poc_role || 'SITE_MANAGER') === r ? ' selected' : '') + '>' + ROLE_LABEL[r] + '</option>'; }).join('');
                return ''
                     + '<div class="row g-2"><div class="col-5"><label class="form-label small text-muted">Role</label><select id="p_role" class="form-select form-select-sm">' + opts + '</select></div>'
                     + '<div class="col-7"><label class="form-label small text-muted">Name *</label><input type="text" id="p_name" class="form-control form-control-sm" value="' + esc(pc.poc_name) + '"></div></div>'
                     + '<div class="row g-2 mt-1"><div class="col-6"><label class="form-label small text-muted">Phone</label><input type="text" id="p_phone" class="form-control form-control-sm" value="' + esc(pc.poc_phone) + '"></div>'
                     + '<div class="col-6"><label class="form-label small text-muted">Email</label><input type="email" id="p_email" class="form-control form-control-sm" value="' + esc(pc.poc_email) + '"></div></div>'
                     + '<div class="form-check mt-2"><input type="checkbox" id="p_prim" class="form-check-input"' + ((pc.is_primary | 0) === 1 ? ' checked' : '') + '><label for="p_prim" class="form-check-label small">Primary POC for this site</label></div>';
            },
            collectPocPayload: function () {
                return { poc_role: $('#p_role').val(), poc_name: $('#p_name').val(),
                         poc_phone: $('#p_phone').val(), poc_email: $('#p_email').val(),
                         is_primary: $('#p_prim').is(':checked') ? 1 : 0 };
            },
            openPocAdd: function () {
                var self = this;
                $.confirm({
                    title: 'Add POC', content: self.pocFormHtml({}), type: 'blue',
                    buttons: {
                        save: { text: 'Add', btnClass: 'btn-primary', action: function () {
                            var p = self.collectPocPayload(); p.site_id = self.SID;
                            $.post(PROOT + 'Projects/sitePocSaveAj', p, 'json').done(function (res) {
                                if (res && res.ok) { if (window.rsToast) rsToast('POC added.', 'success'); self.refresh(); }
                                else if (window.rsToast) rsToast('Failed: ' + (res && res.msg || 'unknown'), 'error');
                            });
                        }},
                        cancel: { text: 'Cancel', btnClass: 'btn-light' }
                    }
                });
            },
            openPocEdit: function (pc) {
                var self = this;
                $.confirm({
                    title: 'Edit POC', content: self.pocFormHtml(pc), type: 'blue',
                    buttons: {
                        save: { text: 'Save', btnClass: 'btn-primary', action: function () {
                            var p = self.collectPocPayload(); p.site_id = pc.site_id; p.c0d = pc.c0d;
                            $.post(PROOT + 'Projects/sitePocSaveAj', p, 'json').done(function (res) {
                                if (res && res.ok) { if (window.rsToast) rsToast('Saved.', 'success'); self.refresh(); }
                                else if (window.rsToast) rsToast('Failed: ' + (res && res.msg || 'unknown'), 'error');
                            });
                        }},
                        cancel: { text: 'Cancel', btnClass: 'btn-light' }
                    }
                });
            },
            removePoc: function (pc) {
                var self = this;
                $.confirm({
                    title: 'Remove POC?', type: 'orange',
                    content: 'POC "' + esc(pc.poc_name) + '" will be removed from this site.',
                    buttons: {
                        yes: { text: 'Remove', btnClass: 'btn-warning', action: function () {
                            $.post(PROOT + 'Projects/sitePocRemoveAj', { poc_id: pc.c0d }, 'json').done(function (res) {
                                if (res && res.ok) { if (window.rsToast) rsToast('Removed.', 'success'); self.refresh(); }
                                else if (window.rsToast) rsToast('Failed: ' + (res && res.msg || 'unknown'), 'error');
                            });
                        }},
                        cancel: { text: 'Cancel', btnClass: 'btn-light' }
                    }
                });
            },
            confirmPocs: function () {
                var self = this;
                $.post(PROOT + 'Projects/sitePocConfirmAj', { site_id: self.SID }, 'json').done(function (res) {
                    if (res && res.ok) { if (window.rsToast) rsToast('POCs confirmed.', 'success'); self.refresh(); }
                    else if (window.rsToast) rsToast('Failed: ' + (res && res.msg || 'unknown'), 'error');
                });
            },
            unconfirmPocs: function () {
                var self = this;
                $.post(PROOT + 'Projects/sitePocConfirmAj', { site_id: self.SID, unconfirm: 1 }, 'json').done(function (res) {
                    if (res && res.ok) { if (window.rsToast) rsToast('Un-confirmed.', 'info'); self.refresh(); }
                });
            },

            /* ── Equipment · inline form (Vue3 Options API · no modal) ─ */
            resetAssetForm: function () {
                this.assetForm = {
                    c0d: null, spec_id: '', asset_type_id: '',
                    asset_no: '', serial_no: '',
                    make: '', model: '',
                    size_value: '', size_unit: '',
                    install_date: '', warranty_end: ''
                };
            },
            openAssetAdd: function () {
                this.resetAssetForm();
                this.assetFormMode = 'add';
                this.assetFormEditingId = null;
                this.assetFormOpen = true;
                var self = this;
                this.$nextTick(function () {
                    var card = document.querySelector('.card [style*="FBF7EE"]') || null;
                    if (card && card.scrollIntoView) card.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
                    void self;
                });
            },
            openAssetEdit: function (a) {
                this.assetFormMode = 'edit';
                this.assetFormEditingId = a.c0d | 0;
                this.assetForm = {
                    c0d: a.c0d,
                    spec_id: a.spec_id || '',
                    asset_type_id: a.asset_type_id || '',
                    asset_no: a.asset_no || '',
                    serial_no: a.serial_no || '',
                    make: a.make || '',
                    model: a.model || '',
                    size_value: a.size_value || '',
                    size_unit: a.size_unit || '',
                    install_date: a.install_date || '',
                    warranty_end: a.warranty_end || ''
                };
                this.assetFormOpen = true;
                var self = this;
                this.$nextTick(function () {
                    var card = document.querySelector('.card [style*="FBF7EE"]') || null;
                    if (card && card.scrollIntoView) card.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
                    void self;
                });
            },
            closeAssetForm: function () {
                this.assetFormOpen = false;
                this.assetFormEditingId = null;
                this.assetFormBusy = false;
                this.resetAssetForm();
            },
            /** Blueprint chosen → auto-fill fields when they are blank (don't overwrite user typing). */
            onSpecChange: function () {
                var sp = this.assetSpecs.find(function (s) { return (s.c0d | 0) === (this.assetForm.spec_id | 0); }.bind(this));
                if (!sp) return;
                if (!this.assetForm.asset_type_id) this.assetForm.asset_type_id = sp.asset_type_id;
                if (!this.assetForm.make)          this.assetForm.make          = sp.make || '';
                if (!this.assetForm.model)         this.assetForm.model         = sp.model || '';
                if (!this.assetForm.size_value)    this.assetForm.size_value    = sp.size_value || '';
                if (!this.assetForm.size_unit)     this.assetForm.size_unit     = sp.size_unit || '';
            },
            /** Asset type changed → the suggested asset_no computed prop will recompute automatically. */
            onTypeChange: function () { /* placeholder; kept so the template stays declarative */ },
            saveAssetForm: function () {
                var self = this;
                if (!self.assetForm.asset_type_id || self.assetFormBusy) return;
                self.assetFormBusy = true;
                // If asset_no is empty, use the suggested value so we never insert a nameless unit.
                var payload = Object.assign({}, self.assetForm);
                if (!payload.asset_no && self.suggestedAssetNo) payload.asset_no = self.suggestedAssetNo;
                payload.contract_id = self.CID;
                payload.site_id     = self.SID;
                payload.is_active   = 1;
                // Drop the null c0d on add so the backend inserts a new row.
                if (self.assetFormMode === 'add') delete payload.c0d;
                $.post(PROOT + 'Projects/svcAssetSaveAj', payload, 'json').done(function (res) {
                    self.assetFormBusy = false;
                    if (res && res.ok) {
                        if (window.rsToast) rsToast(self.assetFormMode === 'edit' ? 'Saved.' : 'Equipment added.', 'success');
                        self.closeAssetForm();
                        self.refresh();
                    } else if (window.rsToast) rsToast('Failed: ' + (res && res.msg || 'unknown'), 'error');
                }).fail(function (xhr) {
                    self.assetFormBusy = false;
                    if (window.rsToast) rsToast('Network error (HTTP ' + xhr.status + ').', 'error');
                });
            },
            removeAsset: function (a) {
                var self = this;
                $.confirm({
                    title: 'Remove this equipment unit?', type: 'orange',
                    content: 'Asset ' + esc(a.asset_no || a.serial_no || '#' + a.c0d) + ' will be removed from this site.',
                    buttons: {
                        yes: { text: 'Remove', btnClass: 'btn-warning', action: function () {
                            $.post(PROOT + 'Projects/svcAssetRemoveAj', { asset_id: a.c0d }, 'json').done(function (res) {
                                if (res && res.ok) { if (window.rsToast) rsToast('Removed.', 'success'); self.refresh(); }
                                else if (window.rsToast) rsToast('Failed: ' + (res && res.msg || 'unknown'), 'error');
                            });
                        }},
                        cancel: { text: 'Cancel', btnClass: 'btn-light' }
                    }
                });
            }
        }
    });

    $(function () {
        if (!document.getElementById('site_view_root')) return;
        try { app.mount('#site_view_root'); }
        catch (e) {
            console.error('[site-view-vue] mount threw:', e);
            $('#site_view_root').removeAttr('v-cloak').css('display','block');
            $('#site_view_root').prepend('<div class="alert alert-danger small m-3">Site UI failed to mount: ' + (e && e.message || e) + '</div>');
        }
    });
})();
</script>

<?php $this->end(); ?>
