@import "tailwindcss";
@import "swiper/css";
@import "swiper/css/free-mode";
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');

/* The app's dark mode is a manual user choice (stored as [data-theme="dark"]
   on <html>), not the OS preference, so `dark:` utilities must key off that
   attribute instead of Tailwind's default `prefers-color-scheme` variant. */
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));

/* One-shot correct/incorrect flashes used by the matching activities.
   Exposed as `animate-flash-success` / `animate-flash-error` utilities. */
@theme {
  --animate-flash-success: flash-success 0.6s ease-out;
  --animate-flash-error: flash-error 0.6s ease-out;
}
@keyframes flash-success {
  0% { background-color: rgb(34, 197, 94); }
  100% { background-color: transparent; }
}
@keyframes flash-error {
  0% { background-color: rgb(239, 68, 68); }
  100% { background-color: transparent; }
}

  body {
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
    /* Smooth scrolling for a more polished feel */
    scroll-behavior: smooth;
    /* Antialiasing for crisp text */
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    min-height: 100dvh;
    /* viewport-fit=cover makes the page extend behind the status bar / native
       nav bar (webview); keep content below it. Bottom insets are handled
       per-screen so action bars can paint their background into the inset. */
    padding-top: env(safe-area-inset-top, 0px);
  }

/* Full-height app screens (fixed column, internal scroll). Plain 100dvh would
   overflow the bottom by the top inset now that body pads the top. */
@utility h-dvh-safe {
  height: calc(100dvh - env(safe-area-inset-top, 0px));
  max-height: calc(100dvh - env(safe-area-inset-top, 0px));
}

  @keyframes float {

    0%,
    100% {
      transform: translateY(0);
    }

    50% {
      transform: translateY(-8px);
    }
  }

  .float-animate {
    animation: float 2.5s ease-in-out infinite;
    /* Slightly slower animation for a more gentle float */
  }

  /* Custom scrollbar for a more integrated dark theme look (optional, but a nice touch) */
  ::-webkit-scrollbar {
    width: 10px;
  }

  ::-webkit-scrollbar-track {
    background: var(--color-gray-100);
  }

  ::-webkit-scrollbar-thumb {
    background: var(--color-gray-300);
    border-radius: 5px;
  }

  ::-webkit-scrollbar-thumb:hover {
    background: var(--color-gray-400);
  }

  [data-theme="dark"] ::-webkit-scrollbar-track {
    background: var(--color-gray-900);
  }

  [data-theme="dark"] ::-webkit-scrollbar-thumb {
    background: var(--color-gray-700);
  }

  [data-theme="dark"] ::-webkit-scrollbar-thumb:hover {
    background: var(--color-gray-600);
  }


  @keyframes fade-in {
  from {
    opacity: 0;
  }
}

@keyframes fade-out {
  to {
    opacity: 0;
  }
}

@keyframes slide-from-right {
  from {
    transform: translateX(60px);
  }
}

@keyframes slide-up {
  from {
    transform: translateY(130px);
  }
}

@keyframes slide-to-left {
  to {
    transform: translateX(-30px);
  }
}

/* Global but more fancy */
::view-transition-old(root) {
  animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
    300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}

::view-transition-new(root) {
  animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
    300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}

/* Swiper specific fixes */
.swiper {
  width: 100%;
  overflow: hidden;
}

.swiper-wrapper {
  display: flex;
  align-items: stretch;
}

.swiper-slide {
  flex-shrink: 0;
  width: auto !important;
  height: auto;
}

/* Mobile specific fixes */
@media (max-width: 768px) {
  body, html {
    overflow-x: hidden;
    max-width: 100vw;
  }
  
  main {
    overflow-x: hidden;
    max-width: 100vw;
  }
  
  .swiper {
    overflow: hidden;
    margin-left: calc(-1rem);
    margin-right: calc(-1rem);
    padding-left: 1rem;
    padding-right: 1rem;
    width: calc(100vw - 2rem);
  }
  
  .swiper-slide {
    margin-right: 1rem;
  }
  
  /* Fix grid width on mobile */
  .grid {
    padding-left: 0;
    padding-right: 0;
    max-width: 100%;
  }
  
  /* Ensure course cards don't cause overflow */
  .grid > div {
    min-width: 0;
    max-width: 100%;
  }
  
  /* Ensure sections don't overflow */
  section {
    max-width: 100vw;
    overflow-x: hidden;
  }
}
/* ---------------------------------------------------------------------------
   langlets. mobile app (app/views/app/**)
   ---------------------------------------------------------------------------
   Tokens for the native-app screens, taken from the "Turn iOS App" mockup.
   Namespaced `app-*` so they can't collide with the web palette above.

   IMPORTANT: never use `dark:` utilities under app/views/app/**. These screens
   are dark-only and layouts/app.html.erb hard-codes data-theme="dark", so a
   partial copied over from the web would have its dark:-conditional styling
   silently become unconditional. Use these tokens literally.
--------------------------------------------------------------------------- */
@theme {
  --color-app-bg: #0A1521;         /* screen background */
  --color-app-card: #11202F;       /* cards */
  --color-app-sheet: #101D2B;      /* bottom sheets */
  --color-app-inset: #0A1521;      /* recessed fields inside a sheet */
  --color-app-pill: #1B2C3C;       /* credits pill, avatar, close button */
  --color-app-chip: #1A2A39;       /* inactive chip, micro-tag */
  --color-app-segment: #1E3140;    /* active segmented-control segment */

  --color-app-accent: #1DC77C;
  /* Two on-accent colours, and the difference is real: the mockup uses #052012
     on the large pill CTAs and #04170E on badges, chips, the FAB glyph and
     check marks. One token would rot the distinction. */
  --color-app-on-accent: #052012;
  --color-app-on-accent-sm: #04170E;

  --color-app-text: #FFFFFF;
  --color-app-text-2: #8FA0AE;     /* secondary */
  --color-app-text-3: #66788A;     /* tertiary / footnotes / placeholders */
  --color-app-text-dim: #A9B7C2;   /* settings labels, dimmed titles */
  --color-app-credits: #E7EDF2;    /* the number in the credits pill */

  --radius-app-sheet: 28px;
  --radius-app-hero: 20px;
  --radius-app-card: 16px;
  --radius-app-field: 14px;
  --radius-app-input: 12px;
  --radius-app-thumb: 10px;

  /* The mockup is SF Pro, not the Inter the web app loads. */
  --font-app: -apple-system, "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
}

/* 0.5px borders and backdrop blur don't express cleanly as Tailwind utilities. */
@utility app-hairline {
  border: 0.5px solid rgb(255 255 255 / 0.07);
}

@utility app-hairline-t {
  border-top: 0.5px solid rgb(255 255 255 / 0.08);
}

/* Cards separate by border alone — the mockup has no elevation on them. */
@utility app-tabbar {
  background: rgb(7 15 23 / 0.9);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  border-top: 0.5px solid rgb(255 255 255 / 0.08);
}

/* The only shadow in the whole design. */
@utility app-fab-shadow {
  box-shadow: 0 6px 18px rgb(29 199 124 / 0.35);
}

/* "Ready" queue card: accent-tinted border rather than a fill. */
@utility app-border-ready {
  border: 0.5px solid rgb(29 199 124 / 0.35);
}

/* "Queued" queue card: dashed outline, no fill. */
@utility app-border-queued {
  border: 1.5px dashed rgb(255 255 255 / 0.14);
}

@utility app-sheet-top {
  border-top: 0.5px solid rgb(255 255 255 / 0.1);
}

/* Recessed field inside a sheet. */
@utility app-inset-hairline {
  border: 0.5px solid rgb(255 255 255 / 0.1);
}

/* Add Video's smart input once its contents parse as a video. Defined after
   app-inset-hairline and applied alongside it, rather than replacing it: the
   hairline is a `border` shorthand, so swapping it for a colour-only class would
   take the width and style with it and leave no border at all. */
@utility app-inset-recognized {
  border: 0.5px solid var(--color-app-accent);
}

/* Two lines, then ellipsis — library card titles. */
@utility app-clamp-2 {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* UIKit's own UITabBar height, which the web view extends underneath in the
   native app. Not a design token — changing it doesn't restyle anything, it just
   stops matching the bar we're trying to clear. The home-indicator inset
   (env(safe-area-inset-bottom)) sits below this, not inside it, so anything
   pinned above the tab bar owes both. */
:root {
  --app-tab-bar-height: 49px;
}

/* Screens scroll under a fixed tab bar; without this the last card hides
   behind it. Tab bar is 10px top + 24px icon + 3px gap + 10px label + 30px
   bottom padding, plus the home-indicator inset. */
@utility app-scroll-pad {
  padding-bottom: calc(96px + env(safe-area-inset-bottom, 0px));
}

/* In the native app (data-native-tabs on <body>) the web view extends under
   the native tab bar. Keep the full tab-bar clearance here as well; the bottom
   safe-area inset alone only clears the home indicator, not UITabBar itself. */
[data-native-tabs] .app-scroll-pad {
  padding-bottom: calc(96px + env(safe-area-inset-bottom, 0px));
}

/* Profile is a regular web page pushed inside a native tab navigator. Its
   content must clear landscape side insets, and its final destructive action
   must remain above both UITabBar and the home indicator. The shared body
   already owns the top safe-area inset. */
@utility profile-safe-area {
  padding-inline-start: max(1rem, env(safe-area-inset-left, 0px));
  padding-inline-end: max(1rem, env(safe-area-inset-right, 0px));
  padding-bottom: max(2rem, env(safe-area-inset-bottom, 0px));
}

@media (min-width: 40rem) {
  .profile-safe-area {
    padding-inline-start: max(1.5rem, env(safe-area-inset-left, 0px));
    padding-inline-end: max(1.5rem, env(safe-area-inset-right, 0px));
  }
}

[data-native-tabs] .profile-safe-area {
  padding-bottom: calc(96px + env(safe-area-inset-bottom, 0px));
}

/* The floating Add button. On the web it only owes the home indicator, but in
   the native app the web view runs underneath UITabBar, and the inset alone
   clears the home indicator rather than the bar — which put the FAB behind the
   tab bar on every notched device. Clear both. */
@utility app-fab-offset {
  bottom: calc(18px + env(safe-area-inset-bottom, 0px));
}

[data-native-tabs] .app-fab-offset {
  bottom: calc(18px + var(--app-tab-bar-height) + env(safe-area-inset-bottom, 0px));
}

/* The Credits sheet sits flush against the bottom of its web view, so its last
   control lands exactly on the home indicator. It's presented as a native modal
   — see config/hotwire/ios_path_configuration.json — so it covers the tab bar
   and owes only the inset, not the bar's height. The fixed part keeps the sheet
   off the indicator on hardware that reports no inset at all.

   Add a video used to share this; it's a pushed screen now and uses
   app-scroll-pad like every other pushed screen. */
@utility app-sheet-pad {
  padding-bottom: calc(20px + env(safe-area-inset-bottom, 0px));
}

/* Course pages still use the regular web layout inside the native app.
   Keep their playlist sheet above the native UITabBar and reduce its available
   height by the same amount so its controls never extend behind the bar. */
.playlist-sheet-overlay {
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

.playlist-sheet {
  max-height: min(85dvh, calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px) - 16px));
}

[data-native-tabs] .playlist-sheet-overlay {
  padding-bottom: calc(49px + env(safe-area-inset-bottom, 0px));
}

[data-native-tabs] .playlist-sheet {
  max-height: min(85dvh, calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px) - 65px));
}
