/* =========================================================================
   Alive play (Sept 2026): a friend beside the play area, cards that are picked up and
   carried to it, a speech bubble that explains a miss, and a scene behind it all.
   Loaded on every play page after streamline.css (js/core/round-stage.js, quiz.js,
   carry.js). Everything here is keyed on .stage[data-alive], which only a quiz round with a
   friend sets, so games without a friend are untouched.
   ========================================================================= */

/* ---- the stage is friend + play area ---------------------------------------------------- */
#game-root .stage[data-alive] {
  --scene-sky: #eaf3ff;
  --scene-sky-2: #d8ebfb;
  --scene-ground: #cfe8d4;
  --scene-hill: #bfe0c7;
  display: grid; overflow: hidden !important; overflow: clip !important; /* clip, whatever a room's own rule says (lesson-room.css lets a quiz stage scroll): the drifting clouds are wider than the stage, and a touch or a click must never scroll it sideways */
  grid-template-columns: minmax(150px, 23%) minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  gap: clamp(6px, 1.4vw, 20px);
  background: linear-gradient(180deg, var(--scene-sky) 0%, var(--scene-sky-2) 66%, var(--scene-ground) 66.2%, var(--scene-ground) 100%);
}
#game-root .stage[data-alive] > .stage-inner { grid-column: 2; grid-row: 1; min-width: 0; position: relative; z-index: 2; }
#game-root .stage[data-alive] > .friend-zone { grid-column: 1; grid-row: 1; }

/* one scene per skill: the same friend, a different place (colours only: nothing in a scene names a letter, a number or a shape) */
#game-root[data-skill="letters"] .stage[data-alive] { --scene-sky: #fff6e3; --scene-sky-2: #ffe8c8; --scene-ground: #f4d9ae; --scene-hill: #efcf9c; }
#game-root[data-skill="numbers"] .stage[data-alive] { --scene-sky: #e8f1ff; --scene-sky-2: #d0e3ff; --scene-ground: #bcd8f5; --scene-hill: #aacbee; }
#game-root[data-skill="shapes"] .stage[data-alive] { --scene-sky: #e9f8ee; --scene-sky-2: #d2efdd; --scene-ground: #bfe6cb; --scene-hill: #addcba; }
#game-root[data-skill="listening"] .stage[data-alive] { --scene-sky: #f3ebff; --scene-sky-2: #e3d5fa; --scene-ground: #d6c4f1; --scene-hill: #c8b3ea; }

/* soft clouds drift behind everything; gentle hills sit on the horizon. Decoration only, never touchable. */
#game-root .stage[data-alive]::before {
  content: ""; position: absolute; left: 0; top: 4%; width: calc(100% + 640px); height: 44%; z-index: -1; pointer-events: none;
  background-repeat: repeat-x; background-size: 640px 100%;
  background-image:
    radial-gradient(ellipse 46px 22px at 90px 54%, #ffffffb8 0 98%, transparent 100%),
    radial-gradient(ellipse 34px 26px at 122px 44%, #ffffffb8 0 98%, transparent 100%),
    radial-gradient(ellipse 40px 20px at 150px 56%, #ffffffb8 0 98%, transparent 100%),
    radial-gradient(ellipse 36px 18px at 430px 30%, #ffffff99 0 98%, transparent 100%),
    radial-gradient(ellipse 28px 22px at 456px 22%, #ffffff99 0 98%, transparent 100%),
    radial-gradient(ellipse 34px 17px at 482px 32%, #ffffff99 0 98%, transparent 100%);
}
#game-root .stage[data-alive]::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 33%; height: 15%; z-index: -1; pointer-events: none;
  background:
    radial-gradient(ellipse 34% 100% at 16% 100%, var(--scene-hill) 0 98%, transparent 100%),
    radial-gradient(ellipse 40% 84% at 62% 100%, var(--scene-hill) 0 98%, transparent 100%),
    radial-gradient(ellipse 28% 70% at 96% 100%, var(--scene-hill) 0 98%, transparent 100%);
  opacity: .9;
}

/* the friend is the host now: the little one in the prompt bar steps aside */
#game-root:has(.stage[data-alive]) .game-prompt .host { display: none; }

/* ---- the friend and its bubble ---------------------------------------------------------------- */
.friend-zone {
  --friend-size: clamp(92px, min(15vw, 26svh), 176px);
  position: relative; z-index: 1; min-width: 0; min-height: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 12px;
  padding: 0 0 2px; border-radius: 30px;
}
.friend-zone::before {          /* where a carried card goes: it shows only while something is carried */
  content: ""; position: absolute; inset: 2% 0 0; border-radius: 30px; pointer-events: none;
  border: 3px dashed color-mix(in srgb, var(--skill) 55%, white); background: #ffffff5c;
  opacity: 0; transform: scale(.96); transition: opacity .18s, transform .22s cubic-bezier(.3, 1.5, .5, 1);
}
.stage:has(.choice.is-carried) .friend-zone::before { opacity: 1; transform: scale(1); }
.friend-zone.drop-ready::before { border-style: solid; border-color: #f2b92f; background: #fff3c2b0; }

.friend-stand { position: relative; display: grid; place-items: end center; padding-top: calc(var(--friend-size) * .18); }
.friend-stand .friend { position: relative; z-index: 1; }
.friend-halo {
  position: absolute; inset: 8% -18% -4%; border-radius: 50%; pointer-events: none;
  background: radial-gradient(closest-side, #ffe98acc 0%, #ffe98a00 100%);
  opacity: 0; transform: scale(.8); transition: opacity .18s, transform .24s cubic-bezier(.3, 1.5, .5, 1);
}
.friend-zone.drop-ready .friend-halo { opacity: 1; transform: scale(1.08); }
.friend-zone.nudge .friend-halo { animation: halo-nudge 1.4s ease-in-out both; }
.friend-zone.nudge .friend-stand { animation: stand-nudge 1.4s ease-in-out both; }

.friend-bubble {
  --edge: color-mix(in srgb, var(--skill) 40%, white); --lip: color-mix(in srgb, var(--skill) 24%, #e2e8f0);
  position: relative; z-index: 1; width: max-content; max-width: min(100%, 320px);
  display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 10px 14px; border-radius: 22px; background: #fff; color: #27484c;
  border: 3px solid var(--edge); box-shadow: 0 4px 0 var(--lip);
  font-size: clamp(15px, 2.1vmin, 20px); font-weight: 600; line-height: 1.2; text-align: left;
}
.friend-bubble[hidden] { display: none; }
.friend-bubble::after {          /* the tail points at the friend */
  content: ""; position: absolute; left: 50%; bottom: -12px; width: 18px; height: 18px; background: #fff;
  border-right: 3px solid var(--edge); border-bottom: 3px solid var(--edge); border-bottom-right-radius: 6px;
  transform: translateX(-50%) rotate(45deg);
}
.friend-bubble.pop { animation: bubble-pop .36s cubic-bezier(.3, 1.6, .5, 1) both; transform-origin: 50% 100%; }
.friend-bubble .bubble-art { display: grid; place-items: center; flex: none; min-width: 48px; max-width: 120px; font-size: clamp(2rem, 6.2vmin, 3.4rem); font-weight: 700; line-height: 1; color: var(--skill-2); }
.friend-bubble .bubble-art[hidden] { display: none; }
.friend-bubble .bubble-art > * { max-width: 100%; }
.friend-bubble .bubble-art .caption { display: none; }
.friend-bubble .bubble-art .emoji, .friend-bubble .bubble-art .pic { font-size: 1em; }
.friend-bubble .bubble-art svg { width: 1.3em; height: 1.3em; }
.friend-bubble .bubble-line { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.friend-bubble .bubble-line[hidden] { display: none; }
.friend-bubble.tone-oops { --edge: #f3c489; --lip: #ecd6b4; background: #fff8ee; }
.friend-bubble.tone-oops::after { background: #fff8ee; }
.friend-bubble.tone-model { --edge: #f2b92f; --lip: #e7cd7a; background: #fffbe0; }
.friend-bubble.tone-model::after { background: #fffbe0; }
.friend-bubble.tone-yes { --edge: #7fd3a1; --lip: #bfe6cd; background: #f3fff7; }
.friend-bubble.tone-yes::after { background: #f3fff7; }
.friend-bubble.tone-meet { --edge: #9cc4f0; --lip: #c9dbef; background: #f4f9ff; }
.friend-bubble.tone-meet::after { background: #f4f9ff; }
/* something new, introduced in the bubble beside live cards (teach.js introduce): in the friend's column its picture sits
   above its words, so the words have the bubble's whole width */
@media (min-aspect-ratio: 1 / 1) and (min-height: 521px) and (min-width: 721px) {
  .friend-bubble.tone-meet { flex-direction: column; gap: 6px; text-align: center; }
}

/* a tall or narrow screen: the friend sits above the play area with its bubble beside it */
@media (max-aspect-ratio: 1 / 1), ((max-width: 720px) and (min-height: 521px)) {
  #game-root .stage[data-alive] { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); gap: 4px; }
  #game-root .stage[data-alive] > .friend-zone { grid-column: 1; grid-row: 1; }
  #game-root .stage[data-alive] > .stage-inner { grid-column: 1; grid-row: 2; }
  .friend-zone { --friend-size: clamp(66px, 17vw, 112px); flex-direction: row; align-items: flex-end; justify-content: flex-start; gap: 14px; padding: 0 4px; }
  .friend-zone::before { inset: 0; }
  .friend-stand { order: -1; padding-top: calc(var(--friend-size) * .1); } /* the friend stands at the left, its bubble beside it */
  .friend-bubble { align-self: center; flex: 1 1 0; width: auto; max-width: none; }
  .friend-bubble::after {
    left: -12px; bottom: auto; top: calc(50% - 9px); border-right: 0; border-bottom: 3px solid var(--edge); border-left: 3px solid var(--edge);
    border-bottom-right-radius: 0; border-bottom-left-radius: 6px; transform: rotate(45deg);
  }
  .friend-bubble.pop { transform-origin: 0 50%; }
}
/* a short landscape phone: there is no room for a friend column, so the cards keep the whole stage */
@media (orientation: landscape) and (max-height: 520px) {
  #game-root .stage[data-alive] { grid-template-columns: minmax(96px, 20%) minmax(0, 1fr); }
  .friend-zone { --friend-size: clamp(72px, 24svh, 100px); }
  .friend-bubble { max-width: 100%; padding: 6px 8px; font-size: 13px; }
  .friend-bubble .bubble-art { font-size: 1.6rem; min-width: 32px; }
}
/* An old, very small phone held sideways (568 x 320 and below) has a play area only about 170px tall: when a round does not fit,
   the stage scrolls up and down rather than cut a picture off where it can never be reached. (Sideways it still never moves.) */
@media (orientation: landscape) and (max-height: 340px) {
  #game-root .stage[data-alive] { overflow-y: auto !important; overflow-x: hidden !important; overscroll-behavior: contain; }
  #game-root .stage[data-alive]::before { display: none; } /* the drifting clouds are wider than the stage: nothing may be left to scroll sideways */
}

/* ---- the cards: things to pick up ----------------------------------------------------------------- */
.choices.carry-cards > .choice {
  cursor: grab; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; -webkit-tap-highlight-color: transparent;
}
.choices.carry-cards > .choice:nth-child(odd) { rotate: -1.1deg; }
.choices.carry-cards > .choice:nth-child(even) { rotate: 1deg; }
/* Things to pick up are still at rest: the base pop-in (wonderpop.css) holds its last frame (`both`), and a held frame on `transform`
   would keep winning over the transform a card is moved with. A card drops in once, with the class that feel.js `dropIn` adds and takes
   off again (a rule about the card's state would start the drop again whenever a wiggle, a shake or a nudge stopped). Those three still
   play on top (their selectors are the stronger ones) and stop when they are done. Nothing animates a card that is held. */
:where(.stage[data-alive]) :is(.choice, .sort-item, .spell-tile) { animation: none; }
.stage[data-alive] .drop-in { animation-name: alive-drop; animation-duration: .5s; animation-timing-function: cubic-bezier(.3, 1.5, .5, 1); animation-fill-mode: backwards; }
.stage[data-alive] :is(.is-pressed, .is-carried) { animation: none !important; }
.choices.carry-cards > .choice.is-pressed {
  transform: translateY(-6px) scale(1.05); box-shadow: 0 12px 0 color-mix(in srgb, var(--skill) 35%, white), 0 18px 26px #1f3b5a26;
  transition: transform .09s, box-shadow .09s; border-color: var(--skill);
}
.choices.carry-cards > .choice.is-carried {
  z-index: 40; cursor: grabbing; transition: none; rotate: 0deg; border-color: var(--skill);
  box-shadow: 0 22px 34px #1f3b5a3d, 0 0 0 6px color-mix(in srgb, var(--skill) 30%, white);
}
.choices.carry-cards > .choice.is-carried:active { transform: none; }
.choices.carry-cards > .choice.carried-away { visibility: hidden; pointer-events: none; }
.choices.is-demo { pointer-events: none; }
.choices.is-demo > .choice.modelled { animation: none; }
@media (hover: hover) and (pointer: fine) {
  .choices.carry-cards > .choice:not(.wrong):not(.locked):not(.is-carried):hover { transform: translateY(-4px); }
}

/* ---- a hand shows the way ------------------------------------------------------------------------------ */
.hand-hint { position: absolute; z-index: 30; pointer-events: none; transform: translate(-50%, -50%); }
.hand-hint .hand-card {
  display: grid; place-items: center; width: var(--hand-size, 90px); height: var(--hand-size, 90px);
  border-radius: 22px; background: #fff; border: 4px solid #ffd25c; overflow: hidden; opacity: .94;
  box-shadow: 0 10px 20px #1f3b5a40; font-size: calc(var(--hand-size, 90px) * .5); font-weight: 700; line-height: 1; color: var(--skill-2);
}
.hand-hint .hand-card .caption { display: none; }
.hand-hint .hand-finger { position: absolute; right: -18%; bottom: -30%; font-size: calc(var(--hand-size, 90px) * .62); line-height: 1; filter: drop-shadow(0 3px 2px #0003); }

/* ---- sparkles, praise, the demonstration and the "meet" card ---------------------------------------------------- */
.spark-layer { position: absolute; inset: 0; z-index: 12; pointer-events: none; overflow: visible; }
.spark-layer .spark { position: absolute; line-height: 1; text-shadow: 0 2px 0 #fff9; animation: spark-fly .95s cubic-bezier(.2, .7, .3, 1) both; }
.stage[data-alive] .praise { z-index: 14; }
.stage[data-alive] .praise.streak { top: 13%; } /* a streak cheer sits at the top: it never covers a card or a "meet" picture */

.demo-skip {
  position: absolute; right: 14px; bottom: 12px; z-index: 20; min-height: 52px; padding: 8px 20px;
  border-radius: 18px; border: 2px solid #d9e2f1; background: #fff; color: #4c5977; font: inherit; font-size: 16px; font-weight: 600;
  box-shadow: 0 3px 0 #dbe3ee; cursor: pointer;
}
.demo-skip:active { transform: translateY(2px); box-shadow: 0 1px 0 #dbe3ee; }

.meet-card {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: clamp(8px, 2vmin, 18px);
  width: 100%; height: 100%; min-height: 0; text-align: center;
}
.meet-card .meet-art {
  display: grid; place-items: center; min-width: clamp(140px, 30vmin, 260px); min-height: clamp(140px, 30vmin, 260px); padding: 4%;
  border-radius: 34px; background: #fff; border: 5px solid var(--skill-soft); box-shadow: 0 8px 0 color-mix(in srgb, var(--skill) 30%, white);
  font-size: clamp(4.5rem, 20vmin, 9.5rem); font-weight: 700; line-height: 1; color: var(--skill-2); cursor: pointer;
  animation: alive-drop .55s cubic-bezier(.3, 1.5, .5, 1) both;
}
.meet-card .meet-art .caption { position: static; font-size: .22em; }
.meet-card .meet-title { margin: 0; font-size: clamp(20px, 3.4vmin, 30px); font-weight: 600; color: #27484c; }
.meet-card .meet-go {
  display: inline-flex; align-items: center; gap: 8px; min-height: 68px; padding: 10px 30px; border: 0; border-radius: 34px;
  background: var(--ok); color: #fff; font: inherit; font-size: 24px; font-weight: 600; box-shadow: 0 6px 0 var(--ok-2); cursor: pointer;
}
.meet-card .meet-go:active { transform: translateY(4px); box-shadow: 0 2px 0 var(--ok-2); }

/* ---- the voice's pills --------------------------------------------------------------------------------------------- */
/* A round with a friend never waits for the voice, so "Listen…" and "Sound is off" are left out: they come and go with the
   voice, a child may be holding a card when one goes, and the play area would move under the finger (the header's sound button
   says the same). They stay for a grown-up's "Listen first", which does hold the pictures back. */
#game-root[data-friend]:not([data-listening="true"]) .game-prompt .instruction-cue-slot { display: none; }

/* ---- sort rounds (core/sort.js): the homes on top, the pictures to carry below ------------------------------------- */
#game-root .stage[data-alive] .sort-layout { grid-template-rows: auto minmax(0, 1fr); gap: clamp(8px, 2vmin, 18px); align-content: start; }
#game-root .stage[data-alive] .sort-layout > .bins { order: -1; }
.stage[data-alive] .bins[data-count="1"] { grid-template-columns: minmax(0, 1fr); }
.stage[data-alive] .bins[data-count="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.stage[data-alive] .bins[data-count="4"] { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.stage[data-alive] .bins .bin {
  min-height: clamp(92px, 22vmin, 160px); border: 4px solid color-mix(in srgb, var(--skill) 50%, white); border-radius: 26px;
  background: #ffffffe0; box-shadow: 0 6px 0 color-mix(in srgb, var(--skill) 22%, white);
  transition: transform .16s, background .16s, box-shadow .16s, border-color .16s;
}
.stage[data-alive] .bins .bin.drop-ready, .stage[data-alive] .bins .bin.over { background: #fff3c2; border-color: #f2b92f; transform: scale(1.04); box-shadow: 0 8px 0 #ecd28a; }
.stage[data-alive] .bins .bin.correct { background: #f1fff5; border-color: var(--ok); }
.stage[data-alive] .bins.wants .bin { animation: bin-wants 1.1s ease-in-out infinite; }
.stage[data-alive] .bins .bin .placed { display: inline-grid; place-items: center; width: clamp(34px, 8vmin, 58px); font-size: clamp(1.5rem, 5.4vmin, 2.6rem); line-height: 1; }
.stage[data-alive] .bins .bin .placed .caption { display: none; }
.stage[data-alive] .bins .bin .placed .pic { font-size: 1em; }
.stage[data-alive] .bins .bin-fixed { display: grid; place-items: center; min-width: clamp(56px, 13vmin, 96px); }
.stage[data-alive] .bins.strip { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: clamp(6px, 1.6vmin, 14px); }
.stage[data-alive] .bins.strip .bin.slot { min-width: clamp(64px, 15vmin, 110px); min-height: clamp(64px, 15vmin, 110px); border-style: dashed; padding: 4px; justify-content: center; }
.stage[data-alive] .sort-items .sort-item {
  cursor: grab; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; -webkit-tap-highlight-color: transparent;
  border: 4px solid color-mix(in srgb, var(--skill) 30%, white); border-radius: 24px;
  box-shadow: 0 6px 0 color-mix(in srgb, var(--skill) 24%, white), 0 10px 16px #1f3b5a1f;
}
.stage[data-alive] .sort-items .sort-item.is-pressed { transform: translateY(-6px) scale(1.05); border-color: var(--skill); transition: transform .09s; }
.stage[data-alive] .sort-items .sort-item.is-carried { z-index: 40; cursor: grabbing; transition: none; border-color: var(--skill); box-shadow: 0 22px 34px #1f3b5a3d, 0 0 0 6px color-mix(in srgb, var(--skill) 30%, white); }
.stage[data-alive] .sort-items .sort-item.selected { transform: translateY(-6px) scale(1.06); border-color: #f2b92f; box-shadow: 0 0 0 6px #ffe28a99, 0 10px 16px #1f3b5a26; }
.stage[data-alive] .sort-items .sort-item.nudge { animation: item-nudge 1.2s ease-in-out; }
.stage[data-alive] .sort-items .sort-item.modelled { border-color: #f2b92f; box-shadow: 0 0 0 6px #ffe28a99, 0 10px 16px #1f3b5a26; }
.stage[data-alive] .sort-items .sort-item.carried-away { visibility: hidden; pointer-events: none; }
.sort-layout.is-demo { pointer-events: none; }

/* ---- keyframes -------------------------------------------------------------------------------------------------- */
@keyframes alive-drop { from { transform: translateY(-26px) scale(.8) rotate(-4deg); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes bubble-pop { from { transform: scale(.6) translateY(8px); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes halo-nudge { 0%, 100% { opacity: 0; transform: scale(.85); } 40% { opacity: .9; transform: scale(1.1); } }
@keyframes stand-nudge { 0%, 100% { transform: none; } 20% { transform: translateY(-10px); } 40% { transform: none; } 60% { transform: translateY(-6px); } 80% { transform: none; } }
@keyframes spark-fly {
  0% { transform: translate(-50%, -50%) scale(.3) rotate(0deg); opacity: 0; }
  18% { opacity: 1; }
  100% { transform: translate(calc(-50% + var(--dx, 0px)), calc(-50% + var(--dy, -60px))) scale(1) rotate(var(--rot, 90deg)); opacity: 0; }
}
@keyframes bin-wants { 0%, 100% { box-shadow: 0 6px 0 color-mix(in srgb, var(--skill) 22%, white); } 50% { box-shadow: 0 6px 0 color-mix(in srgb, var(--skill) 22%, white), 0 0 0 6px #ffe28aaa; } }
@keyframes item-nudge { 0%, 100% { transform: none; } 20% { transform: translateY(-12px) rotate(-3deg); } 40% { transform: none; } 60% { transform: translateY(-8px) rotate(2deg); } 80% { transform: none; } }
@keyframes scene-drift { from { transform: translate3d(0, 0, 0); } to { transform: translate3d(-640px, 0, 0); } }
@media (prefers-reduced-motion: no-preference) {
  .game-room:not([data-quiet="true"]) .stage[data-alive]::before { animation: scene-drift 110s linear infinite; }
}

/* ---- quiet play and reduced motion: everything still works, nothing travels or loops ------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .friend-bubble.pop, .meet-card .meet-art, .friend-zone.nudge .friend-halo, .friend-zone.nudge .friend-stand { animation: none; }
  .stage[data-alive] .drop-in, .stage[data-alive] .bins.wants .bin { animation: none; }
  .friend-halo, .friend-zone::before { transition: none; }
}
.game-room[data-quiet="true"] :is(.friend-bubble.pop, .meet-card .meet-art, .friend-zone.nudge .friend-halo, .friend-zone.nudge .friend-stand) { animation: none; }
.game-room[data-quiet="true"] :is(.stage[data-alive] .drop-in, .stage[data-alive] .bins.wants .bin) { animation: none; }
.game-room[data-quiet="true"] :is(.friend-halo, .friend-zone::before) { transition: none; }
