/* ============================================================
   TYPE FLOW — animations.css
   The complete motion system. Every keyframe, every entrance,
   every micro-interaction, in one place.
   ============================================================ */

/* ============================================================
   1. GLOBAL ENTRY — Page load orchestration
   ============================================================ */

/* App fades in on first paint */
.app {
  animation: app-enter 420ms var(--ease-out) both;
}

@keyframes app-enter {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Sidebar slides in from left, staggered items */
.sidebar {
  animation: sidebar-enter 480ms var(--ease-out) both;
}

@keyframes sidebar-enter {
  from {
    opacity: 0;
    transform: translateX(-12px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

.brand {
  animation: brand-enter 500ms var(--ease-out) 80ms both;
}

@keyframes brand-enter {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Logo draws in — subtle rotate + scale */
.brand__logo {
  animation: logo-draw 700ms var(--ease-spring) 120ms both;
}

@keyframes logo-draw {
  0% {
    opacity: 0;
    transform: scale(0.7) rotate(-20deg);
  }
  60% {
    opacity: 1;
    transform: scale(1.08) rotate(6deg);
  }
  100% {
    opacity: 1;
    transform: scale(1) rotate(0deg);
  }
}

/* Nav items stagger in */
.nav-item {
  animation: nav-stagger 340ms var(--ease-out) both;
}

.nav-item:nth-child(1) { animation-delay: 180ms; }
.nav-item:nth-child(2) { animation-delay: 220ms; }
.nav-item:nth-child(3) { animation-delay: 260ms; }
.nav-item:nth-child(4) { animation-delay: 300ms; }
.nav-item:nth-child(5) { animation-delay: 340ms; }
.nav-item:nth-child(6) { animation-delay: 380ms; }

@keyframes nav-stagger {
  from {
    opacity: 0;
    transform: translateX(-8px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

/* Main content fades in slightly after sidebar */
.main {
  animation: main-enter 500ms var(--ease-out) 100ms both;
}

@keyframes main-enter {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ============================================================
   2. PAGE TRANSITIONS
   ============================================================ */

.page:not([hidden]) {
  animation: page-enter 340ms var(--ease-out) both;
}

@keyframes page-enter {
  from {
    opacity: 0;
    transform: translateY(12px);
    filter: blur(2px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
    filter: blur(0);
  }
}

.page__header {
  animation: header-enter 400ms var(--ease-out) 40ms both;
}

@keyframes header-enter {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.page__title {
  animation: title-enter 500ms var(--ease-out) 80ms both;
}

@keyframes title-enter {
  from {
    opacity: 0;
    transform: translateY(6px);
    letter-spacing: 0.04em;
  }
  to {
    opacity: 1;
    transform: translateY(0);
    letter-spacing: var(--tracking-tight);
  }
}

.page__subtitle {
  animation: subtitle-enter 500ms var(--ease-out) 160ms both;
}

@keyframes subtitle-enter {
  from {
    opacity: 0;
    transform: translateY(4px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ============================================================
   3. CONFIG BAR STAGGER
   ============================================================ */

.config-bar {
  animation: config-bar-enter 480ms var(--ease-out) 200ms both;
}

@keyframes config-bar-enter {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.config-btn {
  animation: config-btn-enter 320ms var(--ease-out) both;
}

.config-group:nth-child(1) .config-btn:nth-child(1) { animation-delay: 260ms; }
.config-group:nth-child(1) .config-btn:nth-child(2) { animation-delay: 280ms; }
.config-group:nth-child(1) .config-btn:nth-child(3) { animation-delay: 300ms; }
.config-group:nth-child(1) .config-btn:nth-child(4) { animation-delay: 320ms; }
.config-group:nth-child(1) .config-btn:nth-child(5) { animation-delay: 340ms; }

@keyframes config-btn-enter {
  from {
    opacity: 0;
    transform: translateY(4px) scale(0.96);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

/* ============================================================
   4. TYPING AREA ENTRANCE
   ============================================================ */

.typing-wrap {
  animation: typing-enter 520ms var(--ease-out) 320ms both;
}

@keyframes typing-enter {
  from {
    opacity: 0;
    transform: translateY(16px) scale(0.99);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

.live-stats {
  animation: live-stats-enter 460ms var(--ease-out) 400ms both;
}

@keyframes live-stats-enter {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Words stagger in one by one */
.words .word {
  animation: word-enter 320ms var(--ease-out) both;
}

.words .word:nth-child(1)  { animation-delay: 440ms; }
.words .word:nth-child(2)  { animation-delay: 452ms; }
.words .word:nth-child(3)  { animation-delay: 464ms; }
.words .word:nth-child(4)  { animation-delay: 476ms; }
.words .word:nth-child(5)  { animation-delay: 488ms; }
.words .word:nth-child(6)  { animation-delay: 500ms; }
.words .word:nth-child(7)  { animation-delay: 512ms; }
.words .word:nth-child(8)  { animation-delay: 524ms; }
.words .word:nth-child(9)  { animation-delay: 536ms; }
.words .word:nth-child(10) { animation-delay: 548ms; }
.words .word:nth-child(n+11) { animation-delay: 560ms; }

@keyframes word-enter {
  from {
    opacity: 0;
    transform: translateY(4px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.test-footer {
  animation: footer-enter 480ms var(--ease-out) 500ms both;
}

@keyframes footer-enter {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ============================================================
   5. LETTER PRESS MICRO-INTERACTION
   Every keystroke gives a subtle 80ms scale pop
   ============================================================ */

.letter.pressed {
  animation: letter-press 90ms var(--ease-snap);
}

@keyframes letter-press {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.12); }
  100% { transform: scale(1); }
}

/* Correct letter bloom */
.letter.correct.bloom {
  animation: letter-bloom 260ms var(--ease-out);
}

@keyframes letter-bloom {
  0% {
    color: var(--accent);
    text-shadow: 0 0 12px var(--accent-glow);
  }
  100% {
    color: var(--text-primary);
    text-shadow: none;
  }
}

/* Error letter flash */
.letter.incorrect.flash {
  animation: letter-flash 240ms var(--ease-out);
}

@keyframes letter-flash {
  0% {
    background-color: var(--error-dim);
  }
  100% {
    background-color: transparent;
  }
}

/* ============================================================
   6. WORD COMPLETION
   ============================================================ */

.word.completed {
  animation: word-complete 260ms var(--ease-out);
}

@keyframes word-complete {
  0% {
    border-bottom-color: var(--accent);
  }
  100% {
    border-bottom-color: transparent;
  }
}

/* Underline sweep on completion */
.word.completed::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -2px;
  height: 2px;
  background: var(--accent);
  border-radius: var(--radius-full);
  animation: underline-sweep 380ms var(--ease-out) forwards;
}

@keyframes underline-sweep {
  0% {
    transform: scaleX(0);
    transform-origin: left;
    opacity: 1;
  }
  60% {
    transform: scaleX(1);
    transform-origin: left;
    opacity: 1;
  }
  100% {
    transform: scaleX(1);
    transform-origin: left;
    opacity: 0;
  }
}

/* ============================================================
   7. COMBO COUNTER POP
   Every 10 correct words in a row triggers a scale pulse
   ============================================================ */

.combo-pop {
  animation: combo-pop 500ms var(--ease-spring);
}

@keyframes combo-pop {
  0% {
    transform: scale(1);
    filter: brightness(1);
  }
  40% {
    transform: scale(1.18);
    filter: brightness(1.4);
  }
  100% {
    transform: scale(1);
    filter: brightness(1);
  }
}

/* Combo indicator badge (optional) */
.combo-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  padding: var(--space-2xs) var(--space-sm);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--bg);
  background: var(--accent);
  border-radius: var(--radius-full);
  box-shadow: 0 0 20px var(--accent-glow);
  animation: combo-badge-in 400ms var(--ease-spring) both;
}

@keyframes combo-badge-in {
  0% {
    opacity: 0;
    transform: translateY(8px) scale(0.6);
  }
  60% {
    opacity: 1;
    transform: translateY(0) scale(1.15);
  }
  100% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

/* ============================================================
   8. ERROR STREAK — Screen edge vignette pulse
   ============================================================ */

.vignette {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: var(--z-base);
  opacity: 0;
  background: radial-gradient(
    ellipse at center,
    transparent 40%,
    var(--error-dim) 100%
  );
  transition: opacity var(--duration-base) var(--ease-out);
}

.vignette.active {
  opacity: 1;
  animation: vignette-pulse 800ms var(--ease-in-out) infinite;
}

@keyframes vignette-pulse {
  0%, 100% { opacity: 0.5; }
  50%      { opacity: 1; }
}

/* ============================================================
   9. SPEED HUE SHIFT — Background subtle color drift at high WPM
   ============================================================ */

.typing-wrap.speed-boost {
  animation: speed-hue 6s var(--ease-in-out) infinite;
}

@keyframes speed-hue {
  0%, 100% { filter: hue-rotate(0deg); }
  50%      { filter: hue-rotate(-8deg); }
}

/* ============================================================
   10. RESULTS MODAL — Hero numbers count up
   (JS sets --target and this animates via CSS custom property
    fallback; the real count-up is JS-driven, this is the
    accent flash that accompanies it)
   ============================================================ */

.results-hero__value {
  animation: results-value-in 700ms var(--ease-spring) 200ms both;
}

@keyframes results-value-in {
  0% {
    opacity: 0;
    transform: translateY(12px) scale(0.85);
    filter: blur(4px);
  }
  60% {
    opacity: 1;
    filter: blur(0);
  }
  100% {
    opacity: 1;
    transform: translateY(0) scale(1);
    filter: blur(0);
  }
}

/* Result grid items stagger in */
.result-item {
  animation: result-item-in 420ms var(--ease-out) both;
}

.result-item:nth-child(1) { animation-delay: 340ms; }
.result-item:nth-child(2) { animation-delay: 400ms; }
.result-item:nth-child(3) { animation-delay: 460ms; }
.result-item:nth-child(4) { animation-delay: 520ms; }

@keyframes result-item-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ============================================================
   11. CHART LINE DRAW-IN
   SVG paths animate their stroke-dashoffset to draw left-to-right
   ============================================================ */

.results-chart path[stroke] {
  stroke-dasharray: 2000;
  stroke-dashoffset: 2000;
  animation: draw-line 1200ms var(--ease-out) 500ms forwards;
}

@keyframes draw-line {
  to {
    stroke-dashoffset: 0;
  }
}

/* Error markers pop in after lines finish */
.results-chart circle {
  animation: marker-pop 300ms var(--ease-spring) both;
}

.results-chart circle:nth-child(1) { animation-delay: 1400ms; }
.results-chart circle:nth-child(2) { animation-delay: 1500ms; }
.results-chart circle:nth-child(3) { animation-delay: 1600ms; }
.results-chart circle:nth-child(n+4) { animation-delay: 1700ms; }

@keyframes marker-pop {
  0% {
    transform: scale(0);
    opacity: 0;
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}

/* ============================================================
   12. PERSONAL BEST — Confetti + badge stamp
   ============================================================ */

/* PB badge stamps in */
.pb-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-xs) var(--space-md);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  color: var(--bg);
  background: var(--accent);
  border-radius: var(--radius-full);
  box-shadow: 0 0 24px var(--accent-glow);
  animation: pb-stamp 500ms var(--ease-spring) 900ms both;
}

@keyframes pb-stamp {
  0% {
    opacity: 0;
    transform: scale(2.2) rotate(-8deg);
    filter: blur(6px);
  }
  60% {
    opacity: 1;
    transform: scale(0.92) rotate(2deg);
    filter: blur(0);
  }
  100% {
    opacity: 1;
    transform: scale(1) rotate(0deg);
  }
}

/* Confetti particles */
.confetti {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: var(--z-toast);
  overflow: hidden;
}

.confetti__piece {
  position: absolute;
  width: 8px;
  height: 14px;
  top: -20px;
  opacity: 0;
  animation: confetti-fall var(--fall-duration, 3s) var(--ease-out) var(--fall-delay, 0s) forwards;
}

@keyframes confetti-fall {
  0% {
    opacity: 0;
    transform: translateY(0) rotate(0deg);
  }
  10% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translateY(105vh) rotate(720deg);
  }
}

/* ============================================================
   13. XP BAR FILL
   ============================================================ */

.xp-bar {
  position: relative;
  height: 6px;
  background: var(--surface);
  border-radius: var(--radius-full);
  overflow: hidden;
}

.xp-bar__fill {
  height: 100%;
  background: linear-gradient(90deg, var(--accent), var(--accent-hover));
  border-radius: var(--radius-full);
  width: 0;
  transition: width 1.2s var(--ease-out) 400ms;
  position: relative;
}

.xp-bar__fill::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgba(255, 255, 255, 0.4) 50%,
    transparent 100%
  );
  animation: xp-shine 1.8s var(--ease-in-out) 1.4s infinite;
  background-size: 200% 100%;
}

@keyframes xp-shine {
  0%   { background-position: -100% 0; }
  100% { background-position: 200% 0; }
}

/* ============================================================
   14. LEVEL UP GLOW
   ============================================================ */

.level-up {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: var(--z-toast);
  display: flex;
  align-items: center;
  justify-content: center;
  animation: level-up-glow 2.4s var(--ease-out) forwards;
}

.level-up::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(
    circle at center,
    var(--accent-glow) 0%,
    transparent 60%
  );
  opacity: 0;
  animation: level-up-radial 2.4s var(--ease-out) forwards;
}

@keyframes level-up-glow {
  0%   { opacity: 0; }
  20%  { opacity: 1; }
  80%  { opacity: 1; }
  100% { opacity: 0; }
}

@keyframes level-up-radial {
  0% {
    opacity: 0;
    transform: scale(0.5);
  }
  40% {
    opacity: 0.9;
    transform: scale(1.2);
  }
  100% {
    opacity: 0;
    transform: scale(1.6);
  }
}

/* ============================================================
   15. COMMAND PALETTE — List stagger
   ============================================================ */

.command-item {
  animation: command-item-in 240ms var(--ease-out) both;
}

.command-item:nth-child(1)  { animation-delay: 40ms; }
.command-item:nth-child(2)  { animation-delay: 55ms; }
.command-item:nth-child(3)  { animation-delay: 70ms; }
.command-item:nth-child(4)  { animation-delay: 85ms; }
.command-item:nth-child(5)  { animation-delay: 100ms; }
.command-item:nth-child(6)  { animation-delay: 115ms; }
.command-item:nth-child(7)  { animation-delay: 130ms; }
.command-item:nth-child(8)  { animation-delay: 145ms; }
.command-item:nth-child(n+9) { animation-delay: 160ms; }

@keyframes command-item-in {
  from {
    opacity: 0;
    transform: translateX(-6px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

/* Selected item: animated highlight bar */
.command-item.selected {
  position: relative;
}

.command-item.selected::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 60%;
  background: var(--accent);
  border-radius: var(--radius-full);
  animation: command-highlight 200ms var(--ease-out) both;
}

@keyframes command-highlight {
  from {
    transform: translateY(-50%) scaleY(0);
  }
  to {
    transform: translateY(-50%) scaleY(1);
  }
}

/* ============================================================
   16. THEME SWATCH RIPPLE
   When a theme is applied, its swatch ripples outward
   ============================================================ */

.theme-swatch.activating {
  animation: swatch-ripple 500ms var(--ease-out);
}

@keyframes swatch-ripple {
  0% {
    box-shadow: 0 0 0 0 var(--accent-glow);
  }
  100% {
    box-shadow: 0 0 0 24px transparent;
  }
}

/* ============================================================
   17. HOVER MICRO-INTERACTIONS
   ============================================================ */

/* Button lift on hover */
.btn:hover:not(:disabled) {
  transform: translateY(-1px);
}

.btn:active:not(:disabled) {
  transform: translateY(0);
}

/* Card lift */
.card:hover {
  transform: translateY(-2px);
}

/* Nav item accent slide */
.nav-item:hover .nav-item__label {
  animation: nav-label-nudge 240ms var(--ease-spring);
}

@keyframes nav-label-nudge {
  0%, 100% { transform: translateX(0); }
  50%      { transform: translateX(2px); }
}

/* Icon button rotate on hover for the settings gear */
#settingsBtn:hover svg {
  animation: gear-spin 600ms var(--ease-in-out);
}

@keyframes gear-spin {
  0%, 100% { transform: rotate(0deg); }
  100%     { transform: rotate(90deg); }
}

/* Logo subtle bounce on hover */
.brand:hover .brand__logo {
  animation: logo-bounce 500ms var(--ease-spring);
}

@keyframes logo-bounce {
  0%, 100% { transform: scale(1) rotate(0deg); }
  40%      { transform: scale(1.12) rotate(-8deg); }
  70%      { transform: scale(0.96) rotate(4deg); }
}

/* ============================================================
   18. TOAST ENTRANCE/EXIT
   ============================================================ */

.toast {
  animation: toast-slide-in 400ms var(--ease-spring) both;
}

@keyframes toast-slide-in {
  0% {
    opacity: 0;
    transform: translateX(32px) scale(0.96);
  }
  60% {
    opacity: 1;
    transform: translateX(-4px) scale(1.01);
  }
  100% {
    opacity: 1;
    transform: translateX(0) scale(1);
  }
}

.toast.leaving {
  animation: toast-slide-out 220ms var(--ease-out) forwards;
}

@keyframes toast-slide-out {
  to {
    opacity: 0;
    transform: translateX(32px) scale(0.96);
  }
}

/* ============================================================
   19. NOTIFICATION BELL PULSE
   (For the sidebar bell icon when new notifications exist)
   ============================================================ */

.bell-pulse {
  position: relative;
}

.bell-pulse::after {
  content: '';
  position: absolute;
  top: 4px;
  right: 4px;
  width: 8px;
  height: 8px;
  background: var(--error);
  border-radius: var(--radius-full);
  animation: bell-pulse 2s var(--ease-in-out) infinite;
}

@keyframes bell-pulse {
  0%, 100% {
    transform: scale(1);
    opacity: 1;
  }
  50% {
    transform: scale(1.35);
    opacity: 0.6;
  }
}

/* ============================================================
   20. SKELETON PULSE REFINEMENT
   Overrides the base pulse for a smoother, more elegant feel
   ============================================================ */

.skeleton {
  background: linear-gradient(
    90deg,
    var(--text-dim) 0%,
    color-mix(in srgb, var(--text-muted) 40%, transparent) 50%,
    var(--text-dim) 100%
  );
  background-size: 200% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent !important;
  animation: skeleton-shimmer 1.8s var(--ease-in-out) infinite;
}

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

/* Fallback for browsers without background-clip: text */
@supports not (background-clip: text) {
  .skeleton {
    background: none;
    color: var(--text-dim) !important;
    animation: skeleton-pulse 2s var(--ease-in-out) infinite;
  }
}

/* Skeleton chart bars shimmer */
.skeleton-bar {
  background: linear-gradient(
    180deg,
    var(--border) 0%,
    color-mix(in srgb, var(--text-muted) 20%, transparent) 50%,
    var(--border) 100%
  );
  background-size: 100% 200%;
  animation: skeleton-bar-shimmer 2s var(--ease-in-out) infinite;
}

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

/* ============================================================
   21. FOCUS OVERLAY PULSE
   ============================================================ */

.focus-overlay:not(.hidden) .focus-overlay__text {
  animation: focus-hint-pulse 2.8s var(--ease-in-out) infinite;
}

@keyframes focus-hint-pulse {
  0%, 100% { opacity: 0.7; }
  50%      { opacity: 1; }
}

/* ============================================================
   22. CARET IDLE BREATHING
   Applied when no keys have been pressed for >1.5s
   ============================================================ */

.caret.idle {
  animation: caret-breathe 2s var(--ease-in-out) infinite;
}

@keyframes caret-breathe {
  0%, 100% {
    opacity: 1;
    box-shadow: 0 0 0 0 var(--accent-glow);
  }
  50% {
    opacity: 0.65;
    box-shadow: 0 0 12px 2px var(--accent-glow);
  }
}

/* ============================================================
   23. RESULT SUBTLE BOUNCE ON HOVER
   ============================================================ */

.results-hero__value:hover {
  animation: value-bounce 400ms var(--ease-spring);
}

@keyframes value-bounce {
  0%, 100% { transform: scale(1); }
  40%      { transform: scale(1.05); }
}

/* ============================================================
   24. EMPTY STATE FLOAT
   ============================================================ */

.empty-state__icon {
  animation: empty-float 4s var(--ease-in-out) infinite;
}

@keyframes empty-float {
  0%, 100% { transform: translateY(0); opacity: 0.6; }
  50%      { transform: translateY(-6px); opacity: 0.9; }
}

/* ============================================================
   25. MODE SWITCH TRANSITION
   When switching between test modes, words slide out then in
   ============================================================ */

.words.switching-out {
  animation: words-out 180ms var(--ease-out) forwards;
}

.words.switching-in {
  animation: words-in 320ms var(--ease-out) 180ms both;
}

@keyframes words-out {
  to {
    opacity: 0;
    transform: translateX(-16px);
  }
}

@keyframes words-in {
  from {
    opacity: 0;
    transform: translateX(16px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

/* ============================================================
   26. DIGIT ROLL (for count-up numbers)
   Wraps each digit in a span and rolls vertically
   ============================================================ */

.digit-roll {
  display: inline-block;
  overflow: hidden;
  vertical-align: baseline;
  height: 1em;
  position: relative;
}

.digit-roll__inner {
  display: inline-block;
  animation: digit-roll 800ms var(--ease-out) both;
}

@keyframes digit-roll {
  from {
    transform: translateY(-100%);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

/* ============================================================
   27. REDUCED MOTION — Kill everything non-essential
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  .app,
  .sidebar,
  .brand,
  .brand__logo,
  .nav-item,
  .main,
  .page,
  .page__header,
  .page__title,
  .page__subtitle,
  .config-bar,
  .config-btn,
  .typing-wrap,
  .live-stats,
  .words .word,
  .test-footer,
  .toast,
  .skeleton,
  .skeleton-bar,
  .confetti__piece,
  .level-up,
  .caret.idle,
  .focus-overlay:not(.hidden) .focus-overlay__text,
  .empty-state__icon {
    animation: none !important;
  }

  .caret.idle,
  .caret.blink {
    opacity: 1 !important;
  }

  .btn:hover,
  .card:hover,
  .stat-card:hover,
  .theme-swatch:hover,
  .pb-item:hover,
  .nav-item:hover .nav-item__label {
    transform: none !important;
  }
}

html[data-reduce-motion="true"] *,
html[data-reduce-motion="true"] *::before,
html[data-reduce-motion="true"] *::after {
  animation-duration: 0.01ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.01ms !important;
}

html[data-reduce-motion="true"] .caret.idle,
html[data-reduce-motion="true"] .caret.blink,
html[data-reduce-motion="true"] .caret.smooth.blink {
  animation: none !important;
  opacity: 1 !important;
}

html[data-reduce-motion="true"] .btn:hover,
html[data-reduce-motion="true"] .card:hover,
html[data-reduce-motion="true"] .stat-card:hover,
html[data-reduce-motion="true"] .theme-swatch:hover,
html[data-reduce-motion="true"] .pb-item:hover {
  transform: none !important;
}

/* ============================================================
   28. WILL-CHANGE HINTS
   Only on elements that will actually animate frequently
   ============================================================ */

.caret,
.letter,
.word,
.modal,
.command-palette,
.drawer,
.toast {
  will-change: transform, opacity;
}

.caret {
  will-change: left, top, opacity;
}
