/* Progressive Web App chrome: the install banner (built by static/js/pwa.js)
 * and small tweaks for when HermesBot is launched as an installed, standalone app.
 * Loaded on both shells (base.html and base_page.html). Uses the --c-* theme
 * tokens from tailwind.css so it follows light/dark automatically.
 *
 * Reminder: this file is served immutable, so bump ?v= in BOTH base templates
 * whenever it changes. */

/* ── Install banner ───────────────────────────────────────────────────── */
#pwa-install-banner {
  position: fixed;
  top: calc(env(safe-area-inset-top, 0px) + 12px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 12px;
  width: calc(100% - 24px);
  max-width: 26rem;
  padding: 10px 12px;
  border-radius: 16px;
  background: rgb(var(--c-card));
  border: 1px solid rgb(var(--c-line));
  box-shadow: 0 12px 32px rgba(11, 31, 58, 0.22);
  animation: pwa-banner-in 0.25s ease-out both;
}

@keyframes pwa-banner-in {
  from { opacity: 0; transform: translate(-50%, -12px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}

#pwa-install-banner .pwa-install-icon {
  width: 40px;
  height: 40px;
  border-radius: 10px;
  flex: 0 0 auto;
}

#pwa-install-banner .pwa-install-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.25;
}
#pwa-install-banner .pwa-install-text strong {
  font-size: 0.9rem;
  color: rgb(var(--c-ink));
}
#pwa-install-banner .pwa-install-text span {
  font-size: 0.78rem;
  color: rgb(var(--c-muted));
}

#pwa-install-banner .pwa-install-yes {
  flex: 0 0 auto;
  background: rgb(var(--c-teal-500));
  color: #fff;
  border: 0;
  border-radius: 999px;
  padding: 0.45rem 0.95rem;
  font-size: 0.82rem;
  font-weight: 700;
  cursor: pointer;
}
#pwa-install-banner .pwa-install-yes:hover { background: rgb(var(--c-teal-600)); }

#pwa-install-banner .pwa-install-no {
  flex: 0 0 auto;
  background: transparent;
  border: 0;
  color: rgb(var(--c-muted));
  font-size: 1.35rem;
  line-height: 1;
  padding: 0 0.25rem;
  cursor: pointer;
}
#pwa-install-banner .pwa-install-no:hover { color: rgb(var(--c-ink)); }

@media (prefers-reduced-motion: reduce) {
  #pwa-install-banner { animation: none; }
}

/* ── Native feel ──────────────────────────────────────────────────────── */
/* viewport-fit=cover (head_common.html) puts the page under the notch and the
   home indicator, so each edge gives its inset back. All are 0 where there is
   none. Bottom insets belong to whatever sits at the bottom of a page. */
body {
  padding-top: env(safe-area-inset-top, 0px);
  padding-left: env(safe-area-inset-left, 0px);
  padding-right: env(safe-area-inset-right, 0px);
}
html .h-dvh { height: calc(100dvh - env(safe-area-inset-top, 0px)); }
/* On the rows, not the sidebar: its padding is forced to 0 on a phone. */
html body .workspace-brand { margin-top: env(safe-area-inset-top, 0px); }
html body :is(.workspace-brand, .workspace-nav) {
  margin-left: env(safe-area-inset-left, 0px);
  margin-right: env(safe-area-inset-right, 0px);
}
main:not(:has(#chat-messages))::after {
  content: '';
  display: block;
  flex: none;
  height: env(safe-area-inset-bottom, 0px);
}

html {
  -webkit-tap-highlight-color: transparent;
  -webkit-text-size-adjust: 100%;
}
a, button, label, select, summary, [role="button"] { touch-action: manipulation; }

@media (pointer: coarse) {
  /* iOS zooms the page into any field under 16px. */
  input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"], [type="color"]),
  select,
  textarea { font-size: max(16px, 1em) !important; }
}

.pwa-standalone, .pwa-standalone body { overscroll-behavior-y: none; }
.pwa-standalone :is(a, button, summary, label, [role="button"]):not([data-markdown] *) {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* Every browser, installed or not: the old page stays up until the new one can
   paint, with a short crossfade. swipe_nav.js skips the transition on a history
   traversal outside the installed app (the browser animates its own swipe). */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
  ::view-transition-old(root), ::view-transition-new(root) { animation-duration: 140ms; }
  /* the drawer, tab strips and same-page redirects: a short crossfade, no push */
  html:active-view-transition-type(fade)::view-transition-old(root),
  html:active-view-transition-type(fade)::view-transition-new(root) { animation-duration: 160ms; }
  /* a card morphing into its page header: the rest of the page just crossfades */
  html:active-view-transition-type(morph)::view-transition-old(root),
  html:active-view-transition-type(morph)::view-transition-new(root) { animation-duration: 240ms; }
  ::view-transition-group(hermesbot-shared) { animation-duration: .35s; animation-timing-function: cubic-bezier(.32, .72, 0, 1); }
  ::view-transition-old(hermesbot-shared), ::view-transition-new(hermesbot-shared) { width: 100%; height: 100%; object-fit: cover; overflow: clip; }
}

/* Directional page transitions, installed app only. swipe_nav.js adds ONE type
   to the transition (forward, back, fade, zoom, morph); with none, the
   crossfade above is what plays. Each rule is its own: a selector the browser
   cannot parse drops only that rule. */
@media (display-mode: standalone) and (prefers-reduced-motion: no-preference) {
  /* push: the new page slides over the old, which shifts left and dims */
  html:active-view-transition-type(forward)::view-transition-old(root) { animation: pwa-push-old .35s cubic-bezier(.32, .72, 0, 1) both; }
  html:active-view-transition-type(forward)::view-transition-new(root) { animation: pwa-push-new .35s cubic-bezier(.32, .72, 0, 1) both; box-shadow: -10px 0 28px rgb(0 0 0 / .16); z-index: 2; }
  /* pop: the old page slides off to the right, uncovering the one behind */
  html:active-view-transition-type(back)::view-transition-old(root) { animation: pwa-pop-old .35s cubic-bezier(.32, .72, 0, 1) both; box-shadow: -10px 0 28px rgb(0 0 0 / .16); z-index: 2; }
  html:active-view-transition-type(back)::view-transition-new(root) { animation: pwa-pop-new .35s cubic-bezier(.32, .72, 0, 1) both; }
  /* a launcher tile opens into its app */
  html:active-view-transition-type(zoom)::view-transition-old(root) { animation: pwa-fade-out .2s linear both; }
  html:active-view-transition-type(zoom)::view-transition-new(root) { animation: pwa-zoom-in .35s cubic-bezier(.32, .72, 0, 1) both; }
}
@keyframes pwa-push-old { to { transform: translateX(-25%); opacity: .6; } }
@keyframes pwa-push-new { from { transform: translateX(100%); } }
@keyframes pwa-pop-old { to { transform: translateX(100%); } }
@keyframes pwa-pop-new { from { transform: translateX(-25%); opacity: .6; } }
@keyframes pwa-zoom-in { from { transform: scale(.92); opacity: 0; } }
@keyframes pwa-fade-out { to { opacity: 0; } }

/* The iPhone edge swipe (swipe_nav.js): the page follows the finger over a
   dimmed backdrop. Added only for the length of a drag. */
.hermesbot-swiping {
  position: relative;
  z-index: 1;
  will-change: transform;
  box-shadow: -10px 0 28px rgb(0 0 0 / .18);
}
.hermesbot-swipe-dim {
  position: fixed;
  inset: 0;
  z-index: 0;
  background: #000;
  pointer-events: none;
}

/* ── Phone density ────────────────────────────────────────────────────── */
/* Every control's phone size, in one place. A component reads its token with
   its desktop size as the fallback (`min-height: var(--btn-h, 38px)`), so the
   tokens exist only below md and nothing changes on a desktop. */
@media (max-width: 767px) {
  :root {
    --btn-h: 32px;          /* text buttons */
    --field-h: 36px;        /* inputs and selects (16px text: iOS zooms below it) */
    --icon-btn: 34px;       /* round and square icon buttons */
    --seg-pad: 5px 12px;    /* a tab in a segmented strip */
    --phone-bar: 48px;      /* the row the floating menu button sits in */
  }
  /* Template buttons carry their size in Tailwind classes: a rounded element
     with text padding is a button, whatever its colour. Padding only, never a
     height, so a two-line link still fits. */
  :is(.workspace-shell, .admin-shell) :is(button, a, input[type=submit], input[type=button], label):is(.rounded-md, .rounded-lg, .rounded-xl, .rounded-full):is(.py-1\.5, .py-2, .py-2\.5, .py-3) {
    padding-block: 6px;
    line-height: 20px;
  }
  :is(.workspace-shell, .admin-shell) :is(button, a, input[type=submit], input[type=button], label):is(.rounded-md, .rounded-lg, .rounded-xl, .rounded-full):is(.px-4, .px-5, .px-6, .px-7):is(.py-1\.5, .py-2, .py-2\.5, .py-3) {
    padding-inline: 14px;
  }
  :is(.workspace-shell, .admin-shell) :is(button, a, input[type=submit], input[type=button], label):is(.rounded-md, .rounded-lg, .rounded-xl, .rounded-full):is(.text-sm, .text-base):is(.py-1\.5, .py-2, .py-2\.5, .py-3) {
    font-size: 13.5px;
  }
  :is(.workspace-shell, .admin-shell) main :is(input:not([type=checkbox], [type=radio], [type=range], [type=file], [type=color], [type=hidden]), select) {
    padding-block: 7px;
    line-height: 20px;
  }
  /* A select ignores line-height, so it takes the height itself. */
  :is(.workspace-shell, .admin-shell) main select:not([multiple], [size]) {
    height: var(--field-h);
    padding-block: 0;
  }
  .signin-form-panel :is(input, button) { padding-block: 7px; line-height: 20px; }
  :is(.workspace-shell, .admin-shell) main input[type=file]::file-selector-button {
    padding: 5px 12px;
    font-size: 13.5px;
  }
  /* Page content starts under the menu button's row, so it needs no top
     padding of its own. */
  main > .workspace-mobile-bar + * { padding-top: 6px; }
}

/* ── Native motion ────────────────────────────────────────────────────── */
@media (pointer: coarse) and (prefers-reduced-motion: no-preference) {
  /* Quick down, slower back. `scale` rather than `transform`, which some of
     these already use to position themselves. */
  :is(button, [role="button"], summary, .ws-tile, .hub-app, .cn-item, .rx-item, .ws-round, .session-item, .day-glance-tile, [data-tab-strip] > a) {
    transition-property: scale, opacity, transform, background-color, border-color, color, box-shadow;
    transition-duration: .28s;
    transition-timing-function: cubic-bezier(.32, .72, 0, 1);
  }
  :is(button, [role="button"], summary, .ws-tile, .hub-app, .cn-item, .rx-item, .ws-round, .session-item, .day-glance-tile, [data-tab-strip] > a):active:not(:disabled) {
    scale: .97;
    opacity: .75;
    transition-duration: .07s;
  }

  dialog[open] { animation: pwa-sheet-in .28s cubic-bezier(.32, .72, 0, 1); }
}
@keyframes pwa-sheet-in {
  from { opacity: 0; translate: 0 18px; }
  to   { opacity: 1; translate: 0 0; }
}

/* The phone drawer slides in; sidebar.js slides it out. */
@media (max-width: 767px) and (prefers-reduced-motion: no-preference) {
  :is(#app-sidebar:not(.admin-sidebar), #sessions-sidebar).sidebar-open {
    animation: pwa-drawer-in .3s cubic-bezier(.32, .72, 0, 1);
  }
}
@keyframes pwa-drawer-in {
  from { transform: translateX(-100%); }
  to   { transform: none; }
}

/* The chat page keeps still under the keyboard (chat_keyboard.js): nothing but
   the composer and the thread move. */
@media (pointer: coarse) {
  html:has(#chat-area), html:has(#chat-area) body { overflow: hidden; }
}
/* A touch screen shows no scroll bars, as a native app doesn't: the content
   moving under the finger is the indicator. style.css draws a custom 6px bar,
   and a styled bar never fades the way Android's own overlay one does. */
@media (pointer: coarse) {
  * { scrollbar-width: none; }
  ::-webkit-scrollbar { display: none; }
}
/* While they move: no blur to re-run each frame, and the header stays on top. */
html.theme-glass.kb-moving :is(#chat-form, #chat-nav) {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  transition: none;
}
html.theme-glass.kb-moving #chat-form { background: rgb(var(--c-card) / .96); }
html.kb-moving #chat-nav { position: relative; z-index: 30; background: rgb(var(--c-card)); }

/* Only an iPhone or iPad in a browser, where the install steps apply. */
html:not(.pwa-ios-browser) .pwa-ios-only { display: none; }
