/* This file is for your main application CSS */

/* ── iOS Safari viewport fixes ──────────────────────────────────────────────
   1. overflow-x: clip  — prevents the horizontal "jiggle" where fixed/absolute
      elements that slightly overshoot the viewport make the document scrollable
      side-to-side.  "clip" is used instead of "hidden" because it does NOT
      create a new stacking context, so position:fixed elements still work.
   2. overscroll-behavior: none  — disables iOS rubber-band / elastic bounce on
      the outer document.  Without this, users can drag the page beyond its
      edges, making it look like content extends off-screen.
   Both properties are no-ops on Android/desktop (which don't exhibit these
   behaviours), so they're safe to apply globally. */
html, body {
  overflow-x: clip;
  overscroll-behavior: none;
}

/* ── Mobile WebKit / iOS: LiveView phx-click relies on synthesized "click" events.
   Safari requires elements (or body) to look "interactive" (#168, PR #315). */
.hopara-app-shell {
  display: block;
  min-height: 100%;
}

[phx-click],
[phx-click-away],
[phx-submit],
button,
[type="submit"],
[type="button"],
a[href] {
  cursor: pointer;
  -webkit-tap-highlight-color: rgba(13, 35, 56, 0.15);
}

/* Don't fight text fields */
input,
textarea {
  cursor: text;
  -webkit-tap-highlight-color: rgba(13, 35, 56, 0.08);
}

/* ── Practice-word tap feedback ──────────────────────────────────────────────
   LiveView adds `phx-click-loading` to the tapped element IMMEDIATELY on click,
   before the server round-trip.  Users on slow connections (e.g. Spain → Chicago)
   previously saw nothing for 200-400 ms and tapped again thinking the first tap
   was missed.  The scale + fade below makes the response feel instant. */
span[phx-click="tap_japanese"],
button[phx-click="tap_and_practice"] {
  transition: opacity 0.12s ease, transform 0.12s ease;
}

span[phx-click="tap_japanese"].phx-click-loading,
button[phx-click="tap_and_practice"].phx-click-loading {
  opacity: 0.5;
  transform: scale(0.93);
}

/* ── Tab panels ──────────────────────────────────────────────────────────────
   All panels live in the DOM; CSS controls which one is visible.
   `tab-panel--active` is set by both the server (@tab assign) and instantly
   by JS.add_class/remove_class on click — no show/hide race conditions. */
.tab-panel {
  display: none;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  max-width: 600px;
  width: 100%;
  margin: 0 auto;
  padding: 16px 16px 0;
}

/* !important overrides the inline style="display:none" set by the server on
   inactive panels — ensures the panel is visible even if CSS loads late. */
.tab-panel.tab-panel--active {
  display: block !important;
}

/* Today's chat tab is a flex column so the message list scrolls correctly */
.tab-panel-today {
  padding: 0;
  flex-direction: column;
  overflow: hidden;
}

.tab-panel-today.tab-panel--active {
  display: flex !important;
}

/* ── Tab bar active/inactive states ─────────────────────────────────────────
   Controlled by JS.add_class/remove_class on click so the highlight is instant.
   The server also sets the class on initial render and page reload via @tab. */
.tab-btn {
  border: none;
  border-bottom: 3px solid transparent;
  /* !important beats the browser's built-in ButtonText color on Android/iOS */
  color: rgba(255, 255, 255, 0.5) !important;
}
.tab-btn.tab-btn-active {
  border-bottom: 3px solid #1FA1B0;
  color: #B2E4EA !important;
}

/* ── Shared typing-dots animation used by the NPC "thinking" indicator ──────
   Defined globally so it is available both in the chat tab and in the
   practice bottom-sheet (which renders outside the tab content). */
@keyframes typing {
  0%, 60%, 100% { opacity: 0.3; transform: translateY(0); }
  30% { opacity: 1; transform: translateY(-4px); }
}

/* ── LiveView toast pill (bottom-center) — slide + fade in ───────────────
   Used by `<div phx-hook="LiveToast">` in JourneyLive. The hook
   auto-dismisses by clearing the `@toast` assign after ~2.8s. */
@keyframes live-toast-in {
  from { opacity: 0; transform: translateX(-50%) translateY(10px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

/* ── Scene bar animations ────────────────────────────────────────────────
   All animations here are CSS-only so they survive when Lottie is loading,
   when the Lottie asset is missing, and when we ship as a thin iOS / Android
   wrapper (WebKit / Chromium both hardware-accelerate transform + opacity). */

/* Slow diagonal sheen across the scene bar — tiny bit of "alive" feel even
   when no Lottie is loaded. GPU-accelerated (transform only). */
@keyframes scene-sheen {
  0%   { transform: translateX(-60%); }
  100% { transform: translateX(160%); }
}
.scene-bar-sheen {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
}
.scene-bar-sheen::before {
  content: "";
  position: absolute;
  top: 0; bottom: 0;
  left: 0;
  width: 40%;
  background: linear-gradient(
    100deg,
    transparent 0%,
    rgba(255, 255, 255, 0.07) 45%,
    rgba(255, 255, 255, 0.14) 50%,
    rgba(255, 255, 255, 0.07) 55%,
    transparent 100%
  );
  transform: translateX(-60%);
  animation: scene-sheen 7s linear infinite;
}

/* Gentle pulse on the active scene segment in the progress track. */
@keyframes scene-pulse {
  0%, 100% { opacity: 0.9; box-shadow: 0 0 0 0 rgba(255,255,255,0.55); }
  50%      { opacity: 1;   box-shadow: 0 0 6px 1px rgba(255,255,255,0.75); }
}
.scene-dot-active {
  animation: scene-pulse 1.8s ease-in-out infinite;
}

/* Subtle bob for the fallback 🎬 emoji so the thumbnail isn't static when
   a Lottie is missing. */
@keyframes scene-bob {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50%      { transform: translateY(-2px) rotate(2deg); }
}
.scene-thumb-fallback {
  animation: scene-bob 2.4s ease-in-out infinite;
}

/* ═══════════════════════════════════════════════════════════════════════
   COSMETICS — CHAT BUBBLE EFFECTS, BORDERS, SHAPES, AVATAR FRAMES
   Ported from vision_builder. Everything below is pure CSS so the same
   rules render identically inside an iOS (WKWebView) or Android
   (WebView / Chromium) wrapper — no native bridge required.
   All animations use transform / opacity / filter / box-shadow so they
   are GPU-accelerated and cheap on low-end devices.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── CHAT EFFECTS (aura around the bubble) ───────────────────────────── */
/* Every effect class sits on an *outer wrapper* whose only job is to frame
   the bubble. We paint directly with box-shadow on the wrapper itself —
   no negative-z-index pseudo-elements — so these always compose with the
   bubble's background + border + the page, independent of stacking order. */

.chat-effect-sparkles,
.chat-effect-glow,
.chat-effect-neon,
.chat-effect-fire,
.chat-effect-rainbow {
  position: relative;
  isolation: isolate;
}

/* Sparkles — two drifting emoji floats anchored to the wrapper corners. */
.chat-effect-sparkles::before,
.chat-effect-sparkles::after {
  content: '✨';
  position: absolute;
  font-size: 12px;
  animation: sparkle 2s ease-in-out infinite;
  pointer-events: none;
  z-index: 2;
}
.chat-effect-sparkles::before { top: -8px;    right: 10%; animation-delay: 0s; }
.chat-effect-sparkles::after  { bottom: -8px; left: 15%;  animation-delay: 1s; }
@keyframes sparkle {
  0%, 100% { opacity: 0.3; transform: scale(0.8); }
  50%      { opacity: 1;   transform: scale(1.2); }
}

/* Soft glow — indigo halo that breathes. */
.chat-effect-glow {
  animation: soft-glow 2s ease-in-out infinite;
}
@keyframes soft-glow {
  0%, 100% { box-shadow: 0 0 10px 2px rgba(99,102,241,0.45); }
  50%      { box-shadow: 0 0 22px 4px rgba(99,102,241,0.85); }
}

/* Neon pulse — violet shadow with an intermittent flicker. */
.chat-effect-neon {
  animation: neon-flicker 3s ease-in-out infinite;
}
@keyframes neon-flicker {
  0%, 92%, 100% {
    box-shadow:
      0 0 5px  rgba(168,85,247,0.8),
      0 0 12px rgba(168,85,247,0.6),
      0 0 22px rgba(168,85,247,0.4);
  }
  93% { box-shadow: 0 0 3px rgba(168,85,247,0.4), 0 0 6px rgba(168,85,247,0.3); }
  94%, 96% {
    box-shadow:
      0 0 5px  rgba(168,85,247,0.8),
      0 0 12px rgba(168,85,247,0.6),
      0 0 22px rgba(168,85,247,0.4);
  }
  95% { box-shadow: 0 0 4px rgba(168,85,247,0.5), 0 0 8px rgba(168,85,247,0.4); }
}

/* Fire glow — warm ember shadow beneath and around the bubble. */
.chat-effect-fire {
  animation: fire-ambient-glow 1.2s ease-in-out infinite alternate;
}
@keyframes fire-ambient-glow {
  0%   {
    box-shadow:
      0 2px 12px rgba(255, 88, 0, 0.45),
      0 0 18px   rgba(255,140, 0, 0.35);
  }
  100% {
    box-shadow:
      0 4px 22px rgba(255, 88, 0, 0.75),
      0 0 28px   rgba(255,140, 0, 0.65);
  }
}

/* Rainbow ring — gradient ring kept OUTSIDE the bubble using the
   wrapper's ::before with an "inside-out" mask trick. isolation: isolate
   on the wrapper keeps the ring below sibling content but above the
   page, and the mask hides its centre so the bubble stays readable. */
.chat-effect-rainbow::before {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 18px;
  padding: 3px;
  background: linear-gradient(90deg,
    #ef4444, #f97316, #eab308, #22c55e, #3b82f6, #8b5cf6, #ec4899, #ef4444);
  background-size: 200% 100%;
  animation: rainbow-shift 2s linear infinite;
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
          mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  z-index: -1;
  pointer-events: none;
}
@keyframes rainbow-shift {
  0%   { background-position:   0% 50%; }
  100% { background-position: 200% 50%; }
}

/* ── BUBBLE BORDERS ─────────────────────────────────────────────────── */

.border-holographic {
  animation: holo-shimmer 3s linear infinite;
}
@keyframes holo-shimmer {
  0%   { filter: hue-rotate(0deg);   }
  100% { filter: hue-rotate(360deg); }
}

/* ── ANIMATED BUBBLE BACKGROUNDS ───────────────────────────────────── */

.bubble-animated-aurora {
  background-size: 300% 300% !important;
  animation: aurora-shift 8s ease-in-out infinite;
}
@keyframes aurora-shift {
  0%, 100% { background-position:   0% 50%; }
  50%      { background-position: 100% 50%; }
}

.bubble-animated-galaxy {
  animation: galaxy-breathe 6s ease-in-out infinite;
}
@keyframes galaxy-breathe {
  0%, 100% { filter: brightness(1)    saturate(1);   }
  50%      { filter: brightness(1.15) saturate(1.25);}
}

/* ── BUBBLE SHAPE ──────────────────────────────────────────────────── */
/* Every bubble uses the same rounded silhouette — we chose to drop
   user-picked shapes because clip-path/corner variants collided with chat
   effect pseudo-elements and animated borders. */

.bubble-shape-rounded { border-radius: 14px 14px 4px 14px; }

/* ── AVATAR FRAMES ─────────────────────────────────────────────────── */
/* Each frame is a small wrapper that lives around a circular avatar image.
   Uses box-shadow rings instead of `ring-*` utility classes so it works
   without Tailwind. */

.avatar-frame {
  position: relative;
  border-radius: 50%;
  padding: 3px;
  display: inline-block;
  line-height: 0;
}
.avatar-frame > * {
  border-radius: 50%;
  display: block;
}

.avatar-frame-sakura  { background: linear-gradient(135deg,#fbcfe8,#f472b6,#fbcfe8); box-shadow: 0 0 10px rgba(244,114,182,0.45); }
.avatar-frame-gold    { background: linear-gradient(135deg,#fde68a,#fbbf24,#b45309); box-shadow: 0 0 10px rgba(251,191,36,0.55); }
.avatar-frame-neon    { background: #a855f7; box-shadow: 0 0 12px rgba(168,85,247,0.7); animation: frame-pulse 2s ease-in-out infinite; }
.avatar-frame-ocean   { background: linear-gradient(135deg,#22d3ee,#0EA5E9,#167D89); box-shadow: 0 0 10px rgba(34,211,238,0.55); }
.avatar-frame-fire    { background: linear-gradient(135deg,#fb923c,#f97316,#dc2626); box-shadow: 0 0 12px rgba(249,115,22,0.65); animation: frame-flicker 2.5s ease-in-out infinite; }
.avatar-frame-galaxy  { background: linear-gradient(135deg,#1a0033,#4b0082,#800080,#1a0033); box-shadow: 0 0 12px rgba(139,92,246,0.55); }
.avatar-frame-rainbow {
  background: conic-gradient(#ef4444,#f97316,#eab308,#22c55e,#3b82f6,#8b5cf6,#ec4899,#ef4444);
  animation: frame-rotate 3s linear infinite;
}
.avatar-frame-rainbow > * { background: white; }

@keyframes frame-pulse {
  0%, 100% { box-shadow: 0 0 8px  rgba(168,85,247,0.5); }
  50%      { box-shadow: 0 0 18px rgba(168,85,247,0.9); }
}
@keyframes frame-flicker {
  0%, 100% { filter: brightness(1); }
  50%      { filter: brightness(1.2); }
}
@keyframes frame-rotate {
  to { transform: rotate(360deg); }
}

/* Fallback avatar when we don't have an image — coloured circle with
   initials, used in lieu of a Lottie/GIF the user hasn't picked yet. */
.avatar-initials {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: linear-gradient(135deg, #0EA5E9, #167D89);
  color: white;
  font-weight: 800;
  font-family: inherit;
  user-select: none;
}

/* Decoration overlay sits on top of the avatar — pure emoji/text
   overlay so it works on every platform.

   NOTE: `.avatar-frame > *` above forces `border-radius: 50%; display:
   block` on every direct child. We re-declare those here so the
   overlay stays a small inline-block emoji pinned to the corner
   (otherwise it inherits the circular clipping and gets swallowed by
   the avatar). Stagger horizontally when multiple decorations are
   equipped so a crown + halo + sparkles don't pile on top of each
   other. */
.avatar-overlay {
  position: absolute;
  top: -8px;
  right: -8px;
  font-size: 16px;
  line-height: 1;
  pointer-events: none;
  border-radius: 0 !important;
  display: inline-block !important;
  width: auto !important;
  height: auto !important;
  background: transparent !important;
  z-index: 3;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,0.35));
}
.avatar-overlay ~ .avatar-overlay         { right: -22px; top: -4px; }
.avatar-overlay ~ .avatar-overlay ~ .avatar-overlay { right: -4px;  top:  10px; }

/* ── CHAT BACKGROUNDS ──────────────────────────────────────────────── */
/* Applied on the chat scroll container so the wallpaper fills the whole
   pane. Each background is a pure CSS gradient (+ optional animation)
   so it renders identically on web and inside iOS / Android WebView
   wrappers with zero extra assets. */

.chat-bg-tokyo {
  background:
    linear-gradient(180deg, rgba(12,9,24,0.92) 0%, rgba(20,10,40,0.85) 60%, rgba(236,72,153,0.35) 100%),
    repeating-linear-gradient(90deg, rgba(236,72,153,0.06) 0 2px, transparent 2px 40px) !important;
  color: #f5f3ff;
}
.chat-bg-sakura {
  background:
    linear-gradient(180deg, #fdf2f8 0%, #fce7f3 60%, #fbcfe8 100%) !important;
}
.chat-bg-ocean {
  background:
    linear-gradient(180deg, #ecfeff 0%, #cffafe 50%, #a5f3fc 100%) !important;
}
.chat-bg-mountain {
  background:
    linear-gradient(180deg, #f8fafc 0%, #e2e8f0 55%, #94a3b8 100%) !important;
}
.chat-bg-stars {
  background:
    radial-gradient(1px 1px at 20% 30%, rgba(255,255,255,0.9) 50%, transparent 51%),
    radial-gradient(1px 1px at 70% 20%, rgba(255,255,255,0.7) 50%, transparent 51%),
    radial-gradient(1px 1px at 40% 70%, rgba(255,255,255,0.8) 50%, transparent 51%),
    radial-gradient(1px 1px at 85% 85%, rgba(255,255,255,0.6) 50%, transparent 51%),
    radial-gradient(1px 1px at 10% 85%, rgba(255,255,255,0.7) 50%, transparent 51%),
    linear-gradient(180deg, #020617 0%, #0f172a 60%, #1e1b4b 100%) !important;
  color: #f8fafc;
}
/* On dark backgrounds, NPC bubbles + translation chips stay readable.
   (Kept intentionally minimal — we don't want to clobber user-picked
   bubble colour cosmetics.) */
.chat-bg-tokyo .bubble-shape-rounded[style*="background: #EEF8F9"],
.chat-bg-stars .bubble-shape-rounded[style*="background: #EEF8F9"] {
  background: rgba(255,255,255,0.08) !important;
  color: #f5f3ff !important;
}
