/* SchoolDay v229: the maths games.

   Everything here is CSS keyframes on spans the game creates and removes. No
   canvas, no library, and nothing left animating after the overlay closes --
   the game removes the overlay, and every effect goes with it. */

.maths-game-overlay-v229 {
  position: fixed;
  inset: 0;
  z-index: 1200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  overflow: hidden;
  background: linear-gradient(180deg, #eaf6ff 0%, #fdfbe9 100%);
}
body.maths-game-open-v229 { overflow: hidden; }

.maths-game-panel-v229 {
  position: relative;
  width: min(46rem, 96vw);
  max-height: 94vh;
  overflow-y: auto;
  padding: 1.4rem 1.2rem 1.6rem;
  border-radius: 1.2rem;
  background: rgba(255, 255, 255, .92);
  box-shadow: 0 1.2rem 2.4rem rgba(23, 63, 104, .18);
  text-align: center;
}
.maths-game-close-v229 {
  position: absolute;
  top: .5rem;
  right: .6rem;
  width: 2.4rem;
  height: 2.4rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: #e7eef5;
  color: #173f68;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}
.maths-game-progress-v229 { margin: .2rem 0 .6rem; color: #5b7185; font-size: .9rem; }
.maths-game-prompt-v229 { margin: 0; font-size: clamp(1.3rem, 3.4vw, 1.9rem); color: #173f68; }
.maths-game-number-v229 {
  display: inline-block;
  min-width: 2.4ch;
  padding: 0 .3rem;
  font-size: clamp(3.4rem, 13vw, 6rem);
  font-weight: 800;
  line-height: 1.05;
  color: #0f3d6b;
}
.maths-game-word-v229 { margin: 0 0 .5rem; font-size: 1.15rem; color: #446079; text-transform: lowercase; }
.maths-game-repeat-v229 { margin-bottom: 1rem; }
.maths-game-score-v229 { margin: 1rem 0 0; color: #5b7185; }

.maths-game-choices-v229 {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: center;
}
.maths-game-choice-v229 {
  position: relative;
  display: grid;
  place-items: center;
  width: clamp(7rem, 26vw, 10rem);
  height: clamp(7rem, 26vw, 10rem);
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  touch-action: manipulation;
  transition: transform .16s ease;
}
.maths-game-choice-v229:active { transform: scale(.96); }
/* BOTH of these are positioned with an explicit z-index, and that is not
   decoration. clip-path creates a stacking context, so a clipped shape paints
   in the positioned layer while a plain sibling span paints below it -- the
   triangle and the hexagon covered their own numbers, while the circle, which
   uses border-radius and makes no stacking context, did not. A child cannot
   play a game whose numbers are hidden behind the shapes. */
.maths-game-choice-shape-v229 {
  grid-area: 1 / 1;
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  background: #7cc6f5;
  box-shadow: 0 .35rem .8rem rgba(23, 63, 104, .22);
}
.maths-game-choice-label-v229 {
  grid-area: 1 / 1;
  position: relative;
  z-index: 2;
  font-size: clamp(2.4rem, 9vw, 3.6rem);
  font-weight: 800;
  color: #0b2d4d;
  /* The number sits on top of a coloured shape, so it needs an outline to stay
     readable whichever colour the shape is. */
  text-shadow: 0 0 .35rem #fff, 0 0 .7rem #fff;
  pointer-events: none;
}

/* The shapes. clip-path rather than images, so they scale to any size and add
   nothing to download. */
.maths-shape-circle   { border-radius: 50%; background: #7cc6f5; }
.maths-shape-square   { border-radius: 1rem; background: #9be59b; }
.maths-shape-triangle { clip-path: polygon(50% 4%, 96% 96%, 4% 96%); background: #ffd166; }
.maths-shape-hexagon  { clip-path: polygon(25% 5%, 75% 5%, 100% 50%, 75% 95%, 25% 95%, 0 50%); background: #f7a4c8; }
.maths-shape-star     { clip-path: polygon(50% 2%, 61% 36%, 98% 36%, 68% 58%, 79% 94%, 50% 72%, 21% 94%, 32% 58%, 2% 36%, 39% 36%); background: #c4b5fd; }
.maths-shape-heart    { clip-path: polygon(50% 96%, 6% 47%, 6% 24%, 27% 8%, 50% 26%, 73% 8%, 94% 24%, 94% 47%); background: #fca5a5; }

.maths-game-choice-v229.is-correct-v229 .maths-game-choice-shape-v229 {
  animation: maths-correct-v229 .7s ease-out;
}
.maths-game-choice-v229.is-wrong-v229 .maths-game-choice-shape-v229 {
  animation: maths-wrong-v229 .5s ease-in-out;
}
@keyframes maths-correct-v229 {
  0% { transform: scale(1); }
  40% { transform: scale(1.16); filter: brightness(1.25); }
  100% { transform: scale(1); }
}
@keyframes maths-wrong-v229 {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-9px); }
  50% { transform: translateX(9px); }
  80% { transform: translateX(-5px); }
}

.maths-game-finish-v229 h2 { font-size: clamp(1.6rem, 5vw, 2.4rem); color: #0f3d6b; }
.maths-game-actions-v229 { display: flex; gap: .6rem; justify-content: center; flex-wrap: wrap; margin-top: 1rem; }

/* --- The effects ------------------------------------------------------- */

.maths-balloon-v229,
.maths-confetti-v229,
.maths-float-v229,
.maths-flyby-v229 { position: absolute; pointer-events: none; }

.maths-balloon-v229 {
  bottom: -14vh;
  width: 3.2rem;
  height: 4rem;
  border-radius: 50% 50% 46% 46%;
  background: var(--balloon-colour, #ef4444);
  z-index: 5;
  animation: maths-balloon-v229 3.2s ease-in forwards;
}
/* The string. A pseudo-element rather than a second span, because seven
   balloons should not be fourteen nodes. */
.maths-balloon-v229::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 100%;
  width: 1px;
  height: 2.2rem;
  background: rgba(30, 41, 59, .35);
}
@keyframes maths-balloon-v229 {
  0% { transform: translate(0, 0) rotate(-4deg); opacity: 0; }
  10% { opacity: 1; }
  100% { transform: translate(var(--balloon-drift, 0), -118vh) rotate(6deg); opacity: 0; }
}

.maths-confetti-v229 {
  top: -4vh;
  width: .55rem;
  height: .9rem;
  border-radius: .1rem;
  z-index: 6;
  animation: maths-confetti-v229 2.4s cubic-bezier(.3, .6, .5, 1) forwards;
}
@keyframes maths-confetti-v229 {
  0% { transform: translate(0, 0) rotate(0deg); opacity: 0; }
  8% { opacity: 1; }
  100% { transform: translate(var(--confetti-drift, 0), 108vh) rotate(var(--confetti-spin, 360deg)); opacity: .9; }
}

.maths-float-v229 {
  z-index: 7;
  font-size: clamp(1.6rem, 4vw, 2.4rem);
  line-height: 1;
  transform: translate(-50%, -50%);
  animation: maths-float-v229 .9s ease-out forwards;
}
@keyframes maths-float-v229 {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(.5); }
  15% { opacity: 1; }
  100% { opacity: 0; transform: translate(calc(-50% + var(--float-x, 0)), calc(-50% + var(--float-y, -60px))) scale(1.1); }
}

.maths-flyby-v229 {
  left: -12vw;
  z-index: 4;
  font-size: clamp(2rem, 6vw, 3.4rem);
  animation: maths-flyby-v229 3s linear forwards;
}
.maths-flyby-v229.from-right {
  left: auto;
  right: -12vw;
  animation-name: maths-flyby-right-v229;
}
@keyframes maths-flyby-v229 {
  0% { transform: translateX(0) translateY(0); }
  50% { transform: translateX(56vw) translateY(-2.2rem); }
  100% { transform: translateX(124vw) translateY(0); }
}
@keyframes maths-flyby-right-v229 {
  0% { transform: translateX(0) translateY(0) scaleX(-1); }
  50% { transform: translateX(-56vw) translateY(-2.2rem) scaleX(-1); }
  100% { transform: translateX(-124vw) translateY(0) scaleX(-1); }
}

/* A child who is sensitive to motion should still be able to play. The game
   itself is unchanged; only the decoration stops moving. */
@media (prefers-reduced-motion: reduce) {
  .maths-balloon-v229,
  .maths-confetti-v229,
  .maths-float-v229,
  .maths-flyby-v229 { display: none; }
  .maths-game-choice-v229.is-correct-v229 .maths-game-choice-shape-v229,
  .maths-game-choice-v229.is-wrong-v229 .maths-game-choice-shape-v229 { animation: none; }
  .maths-game-choice-v229.is-correct-v229 .maths-game-choice-shape-v229 { filter: brightness(1.25); }
}

/* --- The launch buttons on the public maths homework card --------------- */
.public-maths-games-v229 { display: flex; flex-wrap: wrap; gap: .5rem; margin: .8rem 0 0; }
.public-maths-games-v229 > p { flex: 1 1 100%; margin: 0 0 .2rem; }

/* The panel and the effects are siblings, not parent and child: the game
   rewrites the panel on every question, and a celebration appended inside it
   would be deleted by the next render -- which is exactly what the finish
   screen's balloons did before this existed. */
.maths-game-host-v229 { position: relative; z-index: 2; display: contents; }
.maths-game-effect-layer-v229 {
  position: absolute;
  inset: 0;
  z-index: 3;
  overflow: hidden;
  pointer-events: none;
}

/* v235: the gradient is the window, the question is a card, the answers are
   bright rows, and the shapes are emoji.

   WHY THE SHAPES ARE TEXT NOW
   ---------------------------
   They were spans with a clip-path and a background colour, and v234 made that
   background the school's ANSWER BUBBLE colour, which defaults to white. One
   large bubble with a numeral painted on it read fine. Four small ones with
   nothing on them were white on white -- invisible in the counting game, and
   in the other one the border was the only thing left, which drew fragments
   rather than shapes. Emoji carry their own colour and cannot be styled into
   nothing by a colour chosen for something else.

   The old .maths-game-choice-shape-v229 / -label-v229 class names are gone
   from the answers on purpose: v234 targets them, and those rules are right
   for a bubble and wrong for a row of emoji. */

/* The whole window is the school's gradient. The panel was a white card
   sitting on top of it, so the gradient was a border. */
.maths-game-overlay-v229 .maths-game-panel-v229 {
  background: none;
  box-shadow: none;
  padding-top: .6rem;
}

/* The question is the only white card on the screen. */
.maths-question-box-v235 {
  margin: .2rem auto 1.1rem;
  padding: .8rem 1rem 1rem;
  max-width: 32rem;
  border-radius: 1.1rem;
  background: #fff;
  box-shadow: 0 .5rem 1.2rem rgba(23, 63, 104, .16);
}
.maths-question-box-v235 .maths-game-prompt-v229 {
  margin: 0;
  font-size: clamp(1.1rem, 3.2vw, 1.5rem);
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--maths-question-colour-v234, #173f68);
}
.maths-question-box-v235 .maths-game-word-v229 { margin: .35rem 0 0; }

/* The numeral being asked for. */
.maths-tap-number-v235 {
  display: inline-block;
  margin-left: .2rem;
  font-size: clamp(2.6rem, 11vw, 4.2rem);
  font-weight: 800;
  line-height: 1.02;
  letter-spacing: 0;
  vertical-align: -.12em;
  color: var(--maths-question-colour-v234, #0f3d6b);
}

/* The shapes being counted. A run of nine wraps rather than running off a
   phone, and the line-height keeps two rows from touching. */
/* Balanced rows of at most four, the column count computed per question by
   the engine and handed over as a custom property. */
.maths-tap-shapes-v235 {
  display: grid;
  grid-template-columns: repeat(var(--maths-columns-v235, 3), auto);
  justify-content: center;
  gap: .15rem .35rem;
  margin-top: .5rem;
  font-size: clamp(1.7rem, 7.5vw, 2.6rem);
  line-height: 1.25;
  word-break: break-word;
}
.maths-emoji-v235 { display: inline-block; line-height: 1.15; }

/* ---- The answers: bright bordered rows ------------------------------- */

.maths-game-choices-v229 {
  flex-direction: column;
  /* nowrap MATTERS. The v229 rule sets flex-wrap: wrap, and wrap in a COLUMN
     with a bounded height packs the rows into narrow side-by-side columns --
     which is why the buttons came out about a hundred pixels wide with the
     emoji spilling out of the bottom of them. */
  flex-wrap: nowrap;
  align-items: stretch;
  gap: .6rem;
  width: 100%;
  max-width: 32rem;
  margin: 0 auto;
}

/* Overrides the v229 fixed square. A row holds nine emoji; a square of
   clamp(7rem,26vw,10rem) does not. */
.maths-game-choice-v229.maths-answer-v235 {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  width: auto;
  height: auto;
  min-height: 3.6rem;
  /* Grow into whatever width the panel gives. The v229 button is a fixed
     square; at phone width that left the emoji nowhere to go. */
  width: 100%;
  box-sizing: border-box;
  overflow: hidden;
  padding: .6rem .8rem;
  border: .2rem solid var(--maths-bubble-border-v234, #2f7dbd);
  border-radius: 1.1rem;
  background: var(--maths-bubble-v234, #fff);
  box-shadow: 0 .25rem .6rem rgba(23, 63, 104, .14);
  transition: transform .14s ease, filter .2s ease, opacity .2s ease;
}
.maths-answer-shapes-v235 {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: .1rem .3rem;
  max-width: 100%;
  font-size: clamp(1.5rem, 6.5vw, 2.2rem);
  line-height: 1.25;
}
.maths-answer-number-v235 {
  font-size: clamp(1.9rem, 8vw, 2.9rem);
  font-weight: 800;
  line-height: 1.1;
  color: var(--maths-answer-text-v234, #0f3d6b);
}

/* ---- Right and wrong ------------------------------------------------- */

/* A small happy hop. Big enough to notice out of the corner of an eye,
   small enough not to move the row below it. */
.maths-game-choice-v229.maths-answer-v235.is-correct-v229 {
  animation: maths-hop-v235 .55s ease-out;
  border-color: #1e9e5a;
  background: #eafaf1;
}
@keyframes maths-hop-v235 {
  0%   { transform: translateY(0) scale(1); }
  30%  { transform: translateY(-.45rem) scale(1.03); }
  55%  { transform: translateY(0) scale(1); }
  72%  { transform: translateY(-.16rem) scale(1.01); }
  100% { transform: translateY(0) scale(1); }
}

/* Wrong: a short shake, then it greys out and stays greyed while the child
   looks at the others. It is not removed -- the same question is asked again,
   and a button that vanished would make the screen change under them. */
.maths-game-choice-v229.maths-answer-v235.is-wrong-v229 {
  animation: maths-shake-v235 .42s ease-in-out;
  filter: grayscale(1);
  opacity: .5;
  border-color: #9aa7b4;
  background: #eef1f4;
}
@keyframes maths-shake-v235 {
  0%, 100% { transform: translateX(0); }
  22%      { transform: translateX(-.42rem); }
  48%      { transform: translateX(.42rem); }
  76%      { transform: translateX(-.2rem); }
}

/* A child sensitive to motion still gets the colour change, which is what
   carries the meaning. Only the movement stops. */
@media (prefers-reduced-motion: reduce) {
  .maths-game-choice-v229.maths-answer-v235.is-correct-v229,
  .maths-game-choice-v229.maths-answer-v235.is-wrong-v235,
  .maths-game-choice-v229.maths-answer-v235.is-wrong-v229 { animation: none; }
}

.maths-visually-hidden-v235 {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

/* ======================================================================
   v235.1: the shapes shrink, the background drifts, and a balloon can be
   caught.
   ====================================================================== */

/* SHRINK TO FIT. The emoji had a floor of 1.5rem, so nine of them in a
   phone-width row could not get any smaller and pushed out of the button
   instead. The floor is now low enough that the widest row the game can ask
   for still fits, and min-width:0 lets a flex child actually shrink -- without
   it a flex item refuses to go below its content width and overflows. */
.maths-answer-shapes-v235 {
  min-width: 0;
  font-size: clamp(1rem, 5.2vw, 2.1rem);
}
.maths-tap-shapes-v235 { font-size: clamp(1.1rem, 6.4vw, 2.5rem); }
.maths-game-choice-v229.maths-answer-v235 > * { min-width: 0; }

/* The answer rows match the question card exactly, so the two read as one
   column rather than as a card with some buttons under it. */
.maths-question-box-v235,
.maths-game-choices-v229 { width: 100%; max-width: 32rem; margin-left: auto; margin-right: auto; }

/* ---- Quicker confetti ------------------------------------------------ */

/* Was 2.4s with up to .35s of stagger, so the last piece landed nearly three
   seconds after the answer -- long enough that a child had moved on. */
.maths-confetti-v229 { animation-duration: 1.2s; }

/* ---- The drifting background ----------------------------------------- */

.maths-drifter-v235 {
  position: absolute;
  bottom: -12vh;
  z-index: 1;
  opacity: .28;
  pointer-events: none;
  will-change: transform;
  animation: maths-drift-v235 var(--drift-duration, 30s) linear infinite;
  animation-delay: var(--drift-delay, 0s);
}
@keyframes maths-drift-v235 {
  0%   { transform: translate(0, 0) rotate(0deg); opacity: 0; }
  8%   { opacity: .3; }
  25%  { transform: translate(var(--drift-sway, 24px), -30vh) rotate(calc(var(--drift-spin, 200deg) * .25)); }
  50%  { transform: translate(0, -58vh) rotate(calc(var(--drift-spin, 200deg) * .5)); }
  75%  { transform: translate(calc(var(--drift-sway, 24px) * -1), -86vh) rotate(calc(var(--drift-spin, 200deg) * .75)); }
  92%  { opacity: .28; }
  100% { transform: translate(0, -118vh) rotate(var(--drift-spin, 200deg)); opacity: 0; }
}

/* ---- The balloon you can catch --------------------------------------- */

/* The effect layer sets pointer-events: none so a celebration can never eat a
   tap meant for an answer. This one element opts back in, and nothing else
   does. */
.maths-pop-balloon-v235 {
  position: absolute;
  bottom: -10vh;
  z-index: 8;
  padding: .1rem .25rem;
  border: 0;
  background: none;
  font-size: clamp(2.2rem, 9vw, 3.4rem);
  line-height: 1;
  cursor: pointer;
  pointer-events: auto;
  animation: maths-balloon-rise-v235 10s linear forwards;
}
.maths-pop-balloon-v235:active { transform: scale(.94); }
@keyframes maths-balloon-rise-v235 {
  0%   { transform: translate(0, 0) rotate(-5deg); opacity: 0; }
  6%   { opacity: 1; }
  50%  { transform: translate(var(--balloon-drift, 0), -58vh) rotate(5deg); }
  100% { transform: translate(0, -122vh) rotate(-4deg); opacity: .9; }
}

/* ---- The pop --------------------------------------------------------- */

.maths-boom-v235 {
  position: absolute;
  z-index: 9;
  font-size: 2rem;
  line-height: 1;
  pointer-events: none;
  transform: translate(-50%, -50%);
  animation: maths-boom-v235 .55s ease-out forwards;
}
@keyframes maths-boom-v235 {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(.35); }
  18%  { opacity: 1; transform: translate(-50%, -50%) scale(1.35); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(2.4); }
}

/* Confetti from a pop starts AT the balloon and throws outward, rather than
   falling from the top of the screen like the celebration confetti. */
.maths-confetti-v229.is-burst-v235 {
  top: auto;
  width: .4rem;
  height: .62rem;
  transform: translate(-50%, -50%);
  animation: maths-burst-v235 .7s cubic-bezier(.2, .7, .4, 1) forwards;
}
@keyframes maths-burst-v235 {
  0%   { opacity: 0; transform: translate(-50%, -50%) rotate(0deg) scale(.6); }
  12%  { opacity: 1; }
  100% { opacity: 0;
         transform: translate(calc(-50% + var(--confetti-drift, 0)), calc(-50% + 90px))
                    rotate(var(--confetti-spin, 300deg)) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .maths-drifter-v235,
  .maths-pop-balloon-v235 { display: none; }
  .maths-boom-v235 { animation-duration: .3s; }
}
