/* XO Tours — native / installed-app layout adjustments
 *
 * Loaded on every page, but every rule is scoped to a class that only
 * js/app-bridge.js adds:
 *
 *   .xo-native      running inside the Capacitor or desktop shell
 *   .xo-standalone  the above, plus an installed PWA (no browser chrome)
 *
 * So this file is inert in a normal browser tab — which is the overwhelming
 * majority of traffic — and does not need its own media queries to stay that
 * way. Kept as a separate stylesheet rather than bolted onto the 6k-line
 * assets/css/core.mobile.css so the app-specific rules stay reviewable.
 *
 * The --xo-safe-* custom properties are defined by js/app-bridge.js; the
 * env() fallbacks here mean the file still behaves correctly if that script
 * fails to run.
 */

/* ---------------------------------------------------------------- viewport
   Without browser chrome there is nothing between the page and the status bar
   / home indicator, so the page has to inset itself. The middleware adds
   viewport-fit=cover, which is what makes env() non-zero in the first place. */

/* No bottom inset here on purpose: the bottom nav is fixed-position and takes
   its own, below. Adding it to body as well would double the gap. */
.xo-standalone body {
  padding-top: var(--xo-safe-top, env(safe-area-inset-top, 0px));
  /* Landscape on a notched phone insets sideways too. */
  padding-left: var(--xo-safe-left, env(safe-area-inset-left, 0px));
  padding-right: var(--xo-safe-right, env(safe-area-inset-right, 0px));
}

/* The sticky header must sit below the status bar, not under it. Padding
   rather than margin so its background still paints into the inset area —
   otherwise a transparent strip shows the page scrolling underneath. */
.xo-standalone .row.header,
.xo-standalone nav.navbar {
  padding-top: var(--xo-safe-top, env(safe-area-inset-top, 0px));
}

.xo-standalone #mobile-bottom-nav,
.xo-standalone .mobile-bottom-nav {
  padding-bottom: var(--xo-safe-bottom, env(safe-area-inset-bottom, 0px));
  box-sizing: content-box;
}

/* Anything pinned to the bottom (cart bar, chat launcher) has to clear the
   home indicator as well. */
.xo-standalone .fixed-bottom,
.xo-standalone #xo-chat-launcher {
  bottom: var(--xo-safe-bottom, env(safe-area-inset-bottom, 0px));
}

/* --------------------------------------------------------------- app feel
   Small things that separate "a website in a window" from "an app", and that
   Apple reviewers notice. */

.xo-native {
  /* Kill the iOS rubber-band overscroll that reveals a blank strip above the
     header and immediately reads as a wrapped webview. */
  overscroll-behavior-y: none;
}

.xo-native body {
  /* Long-press on any non-input element pops the iOS text-selection callout.
     Re-enabled for real content below. */
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  /* Grey flash on every tap is a browser default, not an app one. */
  -webkit-tap-highlight-color: transparent;
}

/* Text the visitor may legitimately want to copy — itinerary details, prices,
   confirmation numbers — must stay selectable. */
.xo-native p,
.xo-native h1, .xo-native h2, .xo-native h3, .xo-native h4,
.xo-native li, .xo-native td, .xo-native span,
.xo-native input, .xo-native textarea, .xo-native [contenteditable] {
  -webkit-touch-callout: default;
  -webkit-user-select: text;
  user-select: text;
}

/* Hide web-only chrome that makes no sense once installed: install prompts,
   "open in app" interstitials and store badges. */
.xo-native .xo-web-only,
.xo-standalone .xo-web-only,
.xo-native .app-install-banner,
.xo-native .app-store-badges {
  display: none !important;
}

/* iOS zooms the page when focusing an input under 16px and never zooms back,
   leaving the layout stranded at the zoomed scale.
 *
 * !important is load-bearing here, not laziness. MOBILE_INLINE_CSS in
 * functions/_middleware.js ships
 *     @media (max-width: 768px) { .standalone-search-filter { font-size: 13px !important } }
 * and that media query is always true on a phone — exactly where the zoom trap
 * happens. Only !important can beat !important; the .xo-native prefix then wins
 * on specificity, and confines this to the shells so no browser visitor is
 * affected. Verified: without it the search box computes to 13px. */
.xo-native input,
.xo-native select,
.xo-native textarea {
  font-size: max(16px, 1rem) !important;
}

/* Touch targets below ~44px fail Apple's HIG and are a common review note. */
.xo-native button,
.xo-native .btn,
.xo-native a.btn {
  min-height: 44px;
}

/* ------------------------------------------------------ biometric app lock
   Built by js/app-lock.js. Unscoped by .xo-native on purpose: the element only
   ever exists in a native shell, and scoping it would mean a rule that fails
   silently if the class were ever removed — the one place where failing open
   shows a member's booking history to whoever is holding the phone. */

#xo-app-lock {
  position: fixed;
  inset: 0;
  z-index: 2147483647;          /* above the site's own splash (99999) */
  background: #34808A;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: max(24px, env(safe-area-inset-top)) 24px max(24px, env(safe-area-inset-bottom));
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang TC",
               "Microsoft JhengHei", "Noto Sans TC", Roboto, sans-serif;
}

/* Belt and braces: while locked the page behind must not scroll, and must not
   be reachable by touch even if the overlay were somehow made transparent. */
html.xo-locked,
html.xo-locked body {
  overflow: hidden !important;
  touch-action: none !important;
}

#xo-app-lock .xo-lock-inner { max-width: 22rem; width: 100%; }

#xo-app-lock .xo-lock-mark {
  width: 78px; height: 78px; margin: 0 auto 24px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, .14);
  border-radius: 22px;
}

#xo-app-lock h2 {
  font-size: 1.35rem; font-weight: 700; margin: 0 0 .6rem;
}

#xo-app-lock p {
  font-size: .95rem; line-height: 1.6; margin: 0 0 1.9rem;
  color: rgba(255, 255, 255, .87);
}

#xo-app-lock h2 span,
#xo-app-lock p span {
  display: block; font-size: .8rem; margin-top: .25rem;
  color: rgba(255, 255, 255, .68); font-weight: 400;
}

#xo-app-lock button {
  font: inherit; font-weight: 700; cursor: pointer;
  background: #fff; color: #34808A; border: 0;
  border-radius: 10px; padding: .85rem 2.4rem; min-height: 48px;
}

#xo-app-lock button:active { opacity: .78; }
#xo-app-lock button:focus-visible {
  outline: 3px solid rgba(255, 255, 255, .65); outline-offset: 3px;
}

/* -------------------------------------------------- faux fullscreen (maps)
   Used by js/fullscreen-bridge.js when the real Element Fullscreen API is
   unavailable — which is the default in iOS WKWebView. Unscoped by .xo-native
   for the same reason as the lock screen: the class is only ever applied by
   that shim, and scoping it would mean the map silently stays letterboxed if
   the platform class were ever missing. */

.xo-faux-fullscreen {
  position: fixed !important;
  inset: 0 !important;
  width: 100vw !important;
  height: 100vh !important;
  /* Below the app lock (max int) and the settings sheet, above page chrome. */
  z-index: 2147481000 !important;
  margin: 0 !important;
  border-radius: 0 !important;
  background: #000;
}

/* The page behind must not scroll while a map is filling the screen. */
html.xo-faux-fullscreen-active,
html.xo-faux-fullscreen-active body {
  overflow: hidden !important;
}

/* Leaflet/MapLibre size their canvas to the container, so it has to fill. */
.xo-faux-fullscreen canvas,
.xo-faux-fullscreen .leaflet-container,
.xo-faux-fullscreen .maplibregl-canvas-container {
  width: 100% !important;
  height: 100% !important;
}

/* -------------------------------------------------------- desktop shell
   Tauri gives a real window; the mobile-first bottom nav is redundant there
   and the safe-area insets are always zero. */

.xo-platform-desktop #mobile-bottom-nav,
.xo-platform-desktop .mobile-bottom-nav {
  display: none;
}
