/* ==========================================================================
   1. BASE STYLES & OVERRIDES
   ========================================================================== */
body { 
  font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif; 
  overscroll-behavior-y: none; 
}

img, span { 
  -webkit-touch-callout: none; 
  -webkit-user-select: none; 
  user-select: none; 
  pointer-events: auto; 
}

button svg, button i {
  pointer-events: none;
}

/* ==========================================================================
   2. ANIMATIONS & KEYFRAMES
   ========================================================================== */

/* Piece Destruction/Explosion */
@keyframes shatter-particle { 
  0% { 
    transform: translate(0, 0) rotate(0deg) scale(1); 
    opacity: 1; 
  } 
  100% { 
    transform: translate(var(--tx), var(--ty)) rotate(var(--rot)) scale(0); 
    opacity: 0; 
  } 
}

/* Colorful Confetti Burst */
@keyframes mc-confetti {
  0% { transform: translate(0, 0) rotate(0deg) scale(1); opacity: 1; }
  50% { opacity: 1; }
  100% { transform: translate(var(--tx), calc(var(--ty) + 200px)) rotate(var(--rot)) scale(0); opacity: 0; }
}

/* Check Status Shake */
@keyframes king-shake { 
  0%, 100% { transform: translateX(0) rotate(0deg); } 
  25% { transform: translateX(-3px) rotate(-3deg); } 
  75% { transform: translateX(3px) rotate(3deg); } 
}
.king-shake { animation: king-shake 0.4s ease-in-out; }

/* Dance Parties */
@keyframes piece-dance { 
  0%, 100% { transform: translateY(0) rotate(0) scale(1) translateZ(0); } 
  25% { transform: translateY(-12%) rotate(-15deg) scale(1.15) translateZ(0); } 
  50% { transform: translateY(-5%) rotate(0) scale(1.05) translateZ(0); } 
  75% { transform: translateY(-12%) rotate(15deg) scale(1.15) translateZ(0); } 
}

@keyframes piece-dance-sync { 
  0%, 100% { transform: translateY(0) scale(1) translateZ(0); } 
  50% { transform: translateY(-20%) scale(1.1) translateZ(0); } 
}

@keyframes piece-dance-slide { 
  0%, 100% { transform: translateX(0) translateZ(0); } 
  10% { transform: translateX(-30%) translateZ(0); } 
  20% { transform: translateX(-60%) translateZ(0); } 
  30% { transform: translateX(-90%) translateZ(0); } 
  50% { transform: translateX(0) translateZ(0); } 
  60% { transform: translateX(30%) translateZ(0); } 
  70% { transform: translateX(60%) translateZ(0); } 
  80% { transform: translateX(90%) translateZ(0); } 
}

@keyframes piece-dance-cupid { 
  0%, 100% { transform: translateX(0) rotate(0) translateZ(0); } 
  5% { transform: translateX(20%) translateY(-5%) translateZ(0); } 
  10% { transform: translateX(40%) translateZ(0); } 
  15% { transform: translateX(60%) translateY(-5%) translateZ(0); } 
  20% { transform: translateX(80%) translateZ(0); } 
  25% { transform: translateX(60%) translateY(-5%) translateZ(0); } 
  30% { transform: translateX(40%) translateZ(0); } 
  35% { transform: translateX(20%) translateY(-5%) translateZ(0); } 
  40% { transform: translateX(0) translateZ(0); } 
  45% { transform: translateY(-15%) rotate(15deg) translateZ(0); } 
  50% { transform: translateY(0) rotate(0) translateZ(0); } 
  55% { transform: translateY(-15%) rotate(-15deg) translateZ(0); } 
  60% { transform: translateY(0) rotate(0) translateZ(0); } 
  65% { transform: translateY(-15%) rotate(15deg) translateZ(0); } 
  70% { transform: translateY(0) rotate(0) translateZ(0); } 
  75% { transform: translateY(-15%) rotate(-15deg) translateZ(0); } 
  80% { transform: translateY(0) rotate(0) translateZ(0); } 
  90% { transform: rotate(180deg) scale(0.9) translateZ(0); } 
  100% { transform: rotate(360deg) scale(1) translateZ(0); } 
}

.dancing-piece { animation: piece-dance 0.6s infinite ease-in-out; }
.dancing-piece-sync { animation: piece-dance-sync 0.6s infinite ease-in-out; }
.dancing-piece-slide { animation: piece-dance-slide 3s infinite ease-in-out; }
.dancing-piece-cupid { animation: piece-dance-cupid 4s infinite linear; }

/* Visual Effects */
@keyframes dust-puff { 
  0% { transform: translate(-50%, -50%) scale(0.3); opacity: 1; border: 4px solid currentColor; } 
  100% { transform: translate(-50%, -50%) scale(1.8); opacity: 0; border: 0px solid currentColor; } 
}

@keyframes piece-slam { 
  0% { transform: scale(1); } 
  30% { transform: scale(0.8) translateY(10%); filter: brightness(1.2); } 
  100% { transform: scale(1) translateY(0); filter: brightness(1); } 
}
.slam-piece { animation: piece-slam 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards !important; }

@keyframes pop-fade-symbol { 
  0% { 
    transform: translate(-50%, -50%) scale(0) rotate(calc(var(--rot) - 15deg)); 
    opacity: 0; 
    filter: drop-shadow(0 0 0 currentColor); 
  } 
  20% { 
    transform: translate(-50%, -50%) scale(1.2) rotate(calc(var(--rot) + 10deg)); 
    opacity: 1; 
    filter: drop-shadow(0 0 30px currentColor); 
  } 
  70% { 
    transform: translate(-50%, -50%) scale(1) rotate(calc(var(--rot) + 5deg)); 
    opacity: 1; 
    filter: drop-shadow(0 0 10px currentColor); 
  } 
  100% { 
    transform: translate(-50%, -50%) scale(2) rotate(calc(var(--rot) + 20deg)); 
    opacity: 0; 
    filter: blur(10px); 
  } 
}

/* ==========================================================================
   3. CUSTOM SCROLLBARS
   ========================================================================== */
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
.dark ::-webkit-scrollbar-thumb { background: #374151; }

/* ==========================================================================
   4. BOARD & PIECE UTILITIES
   ========================================================================== */
/* Board size is owned by JS fitBoard() (js/core/init.js), which measures the
   real available width AND height and sets an inline `max-width: …px !important`.
   This rule is only a conservative fallback for first paint / if JS fails. */
.board-wrapper {
  max-width: min(100%, calc(100dvh - 220px));
  /* No max-width transition: fitBoard() sets an inline px max-width, and animating
     it over 200ms re-lays-out the board + every piece each frame on resize/panel
     toggles — that read as a visible "shake". Snap instantly instead. */
}

/* Large Board Size Mode — sizing handled by fitBoard(); keep the class hook
   for other large-board styling without fighting the JS-driven max-width. */
body.large-board-on #move-input:not(.hidden) {
  position: fixed;
  bottom: 1rem;
  right: 1.5rem;
  z-index: 200;
  box-shadow: 0 10px 30px rgba(0,0,0,0.5);
  border: 2px solid #3b82f6;
}

/* ==========================================================================
   4b. MACBOOK & HIGH-RES SCALING
   ========================================================================== */
@media (min-width: 1280px) { /* e.g., MacBook Air/Pro default logical width */
  html { font-size: 18px; }
}
@media (min-width: 1536px) { /* e.g., larger external monitors */
  html { font-size: 20px; }
}

#board-surface { 
  -webkit-touch-callout: none; 
  touch-action: none; 
  overflow: hidden;
  /* Fix: items-stretch on the parent overrides aspect-ratio, making the board
     non-square (squished). align-self: flex-start lets aspect-ratio: 1/1
     correctly set height = width so the board is always a perfect square. */
  align-self: flex-start !important;
  aspect-ratio: 1 / 1 !important;
  height: auto !important;
}

.ghost-piece { 
  opacity: 0; 
  pointer-events: none; 
}

.pawn-mode-active { 
  border-color: #eab308 !important; 
  box-shadow: 0 0 30px rgba(234, 179, 8, 0.3) !important; 
}

.palette-selected { 
  border-radius: 0.5rem; 
  background-color: rgba(59, 130, 246, 0.2); 
  box-shadow: 0 0 0 2px #3b82f6; 
  transform: scale(1.1); 
}

/* ==========================================================================
   5. TEACHING MODE OVERRIDES
   ========================================================================== */
/* Teaching mode: board sizing also handled by fitBoard() (it measures whatever
   space this mode leaves). Only non-sizing overrides remain below. */
.teaching-mode #main-area {
  padding-top: 0.5rem !important; 
  padding-bottom: 0.5rem !important; 
  max-width: 100% !important; 
}

/* Teaching mode is what actually runs in the classroom, so the header has to be
   tall enough to carry a readable name badge and shot clock. It used to be 3rem,
   which forced the badge down to 0.65rem (~10px) and the clock to 1.25rem —
   unreadable from the back of the room on the academy's dim projector. The extra
   ~20px of header costs the board almost nothing (fitBoard() re-measures). */
.teaching-mode #main-header { height: 4.25rem !important; }

.teaching-mode #active-student-name {
  font-size: 1.6rem !important;
  padding: 0.3rem 1.1rem !important;
  max-width: 18rem !important;
  letter-spacing: 0.08em !important;
}
.teaching-mode #shot-clock {
  font-size: 3rem !important;
  padding: 0.15rem 1rem !important;
  line-height: 1.05 !important;
}
@media (min-width: 1024px) {
  .teaching-mode #main-header { height: 4.75rem !important; }
  .teaching-mode #active-student-name { font-size: 2rem !important; max-width: 24rem !important; }
  .teaching-mode #shot-clock { font-size: 3.5rem !important; }
}

.teaching-mode #header-text { font-size: 0.875rem !important; }
@media (min-width: 1024px) { 
  .teaching-mode #header-text { font-size: 1.125rem !important; } 
}

.teaching-mode #right-panel { 
  position: fixed; 
  right: 1.5rem; 
  top: 15%; 
  bottom: auto; 
  left: auto; 
  transform: none; 
  width: auto !important; 
  border: none !important; 
  background: transparent !important; 
  box-shadow: none !important; 
  z-index: 100; 
  overflow: visible !important; 
}

.teaching-mode #analysis-toolbar, 
.teaching-mode #move-list, 
.teaching-mode #engine-clean-lines, 
.teaching-mode #game-info-panel { 
  display: none !important; 
}

.teaching-mode #analysis-panel { 
  background: transparent !important; 
  border: none !important; 
}

.teaching-mode #playback-controls { 
  flex-direction: column; 
  border-radius: 9999px; 
  padding: 1.25rem 0.5rem !important; 
  gap: 0.5rem; 
  opacity: 0; 
  transform: translateX(20px); 
}

.teaching-mode #right-panel:hover #playback-controls { 
  opacity: 1; 
  transform: translateX(0); 
}

.teaching-mode #control-divider { 
  width: 24px !important; 
  height: 1px !important; 
  margin: 0.25rem 0 !important; 
}

#teaching-copy-btn { display: none; }
.teaching-mode #teaching-copy-btn { display: block; }

/* Always-visible "exit full screen" button for teaching mode. The minimize button
   in the right-panel playback bar is opacity:0 until you hover the panel, which is
   impossible on a touchscreen — so this floating button guarantees a tappable exit. */
#exit-teaching-fab { display: none; }
.teaching-mode #exit-teaching-fab {
  display: flex;
  align-items: center;
  justify-content: center;
  position: fixed;
  top: 0.75rem;
  right: 0.75rem;
  z-index: 200;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 9999px;
  border: 1px solid rgba(255, 255, 255, 0.15);
  background: rgba(15, 17, 21, 0.55);
  color: #fff;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
  opacity: 0.4;
  cursor: pointer;
  transition: opacity 0.2s ease, transform 0.15s ease;
}
.teaching-mode #exit-teaching-fab:hover,
.teaching-mode #exit-teaching-fab:focus-visible { opacity: 1; }
.teaching-mode #exit-teaching-fab:active { transform: scale(0.92); opacity: 1; }
/* On touch/coarse-pointer devices keep it clearly visible (no hover to reveal it). */
@media (hover: none), (pointer: coarse) {
  .teaching-mode #exit-teaching-fab { opacity: 0.85; }
}

/* ==========================================================================
   6. UI COMPONENTS (Timers, Names, Toasts)
   ========================================================================== */
.name-shuffle-pill { 
  position: fixed; 
  top: 50%; 
  left: 50%; 
  transform: translate(-50%, -50%); 
  z-index: 300; 
  background: rgba(15, 23, 42, 0.95); 
  color: #e2e8f0; 
  padding: 2rem 4.5rem;
  border-radius: 9999px;
  /* Sized for the academy's dim projector / old TV — readable from the back
     of the room. nowrap + ellipsis + max-width below keep long names contained. */
  font-size: clamp(3.25rem, 11vw, 8rem);
  font-weight: 900; 
  text-transform: uppercase; 
  letter-spacing: 0.15em; 
  pointer-events: none; 
  box-shadow: 0 15px 40px rgba(0,0,0,0.6); 
  border: 3px solid rgba(255,255,255,0.1); 
  backdrop-filter: blur(8px); 
  text-align: center; 
  white-space: nowrap; 
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 90vw;
  transition: opacity 0.3s, transform 0.3s; 
}

.name-winner { 
  color: #4ade80; 
  border-color: #4ade80; 
  box-shadow: 0 0 60px rgba(74, 222, 128, 0.6); 
  transform: translate(-50%, -50%) scale(1.2); 
  transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); 
}

@keyframes timer-flash { 
  0%, 100% { color: #ef4444; text-shadow: 0 0 20px rgba(239, 68, 68, 0.8); } 
  50% { color: #fca5a5; text-shadow: 0 0 10px rgba(239, 68, 68, 0.4); } 
}
.timer-urgent { animation: timer-flash 0.5s ease-in-out infinite; }

@keyframes success-glow { 
  0% { box-shadow: 0 0 0 0 rgba(74, 222, 128, 0.7); } 
  70% { box-shadow: 0 0 0 20px rgba(74, 222, 128, 0); } 
  100% { box-shadow: 0 0 0 0 rgba(74, 222, 128, 0); } 
}
.success-pulse { animation: success-glow 1.5s ease-out; }

/* ==========================================================================
   7. GLASS-MORPHISM PANELS
   ========================================================================== */
.glass-panel {
  background: rgba(255, 255, 255, 0.65);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid rgba(255, 255, 255, 0.6);
  box-shadow: 0 10px 40px -10px rgba(31, 38, 135, 0.15);
}

.dark .glass-panel {
  background: rgba(19, 21, 26, 0.65);
  border: 1px solid rgba(255, 255, 255, 0.08);
  box-shadow: 0 10px 40px -10px rgba(0, 0, 0, 0.6);
}

/* Sleeker scrollbar inside the glass panel */
.glass-panel::-webkit-scrollbar-thumb {
  background: rgba(156, 163, 175, 0.5); 
}
.dark .glass-panel::-webkit-scrollbar-thumb {
  background: rgba(75, 85, 99, 0.5); 
}

/* ==========================================================================
   8. PREMIUM VISUAL EFFECTS
   ========================================================================== */

/* Glassmorphism */
body.glassmorphism-on #analysis-panel,
body.glassmorphism-on #right-panel,
body.glassmorphism-on #main-toolbar,
body.glassmorphism-on .modal-glass-target {
    background-color: rgba(255, 255, 255, 0.25) !important;
    backdrop-filter: blur(16px) !important;
    -webkit-backdrop-filter: blur(16px) !important;
    border-color: rgba(255, 255, 255, 0.4) !important;
    transform: translateZ(0);
    /* No `will-change: backdrop-filter` — leaving it on permanently keeps the blur
       layer hot and produced faint ghost edges over the animated background.
       translateZ(0) alone is enough to composite the panel on its own layer. */
}
html.dark body.glassmorphism-on #analysis-panel,
html.dark body.glassmorphism-on #right-panel,
html.dark body.glassmorphism-on #main-toolbar,
html.dark body.glassmorphism-on .modal-glass-target {
    background-color: rgba(15, 23, 42, 0.4) !important;
    backdrop-filter: blur(16px) !important;
    -webkit-backdrop-filter: blur(16px) !important;
    border-color: rgba(255, 255, 255, 0.1) !important;
    transform: translateZ(0);
    /* See note above: no permanent will-change on the blur. */
}

/* Animated Gradient Background
   Old version animated `background-position` on <body>, which is a main-thread
   paint: the whole body re-rasterized every frame, forcing every glass panel's
   backdrop-filter to re-blur → smearing/ghosting. Now the gradient lives on a
   fixed, over-sized, GPU-promoted ::after layer and we animate its TRANSFORM
   (compositor-only). Same drifting look, no per-frame body repaint. */
body.gradient-bg-on {
    background: #eef2ff !important;
}
html.dark body.gradient-bg-on {
    background: #0b1020 !important;
}
body.gradient-bg-on::after {
    content: '';
    position: fixed;
    inset: -40%;
    z-index: -2;
    pointer-events: none;
    background: linear-gradient(-45deg, #e0f2fe, #ddd6fe, #fbcfe8, #e0f2fe);
    background-size: 100% 100%;
    animation: gradientDrift 16s ease-in-out infinite alternate;
    transform: translateZ(0);
    backface-visibility: hidden;
    will-change: transform;
}
html.dark body.gradient-bg-on::after {
    background: linear-gradient(-45deg, #0f172a, #2e1065, #1e3a8a, #0f172a);
}
@keyframes gradientDrift {
    0%   { transform: translate3d(-12%, -12%, 0) rotate(0deg); }
    100% { transform: translate3d(12%, 12%, 0) rotate(8deg); }
}

/* Smooth Board Transitions */
body.smooth-themes-on div[id^="sq-"] {
    transition: background-color 0.5s ease-in-out;
}

/* Eval Glows */
@keyframes glow-brilliant {
    0%, 100% { box-shadow: inset 0 0 15px rgba(45, 212, 191, 0.5); }
    50% { box-shadow: inset 0 0 30px rgba(45, 212, 191, 1); }
}
@keyframes glow-blunder {
    0%, 100% { box-shadow: inset 0 0 15px rgba(239, 68, 68, 0.5); }
    50% { box-shadow: inset 0 0 30px rgba(239, 68, 68, 1); }
}
.eval-glow-brilliant { animation: glow-brilliant 1.5s ease-in-out 2 !important; }
.eval-glow-blunder { animation: glow-blunder 1.5s ease-in-out 2 !important; }

/* Glowing Background */
/* Animate opacity ONLY (no scale): a transform animation on this viewport-sized
   layer forces the radial gradient to re-rasterize every frame and every glass
   panel's backdrop-filter to re-blur, which caused flicker/ghosting trails.
   Opacity is GPU-composited and cheap. The layer is also promoted with
   translateZ(0)/will-change so it composites on its own and never repaints siblings. */
@keyframes ambientGlow {
    0%, 100% { opacity: 0.5; }
    50%       { opacity: 0.8; }
}
body.glowing-bg-on::before {
    content: '';
    position: fixed;
    inset: -20%;
    z-index: -1;
    border-radius: 50%;
    background: radial-gradient(ellipse at center,
        rgba(99, 102, 241, 0.45) 0%,
        rgba(139, 92, 246, 0.35) 25%,
        rgba(59, 130, 246, 0.25) 50%,
        transparent 70%);
    animation: ambientGlow 12s ease-in-out infinite alternate;
    pointer-events: none;
    transform: translateZ(0);
    backface-visibility: hidden;
    will-change: opacity;
}
html.dark body.glowing-bg-on::before {
    background: radial-gradient(ellipse at center,
        rgba(99, 102, 241, 0.6) 0%,
        rgba(139, 92, 246, 0.45) 25%,
        rgba(59, 130, 246, 0.3) 50%,
        transparent 70%);
}

/* Glow Color Variants — override only the ::before radial colors of the shared
   .glowing-bg-on aura. Declared AFTER the base so equal-specificity rules win. */
body.glow-emerald::before {
    background: radial-gradient(ellipse at center,
        rgba(16, 185, 129, 0.45) 0%, rgba(5, 150, 105, 0.35) 25%, rgba(20, 184, 166, 0.25) 50%, transparent 70%);
}
html.dark body.glow-emerald::before {
    background: radial-gradient(ellipse at center,
        rgba(16, 185, 129, 0.6) 0%, rgba(5, 150, 105, 0.45) 25%, rgba(20, 184, 166, 0.3) 50%, transparent 70%);
}

body.glow-sunset::before {
    background: radial-gradient(ellipse at center,
        rgba(251, 191, 36, 0.45) 0%, rgba(249, 115, 22, 0.35) 25%, rgba(244, 114, 182, 0.25) 50%, transparent 70%);
}
html.dark body.glow-sunset::before {
    background: radial-gradient(ellipse at center,
        rgba(251, 191, 36, 0.6) 0%, rgba(249, 115, 22, 0.45) 25%, rgba(244, 114, 182, 0.3) 50%, transparent 70%);
}

body.glow-crimson::before {
    background: radial-gradient(ellipse at center,
        rgba(239, 68, 68, 0.45) 0%, rgba(190, 24, 93, 0.35) 25%, rgba(127, 29, 29, 0.25) 50%, transparent 70%);
}
html.dark body.glow-crimson::before {
    background: radial-gradient(ellipse at center,
        rgba(239, 68, 68, 0.6) 0%, rgba(190, 24, 93, 0.45) 25%, rgba(127, 29, 29, 0.3) 50%, transparent 70%);
}

body.glow-cyan::before {
    background: radial-gradient(ellipse at center,
        rgba(34, 211, 238, 0.45) 0%, rgba(14, 165, 233, 0.35) 25%, rgba(59, 130, 246, 0.25) 50%, transparent 70%);
}
html.dark body.glow-cyan::before {
    background: radial-gradient(ellipse at center,
        rgba(34, 211, 238, 0.6) 0%, rgba(14, 165, 233, 0.45) 25%, rgba(59, 130, 246, 0.3) 50%, transparent 70%);
}

body.glow-amethyst::before {
    background: radial-gradient(ellipse at center,
        rgba(168, 85, 247, 0.45) 0%, rgba(139, 92, 246, 0.35) 25%, rgba(217, 70, 239, 0.25) 50%, transparent 70%);
}
html.dark body.glow-amethyst::before {
    background: radial-gradient(ellipse at center,
        rgba(168, 85, 247, 0.6) 0%, rgba(139, 92, 246, 0.45) 25%, rgba(217, 70, 239, 0.3) 50%, transparent 70%);
}

body.glow-blossom::before {
    background: radial-gradient(ellipse at center,
        rgba(244, 114, 182, 0.45) 0%, rgba(251, 113, 133, 0.35) 25%, rgba(249, 168, 212, 0.25) 50%, transparent 70%);
}
html.dark body.glow-blossom::before {
    background: radial-gradient(ellipse at center,
        rgba(244, 114, 182, 0.6) 0%, rgba(251, 113, 133, 0.45) 25%, rgba(249, 168, 212, 0.3) 50%, transparent 70%);
}

body.glow-abyss::before {
    background: radial-gradient(ellipse at center,
        rgba(20, 184, 166, 0.45) 0%, rgba(13, 148, 136, 0.35) 25%, rgba(30, 64, 175, 0.25) 50%, transparent 70%);
}
html.dark body.glow-abyss::before {
    background: radial-gradient(ellipse at center,
        rgba(20, 184, 166, 0.6) 0%, rgba(13, 148, 136, 0.45) 25%, rgba(30, 64, 175, 0.3) 50%, transparent 70%);
}

body.glow-magma::before {
    background: radial-gradient(ellipse at center,
        rgba(249, 115, 22, 0.45) 0%, rgba(234, 88, 12, 0.35) 25%, rgba(220, 38, 38, 0.25) 50%, transparent 70%);
}
html.dark body.glow-magma::before {
    background: radial-gradient(ellipse at center,
        rgba(249, 115, 22, 0.6) 0%, rgba(234, 88, 12, 0.45) 25%, rgba(220, 38, 38, 0.3) 50%, transparent 70%);
}

body.glow-graphite::before {
    background: radial-gradient(ellipse at center,
        rgba(148, 163, 184, 0.45) 0%, rgba(100, 116, 139, 0.35) 25%, rgba(71, 85, 105, 0.25) 50%, transparent 70%);
}
html.dark body.glow-graphite::before {
    background: radial-gradient(ellipse at center,
        rgba(148, 163, 184, 0.6) 0%, rgba(100, 116, 139, 0.45) 25%, rgba(71, 85, 105, 0.3) 50%, transparent 70%);
}

/* Noir: black-and-white. Light theme = dark halo bleeding out; dark theme = bright white halo. */
body.glow-noir::before {
    background: radial-gradient(ellipse at center,
        rgba(30, 41, 59, 0.4) 0%, rgba(15, 23, 42, 0.3) 25%, rgba(2, 6, 23, 0.2) 50%, transparent 70%);
}
html.dark body.glow-noir::before {
    background: radial-gradient(ellipse at center,
        rgba(255, 255, 255, 0.55) 0%, rgba(226, 232, 240, 0.4) 25%, rgba(203, 213, 225, 0.25) 50%, transparent 70%);
}

body.glow-silver::before {
    background: radial-gradient(ellipse at center,
        rgba(226, 232, 240, 0.45) 0%, rgba(203, 213, 225, 0.35) 25%, rgba(148, 163, 184, 0.25) 50%, transparent 70%);
}
html.dark body.glow-silver::before {
    background: radial-gradient(ellipse at center,
        rgba(226, 232, 240, 0.6) 0%, rgba(203, 213, 225, 0.45) 25%, rgba(148, 163, 184, 0.3) 50%, transparent 70%);
}

/* Coordinate Visibility Fix */
body.glowing-bg-on #rank-labels,
body.glowing-bg-on #file-labels,
body.glowing-bg-on #rank-labels-right,
body.glowing-bg-on #file-labels-top {
    text-shadow: 0 1px 3px rgba(255,255,255,0.9), 0 0 10px rgba(255,255,255,1);
    color: #1e293b !important;
}

html.dark body.glowing-bg-on #rank-labels,
html.dark body.glowing-bg-on #file-labels,
html.dark body.glowing-bg-on #rank-labels-right,
html.dark body.glowing-bg-on #file-labels-top {
    text-shadow: 0 1px 4px rgba(0,0,0,0.9), 0 0 12px rgba(0,0,0,1);
    color: #f8fafc !important;
}

/* Resting Piece Shadows */
.piece-shadow {
    filter: drop-shadow(0 4px 4px rgba(0,0,0,0.4));
    transform: translateZ(0);
    /* Only transition opacity (composited & cheap). Transitioning `filter` left
       drop-shadow ghost-trails when holding ←/→ to scrub moves, and the permanent
       `will-change: filter` kept that layer hot. */
    transition: opacity 0.5s ease-in-out !important;
}
html.dark .piece-shadow {
    filter: drop-shadow(0 4px 4px rgba(0,0,0,0.8));
    transform: translateZ(0);
    transition: opacity 0.5s ease-in-out !important;
}

/* Elegant physical table shadow beneath the board outer ring */
.premium-board-ring {
  box-shadow: 0 30px 60px -15px rgba(0, 0, 0, 0.65), 
              0 0 60px rgba(59, 130, 246, 0.15),
              inset 0 1px 1px rgba(255, 255, 255, 0.2);
  border: 1px solid rgba(255, 255, 255, 0.05);
}

.teaching-mode #app-toolbar { opacity: 0; }
.teaching-mode #app-toolbar:hover { opacity: 1; }

/* ============================================================
   WSB PANEL STYLES
   ============================================================ */

.wsb-row {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px 18px;
  border-radius: 16px;
  border: 2px solid transparent;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease, background 0.15s ease;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.wsb-row-idle {
  background: #fff;
  border-color: #e2e8f0;
}
.wsb-row-idle:hover {
  border-color: #93c5fd;
  box-shadow: 0 4px 12px rgba(59,130,246,0.13);
  transform: translateY(-2px);
}
html.dark .wsb-row-idle {
  background: #1a1d24;
  border-color: #2a2e38;
}
html.dark .wsb-row-idle:hover {
  border-color: #3b5bdb;
}
.wsb-row-selected {
  background: #eff6ff;
  border-color: #3b82f6;
  box-shadow: 0 0 0 3px rgba(59,130,246,0.2), 0 6px 16px rgba(59,130,246,0.15);
  transform: translateY(-2px);
}
html.dark .wsb-row-selected {
  background: rgba(59,130,246,0.10);
  border-color: #60a5fa;
}

/* Label + chip row */
.wsb-row-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.wsb-label {
  font-weight: 800;
  font-size: 22px;
  color: #1e293b;
  letter-spacing: -0.01em;
  line-height: 1;
}
html.dark .wsb-label { color: #f1f5f9; }

/* Value pill — big and bold */
.wsb-chip {
  font-size: 15px;
  font-weight: 800;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 6px 18px;
  border-radius: 999px;
  flex-shrink: 0;
  line-height: 1;
}
.wsb-chip-white {
  background: #f1f5f9;
  color: #334155;
  border: 2px solid #cbd5e1;
}
html.dark .wsb-chip-white {
  background: #e2e8f0;
  color: #1e293b;
  border-color: #cbd5e1;
}
.wsb-chip-black {
  background: #1e293b;
  color: #f8fafc;
  border: 2px solid #475569;
}
html.dark .wsb-chip-black {
  background: #0f172a;
  color: #e2e8f0;
  border-color: #334155;
}
.wsb-chip-equal {
  background: #94a3b8;
  color: #1e293b;
  border: 2px solid #64748b;
}
html.dark .wsb-chip-equal {
  background: #64748b;
  color: #f1f5f9;
  border-color: #94a3b8;
}

/* Advantage split bar */
.wsb-bar-track {
  display: flex;
  height: 10px;
  border-radius: 999px;
  overflow: hidden;
  background: #e2e8f0;
}
html.dark .wsb-bar-track { background: #2a2e38; }
.wsb-bar-white-seg {
  background: linear-gradient(90deg, #f1f5f9, #94a3b8);
  border-right: 2px solid #64748b;
  transition: width 0.4s cubic-bezier(0.4,0,0.2,1);
}
html.dark .wsb-bar-white-seg {
  background: linear-gradient(90deg, #e2e8f0, #94a3b8);
}
.wsb-bar-black-seg {
  background: linear-gradient(90deg, #334155, #0f172a);
  transition: width 0.4s cubic-bezier(0.4,0,0.2,1);
  flex: 1;
}
html.dark .wsb-bar-black-seg {
  background: linear-gradient(90deg, #475569, #1e293b);
}

/* Checkmate Animations */
@keyframes sad-balloon {
  0% { transform: scale(1) rotate(0deg); opacity: 1; }
  25% { transform: scale(0.8) rotate(15deg); }
  50% { transform: scale(0.6) rotate(-15deg); }
  75% { transform: scale(0.4) rotate(10deg); }
  100% { transform: scale(0) rotate(0deg); opacity: 0; }
}
.anim-sad-balloon { animation: sad-balloon 2.5s ease-in-out forwards !important; pointer-events: none; }

@keyframes supernova-explosion {
  0% { transform: scale(1); filter: brightness(1); opacity: 1; }
  50% { transform: scale(3); filter: brightness(3) sepia(1) hue-rotate(-50deg) saturate(5); opacity: 1; }
  100% { transform: scale(4); filter: brightness(5); opacity: 0; }
}
.anim-supernova { animation: supernova-explosion 1s cubic-bezier(0.25, 1, 0.5, 1) forwards !important; pointer-events: none; }

@keyframes fleeing-coward {
  0% { transform: translate(0, 0) rotate(0deg); opacity: 1; }
  20% { transform: translate(-20px, 0) rotate(-10deg); }
  40% { transform: translate(40px, -20px) rotate(45deg); }
  100% { transform: translate(1000px, -500px) rotate(720deg); opacity: 0; }
}
.anim-fleeing-coward { animation: fleeing-coward 1.5s cubic-bezier(0.5, 0, 1, 1) forwards !important; pointer-events: none; }

@keyframes toilet-flush {
  0% { transform: scale(1) rotate(0deg); opacity: 1; }
  100% { transform: scale(0) rotate(1080deg); opacity: 0; }
}
.anim-toilet-flush { animation: toilet-flush 1.5s ease-in forwards !important; pointer-events: none; }

@keyframes spring-back {
  0% { transform: scale(0); opacity: 0; }
  60% { transform: scale(1.25); opacity: 1; }
  85% { transform: scale(0.9); }
  100% { transform: scale(1); opacity: 1; }
}
.anim-spring-back { animation: spring-back 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards !important; }


/* Annotation box active state */
.annotation-active {
  border-top: 3px solid #3b82f6 !important;
  box-shadow: 0 -6px 30px rgba(59, 130, 246, 0.3) !important;
}

/* Print view */
#print-pgn-block { display: none; }
@media print {
  header, footer, #right-panel, #analysis-toolbar, #playback-controls,
  #eval-bar-container, #engine-clean-lines, #palette-dropdown,
  #chalkboard-overlay, #annotation-overlay, #name-shuffle-overlay,
  #toast, canvas, .fixed:not(#board-wrapper) { display: none !important; }
  body { background: white !important; color: black !important;
         position: static !important; overflow: auto !important; }
  main { display: block !important; padding: 0 !important; }
  #board-wrapper { width: 100% !important; max-width: 560px; margin: 0 auto !important;
                   print-color-adjust: exact; -webkit-print-color-adjust: exact; }
  /* Force background colors/images to print (squares and piece SVGs) */
  #board-wrapper * { print-color-adjust: exact; -webkit-print-color-adjust: exact; }
  #print-pgn-block { display: block !important; font-family: monospace; font-size: 11px;
                     margin-top: 16px; white-space: pre-wrap; color: black; }
}

/* Camp team scoreboard (/teams) — score pop on +1 */
@keyframes team-pop { 0% { transform: scale(1); } 40% { transform: scale(1.12); } 100% { transform: scale(1); } }
.team-pop { animation: team-pop 0.3s ease-out; }

/* Projector / Camp mode (/projector) — scale UI for the back of the room.
   zoom scales all Tailwind sizes inside each region together. */
.projector-mode #right-panel { zoom: 1.25; }
.projector-mode #main-header { zoom: 1.15; }
.projector-mode #toast { zoom: 1.5; }
.projector-mode #teams-overlay { zoom: 1.4; }

/* ============ SIMPLE MODE (/simple) — substitute-teacher demo board ============ */
/* Plain background: overrides gradient/glow/color themes but respects dark mode. */
body.simple-mode { background-color: #e2e8f0 !important; background-image: none !important; }
html.dark body.simple-mode { background-color: #0f1115 !important; }
body.simple-mode::before, body.simple-mode::after { display: none !important; } /* kill animated bg layers */
/* Hide all advanced chrome — the sub gets the board, the move list, and the dock. */
.simple-mode #analysis-toolbar,
.simple-mode #engine-clean-lines,
.simple-mode #game-info-panel,
.simple-mode #eval-graph-container,
.simple-mode #pgn-panel { display: none !important; }

/* ============ SUMMER CAMP MODE (settings toggle) ============ */
/* Banner: "18th Annual..." overlays the zen input for 10s, then fades out.
   Gold gradient text with a dark glow so it reads on any background. */
.camp-banner-text {
  background: linear-gradient(180deg, #fde68a 0%, #f59e0b 55%, #d97706 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 2px 3px rgba(0,0,0,0.7)) drop-shadow(0 0 14px rgba(245,158,11,0.35));
}
@keyframes camp-banner-in {
  0%   { opacity: 0; transform: translateY(-1.25rem) scale(0.92); }
  60%  { opacity: 1; transform: translateY(0.15rem) scale(1.03); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes camp-banner-out {
  from { opacity: 1; transform: translateY(0); }
  to   { opacity: 0; transform: translateY(-1rem); }
}
#camp-banner.camp-banner-in  { animation: camp-banner-in 0.7s cubic-bezier(0.22, 1, 0.36, 1) both; }
#camp-banner.camp-banner-out { animation: camp-banner-out 0.65s ease both; }

/* --- High Contrast (old TV): pure black canvas, white text, solid panels --- */
/* Overrides gradient/glow bg themes; pairs with the white/green 'camp' board theme. */
body.camp-high-contrast { background-color: #000 !important; background-image: none !important; }
body.camp-high-contrast::before, body.camp-high-contrast::after { display: none !important; }
.camp-high-contrast .glass-panel {
  background: #0a0a0d !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  border: 2px solid #9ca3af !important;
  box-shadow: none !important;
}
/* Coordinates: bright white and extra bold so they survive a washed-out TV */
.camp-high-contrast #file-labels,
.camp-high-contrast #file-labels-top,
.camp-high-contrast #rank-labels,
.camp-high-contrast #rank-labels-right {
  color: #ffffff !important;
  font-weight: 900;
  text-shadow: 0 1px 2px #000;
}
.camp-high-contrast #board-surface { border-color: #ffffff !important; }
.camp-high-contrast #zen-input { color: #ffffff !important; }

/* --- Projector Boost: punch up a dim 320-ANSI-lumen image --- */
/* Filter only the board area + header (not body: filtering an ancestor of
   position:fixed elements like the toast would break their positioning). */
.camp-projector-boost #board-wrapper,
.camp-projector-boost #main-header {
  filter: contrast(1.3) brightness(1.12) saturate(1.3);
}

/* ============ BLACK & WHITE MODE (settings toggle) ============ */
/* Full grayscale at the <html> level: covers every panel, modal, toast, and
   piece without breaking position:fixed (html's box is the viewport here).
   Pairs with the 'bw' white/gray board theme for print-diagram legibility. */
html.bw-mode { filter: grayscale(1) contrast(1.08); }
html.bw-mode body { background-color: #000 !important; background-image: none !important; }
html.bw-mode body::before, html.bw-mode body::after { display: none !important; }
html.bw-mode .glass-panel {
  background: #0b0b0b !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  border: 2px solid #d4d4d4 !important;
  box-shadow: none !important;
}
html.bw-mode #file-labels,
html.bw-mode #file-labels-top,
html.bw-mode #rank-labels,
html.bw-mode #rank-labels-right {
  color: #ffffff !important;
  font-weight: 900;
  text-shadow: 0 1px 2px #000;
}
html.bw-mode #board-surface { border-color: #ffffff !important; }
html.bw-mode #zen-input { color: #ffffff !important; }
/* Banner glow reads as a white flash in grayscale — keep it but tame it */
html.bw-mode .camp-banner-text { filter: drop-shadow(0 2px 3px rgba(0,0,0,0.8)); }

/* ============ SILLY GIMMICKS ============ */
/* Pancake: squash flat with a splat, then slowly reinflate */
@keyframes gimmick-pancake {
  0%   { transform: scaleY(1); }
  12%  { transform: scaleY(0.15) scaleX(1.45); }
  30%  { transform: scaleY(0.15) scaleX(1.45); }
  60%  { transform: scaleY(1.12) scaleX(0.94); }
  80%  { transform: scaleY(0.96) scaleX(1.03); }
  100% { transform: scaleY(1) scaleX(1); }
}
img.gimmick-pancake, span.gimmick-pancake {
  animation: gimmick-pancake 1.25s cubic-bezier(0.34, 1.56, 0.64, 1) both !important;
  transform-origin: 50% 100% !important;
}
/* Banana peel: 720° wipeout with a hop, lands back upright */
@keyframes gimmick-banana {
  0%   { transform: rotate(0deg) translateY(0); }
  20%  { transform: rotate(200deg) translateY(-35%); }
  45%  { transform: rotate(430deg) translateY(-10%); }
  70%  { transform: rotate(650deg) translateY(-20%); }
  88%  { transform: rotate(716deg) translateY(0); }
  100% { transform: rotate(720deg) translateY(0); }
}
img.gimmick-banana, span.gimmick-banana {
  animation: gimmick-banana 1.5s cubic-bezier(0.3, 0.7, 0.4, 1) both !important;
}
/* Googly eyes overlay */
.googly-eye {
  position: absolute;
  background: #fff;
  border: 2px solid #111;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 3px rgba(0,0,0,0.4);
}
.googly-pupil {
  width: 42%;
  height: 42%;
  background: #111;
  border-radius: 50%;
  transition: transform 0.08s linear;
}
/* Wrong-PIN shake on the vault door */
@keyframes gimmick-shake {
  0%,100% { transform: translateX(0) scale(1); }
  20% { transform: translateX(-10px) scale(1); }
  40% { transform: translateX(10px) scale(1); }
  60% { transform: translateX(-7px) scale(1); }
  80% { transform: translateX(7px) scale(1); }
}
.gimmick-shake { animation: gimmick-shake 0.45s ease both; }

/* Beginner mode help card slide-in */
@keyframes beginner-card-in {
  from { opacity: 0; transform: translate(-1.5rem, -50%); }
  to   { opacity: 1; transform: translate(0, -50%); }
}
#beginner-card.beginner-card-in { animation: beginner-card-in 0.35s cubic-bezier(0.22, 1, 0.36, 1) both; }

/* ============ GIMMICKS WAVE 2 ============ */
/* Sleepy: floating 💤 over undeveloped pieces */
@keyframes gimmick-zzz-float {
  0%   { opacity: 0; transform: translate(0, 0) scale(0.7); }
  20%  { opacity: 1; }
  100% { opacity: 0; transform: translate(8px, -22px) scale(1.15); }
}
.gimmick-zzz {
  position: absolute; top: 0; right: 2px; z-index: 45; pointer-events: none;
  font-size: 14px;
  animation: gimmick-zzz-float 2.4s ease-in-out infinite;
}
/* Startled awake */
@keyframes gimmick-startle {
  0%, 100% { transform: translateY(0) rotate(0); }
  15% { transform: translateY(-18%) rotate(-8deg); }
  35% { transform: translateY(0) rotate(6deg); }
  55% { transform: translateY(-8%) rotate(-4deg); }
  75% { transform: translateY(0) rotate(2deg); }
}
img.gimmick-startle, span.gimmick-startle { animation: gimmick-startle 0.7s ease both !important; }
/* Panic: trembling + sweat drop */
@keyframes gimmick-tremble {
  0%, 100% { transform: translateX(0) rotate(0); }
  25% { transform: translateX(-4%) rotate(-2deg); }
  50% { transform: translateX(4%) rotate(2deg); }
  75% { transform: translateX(-3%) rotate(-1deg); }
}
img.gimmick-tremble, span.gimmick-tremble { animation: gimmick-tremble 0.28s linear infinite !important; }
@keyframes gimmick-sweat-drip {
  0%   { opacity: 0; transform: translateY(-4px) scale(0.7); }
  30%  { opacity: 1; }
  100% { opacity: 0; transform: translateY(14px) scale(1.05); }
}
.gimmick-sweat {
  position: absolute; top: 4px; left: 2px; z-index: 45; pointer-events: none;
  font-size: 13px;
  animation: gimmick-sweat-drip 1.4s ease-in infinite;
}
/* Game show verdict stamps */
@keyframes gimmick-stamp-in {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(3) rotate(-18deg); }
  35%  { opacity: 1; transform: translate(-50%, -50%) scale(1) rotate(-8deg); }
  45%  { transform: translate(-50%, -50%) scale(1.15) rotate(-8deg); }
  55%  { transform: translate(-50%, -50%) scale(1) rotate(-8deg); }
  80%  { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(0.95) rotate(-8deg); }
}
.gimmick-stamp {
  position: absolute; left: 50%; top: 50%; z-index: 60; pointer-events: none;
  font-size: clamp(4rem, 18vmin, 9rem); font-weight: 900; line-height: 1;
  animation: gimmick-stamp-in 1.2s cubic-bezier(0.2, 1.4, 0.4, 1) both;
  text-shadow: 0 4px 16px rgba(0,0,0,0.5);
}
.gimmick-stamp-good { color: #22c55e; }
.gimmick-stamp-bad  { color: #ef4444; }
/* Ejector seat launch */
@keyframes gimmick-eject-fly {
  0%   { transform: translate(0, 0) rotate(0); opacity: 1; }
  15%  { transform: translate(calc(var(--ex) * 0.15), -18vh) rotate(160deg); }
  100% { transform: translate(var(--ex), -120vh) rotate(940deg); opacity: 1; }
}
img.gimmick-eject { animation: gimmick-eject-fly 1.25s cubic-bezier(0.3, 0.1, 0.6, 1) both; }

/* ============ GIMMICKS WAVE 3 ============ */
/* Pawn dreams: 👑 thought bubble over an advanced pawn */
@keyframes gimmick-dream-float {
  0%   { opacity: 0; transform: translate(-50%, 2px) scale(0.6); }
  25%  { opacity: 0.95; }
  100% { opacity: 0; transform: translate(-50%, -20px) scale(1.2); }
}
.gimmick-dream {
  position: absolute; top: 0; left: 50%; z-index: 46; pointer-events: none;
  font-size: 15px; filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.45));
  animation: gimmick-dream-float 2.8s ease-in-out infinite;
}
/* One square from promoting — dreams bigger and faster */
.gimmick-dream-close { font-size: 21px; animation-duration: 1.8s; }
/* Promotion: red carpet unrolls up the file (origin set inline for board flip) */
@keyframes gimmick-carpet-roll {
  0%   { opacity: 0; transform: scaleY(0); }
  35%  { opacity: 1; transform: scaleY(1); }
  80%  { opacity: 1; transform: scaleY(1); }
  100% { opacity: 0; transform: scaleY(1); }
}
.gimmick-carpet {
  position: absolute; z-index: 40; pointer-events: none;
  background: linear-gradient(90deg, #7f1d1d, #dc2626 18%, #ef4444 50%, #dc2626 82%, #7f1d1d);
  border-left: 2px solid #fbbf24; border-right: 2px solid #fbbf24;
  box-shadow: 0 0 26px rgba(220, 38, 38, 0.7);
  animation: gimmick-carpet-roll 2.6s ease-out both;
}
@keyframes gimmick-crown-pop {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(0.2) rotate(-25deg); }
  45%  { opacity: 1; transform: translate(-50%, -50%) scale(1.35) rotate(6deg); }
  70%  { opacity: 1; transform: translate(-50%, -50%) scale(1.05) rotate(0); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1.15) rotate(0); }
}
.gimmick-crown {
  position: absolute; z-index: 62; pointer-events: none; line-height: 1;
  font-size: clamp(2.5rem, 11vmin, 6rem);
  filter: drop-shadow(0 6px 18px rgba(0, 0, 0, 0.55));
  animation: gimmick-crown-pop 2.2s cubic-bezier(0.2, 1.4, 0.4, 1) both;
}
/* Dramatic check: trailer zoom-punch, and a slower desaturated push-in on mate */
@keyframes gimmick-drama-zoom {
  0%   { transform: scale(1); }
  18%  { transform: scale(1.12); }
  55%  { transform: scale(1.09); }
  100% { transform: scale(1); }
}
.gimmick-drama-zoom { animation: gimmick-drama-zoom 0.75s cubic-bezier(0.2, 1.2, 0.3, 1) both; will-change: transform; }
@keyframes gimmick-drama-mate {
  0%   { transform: scale(1); filter: saturate(1); }
  12%  { transform: scale(1.18); filter: saturate(1.5); }
  70%  { transform: scale(1.14); filter: saturate(0.55) contrast(1.15); }
  100% { transform: scale(1); filter: saturate(1); }
}
.gimmick-drama-mate { animation: gimmick-drama-mate 2.1s cubic-bezier(0.15, 0.9, 0.2, 1) both; will-change: transform; }
@keyframes gimmick-vignette-in {
  0%   { opacity: 0; }
  20%  { opacity: 1; }
  70%  { opacity: 0.8; }
  100% { opacity: 0; }
}
.gimmick-vignette {
  position: fixed; inset: 0; z-index: 300; pointer-events: none;
  background: radial-gradient(ellipse at center, rgba(220, 38, 38, 0) 38%, rgba(190, 18, 18, 0.5) 78%, rgba(120, 8, 8, 0.82) 100%);
  animation: gimmick-vignette-in 0.9s ease-out both;
}
.gimmick-vignette-mate {
  background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0) 30%, rgba(60, 0, 0, 0.6) 72%, rgba(0, 0, 0, 0.9) 100%);
  animation-duration: 2.2s;
}

/* ------------------------------------------------------------------ *
 * Legibility: kill the tiny "useless" font sizes app-wide.
 * These Tailwind arbitrary sizes (8–11px) were too small to read on a
 * projector or for young students. Bump them to a readable floor here
 * with !important so it wins over the runtime Tailwind utilities,
 * regardless of stylesheet order. One place to tune, covers every use.
 * (Leaves text-[13px]/[15px] alone — already legible.)
 * ------------------------------------------------------------------ */
.text-\[8px\]  { font-size: 15px !important; line-height: 1.35 !important; }
.text-\[9px\]  { font-size: 15px !important; line-height: 1.35 !important; }
.text-\[10px\] { font-size: 15px !important; line-height: 1.4  !important; }
.text-\[11px\] { font-size: 15px !important; line-height: 1.4  !important; }

/* Same idea one step up: Tailwind's text-xs (12px) and text-sm (14px) carry most
   of the app's chrome — toolbar labels, panel headers, help text, the move list.
   Both are a strain on the academy's dim projector and its old TV. Raise the
   floor for every use; anything that needs to stay small sets an explicit size.
   Line-heights are lifted with them so nothing collides in the dense panels. */
.text-xs { font-size: 14px !important; line-height: 1.45 !important; }
.text-sm { font-size: 15.5px !important; line-height: 1.5 !important; }

/* The floors above MUST be !important — Tailwind's runtime injects its utilities
   after this stylesheet, so a plain rule would lose. But !important also beats
   the responsive variants regardless of media query, which would pin an element
   like `text-sm sm:text-base` at 15.5px and make it SHRINK past 640px. Lift the
   variants that are actually paired with text-xs/text-sm so they still step up. */
@media (min-width: 640px) {
  .sm\:text-sm   { font-size: 15.5px !important; line-height: 1.5 !important; }
  .sm\:text-base { font-size: 17px   !important; line-height: 1.55 !important; }
}
@media (min-width: 768px) {
  .md\:text-sm   { font-size: 15.5px !important; line-height: 1.5 !important; }
  .md\:text-base { font-size: 17px   !important; line-height: 1.55 !important; }
}
