@keyframes bounce-in {
    0% { transform: scale(0.95); }
    50% { transform: scale(1.03); }
    100% { transform: scale(1); }
}
@keyframes pulse-success {
    0% { box-shadow: 0 0 0 0 rgba(123, 201, 122, 0.6); }
    100% { box-shadow: 0 0 0 24px rgba(123, 201, 122, 0); }
}
@keyframes welcome-in {
    0% { opacity: 0; transform: scale(0.95); }
    100% { opacity: 1; transform: scale(1); }
}
@keyframes welcome-fade {
    0% { opacity: 0; }
    100% { opacity: 1; }
}

/* Errorless baseline — no shake, no red, no harsh borders */
.is-correct {
    border-color: #7BC97A !important;
    animation: bounce-in 0.5s ease, pulse-success 0.8s ease;
}
.is-wrong-card {
    border-color: #E5E0F8 !important;
}
.is-dimmed {
    opacity: 0.6;
    transition: opacity 0.3s ease;
}

/* Sensory mode: CALM — minimal celebration */
[data-sensory-mode="calm"] #card.is-correct {
    animation: welcome-fade 0.2s linear;
    border-color: #C9E5C9 !important;
    box-shadow: none;
}

/* Sensory mode: STANDARD (default) — bounce + pulse */
[data-sensory-mode="standard"] #card.is-correct {
    animation: bounce-in 0.5s ease;
}

/* Sensory mode: HIGH — bounce + pulse stack */
[data-sensory-mode="high"] #card.is-correct {
    animation: bounce-in 0.5s ease, pulse-success 0.8s ease;
}

/* Welcome ritual scaling per sensory mode */
[data-sensory-mode="calm"] .welcome-anim {
    animation: welcome-fade 0.2s linear both;
}
[data-sensory-mode="standard"] .welcome-anim,
[data-sensory-mode="high"] .welcome-anim {
    animation: welcome-in 0.3s ease-out both;
}

/* Hard kill-switch — parent-controlled reduced motion */
[data-reduced-motion="1"] *,
[data-reduced-motion="1"] *::before,
[data-reduced-motion="1"] *::after {
    animation: none !important;
    transition: none !important;
}

/* Game 4 — Drag-to-Slot tile and slot CSS */
@keyframes tile-shimmer {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.7; }
}
.tile-shimmer { animation: tile-shimmer 200ms ease-out; }

@keyframes slot-pulse {
    0%, 100% { border-color: var(--color-brand-100, #e0d7ff); box-shadow: none; }
    50% { border-color: var(--color-brand-500, #6D4DFF); box-shadow: 0 0 0 4px rgba(109,77,255,0.2); }
}
.slot-pulse { animation: slot-pulse 1000ms ease-in-out 1.5; }

.tile-correct {
    transition: box-shadow 200ms ease-out;
    box-shadow: 0 0 0 6px rgba(91,179,90,0.25);
}

.sortable-chosen.tile {
    background-color: var(--color-brand-500, #6D4DFF);
    color: white;
    box-shadow: 0 10px 25px rgba(0,0,0,0.15);
    transform: scale(1.05);
}

.slot {
    min-height: 72px;
}

/* Wrong-answer input border — appears for 5s after incorrect answer */
@keyframes wrong-pulse {
    0%, 100% { border-color: #EF4444; }
    50% { border-color: #FCA5A5; }
}
.is-wrong-input {
    border-color: #EF4444 !important;
    animation: wrong-pulse 1s ease-in-out infinite;
}
[data-sensory-mode="calm"] .is-wrong-input {
    animation: none;
    border-color: #FCA5A5 !important;
}
[data-reduced-motion="1"] .is-wrong-input {
    animation: none !important;
}

/* Tap-to-Match choice grid */
.choices-grid {
    display: grid;
    gap: 1rem;
}
.choices-grid--2 {
    grid-template-columns: repeat(2, 1fr);
}
.choices-grid--3 {
    grid-template-columns: repeat(3, 1fr);
}
.choices-grid--4 {
    grid-template-columns: repeat(2, 1fr);
}
.choice-btn {
    border-radius: 1rem;
    border: 4px solid transparent;
    min-height: 80px;
    cursor: pointer;
    background: #F5F3FF;
    transition: border-color 0.2s ease, opacity 0.2s ease;
    padding: 0.5rem;
}
.choice-btn img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    border-radius: 0.75rem;
}
.choice-btn--correct {
    border-color: #7BC97A;
    animation: bounce-in 0.4s ease;
}
.choice-btn--wrong {
    opacity: 0.45;
    pointer-events: none;
}
[data-reduced-motion="1"] .choice-btn--correct {
    animation: none;
}

/* === Shared image frame — consistent across all child-facing games === */
/* Use on any container that wraps an AI-generated image. Children see a clean
   white card with the full picture (object-fit:contain) — no cropping, equal
   padding, identical look between Tap / Drag / Sentence Strip / Typing. */
.image-frame {
    background: #ffffff;
    border-radius: 1rem;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0.5rem;
    box-shadow: 0 1px 3px rgba(76, 29, 149, 0.10);
    box-sizing: border-box;
}
.image-frame > img {
    width: 100%;
    height: 100%;
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    display: block;
}

/* === Tap game grid — biggest square fitting the available area === */
/* Wrap the grid in .tap-grid-wrap (container-type: size) and the grid uses
   container query units to size itself as min(width, height). Each cell is
   guaranteed square because the whole grid is square in a 2×2 / 3×3 layout. */
.tap-grid-wrap {
    container-type: size;
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}
.tap-grid {
    width: min(100cqw, 100cqh);
    aspect-ratio: 1 / 1;
    display: grid;
    gap: 0.75rem;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: repeat(2, minmax(0, 1fr));
}
.tap-grid--3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-template-rows: repeat(3, minmax(0, 1fr));
}
