/* ==========================================================================
   Mobile platform baseline — tap, scroll, safe areas, keyboard.
   --------------------------------------------------------------------------
   The handful of platform-level declarations that separate "an installed app"
   from "a website in a browser". Component styling belongs in
   design-system.css / components.css / responsive.css; nothing here paints.

   Loaded from partials/pwa_head.html (every page that has a head partial) and
   linked directly by the standalone status pages, so it is on every page.
   The shell-scoped rules below only fire where a shell opts in:

     html.app-shell      clinic (master_dashboard.html) + patient (patient/base.html)
     html.console-shell  SaaS admin (saas_admin_base.html)
     .app-topbar         the sticky top header of each of those shells

   The safe-area rules are inert (env() = 0px) on devices without a cutout, so
   they cost nothing on desktop or on flat-screen phones.
   ========================================================================== */

/* ── Tap ───────────────────────────────────────────────────────────────────
   iOS/Android paint a gray translucent flash over anything tappable that has
   a click handler. Every tap target below owns its :active state instead. */
html {
  -webkit-tap-highlight-color: transparent;
  -webkit-text-size-adjust: 100%; /* no font inflation when turned to landscape */
  text-size-adjust: 100%;
}

/* Controls, never content. Long-pressing a button must not select its label
   or raise the copy/share callout. Body text stays selectable on purpose —
   addresses, error messages and invoice numbers are things users copy. */
button,
[role="button"],
summary,
.tab,
.chip,
.drag-handle,
.app-tab,
.btn-tactile {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* A tap fires immediately: the browser stops waiting to see whether a second
   tap (double-tap-zoom) is coming. */
button,
a,
[role="button"],
summary,
label[for],
.tab,
.chip,
.app-tab,
.btn-tactile {
  touch-action: manipulation;
}

/* ── Keyboard + input zoom ─────────────────────────────────────────────────
   iOS Safari zooms the page when focus lands on an input under 16px and does
   not zoom back out on blur. 16px is the floor that prevents it — never
   "fix" this with maximum-scale or user-scalable=no, which takes pinch-zoom
   away from everyone. Touch pointers only, and never on controls that cannot
   be typed into (readonly previews, disabled fields, file pickers).
   .no-input-zoom opts an editable field out. */
@media (pointer: coarse) {
  input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([readonly]):not([disabled]):not(.no-input-zoom),
  textarea:not([readonly]):not([disabled]):not(.no-input-zoom),
  select:not([disabled]):not(.no-input-zoom) {
    font-size: 16px !important;
  }
}

/* ── Scroll ────────────────────────────────────────────────────────────────
   The app owns the top and bottom of the page: pull-to-refresh here is the
   in-app handler (static/js/pull-to-refresh.js) and the bottom bar is a
   control surface, so the browser's own refresh gesture and the whole-page
   rubber band are in the way. Public/marketing pages keep both. */
html.app-shell,
html.console-shell {
  overscroll-behavior: none;
}

/* Inner scrollers bounce (native) but must not chain their scroll to the page
   behind them when they hit the end. */
html.app-shell #sidebar-wrapper nav,
html.app-shell #patient-sidebar-wrapper nav,
html.console-shell #sidebar nav {
  overscroll-behavior: contain;
}

/* ── Safe areas ────────────────────────────────────────────────────────────
   viewport-fit=cover (viewport meta) lets the page paint under the notch,
   Dynamic Island and home indicator; these rules pad the chrome back out.
   Without that meta every env() below resolves to 0px. */

/* Sticky shell headers: the header background runs edge to edge, its content
   stays below the status bar. `html` is in the selector because utilities load
   after this file — the patient header also carries `py-2`, which would win a
   plain class-vs-class tie. */
html .app-topbar {
  padding-top: env(safe-area-inset-top, 0px);
}

/* Below 768px the clinic and patient shells hide their header (tablet:block),
   so the page content itself has to clear the status bar. The console shell
   keeps its header at every width and needs no content inset.
   The clinic's subscription banner is fixed at top-0 with its own inset padding
   and `--banner-h` is its full height (inset included), so `#app-main` already
   starts below the status bar while the banner is up: subtract it to avoid a
   second inset of dead space. */
@media (max-width: 767px) {
  html.app-shell #main-content {
    padding-top: max(0px, calc(env(safe-area-inset-top, 0px) - var(--banner-h, 0px)));
  }
}

/* Fixed side rails: the rail keeps its edge-to-edge background in landscape,
   its contents stay off the notch. The inner panel of each rail is h-dvh, so
   this padding sits inside its height instead of overflowing it. */
html.app-shell #sidebar-wrapper > aside,
html.app-shell #patient-sidebar-wrapper,
html.console-shell #sidebar {
  padding-top: env(safe-area-inset-top, 0px);
  padding-left: env(safe-area-inset-left, 0px);
}

/* 3D intraoral viewer geometry (top bar / canvas / floating panels) owns its
   own safe-area maths in the two intraoral_3d.html stylesheets: the canvas has
   a mobile-specific top offset there, and a rule from this file would outrank
   it. */

/* Hover-revealed actions (a row's edit/delete buttons) can never appear on a
   touch screen once the reveal is gated, so on those devices they are always
   visible. Mark the revealed container with .hover-reveal next to its
   `opacity-0 group-hover:opacity-100` pair. */
@media (hover: none) {
  html .hover-reveal {
    opacity: 1;
  }
}

/* Chart tool pages size the document with 100vh (the tallest viewport, chrome
   collapsed), so their bottom action bar hides under the URL bar. */
body.intraoral-2d {
  min-height: 100dvh;
}

/* Full-height drawers and sheets are their own top and bottom edges. The
   calendar drawer carries `pt-5` / `pb-4`, the two rail drawers `padding: 20px`;
   max() keeps those exact values where there is no cutout. Below 1024px only —
   at lg+ that section is the inline calendar column and `lg:pt-6` (24px) rules. */
@media (max-width: 1023px) {
  html #apptCalDrawer {
    padding-top: max(1.25rem, env(safe-area-inset-top, 0px));
    padding-bottom: max(1rem, env(safe-area-inset-bottom, 0px));
  }
}
html .cm-drawer,
html .wm-drawer {
  padding-top: max(20px, env(safe-area-inset-top, 0px));
  padding-bottom: max(20px, env(safe-area-inset-bottom, 0px));
}
html #detailDrawer,
html #contact-drawer,
html .comms-contact {
  padding-top: env(safe-area-inset-top, 0px);
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

/* The offline banner is the one fixed top-0 strip that never got the inset. */
html #offline-banner {
  padding-top: calc(0.5rem + env(safe-area-inset-top, 0px));
}
