/* ==========================================================================
   Kartuli — shared game surfaces (CONTRACT.md "Game contract")
   A game module renders ONLY inside its root and reuses these classes:
     .g-stage  .g-card  .g-choice  .g-grid  .g-hud  .g-btn
   States:  .is-correct  .is-wrong  .is-selected  .is-done

   HARD RULE (contract #2): letter grids are CSS Grid, one cell per glyph.
   Never letter-spacing a Georgian string — it breaks Mkhedruli apart.

   Sections
     1 Stage & layout
     2 HUD: progress, score, streak, timer
     3 Prompt card
     4 Choices
     5 Letter / tile grids
     6 Game buttons
     7 States
     8 Result & celebration
     9 Keyframes
   ========================================================================== */

/* ==========================================================================
   1. Stage — the game viewport. Fits 360x640 with the HUD and the answer
   area both visible; the answer area sits low, in thumb reach.
   ========================================================================== */

.g-stage {
  --g-accent: var(--wine);
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: var(--sp-4);
  width: 100%;
  max-width: 46rem;
  margin-inline: auto;
  min-height: calc(100svh - var(--topbar-h));
  padding: var(--sp-4) var(--gutter) calc(var(--sp-4) + var(--safe-b));
  /* Stop a double-tap from zooming the board mid-game. */
  touch-action: manipulation;
  -webkit-user-select: none;
  user-select: none;
}
.g-stage--wide { max-width: 60rem; }

/* The middle band: prompt, board, whatever the game puts there. */
.g-body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--sp-5);
  min-height: 0;
}

/* The bottom band: answers. Always reachable with a thumb. */
.g-actions {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

/* ==========================================================================
   2. HUD — progress, score, streak, timer
   ========================================================================== */

.g-hud {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-height: var(--tap);
}
.g-hud__quit {
  display: grid;
  place-items: center;
  width: var(--tap); height: var(--tap);
  margin-inline-start: calc(-1 * var(--sp-2));
  border-radius: var(--radius-s);
  color: var(--ink-faint);
  cursor: pointer;
}
.g-hud__quit:hover { background: var(--surface-2); color: var(--ink); }
.g-hud__mid { flex: 1 1 auto; min-width: 0; }
.g-hud__end { display: flex; align-items: center; gap: var(--sp-3); flex: 0 0 auto; }

/* --- Progress bar: how far through the round ---------------------------- */
.g-progress {
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--surface-3);
  overflow: hidden;
}
.g-progress__fill {
  height: 100%;
  width: 0;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--apricot), var(--wine));
  transition: width var(--dur-5) var(--ease-out);
}
.g-progress__label {
  display: block;
  margin-top: var(--sp-1);
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

/* --- Score & streak readout --------------------------------------------- */
.g-score {
  display: inline-flex;
  align-items: baseline;
  gap: 2px;
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-bold);
  font-size: var(--fs-lg);
  color: var(--ink);
}
.g-score__total { color: var(--ink-faint); font-size: var(--fs-sm); font-weight: var(--fw-medium); }
/* Bumps when it changes (add the class for one animation frame from JS). */
.g-score.is-up { animation: kt-pop var(--dur-4) var(--ease-spring); color: var(--olive); }

.g-streak {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: var(--sp-1) var(--sp-2);
  border-radius: var(--radius-pill);
  background: var(--gold-tint);
  color: var(--gold-ink);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums;
  transition: transform var(--dur-2) var(--ease-spring);
}
.g-streak[hidden] { display: none; }
.g-streak.is-up { animation: kt-pop var(--dur-4) var(--ease-spring); }
/* A hot streak glows. */
.g-streak.is-hot {
  background: linear-gradient(120deg, var(--gold), var(--apricot));
  color: #3A2A06;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--gold) 26%, transparent);
}

/* --- Countdown timer bar ------------------------------------------------- */
/* Drive it by setting --g-time (seconds) and adding .is-running.            */
.g-timer {
  position: relative;
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--surface-3);
  overflow: hidden;
}
.g-timer__fill {
  height: 100%;
  width: 100%;
  transform-origin: left center;
  border-radius: inherit;
  background: var(--olive);
  transition: background var(--dur-3) var(--ease);
}
[dir="rtl"] .g-timer__fill { transform-origin: right center; }
.g-timer.is-running .g-timer__fill {
  animation: kt-drain var(--g-time, 30s) linear forwards;
}
.g-timer.is-paused .g-timer__fill { animation-play-state: paused; }
.g-timer.is-low  .g-timer__fill { background: var(--gold); }
.g-timer.is-out  .g-timer__fill { background: var(--berry); }
.g-timer.is-low  { animation: kt-pulse 900ms var(--ease) infinite; }

.g-clock {
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-semibold);
  font-size: var(--fs-md);
  color: var(--ink-soft);
}
.g-clock.is-low { color: var(--berry); }

/* ==========================================================================
   3. Prompt card — the word being asked
   ========================================================================== */

.g-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  padding: var(--sp-8) var(--sp-5);
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  box-shadow: var(--shadow-2);
  transform-style: preserve-3d;
  /* `backwards`, not `both`: with `both` the entrance keeps applying its last
     keyframe's transform forever, and an animation beats any later rule - so
     .g-flash-card.is-flipped { transform: rotateY(180deg) } toggled its
     class and never turned. The last keyframe is the natural state, so
     releasing it at the end changes nothing visible. */
  animation: kt-fade-up var(--dur-3) var(--ease-out) backwards;
}
.g-card--compact { padding: var(--sp-5) var(--sp-4); }
.g-card--quiet { background: var(--surface-2); box-shadow: none; }

.g-card__label {
  font-size: var(--fs-xs);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--ink-faint);
}
.g-card__word {
  font-size: var(--fs-4xl);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  text-wrap: balance;
}
.g-card__sub { font-size: var(--fs-lg); color: var(--ink-soft); }
.g-card__hint {
  font-size: var(--fs-sm);
  color: var(--ink-faint);
  font-style: italic;
}

/* Flippable card (memory, flashcards). Wrap: .g-flip > .g-flip__inner > 2 faces */
.g-flip { perspective: 900px; }
.g-flip__inner {
  position: relative;
  transition: transform var(--dur-4) var(--ease-out);
  transform-style: preserve-3d;
}
.g-flip.is-flipped .g-flip__inner { transform: rotateY(180deg); }
.g-flip__face {
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
.g-flip__face--back {
  position: absolute;
  inset: 0;
  transform: rotateY(180deg);
}

/* Speak button attached to a prompt */
.g-speak {
  display: inline-grid;
  place-items: center;
  width: var(--tap); height: var(--tap);
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--ink-soft);
  cursor: pointer;
  transition: background var(--dur-1) var(--ease), transform var(--dur-1) var(--ease);
}
.g-speak:hover { background: var(--surface-3); color: var(--ink); }
.g-speak:active { transform: scale(.92); }
.g-speak svg { width: 22px; height: 22px; }

/* ==========================================================================
   4. Choices — the answer options
   ========================================================================== */

.g-choices {
  display: grid;
  gap: var(--sp-3);
  grid-template-columns: 1fr;
}
/* Short answers (a single Georgian word) pair up on a phone. */
.g-choices--2col { grid-template-columns: repeat(2, 1fr); }
@media (min-width: 30rem) {
  .g-choices--auto { grid-template-columns: repeat(2, 1fr); }
}

.g-choice {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  min-height: 56px;                  /* comfortably over the 44px floor */
  padding: var(--sp-3) var(--sp-4);
  text-align: start;
  background: var(--surface);
  color: var(--ink);
  border: 2px solid var(--line);
  border-radius: var(--radius-m);
  box-shadow: var(--shadow-1);
  font-size: var(--fs-lg);
  font-weight: var(--fw-medium);
  cursor: pointer;
  transition: transform var(--dur-1) var(--ease),
              border-color var(--dur-2) var(--ease),
              background var(--dur-2) var(--ease),
              box-shadow var(--dur-2) var(--ease),
              color var(--dur-2) var(--ease);
}
@media (hover: hover) {
  .g-choice:hover { border-color: var(--g-accent); transform: translateY(-1px); box-shadow: var(--shadow-2); }
}
.g-choice:active { transform: scale(.985); }

/* Optional key hint (1-4) for a keyboard player */
.g-choice__key {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 28px; height: 28px;
  border-radius: var(--radius-xs);
  background: var(--surface-2);
  color: var(--ink-faint);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}
.g-choice__text { min-width: 0; flex: 1 1 auto; }
.g-choice__mark { flex: 0 0 auto; opacity: 0; transition: opacity var(--dur-2) var(--ease); }

.g-choice[disabled], .g-choice.is-locked { cursor: default; }

/* Centred variant for one-word answers */
.g-choice--center { justify-content: center; text-align: center; }

/* ==========================================================================
   5. Grids — letters and tiles
   CONTRACT #2: one glyph per grid cell. NO letter-spacing anywhere here.
   ========================================================================== */

.g-grid {
  display: grid;
  gap: var(--sp-2);
  grid-template-columns: repeat(var(--g-cols, 6), minmax(0, 1fr));
  justify-content: center;
  letter-spacing: normal;   /* defensive: never inherit tracking into a grid */
}
.g-grid--tight { gap: 4px; }

/* One glyph. Square, never squashed, scales with the column count. */
.g-cell {
  display: grid;
  place-items: center;
  aspect-ratio: 1;
  min-width: 0;
  min-height: 34px;
  padding: 0;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-xs);
  font-family: var(--font-ka);
  font-size: clamp(0.9rem, 3.4vw, 1.5rem);
  font-weight: var(--fw-medium);
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;    /* case-folding breaks Georgian (contract #1) */
  user-select: none;
  transition: background var(--dur-1) var(--ease),
              color var(--dur-1) var(--ease),
              border-color var(--dur-1) var(--ease),
              transform var(--dur-1) var(--ease);
}
.g-cell--tappable { cursor: pointer; }
.g-cell--tappable:active { transform: scale(.94); }
.g-cell--empty { background: transparent; border-color: transparent; }

/* A blank to be filled (hangman, fill-the-gap): underline, not a box. */
.g-cell--blank {
  background: transparent;
  border: 0;
  border-bottom: 3px solid var(--line-strong);
  border-radius: 0;
  aspect-ratio: auto;
  min-height: 44px;
}
.g-cell--blank.is-filled { border-bottom-color: var(--g-accent); }

/* Word-search selection path */
.g-cell.is-path { background: var(--apricot-tint); color: var(--apricot-ink); border-color: var(--apricot); }
.g-cell.is-found { background: var(--olive-tint); color: var(--olive-ink); border-color: transparent; }

/* Bigger rows: scramble / letter bank — one row that wraps, still a grid. */
.g-bank {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(44px, 1fr));
  gap: var(--sp-2);
  letter-spacing: normal;
}
.g-bank .g-cell { min-height: 48px; font-size: clamp(1.1rem, 4.4vw, 1.75rem); box-shadow: var(--shadow-1); }

/* Answer slots the player drops letters into */
.g-slots {
  display: grid;
  grid-template-columns: repeat(var(--g-cols, 8), minmax(0, 1fr));
  gap: 6px;
  justify-content: center;
  letter-spacing: normal;
}
.g-slots .g-cell {
  background: var(--surface-2);
  border-style: dashed;
  min-height: 44px;
  font-size: clamp(1rem, 4vw, 1.6rem);
}
.g-slots .g-cell.is-filled { background: var(--surface); border-style: solid; }

/* Tile board (memory pairs) */
.g-board {
  display: grid;
  grid-template-columns: repeat(var(--g-cols, 3), minmax(0, 1fr));
  gap: var(--sp-2);
}
.g-tile {
  display: grid;
  place-items: center;
  aspect-ratio: 3 / 4;
  padding: var(--sp-2);
  text-align: center;
  background: var(--surface);
  border: 2px solid var(--line);
  border-radius: var(--radius-s);
  box-shadow: var(--shadow-1);
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
  cursor: pointer;
  overflow: hidden;
  overflow-wrap: anywhere;
  transition: transform var(--dur-2) var(--ease-spring),
              background var(--dur-2) var(--ease),
              border-color var(--dur-2) var(--ease),
              opacity var(--dur-3) var(--ease);
}
.g-tile:active { transform: scale(.96); }
.g-tile--back {
  background: linear-gradient(140deg, var(--wine), var(--wine-deep));
  color: var(--ink-invert);
  border-color: transparent;
}

/* ==========================================================================
   6. Game buttons — larger and friendlier than the app .btn
   ========================================================================== */

.g-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: 52px;
  padding: 0 var(--sp-6);
  border: 1px solid transparent;
  border-radius: var(--radius-m);
  background: var(--surface);
  color: var(--ink);
  border-color: var(--line);
  font-size: var(--fs-lg);
  font-weight: var(--fw-semibold);
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--shadow-1);
  transition: transform var(--dur-1) var(--ease),
              filter var(--dur-1) var(--ease),
              background var(--dur-2) var(--ease);
}
.g-btn:active { transform: translateY(1px) scale(.99); }
.g-btn svg { width: 22px; height: 22px; }
/* pointer-events stays on so a disabled button can still show its title
   ("Say it" explains why it is off). */
.g-btn[disabled] { opacity: .45; cursor: not-allowed; }
.g-btn--block { display: flex; width: 100%; }

.g-btn--primary {
  background: linear-gradient(180deg, var(--wine), var(--wine-deep));
  border-color: var(--wine-deep);
  color: var(--ink-invert);
  box-shadow: var(--shadow-2), var(--shadow-inset);
}
.g-btn--accent {
  background: linear-gradient(180deg, var(--apricot), color-mix(in srgb, var(--apricot) 80%, #000));
  border-color: color-mix(in srgb, var(--apricot) 68%, #000);
  color: #fff;
  box-shadow: var(--shadow-2), var(--shadow-inset);
}
.g-btn--ghost {
  background: transparent;
  border-color: transparent;
  box-shadow: none;
  color: var(--ink-soft);
  font-weight: var(--fw-medium);
}
.g-btn--ghost:hover { background: var(--surface-2); color: var(--ink); }

/* Free-text answer input for typing games */
.g-input {
  width: 100%;
  min-height: 56px;
  padding: var(--sp-3) var(--sp-4);
  text-align: center;
  background: var(--surface);
  color: var(--ink);
  border: 2px solid var(--line-strong);
  border-radius: var(--radius-m);
  font-size: var(--fs-xl);
  font-weight: var(--fw-medium);
  letter-spacing: normal;
  box-shadow: var(--shadow-1);
  transition: border-color var(--dur-2) var(--ease), background var(--dur-2) var(--ease);
}
.g-input:focus { outline: none; border-color: var(--g-accent); }

/* ==========================================================================
   7. States — correct / wrong / selected / done
   Colour is never the only signal: motion + a mark accompany each one.
   ========================================================================== */

.is-selected {
  border-color: var(--g-accent) !important;
  background: color-mix(in srgb, var(--g-accent) 10%, var(--surface)) !important;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--g-accent) 20%, transparent);
}
.g-tile.is-selected { transform: scale(1.03); }

.is-correct {
  border-color: var(--olive) !important;
  background: var(--olive-tint) !important;
  color: var(--olive-ink) !important;
  box-shadow: none;
  animation: kt-pop var(--dur-4) var(--ease-spring);
}
.is-correct .g-choice__mark { opacity: 1; }
.is-correct .g-choice__key { background: var(--olive); color: #fff; }

.is-wrong {
  border-color: var(--berry) !important;
  background: var(--berry-tint) !important;
  color: var(--berry-ink) !important;
  box-shadow: none;
  animation: kt-shake var(--dur-4) var(--ease);
}
.is-wrong .g-choice__mark { opacity: 1; }
.is-wrong .g-choice__key { background: var(--berry); color: #fff; }

/* Finished/consumed: a matched pair, a found word, a cleared row. */
.is-done {
  opacity: .38;
  pointer-events: none;
  filter: saturate(.5);
  transition: opacity var(--dur-3) var(--ease), filter var(--dur-3) var(--ease);
}
.g-tile.is-done { opacity: 0; transform: scale(.9); }

/* Whole-stage feedback flash, for the moment an answer lands. */
.g-stage.is-correct-flash { animation: kt-flash-good var(--dur-4) var(--ease) ; }
.g-stage.is-wrong-flash   { animation: kt-flash-bad  var(--dur-4) var(--ease); }

/* Per-game accent: set --g-accent on .g-stage to re-tint the whole surface. */
.g-stage--apricot { --g-accent: var(--apricot); }
.g-stage--olive   { --g-accent: var(--olive); }
.g-stage--indigo  { --g-accent: var(--indigo); }
.g-stage--gold    { --g-accent: var(--gold); }

/* ==========================================================================
   8. Result / celebration screen (rendered by the shell after ctx.finish)
   No confetti library, no canvas: CSS only.
   ========================================================================== */

.g-result {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-5);
  text-align: center;
  padding: var(--sp-8) var(--gutter) calc(var(--sp-8) + var(--safe-b));
  /* The play stage is already viewport-sized; a second full-height box
     inside it clipped 24px under the topbar. */
  min-height: 0;
  animation: kt-fade-up var(--dur-4) var(--ease-out) both;
}

/* The medal: a warm ring whose sweep encodes the score. Set --g-pct (0-100). */
.g-medal {
  position: relative;
  display: grid;
  place-items: center;
  width: 168px; height: 168px;
  border-radius: 50%;
  background:
    conic-gradient(var(--gold) calc(var(--g-pct, 0) * 1%), var(--surface-3) 0);
  animation: kt-sweep var(--dur-5) var(--ease-out) both;
}
.g-medal::after {
  content: "";
  position: absolute;
  inset: 12px;
  border-radius: 50%;
  background: var(--surface);
  box-shadow: var(--shadow-1);
}
.g-medal__num {
  position: relative;
  z-index: 1;
  font-size: var(--fs-5xl);
  font-weight: var(--fw-bold);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.g-medal__of {
  position: relative;
  z-index: 1;
  font-size: var(--fs-md);
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

.g-result__title {
  font-size: var(--fs-3xl);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
}
.g-result__text { color: var(--ink-soft); max-width: 34ch; }

.g-result__stats {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-3);
}
.g-stat {
  min-width: 6.5rem;
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  box-shadow: var(--shadow-1);
}
.g-stat__num {
  display: block;
  font-size: var(--fs-xl);
  font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums;
}
.g-stat__label {
  font-size: var(--fs-xs);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--ink-faint);
}

.g-result__actions {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  width: 100%;
  max-width: 22rem;
}
@media (min-width: 30rem) { .g-result__actions { flex-direction: row; justify-content: center; } }

/* Celebration — a ring of warm sparks, pure CSS, no confetti library.
   Markup: <div class="g-cheer" aria-hidden="true"><i></i> x8 </div>        */
.g-cheer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
}
.g-medal-wrap { position: relative; display: grid; place-items: center; }
.g-cheer i {
  position: absolute;
  top: 50%; left: 50%;
  width: 10px; height: 10px;
  margin: -5px 0 0 -5px;
  border-radius: 2px;
  background: var(--gold);
  opacity: 0;
  animation: kt-spark 900ms var(--ease-out) both;
}
.g-cheer i:nth-child(1) { --a:   0deg; background: var(--gold);    animation-delay: 40ms; }
.g-cheer i:nth-child(2) { --a:  45deg; background: var(--apricot); animation-delay: 90ms; }
.g-cheer i:nth-child(3) { --a:  90deg; background: var(--wine);    animation-delay: 20ms; }
.g-cheer i:nth-child(4) { --a: 135deg; background: var(--olive);   animation-delay: 140ms; }
.g-cheer i:nth-child(5) { --a: 180deg; background: var(--gold);    animation-delay: 70ms; }
.g-cheer i:nth-child(6) { --a: 225deg; background: var(--apricot); animation-delay: 120ms; }
.g-cheer i:nth-child(7) { --a: 270deg; background: var(--indigo);  animation-delay: 10ms; }
.g-cheer i:nth-child(8) { --a: 315deg; background: var(--wine);    animation-delay: 160ms; }

/* Play-again row of the other games */
.g-more { width: 100%; max-width: 34rem; }

/* ==========================================================================
   9. Keyframes
   (kt-fade-up is duplicated from app.css on purpose so a game page can load
   game.css without the app shell.)
   ========================================================================== */

/* Registering --g-pct makes the medal ring animatable. Without @property a
   custom property is an unanimatable token and the sweep would just snap. */
@property --g-pct {
  syntax: '<number>';
  inherits: false;
  initial-value: 0;
}

@keyframes kt-fade-up {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

@keyframes kt-pop {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.08); }
  100% { transform: scale(1); }
}

@keyframes kt-shake {
  0%, 100% { transform: translateX(0); }
  15% { transform: translateX(-8px); }
  30% { transform: translateX(7px); }
  45% { transform: translateX(-5px); }
  60% { transform: translateX(4px); }
  80% { transform: translateX(-2px); }
}

@keyframes kt-flip {
  from { transform: rotateY(0); }
  to   { transform: rotateY(180deg); }
}

@keyframes kt-drain {
  from { transform: scaleX(1); }
  to   { transform: scaleX(0); }
}

@keyframes kt-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: .55; }
}

@keyframes kt-sweep {
  from { --g-pct: 0; opacity: .4; }
}

@keyframes kt-flash-good {
  0%, 100% { background-color: transparent; }
  25%      { background-color: color-mix(in srgb, var(--olive) 12%, transparent); }
}

@keyframes kt-flash-bad {
  0%, 100% { background-color: transparent; }
  25%      { background-color: color-mix(in srgb, var(--berry) 12%, transparent); }
}

@keyframes kt-spark {
  0%   { opacity: 0; transform: rotate(var(--a, 0deg)) translateY(0) scale(.5); }
  20%  { opacity: 1; }
  100% { opacity: 0; transform: rotate(var(--a, 0deg)) translateY(-96px) scale(1) ; }
}

/* Reduced motion: keep the colour change, drop the movement. base.css already
   flattens durations; these rules stop anything from sitting mid-transform. */
@media (prefers-reduced-motion: reduce) {
  .g-cheer { display: none; }
  .g-timer.is-running .g-timer__fill { animation: none; transform: scaleX(var(--g-left, 1)); }
  .is-wrong, .is-correct, .g-score.is-up, .g-streak.is-up { animation: none; }
  .g-flip.is-flipped .g-flip__inner { transition: none; }
}

/* Dark: white on --olive/--berry is 2.6:1 / 3.3:1. Use the tint + ink pairs
   the palette already provides (9.6:1 and up). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .g-hm-key.is-hit,
  :root:not([data-theme="light"]) .g-choice.is-correct .g-choice__key {
    background: var(--olive-tint); color: var(--olive-ink); border-color: var(--olive);
  }
  :root:not([data-theme="light"]) .g-hm-key.is-miss,
  :root:not([data-theme="light"]) .g-choice.is-wrong .g-choice__key {
    background: var(--berry-tint); color: var(--berry-ink); border-color: var(--berry);
  }
}
:root[data-theme="dark"] .g-hm-key.is-hit,
:root[data-theme="dark"] .g-choice.is-correct .g-choice__key {
  background: var(--olive-tint); color: var(--olive-ink); border-color: var(--olive);
}
:root[data-theme="dark"] .g-hm-key.is-miss,
:root[data-theme="dark"] .g-choice.is-wrong .g-choice__key {
  background: var(--berry-tint); color: var(--berry-ink); border-color: var(--berry);
}

/* "Nothing to play here": a result screen with no score behind it. */
.g-medal--quiet { opacity: .45; }
.g-result--empty .g-medal__num { font-size: 28px; }

/* The shell's own HUD: the teacher's timer and lives, above whatever game. */
.play-hud {
  display: flex; gap: var(--sp-4); align-items: center; justify-content: center;
  margin-bottom: var(--sp-3);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-md); font-weight: var(--fw-semibold);
  color: var(--ink-soft);
}
.play-hud__clock.is-low { color: var(--berry); }
.play-hud__lives { color: var(--berry); letter-spacing: normal; }

/* The word search is a drag target: on a phone it gets the page's gutters
   back, so a 12x12 grid has ~30px cells instead of 22px. */
@media (max-width: 30rem) {
  body.is-play .g-ws { margin-inline: calc(var(--gutter) * -1); }
  body.is-play .g-ws-grid { padding: 4px; border-radius: var(--radius-s); }
}
