/*
 * This is a manifest file that'll be compiled into application.css.
 *
 * With Propshaft, assets are served efficiently without preprocessing steps. You can still include
 * application-wide styles in this file, but keep in mind that CSS precedence will follow the standard
 * cascading order, meaning styles declared later in the document or manifest will override earlier ones,
 * depending on specificity.
 *
 * Consider organizing styles into separate files for maintainability.
 *
 */

/* iOS Safari zoom prevention - only on touch devices */
@media (pointer: coarse) {
  input,
  textarea {
    font-size: 16px;
  }
}

[x-cloak] {
  display: none !important;
}

.nl2br {
  white-space: pre-line;
}

/* Safe area padding for mobile navigation bars */
.pb-safe {
  padding-bottom: env(safe-area-inset-bottom);
}

/* Message highlight for jump-to navigation */
.message-highlight {
  animation: highlight-fade 2s ease-out forwards;
  margin-left: -0.75rem;
  margin-right: -0.75rem;
  padding-left: 0.75rem;
  padding-right: 0.75rem;
}

@media (min-width: 640px) {
  .message-highlight {
    margin-left: -1.25rem;
    margin-right: -1.25rem;
    padding-left: 1.25rem;
    padding-right: 1.25rem;
  }
}

@keyframes highlight-fade {
  0% {
    background-color: color-mix(
      in oklch,
      var(--color-primary) 15%,
      transparent
    );
  }
  80% {
    background-color: color-mix(
      in oklch,
      var(--color-primary) 15%,
      transparent
    );
  }
  100% {
    background-color: transparent;
  }
}

/* Reusable highlight animation utility */
.animate-highlight-fade {
  animation: highlight-fade 3s ease-out forwards;
  border-radius: 0.5rem;
}

/* Hide the AI usage notice while the message composer is engaged — but only
   on touch-primary devices. Mobile keyboards eat half the viewport, so the
   notice steals scarce real estate while typing. Desktop browsers have no
   such pressure and benefit from keeping the notice always visible.
   We key on `.ui-message-form:focus-within` (not the textarea's :focus) so
   focus moving to the send button does NOT collapse this rule mid-tap —
   that layout shift was causing send-button taps to miss on Android. */
@media (pointer: coarse) {
  body:has(.ui-message-form:focus-within) .c-messaging-chat-notice {
    display: none;
  }

  /* Without the notice the input would sit flush against the bottom; keep a
     small breathing room equal to the suggestion <-> input gap above. */
  body:has(.ui-message-form:focus-within) .ui-message-form {
    margin-bottom: 0.5rem;
  }
}

/* ===== Message suggestion chips ===== */
.ui-message-suggestions {
  position: relative;
}

/* Track — always anchored to the bottom; grows upward when expanded so the
   layout below it never moves. */
.ui-msg-suggest-track {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.25rem;
  border: 1px solid transparent;
  border-radius: 1.25rem;
  /* Padding intentionally NOT transitioned: it affects layout, which
     would interfere with the FLIP measurement during the morph. */
  transition:
    background-color 0.3s ease,
    border-color 0.3s ease,
    box-shadow 0.3s ease;
}

/* Track — expanded: floating frosted-glass panel */
.ui-msg-suggest-track.is-expanded {
  z-index: 30;
  /* Lift the panel so its visible bottom edge lines up with where the
     collapsed carousel's chips end (track + chips bottom padding), keeping
     the gap above the input identical in both states. */
  bottom: 0.375rem;
  gap: 0.4rem;
  padding: 0.4rem 0.5rem 0.5rem;
  border-radius: 1.5rem;
  background-color: color-mix(in oklch, var(--color-base-100) 80%, transparent);
  backdrop-filter: blur(14px) saturate(150%);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  /* Match the input field's elevation (shadow-md feel) so the panel reads
     as a sibling card to the composer, not a layer floating above it. */
  box-shadow:
    0 4px 6px -1px
      color-mix(in oklch, var(--color-base-content) 10%, transparent),
    0 2px 4px -2px
      color-mix(in oklch, var(--color-base-content) 10%, transparent);
}

/* Grabber handle — the tap target that toggles the panel */
.ui-msg-suggest-grabber {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.35rem 0;
  cursor: pointer;
}

.ui-msg-suggest-grabber::before {
  content: '';
  width: 2.25rem;
  height: 0.25rem;
  border-radius: 9999px;
  background-color: color-mix(
    in oklch,
    var(--color-base-content) 20%,
    transparent
  );
  transition:
    background-color 0.2s ease,
    width 0.2s ease;
}

/* Active (sheet open): the grabber reads as engaged. The existing
   transition on ::before animates the change between states.
   No hover variant — keeping hover identical to the inactive state avoids
   visual confusion with the active style, and sidesteps touch devices'
   sticky `:hover` after a tap. */
.ui-msg-suggest-track.is-expanded .ui-msg-suggest-grabber::before {
  width: 3rem;
  background-color: color-mix(
    in oklch,
    var(--color-base-content) 45%,
    transparent
  );
}

/* Chips area — collapsed: horizontal carousel; expanded: vertical list.
   No mask by default. The Stimulus controller adds `fade-left/right` (in
   carousel) or `fade-top/bottom` (in expanded list) only when there is
   actually off-screen content on that side, and those classes attach the
   matching mask gradient below. */
.ui-msg-suggest-chips {
  display: flex;
  flex-direction: row;
  gap: 0.375rem;
  min-width: 0;
  padding: 0.125rem 0;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
}

.ui-msg-suggest-chips::-webkit-scrollbar {
  display: none;
}

.is-expanded .ui-msg-suggest-chips {
  flex-direction: column;
  align-items: stretch;
  gap: 0.3rem;
  /* Cap whichever is smallest of:
     - 50dvh: aim for ~half the current usable viewport (dvh follows the
       mobile keyboard).
     - 100dvh - 8rem: leave roughly 8rem of breathing room for the textarea
       and form chrome below, so the panel never crowds the input even on
       small screens or when the textarea has expanded to multiple lines.
     - 22rem: hard ceiling so the panel never gets absurdly tall on
       desktop. */
  max-height: min(50dvh, calc(100dvh - 8rem), 22rem);
  overflow-x: hidden;
  overflow-y: auto;
}

/* Horizontal edge fade — applied only when a side actually overflows. */
.ui-msg-suggest-chips.fade-left.fade-right {
  mask-image: linear-gradient(
    to right,
    transparent 0,
    black 1.5rem,
    black calc(100% - 1.5rem),
    transparent 100%
  );
  -webkit-mask-image: linear-gradient(
    to right,
    transparent 0,
    black 1.5rem,
    black calc(100% - 1.5rem),
    transparent 100%
  );
}

.ui-msg-suggest-chips.fade-left:not(.fade-right) {
  mask-image: linear-gradient(
    to right,
    transparent 0,
    black 1.5rem,
    black 100%
  );
  -webkit-mask-image: linear-gradient(
    to right,
    transparent 0,
    black 1.5rem,
    black 100%
  );
}

.ui-msg-suggest-chips.fade-right:not(.fade-left) {
  mask-image: linear-gradient(
    to right,
    black 0,
    black calc(100% - 1.5rem),
    transparent 100%
  );
  -webkit-mask-image: linear-gradient(
    to right,
    black 0,
    black calc(100% - 1.5rem),
    transparent 100%
  );
}

/* Vertical edge fade — applied only when a side actually overflows. */
.ui-msg-suggest-chips.fade-top.fade-bottom {
  mask-image: linear-gradient(
    to bottom,
    transparent 0,
    black 1.25rem,
    black calc(100% - 1.25rem),
    transparent 100%
  );
  -webkit-mask-image: linear-gradient(
    to bottom,
    transparent 0,
    black 1.25rem,
    black calc(100% - 1.25rem),
    transparent 100%
  );
}

.ui-msg-suggest-chips.fade-top:not(.fade-bottom) {
  mask-image: linear-gradient(
    to bottom,
    transparent 0,
    black 1.25rem,
    black 100%
  );
  -webkit-mask-image: linear-gradient(
    to bottom,
    transparent 0,
    black 1.25rem,
    black 100%
  );
}

.ui-msg-suggest-chips.fade-bottom:not(.fade-top) {
  mask-image: linear-gradient(
    to bottom,
    black 0,
    black calc(100% - 1.25rem),
    transparent 100%
  );
  -webkit-mask-image: linear-gradient(
    to bottom,
    black 0,
    black calc(100% - 1.25rem),
    transparent 100%
  );
}

/* In the expanded list each chip stretches to the panel width, the text
   reads left-aligned, and long suggestions WRAP onto multiple lines so
   they're readable on narrow mobile widths instead of being ellipsis-cut.
   The 16rem span cap and the rounded-full pill shape only fit the
   collapsed carousel; relax both for the expanded list-row look. */
.is-expanded .ui-msg-suggest-chip {
  text-align: left;
  white-space: normal;
  line-height: 1.45;
  border-radius: 1.1rem;
  padding: 0.55rem 0.9rem;
}

.is-expanded .ui-msg-suggest-chip > span {
  max-width: 100%;
  overflow: visible;
  white-space: normal;
  text-overflow: clip;
}

/* Chip — identical in both states, so only its position morphs */
.ui-msg-suggest-chip {
  flex: 0 0 auto;
  max-width: 100%;
  cursor: pointer;
  white-space: nowrap;
  border-radius: 9999px;
  border: 1px solid
    color-mix(in oklch, var(--color-base-content) 18%, transparent);
  background-color: color-mix(in oklch, var(--color-base-100) 55%, transparent);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  color: color-mix(in oklch, var(--color-base-content) 88%, transparent);
  font-size: 0.78rem;
  line-height: 1.35;
  padding: 0.4rem 0.9rem;
  transition:
    transform 0.16s cubic-bezier(0.2, 0.8, 0.2, 1),
    background-color 0.18s ease,
    border-color 0.18s ease,
    box-shadow 0.18s ease,
    color 0.18s ease;
}

.ui-msg-suggest-chip > span {
  display: block;
  max-width: 16rem;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ui-msg-suggest-chip:hover {
  border-color: color-mix(in oklch, var(--color-primary) 55%, transparent);
  background-color: color-mix(
    in oklch,
    var(--color-primary) 22%,
    color-mix(in oklch, var(--color-base-100) 60%, transparent)
  );
  color: var(--color-base-content);
}

.ui-msg-suggest-chip:active {
  transform: scale(0.95);
  transition-duration: 0.08s;
}

/* Staggered entrance for freshly generated suggestions */
@keyframes msg-suggest-chip-in {
  from {
    opacity: 0;
    transform: translateY(7px) scale(0.95);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

@media (prefers-reduced-motion: no-preference) {
  .ui-msg-suggest-chip {
    animation: msg-suggest-chip-in 0.34s cubic-bezier(0.2, 0.85, 0.25, 1)
      backwards;
  }
}

/* Offering reveal: full blur veil fades out after streaming completes */
@keyframes offering-reveal {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

.animate-offering-reveal {
  animation: offering-reveal 700ms ease-out 200ms forwards;
}

/* Attention shake: subtle validation-error style nudge to draw the eye to a step card */
@keyframes attention-shake {
  0%,
  100% {
    transform: translateX(0);
  }
  20%,
  60% {
    transform: translateX(-4px);
  }
  40%,
  80% {
    transform: translateX(4px);
  }
}

.animate-attention-shake {
  animation: attention-shake 0.4s ease-in-out;
}

/* Hide messages until JS resolves chat direction (chat-start/chat-end) to prevent layout shift.
   Only applies during initial load; removed by JS after processing so broadcast messages are unaffected. */
[data-direction-pending] [data-message-id] .chat {
  opacity: 0;
}

/* ---------------------------------------------------------------------------
 * Push notification toasts (macOS-style).
 * Separate visual language from flash messages (_flash_messages.html.erb).
 * Flash = user action feedback. Push toast = async event notifications.
 * ------------------------------------------------------------------------- */
.push-toast-stack {
  position: fixed;
  top: max(1rem, env(safe-area-inset-top));
  right: max(1rem, env(safe-area-inset-right));
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  width: min(22rem, calc(100vw - 2rem));
  pointer-events: none;
}

.push-toast {
  pointer-events: auto;
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 0.75rem 0.875rem;
  border-radius: 1rem;
  background-color: color-mix(in oklch, var(--color-base-100) 82%, transparent);
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  box-shadow:
    0 8px 24px -8px color-mix(in oklch, oklch(0 0 0) 25%, transparent),
    0 2px 6px -2px color-mix(in oklch, oklch(0 0 0) 15%, transparent);
  color: var(--color-base-content);
  cursor: default;
  transform: translateX(120%);
  opacity: 0;
  transition:
    transform 300ms cubic-bezier(0.22, 1, 0.36, 1),
    opacity 220ms ease-out;
  will-change: transform, opacity;
  touch-action: pan-y;
}

.push-toast[data-url] {
  cursor: pointer;
}

.push-toast[data-state='visible'] {
  transform: translateX(0);
  opacity: 1;
}

.push-toast[data-state='leaving'] {
  transform: translateX(120%);
  opacity: 0;
  transition:
    transform 240ms cubic-bezier(0.4, 0, 1, 1),
    opacity 200ms ease-in;
}

.push-toast[data-state='leaving'][data-exit-direction='left'] {
  transform: translateX(-120%);
}

/* While user is actively dragging, disable the transition so the toast
   follows the pointer 1:1. Also hint browsers to avoid scroll gestures. */
.push-toast[data-dragging='true'] {
  transition: none;
  cursor: grabbing;
  touch-action: pan-y;
  user-select: none;
}

.push-toast__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: 0.625rem;
  background-color: color-mix(in oklch, var(--color-primary) 15%, transparent);
  color: var(--color-primary);
  flex-shrink: 0;
}

.push-toast__content {
  flex: 1;
  min-width: 0;
}

.push-toast__header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}

.push-toast__title {
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.3;
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.push-toast__time {
  font-size: 0.6875rem;
  line-height: 1;
  color: color-mix(in oklch, var(--color-base-content) 55%, transparent);
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
}

.push-toast__body {
  margin: 0.125rem 0 0;
  font-size: 0.75rem;
  line-height: 1.4;
  color: color-mix(in oklch, var(--color-base-content) 75%, transparent);
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.push-toast__close {
  position: absolute;
  top: -0.375rem;
  left: -0.375rem;
  width: 1.125rem;
  height: 1.125rem;
  border-radius: 999px;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--color-base-content);
  color: var(--color-base-100);
  border: 2px solid var(--color-base-100);
  padding: 0;
  cursor: pointer;
  opacity: 0;
  transform: scale(0.8);
  transition:
    opacity 150ms ease,
    transform 150ms ease,
    background-color 150ms ease;
  box-shadow: 0 1px 3px color-mix(in oklch, oklch(0 0 0) 20%, transparent);
}

.push-toast:hover .push-toast__close,
.push-toast:focus-within .push-toast__close {
  opacity: 1;
  transform: scale(1);
}

/* On touch-primary devices, no hover is available — keep the close button
   always visible so users have an explicit way to dismiss. Swipe-to-dismiss
   still works as a secondary gesture. */
@media (hover: none) {
  .push-toast__close {
    opacity: 1;
    transform: scale(1);
  }
}

.push-toast__close:hover {
  background-color: color-mix(
    in oklch,
    var(--color-base-content) 80%,
    transparent
  );
}

.push-toast__close svg {
  width: 0.625rem;
  height: 0.625rem;
}

@media (max-width: 640px) {
  .push-toast-stack {
    left: max(0.5rem, env(safe-area-inset-left));
    right: max(0.5rem, env(safe-area-inset-right));
    width: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  .push-toast {
    transition: opacity 150ms ease-out;
    transform: translateX(0);
  }
  .push-toast[data-state='leaving'] {
    transform: translateX(0);
  }
}
