/* The phone-app layer.
 *
 * Loaded after app.css, and it only ever adds. Nothing here changes what the
 * app is, it changes how it behaves under a thumb.
 *
 * ON MATERIAL AND CORNERS
 * This takes Material's structure: a real app bar that lifts when the page
 * moves under it, a bottom bar with a moving indicator, elevation that means
 * depth rather than decoration, a ripple on touch, and its motion curves. It
 * does NOT take Material's rounded corners, because square corners are a
 * standing rule for this app and they are most of what makes it look like
 * itself rather than like every other template. Square and Material-shaped is
 * a deliberate combination, not an oversight.
 *
 * Everything below is scoped so the admin area is untouched: that is a desk
 * tool, not a phone app, and it opts out with body.no-app.
 */

:root {
  /* Material's standard easing. Fast out, slow in: things leave briskly and
     arrive gently, which is what makes motion read as physical. */
  --ease-standard: cubic-bezier(.2, 0, 0, 1);
  --ease-decel: cubic-bezier(0, 0, 0, 1);
  --ease-accel: cubic-bezier(.3, 0, 1, 1);

  /* Elevation. Three steps is enough for an app this shape: resting, lifted,
     and floating above everything. */
  --elev-1: 0 1px 2px rgba(60, 40, 25, .08), 0 1px 3px rgba(60, 40, 25, .06);
  --elev-2: 0 2px 6px rgba(60, 40, 25, .10), 0 1px 2px rgba(60, 40, 25, .08);
  --elev-3: 0 6px 16px rgba(60, 40, 25, .14), 0 2px 6px rgba(60, 40, 25, .10);

  --tap: 48px;              /* the smallest thing a thumb should have to hit */
}

/* --- feels like an app, not a web page ---------------------------------- */

body:not(.no-app) {
  /* No blue flash on tap, and no accidental text selection when somebody
     drags a list. Inputs get selection back below, because taking it away
     there would be hostile. */
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
  /* Stops the whole page bouncing at the edges on iOS, which is the single
     biggest giveaway that something is a web page in a browser. */
  overscroll-behavior: none;
  /* Text stays put when the keyboard opens and the viewport resizes. */
  text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
}

body:not(.no-app) input,
body:not(.no-app) textarea,
body:not(.no-app) select,
body:not(.no-app) .selectable,
body:not(.no-app) .msg,
body:not(.no-app) .bio,
body:not(.no-app) p {
  -webkit-user-select: text;
  user-select: text;
}

/* --- safe areas: notches, home indicators, curved corners --------------- */

body:not(.no-app) .site-header {
  padding-top: env(safe-area-inset-top, 0px);
  padding-left: max(14px, env(safe-area-inset-left, 0px));
  padding-right: max(14px, env(safe-area-inset-right, 0px));
}

body:not(.no-app) .tabbar {
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

body:not(.no-app) .viewport {
  padding-left: env(safe-area-inset-left, 0px);
  padding-right: env(safe-area-inset-right, 0px);
}

/* --- the app bar --------------------------------------------------------
   Flat while the page is at the top, lifted once content has scrolled under
   it. That shadow is the only thing that tells you the bar is a separate
   surface and not part of the page. */

body:not(.no-app) .site-header {
  transition: box-shadow .2s var(--ease-standard),
              background-color .2s var(--ease-standard);
  box-shadow: none;
  z-index: 40;
}

body:not(.no-app) .site-header.lifted {
  box-shadow: var(--elev-2);
}

/* --- the bottom bar -----------------------------------------------------
   Material's navigation bar: a moving indicator behind the active item, and
   targets big enough to hit without looking. */

body:not(.no-app) .tabbar {
  position: relative;
  box-shadow: 0 -1px 0 var(--line);
}

body:not(.no-app) .tabbar a {
  min-height: var(--tap);
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 3px;
  transition: color .18s var(--ease-standard);
  overflow: hidden;              /* keeps the ripple inside the target */
}

body:not(.no-app) .tabbar a .ico {
  position: relative;
  display: grid; place-items: center;
  width: 44px; height: 26px;
  transition: transform .22s var(--ease-decel);
  isolation: isolate;            /* so the indicator can sit behind the glyph */
}

/* The indicator. It sits behind the icon rather than under the label, which
   is what makes the active item read as selected rather than underlined.
   z-index -1 inside the isolated .ico: a positioned ::before otherwise paints
   OVER the glyph, which is how the active icon used to vanish. */
body:not(.no-app) .tabbar a .ico::before {
  content: "";
  position: absolute; inset: 0;
  z-index: -1;
  background: var(--accent-soft);
  transform: scaleX(0);
  transition: transform .24s var(--ease-decel);
}

body:not(.no-app) .tabbar a.on .ico::before { transform: scaleX(1); }
body:not(.no-app) .tabbar a.on .ico { transform: translateY(-1px); }
body:not(.no-app) .tabbar a.on { color: var(--accent); }

body:not(.no-app) .tabbar a > span:last-child {
  font-size: .68rem; letter-spacing: .02em;
}

/* --- ripple -------------------------------------------------------------
   A press that leaves a mark. Driven from JS because the ripple has to start
   where the finger landed, which CSS alone cannot know. */

body:not(.no-app) .btn,
body:not(.no-app) .tabbar a,
body:not(.no-app) .list-item,
body:not(.no-app) .you-tile,
body:not(.no-app) .group {
  position: relative;
  overflow: hidden;
  -webkit-touch-callout: none;
}

.ripple {
  position: absolute;
  border-radius: 50%;         /* the one circle in the app, and it is a wave */
  transform: scale(0);
  background: currentColor;
  opacity: .18;
  pointer-events: none;
  animation: ripple .45s var(--ease-decel) forwards;
}

@keyframes ripple {
  to { transform: scale(2.4); opacity: 0; }
}

/* --- touch targets ------------------------------------------------------ */

body:not(.no-app) .btn {
  min-height: var(--tap);
  transition: transform .12s var(--ease-standard),
              box-shadow .18s var(--ease-standard);
}
body:not(.no-app) .btn.small { min-height: 36px; }
body:not(.no-app) .btn:active { transform: scale(.985); }

body:not(.no-app) .btn.primary { box-shadow: var(--elev-1); }
body:not(.no-app) .btn.primary:active { box-shadow: none; }

/* Inputs at 16px, because anything smaller makes iOS zoom the whole page in
   when the field is focused, and it never zooms back out. */
body:not(.no-app) input,
body:not(.no-app) select,
body:not(.no-app) textarea {
  font-size: 16px;
  min-height: var(--tap);
}
body:not(.no-app) textarea { min-height: 88px; }

/* --- surfaces ----------------------------------------------------------- */

body:not(.no-app) .card { box-shadow: var(--elev-1); }
body:not(.no-app) .profile-card { box-shadow: var(--elev-2); }
body:not(.no-app) .list-item {
  transition: background-color .15s var(--ease-standard);
}

/* --- page arrival -------------------------------------------------------
   A short rise on the content of each screen. Enough to read as a transition
   between places, not enough to wait for. */

body:not(.no-app) .shell > * {
  animation: page-in .26s var(--ease-decel) both;
}

@keyframes page-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}

/* --- installed, rather than in a browser tab ---------------------------- */

body.standalone .site-header {
  /* No browser chrome above us, so the bar carries the status bar area. */
  padding-top: max(env(safe-area-inset-top, 0px), 8px);
}

/* The install invitation.
   Shown once, dismissible, never nagging, and at the BOTTOM. At the top it
   pushed the first card down and was the first thing anybody saw on opening
   the app, which is a poor trade for a suggestion nobody asked for. Down
   here it sits above the nav like a snackbar, in reach of a thumb and out of
   the way of the content. */
.install-bar {
  position: fixed; z-index: 55;
  left: 0; right: 0;
  bottom: calc(var(--tab-h) + var(--footer-h) + env(safe-area-inset-bottom, 0px));
  display: flex; align-items: center; gap: 10px;
  padding: 12px 14px;
  background: var(--surface);
  border-top: 2px solid var(--accent);
  box-shadow: var(--elev-3);
  animation: install-up .3s var(--ease-decel) both;
}

.install-bar .grow { min-width: 0; flex: 1; }
.install-bar strong { display: block; font-size: .9rem; line-height: 1.25; }
.install-bar span { font-size: .76rem; color: var(--muted); line-height: 1.35; }
.install-bar .btn { min-height: 40px; padding: 9px 14px; flex: none; }

@keyframes install-up {
  from { opacity: 0; transform: translateY(100%); }
  to { opacity: 1; transform: translateY(0); }
}

/* Inside the centred app column on a wide screen, rather than stretched
   across a desktop monitor. */
@media (min-width: 760px) {
  .install-bar {
    left: 50%; transform: translateX(-50%);
    width: 100%; max-width: var(--app-w);
  }
  @keyframes install-up {
    from { opacity: 0; transform: translate(-50%, 100%); }
    to { opacity: 1; transform: translate(-50%, 0); }
  }
}

/* The offline bar and the install bar both live at the bottom, so the
   offline one moves up out of the way when they are both on screen. */
body:has(.install-bar) .net-bar {
  bottom: calc(var(--tab-h) + var(--footer-h) + 68px);
}

/* --- offline ------------------------------------------------------------ */

.net-bar {
  position: fixed; left: 0; right: 0; z-index: 60;
  bottom: calc(var(--tab-h) + var(--footer-h));
  padding: 8px 14px; text-align: center;
  background: var(--ink, #2d1a1c); color: #fff;
  font-size: .8rem; font-weight: 600;
  transform: translateY(120%);
  transition: transform .26s var(--ease-decel);
}
.net-bar.show { transform: translateY(0); }

/* --- less motion if that is what they asked for ------------------------- */

@media (prefers-reduced-motion: reduce) {
  body:not(.no-app) .shell > *,
  .install-bar { animation: none; }
  .ripple { display: none; }
  body:not(.no-app) .tabbar a .ico,
  body:not(.no-app) .tabbar a .ico::before,
  body:not(.no-app) .btn { transition: none; }
}
