/* ============================================================
   GLOBAL.CSS — Design Tokens, Reset, Fonts, Animations, Theme
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600;700&display=swap');

/* --- Design Tokens / CSS Custom Properties --- */
:root {
  /* Primary palette */
  /* Easing, named. The design system writes its rules against these, and the
     app had none -- var(--ease-spring) inside a transition or animation
     shorthand is invalid, and an invalid declaration is dropped whole rather
     than falling back, so the imported rules would have had no motion at all.
     The curves are the ones already written literally throughout this
     codebase, so naming them changes nothing that exists. */
  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  /* Decelerating, and unlike --ease-spring it never overshoots -- for motion
     that should arrive and stop rather than bounce. */
  --ease-expo-out: cubic-bezier(0.16, 1, 0.3, 1);

  --color-primary: #6366f1;
  --color-primary-hover: #818cf8;
  --color-primary-dark: #4f46e5;
  --color-primary-glow: rgba(99, 102, 241, 0.25);
  --color-primary-subtle: rgba(99, 102, 241, 0.08);

  /* Accent */
  --color-accent: #06b6d4;
  --color-accent-hover: #22d3ee;

  /* Success / Warning / Danger */
  --color-success: #10b981;
  --color-success-bg: rgba(16, 185, 129, 0.1);
  --color-warning: #f59e0b;
  --color-warning-bg: rgba(245, 158, 11, 0.1);
  --color-danger: #ef4444;
  --color-danger-hover: #dc2626;
  --color-danger-bg: rgba(239, 68, 68, 0.1);

  /* Subtle variants */
  --color-warning-subtle: rgba(245, 158, 11, 0.15);
  --color-success-subtle: rgba(16, 185, 129, 0.15);
  --color-danger-subtle: rgba(239, 68, 68, 0.15);

  /* Neutrals — Light Theme */
  --bg-body: #f8fafc;
  --bg-surface: #ffffff;
  --bg-surface-hover: #f1f5f9;
  --bg-elevated: #ffffff;
  --bg-nav: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
  --bg-nav-solid: #0f172a;

  --border-color: #e2e8f0;
  --border-color-subtle: #f1f5f9;

  --text-primary: #0f172a;
  --text-secondary: #475569;
  --text-tertiary: #94a3b8;
  --text-on-dark: #f8fafc;
  --text-on-primary: #ffffff;

  /* Shadows */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.07), 0 2px 4px -2px rgba(0, 0, 0, 0.05);
  --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.08), 0 4px 6px -4px rgba(0, 0, 0, 0.05);
  --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.06);
  --shadow-glow: 0 0 20px var(--color-primary-glow);

  /* Glass */
  --glass-bg: rgba(255, 255, 255, 0.7);
  --glass-border: rgba(255, 255, 255, 0.3);
  --glass-blur: 12px;

  /* Spacing */
  --space-xs: 0.25rem;
  --space-sm: 0.5rem;
  --space-md: 1rem;
  --space-lg: 1.5rem;
  --space-xl: 2rem;
  --space-2xl: 3rem;

  /* Radius */
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 16px;
  --radius-xl: 24px;
  --radius-full: 9999px;

  /* Transitions */
  --transition-fast: 150ms ease;
  --transition-base: 250ms ease;
  --transition-slow: 400ms ease;
  --transition-spring: 300ms cubic-bezier(0.34, 1.56, 0.64, 1);

  /* Fonts */
  --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Consolas', monospace;
}

/* ── No programming ligatures, anywhere ──
   JetBrains Mono / Fira Code / Cascadia Code (preinstalled on Windows 11) ship
   with 'calt' ligatures that fuse  <=  >=  ->  !=  ==  into single math-style
   glyphs. In a C learning app students must see the literal characters they
   typed — in the editors, the run-code terminals, snippet Try Coding, admin
   test-case panes, everywhere. Applied universally so no mono context is missed. */
*, *::before, *::after {
  font-variant-ligatures: none !important;
  font-feature-settings: "liga" 0, "calt" 0, "dlig" 0 !important;
}

:root {

  /* Terminal / run-code panel — always dark regardless of app theme */
  --term-bg: #0d1117;
  --term-surface: #161b22;
  --term-border: #30363d;
  --term-text: #e6edf3;
  --term-text-muted: #8b949e;
}

/* --- Dark Theme --- */
[data-theme="dark"] {
  --bg-body: #0b1120;
  --bg-surface: #131c31;
  --bg-surface-hover: #1a2744;
  --bg-elevated: #182236;
  --bg-nav: linear-gradient(135deg, #080e1e 0%, #111827 100%);
  --bg-nav-solid: #080e1e;

  --border-color: #1e3050;
  --border-color-subtle: #162340;

  --text-primary: #e2e8f0;
  --text-secondary: #94a3b8;
  --text-tertiary: #64748b;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
  --shadow-md: 0 4px 8px rgba(0, 0, 0, 0.3);
  --shadow-lg: 0 10px 20px rgba(0, 0, 0, 0.35);
  --shadow-xl: 0 20px 30px rgba(0, 0, 0, 0.4);
  --shadow-glow: 0 0 30px rgba(99, 102, 241, 0.15);

  --glass-bg: rgba(19, 28, 49, 0.8);
  --glass-border: rgba(30, 48, 80, 0.5);
}

/* --- Purple Theme --- */
[data-theme="purple"] {
  --bg-body: #1e1b4b;
  --bg-surface: #312e81;
  --bg-surface-hover: #3730a3;
  --bg-elevated: #28246d;
  --bg-nav: linear-gradient(135deg, #1e1b4b 0%, #312e81 100%);
  --bg-nav-solid: #1e1b4b;

  --border-color: #4338ca;
  --border-color-subtle: #3730a3;

  --text-primary: #e0e7ff;
  --text-secondary: #a5b4fc;
  --text-tertiary: #818cf8;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-md: 0 4px 8px rgba(0, 0, 0, 0.4);
  --shadow-lg: 0 10px 20px rgba(0, 0, 0, 0.45);
  --shadow-xl: 0 20px 30px rgba(0, 0, 0, 0.5);
  --shadow-glow: 0 0 30px rgba(167, 139, 250, 0.2);

  --glass-bg: rgba(49, 46, 129, 0.8);
  --glass-border: rgba(67, 56, 202, 0.5);

  /* Primary color overrides for purple */
  --color-primary: #a855f7;
  --color-primary-hover: #d8b4fe;
  --color-primary-dark: #9333ea;
  --color-primary-glow: rgba(168, 85, 247, 0.25);
  --color-primary-subtle: rgba(168, 85, 247, 0.15);
}

/* --- Green Theme --- */
[data-theme="green"] {
  --bg-body: #064e3b;
  --bg-surface: #065f46;
  --bg-surface-hover: #047857;
  --bg-elevated: #022c22;
  --bg-nav: linear-gradient(135deg, #064e3b 0%, #065f46 100%);
  --bg-nav-solid: #064e3b;

  --border-color: #059669;
  --border-color-subtle: #047857;

  --text-primary: #ecfdf5;
  --text-secondary: #a7f3d0;
  --text-tertiary: #6ee7b7;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-md: 0 4px 8px rgba(0, 0, 0, 0.4);
  --shadow-lg: 0 10px 20px rgba(0, 0, 0, 0.45);
  --shadow-xl: 0 20px 30px rgba(0, 0, 0, 0.5);
  --shadow-glow: 0 0 30px rgba(16, 185, 129, 0.2);

  --glass-bg: rgba(6, 95, 70, 0.8);
  --glass-border: rgba(5, 150, 105, 0.5);

  /* Primary color overrides for green */
  --color-primary: #10b981;
  --color-primary-hover: #34d399;
  --color-primary-dark: #059669;
  --color-primary-glow: rgba(16, 185, 129, 0.25);
  --color-primary-subtle: rgba(16, 185, 129, 0.15);
}

/* --- CSS Reset & Base --- */
*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body {
  font-family: var(--font-sans);
  background-color: var(--bg-body);
  color: var(--text-primary);
  line-height: 1.6;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  transition: background-color var(--transition-base), color var(--transition-base);
}

a {
  color: var(--color-primary);
  text-decoration: none;
}

img {
  max-width: 100%;
  display: block;
}

/* --- Typography --- */
h1,
h2,
h3,
h4,
h5,
h6 {
  line-height: 1.3;
  font-weight: 700;
  color: var(--text-primary);
}

h1 {
  font-size: 1.875rem;
}

h2 {
  font-size: 1.25rem;
}

h3 {
  font-size: 1.125rem;
}

/* --- Custom Scrollbar --- */
::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: var(--text-tertiary);
  border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
  background: var(--text-secondary);
}

/* Firefox scrollbar */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--text-tertiary) transparent;
}

/* --- Selection --- */
::selection {
  background: var(--color-primary);
  color: white;
}

/* --- Animations & Keyframes --- */
@keyframes fadeIn {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(16px);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes slideInRight {
  from {
    opacity: 0;
    transform: translateX(24px);
  }

  to {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes slideInLeft {
  from {
    opacity: 0;
    transform: translateX(-24px);
  }

  to {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes scaleIn {
  from {
    opacity: 0;
    transform: scale(0.92);
  }

  to {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes shimmer {
  0% {
    background-position: -200% 0;
  }

  100% {
    background-position: 200% 0;
  }
}

@keyframes pulse-glow {

  0%,
  100% {
    box-shadow: 0 0 8px var(--color-primary-glow);
  }

  50% {
    box-shadow: 0 0 20px var(--color-primary-glow);
  }
}

@keyframes float {

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

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

/* --- Animation Utility Classes --- */
.fade-in {
  animation: fadeIn var(--transition-base) ease-out;
}

.fade-in-up {
  animation: fadeInUp 0.4s ease-out;
}

.slide-in-right {
  animation: slideInRight 0.35s ease-out;
}

.slide-in-left {
  animation: slideInLeft 0.35s ease-out;
}

.scale-in {
  animation: scaleIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* --- NEW: Additional Keyframes for comprehensive micro-animations --- */

@keyframes slideDown {
  from {
    opacity: 0;
    transform: translateY(-12px) scaleY(0.95);
  }

  to {
    opacity: 1;
    transform: translateY(0) scaleY(1);
  }
}

@keyframes slideUp {
  from {
    opacity: 1;
    transform: translateY(0) scaleY(1);
  }

  to {
    opacity: 0;
    transform: translateY(-12px) scaleY(0.95);
  }
}

@keyframes bounceIn {
  0% {
    opacity: 0;
    transform: scale(0.3);
  }

  50% {
    opacity: 1;
    transform: scale(1.08);
  }

  70% {
    transform: scale(0.95);
  }

  100% {
    transform: scale(1);
  }
}

@keyframes shake {

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

  15% {
    transform: translateX(-6px);
  }

  30% {
    transform: translateX(5px);
  }

  45% {
    transform: translateX(-4px);
  }

  60% {
    transform: translateX(3px);
  }

  75% {
    transform: translateX(-2px);
  }
}

@keyframes gentlePulse {

  0%,
  100% {
    opacity: 1;
  }

  50% {
    opacity: 0.65;
  }
}

@keyframes smoothReveal {
  from {
    opacity: 0;
    transform: translateY(8px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes popIn {
  0% {
    opacity: 0;
    transform: scale(0.6);
  }

  80% {
    transform: scale(1.05);
  }

  100% {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes rippleEffect {
  0% {
    transform: scale(0);
    opacity: 0.5;
  }

  100% {
    transform: scale(4);
    opacity: 0;
  }
}

@keyframes drawLine {
  from {
    stroke-dashoffset: 1000;
  }

  to {
    stroke-dashoffset: 0;
  }
}

@keyframes glowPulse {

  0%,
  100% {
    box-shadow: 0 0 6px var(--color-primary-glow);
  }

  50% {
    box-shadow: 0 0 18px var(--color-primary-glow), 0 0 30px rgba(99, 102, 241, 0.1);
  }
}

@keyframes borderGlow {

  0%,
  100% {
    border-color: var(--border-color);
  }

  50% {
    border-color: var(--color-primary);
  }
}

@keyframes countUp {
  from {
    opacity: 0;
    transform: translateY(10px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* --- NEW: Animation Utility Classes --- */
.slide-down {
  animation: slideDown 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}

.bounce-in {
  animation: bounceIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.shake {
  animation: shake 0.5s ease-in-out;
}

.pop-in {
  animation: popIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.gentle-pulse {
  animation: gentlePulse 2s ease-in-out infinite;
}

.glow-pulse {
  animation: glowPulse 2s ease-in-out infinite;
}

.smooth-reveal {
  animation: smoothReveal 0.35s ease-out;
}

@keyframes fadeInHalf {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

.animate-fade-in {
  animation: fadeInHalf 0.5s ease-out forwards;
}

/* Theme Switching Transition */
.theme-transitioning,
.theme-transitioning *,
.theme-transitioning *::before,
.theme-transitioning *::after {
  transition: background-color 0.5s ease, color 0.5s ease, border-color 0.5s ease, fill 0.5s ease, stroke 0.5s ease, box-shadow 0.5s ease !important;
}

/* Staggered children animation.

   `backwards`, NOT `forwards` — and no resting opacity:0 to go with it.

   A forwards fill keeps the animation in effect for good, and an animation that
   touches `transform` makes the element transformed for as long as it is in
   effect. The last keyframe being translateY(0) — or even `none`, which an
   in-effect animation still resolves to the identity matrix — does not save
   you: the element stays transformed, which means it stays a stacking context
   AND the containing block for anything absolutely positioned inside it.

   That was quietly trapping every menu under a staggered row. The library's
   View menu is z-index 30 over cards at auto and still painted UNDER them,
   because its z-index was resolved inside its parent's context and that context
   was painted in DOM order, before the grid.

   Backwards fill holds the FROM state during the delay — which is all the
   opacity:0 was ever for — and then hands the element back to its own styles
   the moment the animation ends. No lingering transform, no context, nothing
   trapped, and it looks exactly the same. */
.stagger-children>* {
  animation: fadeInUp 0.4s ease-out backwards;
}

.stagger-children>*:nth-child(1) {
  animation-delay: 0.05s;
}

.stagger-children>*:nth-child(2) {
  animation-delay: 0.1s;
}

.stagger-children>*:nth-child(3) {
  animation-delay: 0.15s;
}

.stagger-children>*:nth-child(4) {
  animation-delay: 0.2s;
}

.stagger-children>*:nth-child(5) {
  animation-delay: 0.25s;
}

.stagger-children>*:nth-child(6) {
  animation-delay: 0.3s;
}

.stagger-children>*:nth-child(7) {
  animation-delay: 0.35s;
}

.stagger-children>*:nth-child(8) {
  animation-delay: 0.4s;
}

.stagger-children>*:nth-child(9) {
  animation-delay: 0.45s;
}

.stagger-children>*:nth-child(10) {
  animation-delay: 0.5s;
}

.stagger-children>*:nth-child(11) {
  animation-delay: 0.55s;
}

.stagger-children>*:nth-child(12) {
  animation-delay: 0.6s;
}

/* Everything past the twelfth arrives with the twelfth.
   The delays above stop at :nth-child(12), so in a folder of sixty cards the
   thirteenth onwards fell back to a 0s delay -- they appeared FIRST, before the
   staggered ones had finished trickling in, which reads as broken rather than
   as no animation. Holding them at the last delay caps the whole entrance at
   0.6s, the same ceiling Motion.scan and _adminStaggerCards already use. */
.stagger-children>*:nth-child(n+13) {
  animation-delay: 0.6s;
}

/* --- Utility Classes --- */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Skip-to-content link for keyboard users */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 10000;
  padding: 0.5rem 1.25rem;
  background: var(--color-primary);
  color: var(--text-on-primary);
  font-weight: 700;
  font-size: 0.875rem;
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  text-decoration: none;
  white-space: nowrap;
  transition: none;
}

.skip-link:focus {
  left: 0;
  outline: 2px solid var(--text-on-primary);
  outline-offset: 2px;
}

.text-truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.line-clamp-2 {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.hidden {
  display: none !important;
}

/* Suppress entry animations on page load to prevent layout jump */
.no-entry-animation,
.no-entry-animation * {
  animation-duration: 0s !important;
  animation-delay: 0s !important;
  transition-duration: 0s !important;
}

/* Forces animations to retain their final computed values.

   .stagger-children>* is deliberately NOT in this list any more. It needed a
   forwards fill only because it rested at opacity:0; it now rests visible and
   fills backwards instead (see the rule above), and forcing forwards here undid
   that — leaving every staggered child permanently transformed, a stacking
   context, and a trap for any menu inside it. An !important on a shared list is
   exactly the kind of rule that keeps winning an argument nobody remembers
   having, so the fix is to take the selector out rather than fight it. */
.animate-fade-in,
.fade-in-up,
.pulse-highlight,
.heatmap-cell,
.home-activity-item {
  animation-fill-mode: forwards !important;
}
/* Prevent SVG squishing */
svg.lucide { flex-shrink: 0; }

/* ============================================================
   REDUCED MOTION — Respect user's OS motion preference
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .fade-in-up,
  .slide-in-right,
  .slide-in-left,
  .scale-in,
  .bounce-in,
  .smooth-reveal,
  .pop-in,
  .stagger-children > * {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}

/* ============================================================
   TOUCH ACTION — Eliminate 300ms tap delay on mobile
   ============================================================ */
button,
a,
[role="button"],
.sidebar-link,
.tree-node-row,
.snippet-list-item,
.card,
.study-tab,
.btn {
  touch-action: manipulation;
}

/* ============================================================
   ROUTE TRANSITIONS — Subtle fade+slide on page navigation
   ============================================================ */
@keyframes routeEnter {
  from {
    opacity: 0;
    transform: translateY(5px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* NOT `both`. The forwards half of that fill mode pinned the final keyframe's
   `transform: translateY(0)` on #spa-content permanently — and an element with
   ANY transform, identity included, becomes the containing block for every
   `position: fixed` descendant. So every menu and popup inside a route resolved
   its coordinates against #spa-content instead of the viewport and appeared
   shifted right by exactly the sidebar's width. There is no delay here, so no
   fill mode is needed at all. */
.route-enter {
  animation: routeEnter 200ms ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .route-enter {
    animation: none;
  }
}

/* ── The blue box on tap ───────────────────────────────────────
   Mobile browsers paint their own highlight over whatever you touch, sized to
   the nearest clickable ancestor — so tapping a button inside a card lit the
   whole card. It is not a hover or an active style, which is why nothing in
   this stylesheet appeared to control it, and it fought every state the app
   draws for itself.

   Cleared everywhere. The app already shows what you pressed: buttons have
   :active, cards have their own selected state, and links have focus rings. */
* { -webkit-tap-highlight-color: transparent; }

/* And the long-press menu on controls. TEXT stays selectable on purpose —
   descriptions, code, output and samples are all things you copy — so this
   only covers things you press. */
button,
a,
[role="button"],
[onclick],
.card,
.sidebar-link,
.feel-opt,
.pp-tab,
.drill-mode,
.browse-filter-chip,
.lib-chip {
  -webkit-touch-callout: none;
}

/* A control's LABEL is not something you meant to select when you double-tap
   it. The content areas are deliberately left out. */
button,
.sidebar-link,
.pp-tab,
.drill-mode,
.browse-filter-chip,
.lib-chip,
.feel-opt {
  -webkit-user-select: none;
  user-select: none;
}
