// hydor-page-control.jsx — management operations control room.

const CONTROL_GROUPS = ["field", "travelling", "assigned", "available", "attention", "ended", "off"];
const controlTranslate = (value) => (window.HydorI18n ? window.HydorI18n.t : window.__t || ((x) => x))(value);

function loadLeaflet() {
  if (window.__hydorLeafletPromise) return window.__hydorLeafletPromise;
  // A map without Leaflet's stylesheet renders scattered tiles. Wait for BOTH assets,
  // including when the JS global is already present. Never silently ignore a CSS/SRI error.
  const asset = (tag, url, integrity) => new Promise((resolve, reject) => {
    const el = document.createElement(tag);
    if (tag === "link") { el.rel = "stylesheet"; el.href = url; } else el.src = url;
    el.integrity = integrity; el.crossOrigin = "anonymous";
    const timer = setTimeout(() => fail(), 15000);
    const fail = () => { clearTimeout(timer); el.remove(); reject(new Error("Map assets unavailable")); };
    el.onload = () => { clearTimeout(timer); resolve(); }; el.onerror = fail;
    document.head.appendChild(el);
  });
  window.__hydorLeafletPromise = Promise.all([
    asset("link", "https://unpkg.com/leaflet@1.9.4/dist/leaflet.css", "sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="),
    window.L ? Promise.resolve() : asset("script", "https://unpkg.com/leaflet@1.9.4/dist/leaflet.js", "sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=")
  ]).then(() => window.L).catch((e) => { window.__hydorLeafletPromise = null; throw e; });
  return window.__hydorLeafletPromise;
}

function controlCoordinate(p) {
  return p && typeof p.latitude === "number" && typeof p.longitude === "number"
    && Number.isFinite(p.latitude) && Math.abs(p.latitude) <= 90
    && Number.isFinite(p.longitude) && Math.abs(p.longitude) <= 180;
}

function controlDuration(m, t = (x) => x) {
  if (m == null || !Number.isFinite(Number(m))) return "—";
  if (Number(m) > 0 && Number(m) < 1) return t("Less than 1") + " " + t("min");
  m = Math.max(0, Math.floor(Number(m)));
  return m < 60 ? m + " " + t("min") : Math.floor(m / 60) + " " + t("hr") + " " + (m % 60) + " " + t("min");
}

// Presentation only. Keep observed evidence separate from a complete trip total.
// Older API responses without per-trip timing must remain unknown, not 0% or 100%.
function controlEvidence(travel) {
  const trips = travel && Array.isArray(travel.trips) ? travel.trips : [];
  const number = n => typeof n === "number" && Number.isFinite(n) && n >= 0;
  const timed = trips.length > 0 && trips.every(x => number(x.observedSeconds) && number(x.missingSeconds));
  const observed = timed ? trips.reduce((n, x) => n + x.observedSeconds, 0) : null;
  const missing = travel && number(travel.missingSeconds) ? travel.missingSeconds : null;
  const total = timed ? trips.reduce((n, x) => n + x.observedSeconds + x.missingSeconds, 0) : 0;
  return { trips, observed, missing, coverage: total > 0 ? Math.min(100, Math.floor(observed / total * 100)) : null,
    unmeasured: trips.filter(x => !number(x.distanceKm)).length,
    distance: travel && number(travel.distanceKm) ? travel.distanceKm : null,
    incomplete: !travel || travel.incomplete !== false || !timed };
}

function ControlLiveMap({ team, route, selection, destinations = [], customers = [], endWarnings = [], estimatedRoutes = [], employeeColors = {}, routeColor, selectedId, onSelect }) {
  const language = useLang(), t = controlTranslate;
  const ref = React.useRef(null), mapRef = React.useRef(null), layerRef = React.useRef(null);
  const boundsRef = React.useRef([]), fittedRef = React.useRef(null);
  const customerLayerRef = React.useRef(null), customerFittedRef = React.useRef(false);
  const paintRef = React.useRef(""), selectRef = React.useRef(onSelect); selectRef.current = onSelect;
  const [ready, setReady] = React.useState(false), [failed, setFailed] = React.useState(false);
  const [tileFailed, setTileFailed] = React.useState(false), [attempt, setAttempt] = React.useState(0);
  React.useEffect(() => {
    let disposed = false, observer;
    setFailed(false); setReady(false); setTileFailed(false);
    loadLeaflet().then((L) => {
      if (disposed || !ref.current) return;
      const map = L.map(ref.current, { zoomControl: true, attributionControl: true }); mapRef.current = map;
      L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { maxZoom: 19,
        attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>' })
        .on("tileerror", () => { if (!disposed) setTileFailed(true); }).addTo(map);
      layerRef.current = L.layerGroup().addTo(map);
      customerLayerRef.current = window.HydorCustomerMap.create({ L, map, document, translate: controlTranslate });
      customerFittedRef.current = false;
      map.setView([31.9539, 35.9106], 11);
      if (window.ResizeObserver) { observer = new ResizeObserver(() => map.invalidateSize({ pan: false })); observer.observe(ref.current); }
      fittedRef.current = null; paintRef.current = ""; setReady(true);
    }).catch(() => { if (!disposed) setFailed(true); });
    return () => { disposed = true; if (observer) observer.disconnect(); if (customerLayerRef.current) { customerLayerRef.current.remove(); customerLayerRef.current = null; } if (mapRef.current) { mapRef.current.remove(); mapRef.current = null; } layerRef.current = null; };
  }, [attempt]);
  React.useEffect(() => {
    if (!ready || !customerLayerRef.current) return;
    const bounds = customerLayerRef.current.update(customers, destinations.filter(d => controlCoordinate(d.point)).map(d => d.appointmentId || d.partyId));
    if (bounds.length && !customerFittedRef.current) {
      customerFittedRef.current = true;
      if (!fittedRef.current) mapRef.current.fitBounds(bounds, { padding: [65, 65], maxZoom: 15 });
    }
  }, [customers, destinations, ready, language]);
  React.useEffect(() => {
    if (!ready || !mapRef.current || !layerRef.current) return;
    const paintKey = JSON.stringify([selection, selectedId, language, employeeColors, destinations, endWarnings, estimatedRoutes, route && route.segments,
      (team || []).map(u => [u.userId, u.name, u.latest && [u.latest.latitude, u.latest.longitude, Date.now() - controlTime(u.latest.recordedAt) <= 120000]])]);
    if (paintKey === paintRef.current) return; // A stationary five-second refresh needs no map DOM rebuild.
    paintRef.current = paintKey;
    const L = window.L, layer = layerRef.current, bounds = [], occupied = new Map();
    const label = (point, text, kind) => {
      const key = point.map(n => n.toFixed(5)).join(","), level = occupied.get(key) || 0;
      occupied.set(key, level + 1);
      const tip = document.createElement("span"); tip.textContent = text; tip.dir = "auto";
      return [tip, { permanent: true, direction: "top", offset: [0, -10 - level * 50],
        className: "control-map-label " + kind, opacity: 1 }];
    };
    layer.clearLayers();
    endWarnings.filter(w=>w.assessment?.status==='outside' && controlCoordinate(w.assessment.point)).forEach(w=>{
      const p=w.assessment.point, point=[p.latitude,p.longitude];bounds.push(point);
      L.circleMarker(point,{radius:13,color:'#ef4444',weight:4,fillOpacity:0})
        .bindTooltip(...label(point,w.name+' · '+t('Red outlined circle: visit ended outside customer area'),'control-end-warning')).addTo(layer);
    });
    (team || []).forEach((u) => {
      const p = u && u.latest; if (!controlCoordinate(p)) return;
      bounds.push([p.latitude, p.longitude]);
      const recent = Number.isFinite(controlTime(p.recordedAt)) && Date.now() - controlTime(p.recordedAt) <= 120000;
      const color = employeeColors[String(u.userId)] || "#2563eb";
      const marker = L.circleMarker([p.latitude, p.longitude], { radius: String(u.userId) === String(selectedId) ? 12 : 8, color, weight: 3, dashArray: recent ? null : "3 3", fillColor: color, fillOpacity: recent ? 1 : .55 })
        .bindTooltip(...label([p.latitude, p.longitude], String(u.name || "—"), "control-employee-label"));
      marker.on("click", () => { if (selectRef.current) selectRef.current(String(u.userId)); }); marker.addTo(layer);
    });
    const located = destinations.filter(d => controlCoordinate(d.point));
    const paintedCustomers = new Set();
    located.forEach(d => {
      const id = String(d.partyId || d.key); if (paintedCustomers.has(id)) return; paintedCustomers.add(id);
      const point = [d.point.latitude, d.point.longitude]; bounds.push(point);
      const names = [...new Set(located.filter(x => String(x.partyId || x.key) === id).map(x => x.employeeName))].join(" · ");
      window.HydorCustomerMap.pin(L, document, point, true)
        .bindTooltip(...label(point, d.customerName + "\n(" + names + ")", "control-customer-label")).addTo(layer);
    });
    // Do not bridge GPS gaps, join separate visits or draw the old whole-day air line.
    const segments = ((route || {}).segments || []).map(s => s.filter(controlCoordinate).map(p => [p.latitude, p.longitude]));
    const estimates = estimatedRoutes.filter(x => x.status === "estimated" && x.basis === "estimated_driving_route"
      && Array.isArray(x.geometry) && x.geometry.length >= 2 && x.geometry.every(controlCoordinate));
    const estimatePoints = [];
    estimates.forEach(x => {
      const shape = x.geometry.map(p => [p.latitude, p.longitude]);
      const tip = document.createElement("span"); tip.textContent = t("Estimated driving road · not recorded travel");
      L.polyline(shape, { color: routeColor || "#7d72ff", weight: 5, opacity: .75, dashArray: "8 8" }).bindTooltip(tip).addTo(layer);
      estimatePoints.push(...shape);
    });
    const pts = segments.flat().concat(estimatePoints);
    segments.forEach(s => { if (s.length > 1) L.polyline(s, { color: routeColor || "#7d72ff", weight: 4, opacity: .9 }).addTo(layer); });
    bounds.push(...pts);
    boundsRef.current = bounds;
    const destinationScope = located.map(d => d.key).sort().join("|");
    // Preserve the manager's pan/zoom during polling. Reframe only for a different selection.
    if (bounds.length && (!fittedRef.current || fittedRef.current.selection !== selection || fittedRef.current.destinations !== destinationScope || (pts.length && !fittedRef.current.route))) {
      const focus = selectedId ? (team || []).filter(u => String(u.userId) === String(selectedId) && controlCoordinate(u.latest)).map(u => [u.latest.latitude, u.latest.longitude])
        .concat(located.filter(d => String(d.userId) === String(selectedId)).map(d => [d.point.latitude, d.point.longitude]), pts) : [];
      mapRef.current.fitBounds(focus.length ? focus : bounds, { padding: [65, 65], maxZoom: 15 }); fittedRef.current = { selection, route: pts.length > 0, destinations: destinationScope };
    }
  }, [team, route, selection, destinations, endWarnings, estimatedRoutes, employeeColors, routeColor, selectedId, ready, language]);
  const hasEstimates = estimatedRoutes.some(x => x.status === "estimated" && x.basis === "estimated_driving_route" && Array.isArray(x.geometry) && x.geometry.length >= 2 && x.geometry.every(controlCoordinate));
  const hasPoints = endWarnings.some(w => w.assessment?.status === 'outside' && controlCoordinate(w.assessment.point)) || customers.some(c => controlCoordinate(c.point)) || hasEstimates || (team || []).some((u) => controlCoordinate(u && u.latest)) || destinations.some(d => controlCoordinate(d.point)) || ((route || {}).points || []).some(controlCoordinate);
  return <div className="control-map-content">
    <div className="control-map-tools"><span className="hx-sub">{t("Purple pin: available appointment · Green pin: taken today · Coloured circle: employee")}</span>
      <Btn sm variant="ghost" disabled={!ready || !hasPoints} onClick={() => { const bounds = boundsRef.current.concat(customerLayerRef.current?.bounds || []); if (mapRef.current && bounds.length) mapRef.current.fitBounds(bounds, { padding: [65, 65], maxZoom: 15 }); }}>{t("Fit locations")}</Btn></div>
    <div className="control-map-frame">
      <div ref={ref} className="control-map" aria-label={t("Live field map")} />
      {!ready && <div className="control-map-message" role="status">{failed ? t("Map could not load. Check your connection and try again; employee details remain available below.") : t("Loading map…")}
        {failed && <Btn sm onClick={() => setAttempt((n) => n + 1)}>{t("Try again")}</Btn>}</div>}
    </div>
    {hasEstimates && <div className="control-note">{t("Dashed line: estimated driving road · Solid line: recorded GPS · Not proof of the route taken")}</div>}
    {endWarnings.some(w=>w.assessment?.status==='outside') && <div className="control-note" style={{color:'#fb7185'}}>{t('Red outlined circle: visit ended outside customer area')}</div>}
    {tileFailed && <div className="control-note" role="status">{t("Some map tiles could not load. Check your connection; the location links below are still available.")}</div>}
    {ready && !hasPoints && <div className="control-note">{t("No location samples for this date. The map shows Amman, not employee positions.")}</div>}
  </div>;
}

// Dates on legacy/imported records are not guaranteed to be parseable. These are display
// guards only: do not repair or re-date a transaction/appointment while opening this page.
function controlTime(value) {
  if (value == null || value === "") return NaN;
  return new Date(value).getTime();
}
function controlDay(value) {
  const ms = controlTime(value);
  if (!Number.isFinite(ms)) return "";
  const p = window.HydorAttendancePolicy;
  return p ? p.ammanDate(new Date(ms)) : new Date(ms).toISOString().slice(0, 10);
}

class ControlRoomBoundary extends React.Component {
  constructor(props) { super(props); this.state = { failed: false }; }
  static getDerivedStateFromError() { return { failed: true }; }
  componentDidCatch(error) { console.error("[HYDOR Control Room] display failed", error); }
  render() {
    if (!this.state.failed) return this.props.children;
    const t = controlTranslate;
    return <Empty icon="warn" title={t("Control Room could not be displayed.")}
      action={<Btn onClick={() => this.setState({ failed: false })}>{t("Try again")}</Btn>}>
      {t("You can continue using the other pages. No records were changed.")}
    </Empty>;
  }
}

function ControlRoomPage() {
  return <ControlRoomBoundary><ControlRoomContent /></ControlRoomBoundary>;
}

function ControlRoomContent() {
  const s = useHydor(), role = useRole(), t = controlTranslate;
  useLang();
  const allowed = ["owner", "cofounder", "manager"].includes(role);
  const [filters, setFilters] = React.useState({ employee: "", department: "", district: "", stage: "", date: "", search: "" });
  const [expanded, setExpanded] = React.useState("");
  const [wallboard, setWallboard] = React.useState(false), [rosterPage, setRosterPage] = React.useState(0);
  const [showAlerts, setShowAlerts] = React.useState(false);
  const [endWarnings, setEndWarnings] = React.useState([]);
  const wallButton = React.useRef(null), wallExit = React.useRef(null);
  const detailRef = React.useRef(null);
  const [live, setLive] = React.useState({ loading: true, users: [], error: "", mode: "" });
  const [routeResult, setRouteResult] = React.useState(null);
  const [roadResult, setRoadResult] = React.useState(null);
  const [destinationResult, setDestinationResult] = React.useState(null);
  const [customerResult, setCustomerResult] = React.useState(null);
  const [, setCalendarDay] = React.useState(() => window.HydorControlMap.day(Date.now()));
  React.useEffect(() => {
    // Only one render per day; works without telemetry/API and after tab suspension.
    let lastDay = window.HydorControlMap.day(Date.now());
    const tick = () => {
      const nextDay = window.HydorControlMap.day(Date.now());
      if (nextDay === lastDay) return;
      // An explicitly selected current day still follows live midnight. An intentionally
      // historical date remains historical for reviewing a past employee route.
      const previousDay = lastDay; lastDay = nextDay;
      setFilters(f => f.date === previousDay ? { ...f, date: "" } : f);
      setCalendarDay(nextDay);
    };
    const timer = setInterval(tick, 1000); document.addEventListener("visibilitychange", tick);
    return () => { clearInterval(timer); document.removeEventListener("visibilitychange", tick); };
  }, []);
  const M = window.HydorControlMap;
  const activeUsers = (s.users || []).filter(M.rosterUser)
    .map((u) => ({ ...u, name: String(u.name || u.code || u.id || "—") }));
  const rosterIds = new Set(activeUsers.map(u => String(u.id)));
  const P = window.HydorAttendancePolicy;
  const nowMs = Date.now(), today = P ? P.ammanDate(nowMs) : new Date().toISOString().slice(0, 10);
  const chosenDate = filters.date || today;
  const fieldLive = (live.date && live.date !== chosenDate ? [] : live.users || []).filter(u => rosterIds.has(String(u.userId)));
  const historical = chosenDate !== today;
  const savedCustomers = allowed && !historical ? M.customerPins(s, today) : [];
  const dailyAppointments = new Set(savedCustomers.map(c => c.appointmentId));
  const targets = allowed ? M.targets(s, chosenDate).filter(d => historical || !d.appointmentId || dailyAppointments.has(d.appointmentId)) : [];
  const customerSignature = JSON.stringify(savedCustomers);
  const resolvedCustomers = new Map((customerResult?.customers || []).map(c => [c.key, c]));
  const customers = savedCustomers.map(c => {
    const resolved = resolvedCustomers.get(c.key), point = M.pointFromLink(c.mapLink) || resolved?.point || null;
    return { ...c, point, status: point ? "resolved" : resolved?.status || (!c.mapLink ? "missing_link" : "pending") };
  });
  const targetSignature = JSON.stringify(targets.map(d => d.key));
  const employeeColors = M.colors(s.users || []);
  // Never paint an old response after visit-end, reassignment, or a date change.
  const destinations = targets.map(d => {
    const resolved = destinationResult && destinationResult.date === chosenDate && (destinationResult.destinations || []).find(x => x.key === d.key);
    return { ...d, point: M.pointFromLink(d.mapLink) || (resolved && resolved.point) || null };
  });
  const routeKey = chosenDate + ":" + expanded;
  const route = rosterIds.has(String(expanded)) && routeResult && routeResult.key === routeKey ? routeResult.data : null;
  const roadReview = rosterIds.has(String(expanded)) && roadResult && roadResult.key === routeKey ? roadResult.data : null;
  const byParty = (id) => (s.parties || []).find((p) => p && String(p.id) === String(id));
  const dateOf = controlDay;
  const minsSince = (v) => Number.isFinite(controlTime(v)) ? Math.max(0, (nowMs - controlTime(v)) / 60000) : null;
  const duration = (m) => controlDuration(m, t);
  const liveByUser = new Map(fieldLive.map((x) => [String(x.userId), x]));
  const selectedAccount = (fieldLive.find(u => String(u.userId) === String(expanded)) || {}).accountId;

  React.useEffect(() => {
    if (!wallboard) return;
    const escape = e => { if (e.key === "Escape") setWallboard(false); };
    document.addEventListener("keydown", escape);
    // The wallboard is a focused, read-only display. Background ERP controls must not
    // receive keyboard focus, and every attribute is restored when it is closed.
    const host = wallExit.current && wallExit.current.closest && wallExit.current.closest(".control-room");
    const siblings = []; let child = host;
    while (child && child.parentElement) {
      Array.from(child.parentElement.children).forEach(el => { if (el !== child && el.tagName !== "SCRIPT" && el.tagName !== "STYLE") { siblings.push([el, el.inert]); el.inert = true; } });
      child = child.parentElement;
    }
    if (wallExit.current && wallExit.current.focus) wallExit.current.focus();
    return () => { document.removeEventListener("keydown", escape); siblings.forEach(([el, value]) => { el.inert = value; }); if (wallButton.current && wallButton.current.focus) wallButton.current.focus(); };
  }, [wallboard]);

  React.useEffect(() => {
    let cancelled = false;
    if (!allowed) return;
    if (!window.HydorAPI || !HydorAPI.enabled || !HydorAPI.isAuthed()) { setLive({ loading: false, users: [], error: "Secure live-location API is not connected.", mode: "" }); return;
    }
    setLive((x) => ({ ...x, loading: true }));
    let pending = false;
    const refresh = () => {
      if (pending || document.hidden) return;
      pending = true;
      return HydorAPI.get("/locations/team?compact=1&date=" + encodeURIComponent(chosenDate)).then((d) => {
      if (!d || !Array.isArray(d.users)) throw new Error(t("Live locations unavailable."));
      if (!cancelled) setLive({ loading: false, users: d.users.filter((u) => u && u.userId != null), error: "", mode: d.mode || "", refreshedAt: Date.now(), date: chosenDate });
    }).catch((e) => { if (!cancelled) setLive({ loading: false, users: [], error: e.message || "Live locations unavailable.", mode: "" }); }).finally(() => { pending = false; });
    };
    refresh(); const id = setInterval(refresh, 5000);
    return () => { cancelled = true; clearInterval(id); };
  }, [chosenDate, allowed]);

  React.useEffect(() => {
    if (!allowed || !targets.length || !window.HydorAPI || !HydorAPI.enabled || !HydorAPI.isAuthed()) return;
    let cancelled = false, pending = false;
    const refresh = () => { if (pending || document.hidden) return; pending = true; return HydorAPI.get("/locations/destinations?date=" + encodeURIComponent(chosenDate))
      .then(d => { if (!cancelled && d && Array.isArray(d.destinations)) setDestinationResult(d); })
      .catch(() => {}).finally(() => { pending = false; }); }; // GPS and the rest of the page remain available.
    refresh(); const timer = setInterval(refresh, 60000);
    return () => { cancelled = true; clearInterval(timer); };
  }, [chosenDate, targetSignature, allowed]);

  React.useEffect(() => {
    if (!allowed || !savedCustomers.length || !window.HydorAPI || !HydorAPI.enabled || !HydorAPI.isAuthed()) return;
    let cancelled = false, pending = false, nextRefresh = 0;
    const refresh = () => {
      if (pending || document.hidden || Date.now() < nextRefresh) return; pending = true;
      return HydorAPI.get("/locations/customers").then(d => {
        if (!d || !Array.isArray(d.customers)) throw new Error("Customer locations unavailable");
        nextRefresh = d.pending > 0 ? 0 : Date.now() + 60000;
        if (!cancelled) setCustomerResult(d);
      }).catch(() => { nextRefresh = Date.now() + 30000; if (!cancelled) setCustomerResult({ customers: [], error: true }); }).finally(() => { pending = false; });
    };
    // Resolve initial short-link batches promptly; a warm catalog is read once a minute.
    refresh(); const timer = setInterval(refresh, 10000);
    return () => { cancelled = true; clearInterval(timer); };
  }, [allowed, customerSignature]);

  React.useEffect(() => {
    if (!allowed) return;
    const hit = fieldLive.find((u) => String(u.userId) === String(expanded));
    if (!hit || !hit.accountId || !window.HydorAPI || !HydorAPI.isAuthed()) return;
    let cancelled = false, pending = false;
    const refresh = () => {
      if (pending || document.hidden) return;
      pending = true;
      return HydorAPI.get("/locations/route/" + encodeURIComponent(hit.accountId) + "?date=" + encodeURIComponent(chosenDate))
        .then((d) => { if (!cancelled) setRouteResult({ key: routeKey, data: d }); })
        .catch(() => { if (!cancelled) setRouteResult(null); }).finally(() => { pending = false; });
    };
    refresh(); const timer = setInterval(refresh, 30000);
    return () => { cancelled = true; clearInterval(timer); };
  }, [expanded, chosenDate, selectedAccount, targetSignature, allowed]);

  React.useEffect(() => {
    // Independent, slower read. No GPS sample/account ID is needed to inspect a
    // visit that started and then lost all foreground telemetry. No road work in TV mode.
    if (!allowed || wallboard || !expanded || !rosterIds.has(String(expanded)) || !window.HydorAPI || !HydorAPI.isAuthed()) return;
    let cancelled = false, pending = false;
    const refresh = () => {
      if (pending || document.hidden) return;
      pending = true;
      return HydorAPI.get("/locations/visit-distance/" + encodeURIComponent(expanded) + "?date=" + encodeURIComponent(chosenDate))
        .then(d => { if (!cancelled) setRoadResult({ key: routeKey, data: d && typeof d.enabled === "boolean" ? d : { error: true } }); })
        .catch(() => { if (!cancelled) setRoadResult({ key: routeKey, data: { error: true } }); })
        .finally(() => { pending = false; });
    };
    refresh(); const timer = setInterval(refresh, 60000);
    return () => { cancelled = true; clearInterval(timer); };
  }, [expanded, chosenDate, targetSignature, allowed, wallboard]);

  if (!allowed) return <Empty icon="shield" title={t("Control Room is restricted to management.")} />;

  const validAttendance = (s.attendance || []).filter((a) => a && !a.deleted && Number.isFinite(controlTime(a.ts || a.createdAt)));
  const rowsAll = activeUsers.map((u) => {
    const att = validAttendance.filter((a) => String(a.userId) === String(u.id) && dateOf(a.ts || a.createdAt) === chosenDate)
      .slice().sort((a, b) => String(a.ts || a.createdAt || "").localeCompare(String(b.ts || b.createdAt || "")));
    const last = att[att.length - 1] || null, journey = M.journey(s, u, chosenDate);
    const lastStart = journey && journey.visit, openVisit = !!lastStart;
    const openShift = chosenDate === today && P ? P.openShift({ ...s, attendance: validAttendance }, u, new Date()) : att.filter((a) => a.type === "checkin").length > att.filter((a) => a.type === "checkout").length;
    const currentAppt = journey && journey.appointment;
    const partyId = (journey && journey.partyId) || (last && last.partyId) || "";
    const party = byParty(partyId);
    const txs = (s.transactions || []).filter((x) => x && !x.deleted && dateOf(x.createdAt || x.date) === chosenDate &&
      (String(x.createdBy || "") === String(u.id) || (u.code && String(x.employee || "") === String(u.code))))
      .sort((a, b) => String(b.createdAt || b.date || "").localeCompare(String(a.createdAt || a.date || "")));
    const approval = (s.approvals || []).find((a) => a && a.status === "pending" && (String(a.userId || "") === String(u.id) || (txs[0] && String(a.saleId || a.txId || "") === String(txs[0].id))));
    const liveRow = liveByUser.get(String(u.id)), latest = liveRow && liveRow.latest;
    const destination = destinations.find(d => String(d.userId) === String(u.id));
    const proximity = M.proximity(journey, destination && destination.point, latest, liveRow && liveRow.previous, nowMs);
    const age = latest ? minsSince(latest.recordedAt) : null;
    let stage = "off", label = "Not checked in", tone = "gray", stageAt = last && (last.ts || last.createdAt);
    if (openVisit) {
      stage = proximity.state === "nearby" ? "field" : "travelling";
      label = proximity.state === "nearby" ? "Within customer area — 1 km" : proximity.state === "en_route" ? "En route to customer" : "Journey started — proximity unconfirmed";
      tone = proximity.state === "unknown" ? "amber" : "blue"; stageAt = lastStart.ts || lastStart.createdAt;
    }
    else if (openShift && currentAppt) { stage = "assigned"; label = "Appointment assigned — visit not started"; tone = "blue"; stageAt = currentAppt.claimedAt || currentAppt.updatedAt || currentAppt.createdAt; }
    else if (last && last.type === "visit_end" && !txs.length) { stage = "attention"; label = "Visit ended — outcome pending"; tone = "amber"; }
    else if (openShift) { stage = "available"; label = "Shift open — no active appointment"; tone = "green"; }
    else if (last && last.type === "checkout") { stage = "ended"; label = "Shift ended"; tone = "gray"; }
    const exceptions = [];
    if (openVisit && minsSince(stageAt) > 120) exceptions.push({ p: 1, text: "Journey open over 120 minutes (including travel)" });
    if (openShift && (!latest || age > 5)) exceptions.push({ p: 1, text: latest ? "Live location older than 5 minutes" : "No live location during open shift" });
    if (stage === "attention") exceptions.push({ p: 1, text: "Visit ended without transaction/result" });
    if (approval) exceptions.push({ p: 2, text: "Management approval pending" });
    return { u, att, last, openShift, openVisit, currentAppt, journey, party, txs, approval, liveRow, latest, age,
      stage, label, tone, stageAt, stageMins: minsSince(stageAt), proximity, exceptions };
  });

  const departments = [...new Set(activeUsers.map((u) => u.department).filter(Boolean))].sort();
  const districts = [...new Set((s.appointments || []).map((a) => a && (a.bookingDistrict || a.bookingArea || a.bookingRegion)).filter(Boolean))].sort();
  const rows = rowsAll.filter((r) => (!filters.employee || String(r.u.id) === filters.employee)
    && (!filters.department || r.u.department === filters.department) && (!filters.stage || r.stage === filters.stage)
    && (!filters.search || [r.u.name, r.u.code, r.party && r.party.name, r.currentAppt && (r.currentAppt.bookingDistrict || r.currentAppt.bookingArea)].filter(Boolean).join(" ").toLowerCase().includes(filters.search.trim().toLowerCase()))
    && (!filters.district || String((r.currentAppt && (r.currentAppt.bookingDistrict || r.currentAppt.bookingArea || r.currentAppt.bookingRegion)) || "") === filters.district));
  const visibleIds = new Set(rows.map(r => String(r.u.id)));
  const counts = { active: rows.filter((r) => r.openShift).length, field: rows.filter((r) => r.openVisit).length,
    nearby: rows.filter(r => r.proximity.state === "nearby").length, exceptions: rows.reduce((n, r) => n + r.exceptions.length, 0) };
  const tile = (label, value, color, sub) => <div className="mini-stat" style={{ borderTop: "3px solid " + color }}><div className="ms-label">{t(label)}</div><div className="ms-value">{value}</div>{sub && <div className="ms-sub">{t(sub)}</div>}</div>;
  const exceptions = rows.flatMap((r) => r.exceptions.map((e) => ({ ...e, user: r.u }))).sort((a, b) => a.p - b.p);
  const groupLabel = (group) => t(({ field: "Within customer area — 1 km", travelling: "Started journeys", assigned: "Assigned appointments", available: "No active appointment", attention: "Needs follow-up", ended: "Shift ended", off: "Not checked in" })[group]);
  const selectedRow = rows.find((r) => String(r.u.id) === String(expanded));
  const summary = route && route.summary, enoughSamples = summary && summary.samples >= 2;
  const travel = route && route.travel;
  const freshRoute = enoughSamples && chosenDate === today && minsSince(summary.lastAt) != null && minsSince(summary.lastAt) <= 2;
  const evidence = controlEvidence(travel);
  const ordered = CONTROL_GROUPS.flatMap(group => rows.filter(r => r.stage === group));
  const pageCount = Math.max(1, Math.ceil(ordered.length / 5)), page = Math.min(rosterPage, pageCount - 1);
  const listed = wallboard ? ordered.slice(page * 5, page * 5 + 5) : ordered;
  const changeFilter = (key, value) => { setFilters({ ...filters, [key]: value }); setRosterPage(0); };
  const select = id => setExpanded(String(id) === String(expanded) ? "" : String(id));
  const hasFilters = !!(filters.employee || filters.department || filters.district || filters.stage || filters.search);
  const canSeeMoney = ["owner", "cofounder"].includes(role);
  const clockText = value => new Date(value).toLocaleTimeString("en-GB", { timeZone: "Asia/Amman", hour: "2-digit", minute: "2-digit" });

  return <section className={"control-room" + (wallboard ? " control-wallboard" : "")} aria-label={t("Operations Control Room")} role={wallboard ? "dialog" : undefined} aria-modal={wallboard ? true : undefined}>
    <header className="control-header">
      <div className="control-title"><span className="control-brand" aria-hidden="true">H</span><div>
        <div className="control-eyebrow">HYDOR <span> / </span> {t("FIELD OPERATIONS")}</div>
        <h2>{t("Operations Control Room")}</h2>
      </div></div>
      <div className="control-header-actions">
        <span className={"control-connection " + (historical ? "is-history" : live.error ? "is-error" : "")}>
          <i aria-hidden="true" />{historical ? t("Day history") : live.error ? t("Connection interrupted") : live.loading ? t("Refreshing…") : t("Monitoring today")}
        </span>
        <input className="inp" type="date" max={today} aria-label={t("Date")} value={chosenDate} onChange={e => changeFilter("date", e.target.value)} />
        {historical && <button className="btn sm" onClick={() => changeFilter("date", today)}>{t("Today")}</button>}
        {wallboard ? <button ref={wallExit} className="btn sm control-wall-exit" onClick={() => setWallboard(false)}>{t("Exit display")} <kbd>Esc</kbd></button>
          : <button ref={wallButton} className="btn sm control-display-button" onClick={() => { setWallboard(true); setShowAlerts(false); }}>{t("Large screen")}</button>}
      </div>
    </header>

    <div className="control-kpis">
      {tile("Open shifts", counts.active, "#34d399", "Checked in, with no recorded check-out")}
      {tile("Started journeys", counts.field, "#60a5fa", "Visit started, with no recorded visit end")}
      {tile("Within customer area — 1 km", counts.nearby, "#a78bfa", "Proximity, not verified arrival")}
      {tile("Alerts to review", counts.exceptions, "#fbbf24", "Review context before taking action")}
    </div>

    {!wallboard && <div className="control-toolbar">
      <input className="inp control-search" type="search" aria-label={t("Search field team")} placeholder={t("Search employee, customer or area…")} value={filters.search} onChange={e => changeFilter("search", e.target.value)} />
      <select className="sel" aria-label={t("All employees")} value={filters.employee} onChange={e => changeFilter("employee", e.target.value)}><option value="">{t("All employees")}</option>{activeUsers.map(u => <option key={u.id} value={u.id}>{u.name}</option>)}</select>
      <select className="sel" aria-label={t("All departments")} value={filters.department} onChange={e => changeFilter("department", e.target.value)}><option value="">{t("All departments")}</option>{departments.map(x => <option key={x}>{x}</option>)}</select>
      <select className="sel" aria-label={t("All districts")} value={filters.district} onChange={e => changeFilter("district", e.target.value)}><option value="">{t("All districts")}</option>{districts.map(x => <option key={x}>{x}</option>)}</select>
      <select className="sel" aria-label={t("All stages")} value={filters.stage} onChange={e => changeFilter("stage", e.target.value)}><option value="">{t("All stages")}</option>{CONTROL_GROUPS.map(x => <option key={x} value={x}>{groupLabel(x)}</option>)}</select>
      {hasFilters && <button className="btn sm" onClick={() => { setFilters({ ...filters, employee: "", department: "", district: "", stage: "", search: "" }); setRosterPage(0); }}>{t("Clear filters")}</button>}
    </div>}

    <div className="control-scope"><span>{historical ? t("Historical snapshot · not a live view") : t("Field team only · office staff excluded")} · <b>{rows.length} / {activeUsers.length}</b> {t("Employees shown")}{hasFilters && <strong> · {t("Filters active")}</strong>}</span>
      <span>{live.error ? t(live.error) : live.refreshedAt ? t("Data checked") + " " + clockText(live.refreshedAt) + " · " + t("Amman time") : t("Refreshing…")}</span>
    </div>

    <div className="control-workspace">
      <div className="control-map-panel">
        <div className="control-panel-heading"><div><span className="control-eyebrow">{historical ? t("RECORDED LOCATIONS") : t("FIELD MAP")}</span><h3>{t("Where the team is")}</h3></div>
          <span className="control-map-count">{fieldLive.filter(u => visibleIds.has(String(u.userId)) && controlCoordinate(u.latest)).length} {t("Located")}</span></div>
        <ControlLiveMap team={fieldLive.filter(u => visibleIds.has(String(u.userId)))} route={selectedRow ? route : null} selection={chosenDate + ":" + (selectedRow ? expanded : "") + ":" + rows.map(r => r.u.id).join("|")}
          customers={customers}
          endWarnings={endWarnings.filter(w => visibleIds.has(String(w.userId)) && controlDay(w.ended_at) === chosenDate)}
          selectedId={selectedRow && expanded} onSelect={select}
          estimatedRoutes={!wallboard && selectedRow && Array.isArray(roadReview?.trips) ? roadReview.trips : []}
          destinations={destinations.filter(d => visibleIds.has(String(d.userId)))} employeeColors={employeeColors} routeColor={employeeColors[String(expanded)]} />
        <div className="control-customer-summary" role="status">
          <span>{t("Appointment pins")}: <strong>{customers.filter(c => controlCoordinate(c.point)).length} / {customers.length}</strong></span>
          <span>{t("Without a resolved location")}: <strong>{customers.filter(c => !controlCoordinate(c.point)).length}</strong></span>
          <span>{customerResult?.error ? t("Some customer locations could not load. Retrying automatically.") : t("Available today and yesterday's unclaimed appointments. Taken appointments stay until midnight, Amman time. Tap a pin for details.")}</span>
        </div>
        <div className="control-map-footer">
          <span>{selectedRow ? t("Selected employee") + ": " + selectedRow.u.name : t("Select an employee to inspect their recorded journey")}</span>
          {!wallboard && selectedRow && <button className="btn sm" onClick={() => { if (detailRef.current) { detailRef.current.scrollIntoView({ block: "start" }); detailRef.current.focus({ preventScroll: true }); } }}>{t("View journey details")} ↓</button>}
          {selectedRow && <button className="btn sm" onClick={() => setExpanded("")}>{t("Clear selection")}</button>}
        </div>
        {!wallboard && destinations.filter(d => visibleIds.has(String(d.userId)) && !controlCoordinate(d.point)).map(d => <div className="control-note control-missing-pin" key={d.key}>
          <b>{d.employeeName} → {d.customerName}</b>: {t("Customer pin unavailable — no guessed location is shown.")}
          {M.mapUrl(d.mapLink) && <a href={d.mapLink} target="_blank" rel="noopener noreferrer"> {t("Open customer map link")}</a>}
        </div>)}
      </div>

      <aside className="control-roster" aria-label={t("Field team")}>
        <div className="control-panel-heading"><div><span className="control-eyebrow">{t("PEOPLE & NEXT STOP")}</span><h3>{t("Field team")} <span className="control-counter">{rows.length}</span></h3></div><span className="control-readonly">{t("Read only")}</span></div>
        <div className="control-roster-scroll">
          {!listed.length && <div className="control-empty"><b>{t("No matching operational journeys.")}</b><p>{t("Change the filters or choose another date.")}</p></div>}
          {listed.map(r => { const a = r.currentAppt, p = r.party, open = String(expanded) === String(r.u.id);
            return <div key={r.u.id} className={"control-person " + (open ? "is-selected" : "")} style={{ "--person-color": employeeColors[String(r.u.id)] }}>
              <button className="control-employee" aria-expanded={open} onClick={() => select(r.u.id)}>
                <div className="control-person-heading"><span className="control-avatar" aria-hidden="true">{r.u.name.slice(0, 1)}</span><span className="control-person-name"><b>{r.u.name}</b><small>{t(r.u.department || r.u.role)}</small></span><span className="control-chevron" aria-hidden="true">{open ? "−" : "+"}</span></div>
                <div className="control-person-state"><Badge color={r.tone} dot>{t(r.label)}</Badge></div>
                <div className="control-next-stop"><span aria-hidden="true">↗</span><span><span className="control-caption">{t("Customer / area")}</span><b>{r.journey ? (p ? p.name : (a && a.bookingCustomerName) || "—") : "—"}</b>{!wallboard && a && <small>{a.bookingDistrict || a.bookingArea || a.bookingRegion || ""}</small>}</span></div>
                <div className="control-person-meta"><span>{historical ? t("Recorded at") : t(r.openVisit ? (wallboard ? "Since departure" : "Time since visit start (includes travel)") : "Time in stage")}<b>{historical ? (Number.isFinite(controlTime(r.stageAt)) ? clockText(r.stageAt) : "—") : duration(r.stageMins)}</b></span>
                  <span className={r.age != null && r.age <= 2 && !historical ? "control-fresh" : "control-stale"}>{r.latest ? (historical ? t("Recorded location") : r.age != null && r.age <= 2 ? t("Recent location") : t("Last known location")) : t("No live sample")}<b>{r.latest ? historical ? clockText(r.latest.recordedAt) : duration(r.age) + " " + t("ago") : "—"}</b></span>
                </div>
                {!wallboard && r.latest && <div className="control-accuracy">{t("GPS accuracy")}: {r.latest.accuracy != null && Number.isFinite(Number(r.latest.accuracy)) ? "±" + Math.round(r.latest.accuracy) + " " + t("metres") : t("Unknown")}</div>}
              </button>
            </div>;
          })}
        </div>
        {wallboard && pageCount > 1 && <div className="control-pagination"><button className="btn sm" disabled={!page} onClick={() => setRosterPage(page - 1)}>{t("Previous")}</button><span>{page + 1} / {pageCount}</span><button className="btn sm" disabled={page + 1 >= pageCount} onClick={() => setRosterPage(page + 1)}>{t("Next")}</button></div>}
        <div className="control-roster-foot">{t("Location age is not time spent at a customer.")}</div>
      </aside>
    </div>

    <div className={"control-alert-strip" + (exceptions.length ? " has-alerts" : "")}>
      <button className="control-alert-toggle" aria-expanded={showAlerts} onClick={() => setShowAlerts(!showAlerts)}><b>{exceptions.length ? "!" : "✓"}</b><span>{exceptions.length ? t("Alerts to review") + " · " + exceptions.length : t("No active exceptions")}</span><span aria-hidden="true">{showAlerts ? "−" : "+"}</span></button>
      <span>{t("Check the cause before taking action; a location gap is not a violation.")}</span>
    </div>
    {showAlerts && <div className="control-alert-list">{exceptions.map((x, i) => <button key={x.user.id + i} className="control-alert" onClick={() => { setExpanded(String(x.user.id)); setRosterPage(Math.floor(ordered.findIndex(r => String(r.u.id) === String(x.user.id)) / 5)); setShowAlerts(false); }}><b>{x.user.name}</b><span>{t(x.text)}</span><span aria-hidden="true">↗</span></button>)}</div>}

    {!wallboard && selectedRow && <section ref={detailRef} tabIndex={-1} className="control-detail" aria-label={t("Selected employee")}>
      <div className="control-panel-heading"><div><span className="control-eyebrow">{t("JOURNEY EVIDENCE")}</span><h3>{selectedRow.u.name}</h3><span className="hx-sub">{chosenDate} · {t("Amman time")}</span></div><button className="btn sm" onClick={() => setExpanded("")}>{t("Close details")}</button></div>
      <div className="control-road-review" aria-label={t("Visit distance for review")}>
        <h4>{t("Visit distance for review")}</h4>
        {!roadReview ? <p role="status">{t("Loading road estimate…")}</p>
          : roadReview.error ? <p role="status">{t("Road estimate unavailable. Recorded GPS remains available below.")}</p>
          : !roadReview.enabled ? <p>{t("Road estimates are not activated. The local road engine needs setup and capacity checks.")}</p>
          : <>
            <strong className="control-review-total">{typeof roadReview.distanceKm === "number" && Number.isFinite(roadReview.distanceKm) ? roadReview.distanceKm.toFixed(3) + " " + t("km") : "—"}</strong>
            <p>{roadReview.provisional ? t("Visit still open · provisional distance") : t("Review before fuel settlement; no payment has been recorded.")}</p>
            {!!roadReview.unknownTrips && <p className="control-note">{t("Some visits have no distance. The total is unknown, not zero.")}</p>}
            <p className="hx-sub">{t("For an incomplete visit, the start-to-customer road estimate replaces its GPS subtotal; the two are never added.")}</p>
            <div className="control-review-trips" role="list">{(Array.isArray(roadReview.trips) ? roadReview.trips : []).map(x => <div role="listitem" key={x.visitSessionId}>
              <div><b>{(byParty(x.partyId) || {}).name || t("Customer")}</b><span>{Number.isFinite(controlTime(x.startedAt)) ? clockText(x.startedAt) : "—"} · {x.provisional ? t("Visit still open") : t("Visit closed")}</span></div>
              <div><b>{typeof x.distanceKm === "number" && Number.isFinite(x.distanceKm) ? x.distanceKm.toFixed(3) + " " + t("km") : "—"}</b><span>{t(({ observed: "Observed GPS estimate", estimated: "Estimated driving road", start_location_unreliable: "Departure location missing or inaccurate", customer_location_missing: "Customer map pin missing", provider_not_configured: "Local road engine not configured", provider_configuration: "Local road engine not configured", provider_busy: "Road estimate pending", estimate_limit: "Road estimate pending", route_out_of_range: "Road estimate outside supported area", truncated: "Route limit reached. This day is only partially loaded." })[x.status] || "Road estimate unavailable")}</span></div>
            </div>)}</div>
            <p className="hx-sub">{t("A road estimate is not a recorded journey or proof of arrival. Waiting does not add kilometres.")}</p>
          </>}
      </div>
      <div className="control-evidence">
        <div className="control-distance"><div className="control-caption">{t("Observed visit distance")}</div><strong>{evidence.distance != null ? evidence.distance.toFixed(3) + " " + t("km") : "—"}</strong>
          <span className={"control-evidence-status " + (evidence.incomplete ? "partial" : "")}>{evidence.distance == null ? t("Insufficient location data — not zero") : evidence.incomplete ? t("Recorded portion only · total distance unknown") : t("Observed GPS estimate")}</span>
          <p>{t("Between GPS samples; not a road-distance or fuel claim.")}</p>
        </div>
        <div className="control-coverage"><div className="control-coverage-title"><span>{t("Visit recording coverage")}</span><b>{evidence.coverage == null ? "—" : evidence.coverage + "%"}</b></div>
          <div className="control-coverage-track" role="meter" aria-label={t("Visit recording coverage")} aria-valuemin={0} aria-valuemax={100} aria-valuenow={evidence.coverage == null ? undefined : evidence.coverage} aria-valuetext={evidence.coverage == null ? t("Unknown") : evidence.coverage + "%"}><span style={{ width: (evidence.coverage || 0) + "%" }} /></div>
          <p>{t("Observed time divided by accepted visit time. This is recording coverage, not productivity.")}</p>
          <div className="control-evidence-counts"><span>{t("Started journeys")}<b>{travel ? evidence.trips.length : "—"}</b></span><span>{t("Trips without measured distance")}<b>{travel ? evidence.unmeasured : "—"}</b></span><span>{t("Unobserved trip time")}<b>{duration(evidence.missing == null ? null : evidence.missing / 60)}</b></span></div>
        </div>
      </div>
      {evidence.incomplete && <div className="control-note control-evidence-warning">{roadReview && roadReview.enabled
        ? t("GPS coverage is incomplete. The separate road estimate is not GPS evidence.")
        : t("Trip data incomplete — missing movement is unknown and is not added to the counter.")}</div>}
      {route && route.truncated && <div className="control-note control-evidence-warning">{t("Route limit reached. This day is only partially loaded.")}</div>}
      <div className="control-detail-columns">
        <div><h4>{t("Journey timeline")}</h4><ol className="control-timeline">{selectedRow.att.map(x => <li key={x.id}><span>{t(({ checkin: "Check in", checkout: "Check out", visit_start: "Visit start", visit_end: "Visit end" })[x.type] || x.type)}</span><time>{clockText(x.ts || x.createdAt)}</time></li>)}</ol>{!selectedRow.att.length && <p className="hx-sub">—</p>}</div>
        <div><h4>{t("Transactions on selected date")} <span className="control-counter">{selectedRow.txs.length}</span></h4>{selectedRow.txs.length ? selectedRow.txs.map(x => <button key={x.id} className="pop-item" onClick={() => window.__hydorGoPage && window.__hydorGoPage("transactions", { highlight: x.id })}>{t(x.type || "transaction")}{canSeeMoney && <span> · {Hydor.fmtMoney ? Hydor.fmtMoney(x.total || x.value || 0) : (x.total || x.value || 0)}</span>}</button>) : <p className="hx-sub">—</p>}</div>
      </div>
      <details className="control-day-observations"><summary>{t("Whole-day observations · separate from visit distance")}</summary>
        <div className="control-day-metrics">{tile("Observed time at locations", enoughSamples ? duration(summary.dwellMinutes) : "—", "var(--pos)", "Samples close to each other; not necessarily at a customer.")}
          {tile("Observed time without appointment", enoughSamples ? duration(summary.idleMinutes) : "—", "var(--warn)", "Samples without a linked appointment; not proof of idle time.")}
          {tile("Since last detected movement", freshRoute ? duration(summary.stationaryMinutes) : "—", "var(--text-3)", freshRoute ? "Estimated from recent samples, not proof of continuous presence." : "No recent samples — current stopping time is unknown.")}</div>
        <p className="hx-sub">{t("Location samples")}: {summary ? summary.samples || 0 : "—"}</p>
      </details>
      <div className="control-detail-links">{selectedRow.latest && controlCoordinate(selectedRow.latest) && <a className="btn sm" href={"https://maps.google.com/?q=" + selectedRow.latest.latitude + "," + selectedRow.latest.longitude} target="_blank" rel="noopener noreferrer">{t("Open current point")}</a>}
        {selectedRow.currentAppt && M.mapUrl(selectedRow.currentAppt.bookingMapLink) && <a className="btn sm" href={selectedRow.currentAppt.bookingMapLink} target="_blank" rel="noopener noreferrer">{t("Customer destination")}</a>}
        <button className="btn sm" onClick={() => window.__hydorGoPage && window.__hydorGoPage("attendance")}>{t("Attendance")}</button></div>
    </section>}

    {["owner", "cofounder"].includes(role) && typeof OdometerEndWarnings !== 'undefined' && <OdometerEndWarnings date={chosenDate} onRows={setEndWarnings}/>}
    {!wallboard && ["owner", "cofounder"].includes(role) && typeof FieldOperationsAdmin !== "undefined" && <FieldOperationsAdmin role={role} />}
    {!wallboard && <details className="control-guide"><summary>{t("How to read these numbers")}</summary><ul>
      <li>{t("Time in stage starts at the recorded shift, appointment or visit event. It is not a measurement of physical presence.")}</li>
      <li>{t("Updated = time since the last location sample. Accuracy = estimated GPS error in metres, not distance travelled.")}</li>
      <li>{t("No active appointment does not mean not working; office work, breaks and missing data need context.")}</li>
      <li>{t("Location gaps can occur when the app is closed or the phone suspends it. Missing data cannot establish where someone is.")}</li>
      <li>{t("Visit start means departure. The 1 km area is a radial proximity check, not road distance or proof of work at the customer.")}</li>
      <li>{t("Trip distance counts only observed movement after an accepted visit start, until visit end or check-out. Gaps and travel between visits are excluded.")}</li>
      <li>{t("When local routing is activated, an incomplete visit can use a driving-road estimate from its departure point to the saved customer pin. It is not added to that visit's GPS subtotal, and is not guaranteed to be the shortest road.")}</li>
    </ul></details>}
    <footer className="control-footer"><span>{t("Checks for new locations every 5 seconds while open. Background delivery is not guaranteed on iPhone or Android.")}</span><span>{wallboard ? t("Display mode · financial details hidden") : t("Map oversight is read-only; fuel payments require explicit founder confirmation")}</span></footer>
  </section>;
}

Object.assign(window, { ControlRoomPage });
