/* OPPOSITES and SPOT THE DIFFERENCE — see assets/games-e.js.

   EVERY CLASS IN HERE IS PREFIXED. index.html is one stylesheet shared by
   sixty-odd games, and a bare single-word rule written for one of them applies
   to all of them: the bird's nest lost its whole scene twice in one afternoon
   to `.laid` and `.sat` written for two other games. `op-` and `sd-` were both
   checked as unused before a line of this was written.

   THE TWO PLACES A GAME MAY NOT PUT ANYTHING. The header (game name, dots)
   takes the top ~15% of the stage, and the play-cast Mo stands over the bottom
   left corner — he is not part of the stage and no game can move him. So the
   working area is 20%–75% down the middle, the bottom RIGHT is free for a
   payoff, and the bottom left belongs to Mo. */

/* ==================================================== OPPOSITES ======== */
.op-scene{position:absolute;inset:0;overflow:hidden}
.op-wall{position:absolute;inset:0 0 22% 0;
  background-color:#FDEBD2;
  background-image:repeating-linear-gradient(90deg,rgba(255,255,255,.5) 0 3px,transparent 3px 34px),
                   linear-gradient(180deg,#FFF3DF,#F6DCBB)}
.op-skirt{position:absolute;left:0;right:0;bottom:22%;height:3.4%;
  background:linear-gradient(180deg,#FFF8E8,#E8D2B4);border-top:3px solid #D9BC93}
.op-floor{position:absolute;left:0;right:0;bottom:0;height:22%;
  background-color:#C9A06A;
  background-image:repeating-linear-gradient(90deg,rgba(0,0,0,.07) 0 2px,transparent 2px 56px),
                   linear-gradient(180deg,#D9B380,#B98F58)}

/* the bunting: a place, not a blank wall */
.op-bunting{position:absolute;left:-2%;right:-2%;top:16%;height:5.4%;pointer-events:none}
.op-bunting::before{content:'';position:absolute;left:0;right:0;top:0;height:3px;
  border-radius:50%;border-top:3px solid #C9A06A}
.op-bunting i{position:absolute;top:2px;width:8%;height:100%;
  left:calc(2% + var(--i) * 14%);
  clip-path:polygon(0 0,100% 0,50% 100%);
  background:var(--f,#FF8FA6);
  animation:opFlag 3.4s ease-in-out infinite;animation-delay:calc(var(--i) * .22s)}
.op-bunting i:nth-child(1),.op-bunting i:nth-child(5){--f:#FF8FA6}
.op-bunting i:nth-child(2),.op-bunting i:nth-child(6){--f:#FFCE3D}
.op-bunting i:nth-child(3),.op-bunting i:nth-child(7){--f:#6EC6F0}
.op-bunting i:nth-child(4){--f:#8AD37F}
@keyframes opFlag{0%,100%{transform:rotate(-2deg)}50%{transform:rotate(2deg)}}

/* the rail the two pictures stand on, and the shelf the book sits on */
.op-rail{position:absolute;left:3%;right:3%;top:45%;height:2.2%;border-radius:6px;
  background:linear-gradient(180deg,#D9B380,#A87F4E);box-shadow:0 4px 0 #8B6A45}
.op-shelf{position:absolute;right:3%;width:44%;top:70%;height:2%;border-radius:5px;
  background:linear-gradient(180deg,#D9B380,#A87F4E);box-shadow:0 4px 0 #8B6A45}

/* A CARD IS SQUARE, BECAUSE THE PICTURE IS. The first cut made them fill a
   tall band, and every picture then sat letterboxed in the middle of it with
   its own backdrop showing as a visible disc on a differently-coloured card —
   and worse, the shared ground line under the big and small apples floated
   half way up instead of along the bottom of both. The picture IS the card. */
.op-cards{position:absolute;left:2%;right:2%;top:23%;
  display:flex;gap:3%;align-items:flex-start;justify-content:center;
  transition:opacity .5s,transform .5s}
.op-cards.op-away{opacity:0;transform:translateY(-8%) scale(.86)}

.op-card{flex:0 0 48.5%;aspect-ratio:1;min-width:0;position:relative;padding:0;cursor:pointer;
  border-radius:24px;background:var(--w,#FFF8E8);
  --a1:#FFCE3D;--a2:#6EC6F0;
  opacity:0;transform:translateY(26px) scale(.9);
  transition:opacity .4s var(--spring),transform .4s var(--spring),filter .3s}
.op-card.in{opacity:1;transform:none;transition-delay:var(--d,0ms)}
.op-frame{position:absolute;inset:6%;border-radius:16px;overflow:hidden;
  display:block;background:var(--w,#FFF8E8)}
.op-art{width:100%;height:100%;display:block}

/* WHICH ONES HAVE BEEN HEARD. Without a mark, a child who has tapped one
   picture and forgotten has no way to tell the two apart, and the game reads
   as two pictures that do nothing. An empty ring that fills with a tick says
   it with no words in it.
   (The first attempt drew a little EAR here. At 30px a CSS ear is a comma.
    Draw it or drop it — this one got dropped.) */
.op-ear{position:absolute;right:-7px;top:-7px;width:30px;height:30px;border-radius:50%;
  background:#FFF8E8;border:3px dashed #C9A06A;
  box-shadow:0 2px 0 rgba(0,0,0,.12);transition:background .25s,border-color .25s}
.op-card.op-heard .op-ear{background:#8AD37F;border:3px solid #3B7C46}
.op-card.op-heard .op-ear::after{content:'';position:absolute;left:8px;top:4px;
  width:8px;height:14px;border:0;
  border-right:3.4px solid #FFF8E8;border-bottom:3.4px solid #FFF8E8;
  transform:rotate(40deg)}

/* the moment the question is asked, both are live */
.op-cards.op-live .op-card{filter:none}
.op-card.op-ping{animation:opPing .7s var(--spring)}
@keyframes opPing{0%,100%{transform:none}40%{transform:translateY(-8px) scale(1.03)}}
.op-card.op-beg{animation:opBeg 1s ease-in-out 2}
@keyframes opBeg{0%,100%{transform:none}25%{transform:rotate(-2.5deg)}75%{transform:rotate(2.5deg)}}
.op-card.op-no{animation:opNo .45s}
@keyframes opNo{0%,100%{transform:none}20%{transform:translateX(-9px)}60%{transform:translateX(9px)}}
.op-card.op-yes{animation:opYes .55s var(--spring);z-index:3}
@keyframes opYes{0%{transform:none}45%{transform:scale(1.09)}100%{transform:none}}
.op-card.op-dim{opacity:.32;filter:grayscale(.5)}

/* ------------------------------------------------------ the book (payoff) */
.op-book{position:absolute;right:8%;top:53%;width:34%;height:17%;
  border-radius:6px 14px 14px 6px;background:linear-gradient(180deg,#FFF8E8,#F0E2C8);
  border:3px solid #C9A06A;box-shadow:0 6px 0 rgba(0,0,0,.14);overflow:hidden;
  transition:transform .5s var(--spring),box-shadow .4s}
.op-spine{position:absolute;left:0;top:0;bottom:0;width:11%;background:#B34F32;
  border-right:3px solid #8E3D26}
.op-pages{position:absolute;left:13%;right:5%;top:8%;bottom:8%;
  display:flex;flex-wrap:wrap;align-content:flex-start;gap:4%}
.op-page{width:30%;aspect-ratio:1;border-radius:5px;overflow:hidden;
  background:#FFF8E8;border:2px solid #D9BC93;display:flex;
  animation:opPage .5s var(--spring) both}
.op-page b{flex:1 1 0;min-width:0;display:block;background:var(--w,#FFF8E8)}
.op-page b + b{border-left:1.5px solid #D9BC93}
.op-page svg{width:100%;height:100%;display:block}
@keyframes opPage{from{opacity:0;transform:scale(.4) rotate(-12deg)}to{opacity:1;transform:none}}
/* .fed pulses BRIGHTNESS, never transform — the book is already being driven
   by a transform of its own and an animation that sets one takes it over */
.op-book.fed{animation:opFed .5s ease-out}
@keyframes opFed{0%,100%{filter:none}40%{filter:brightness(1.3) saturate(1.2)}}
.op-book.op-full{transform:translateY(-6%) scale(1.12) rotate(-2deg);
  box-shadow:0 10px 0 rgba(0,0,0,.16),0 0 0 6px rgba(255,206,61,.5)}

/* something on the wall, so the middle of the room is not a blank stripe */
.op-hang{position:absolute;left:6%;top:53%;width:30%;aspect-ratio:100/74;border-radius:8px;
  background:#FFF8E8;border:5px solid #C9A06A;box-shadow:0 5px 0 rgba(0,0,0,.12);
  transform:rotate(-2deg);overflow:hidden}
.op-hang svg{width:100%;height:100%;display:block}
.op-hang::before{content:'';position:absolute;left:50%;top:-14%;width:3px;height:14%;
  background:#A87F4E}

/* ---------------------------------------------------------- the bubble */
.op-say,.sd-say{position:absolute;padding:11px 16px;border-radius:20px;
  background:#FFF8E8;border:3px solid #C9A06A;color:#3B2A1E;
  font:800 clamp(14px,3.7vw,19px)/1.25 var(--font);text-align:center;
  box-shadow:0 4px 0 rgba(0,0,0,.12);z-index:6;
  opacity:0;transform:translateY(8px) scale(.94);pointer-events:none;
  transition:opacity .25s,transform .25s var(--spring)}
.op-say.on,.sd-say.on{opacity:1;transform:none}
.op-say.her,.sd-say.her{background:#E8F6FD;border-color:#6EC6F0}
.op-say{left:31%;right:3%;bottom:3%}
.sd-say{left:32%;right:3%;bottom:3%}

/* ============================================ SPOT THE DIFFERENCE ====== */
.sd-scene{position:absolute;inset:0;overflow:hidden}
.sd-wall{position:absolute;inset:0;
  background-color:#FDEBD2;
  background-image:radial-gradient(circle at 50% 24%,rgba(255,255,255,.75),transparent 62%),
                   linear-gradient(180deg,#FFF3DF,#EFD9BB)}
/* the two pictures, one above the other — that is how a spot-the-difference
   page is laid out in a book, and on a phone it is the only way either of them
   is big enough to look at */
.sd-pics{position:absolute;left:0;right:0;top:19%;bottom:20%;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3.2%}
/* THE ARRIVAL IS A TRANSITION, NOT AN ANIMATION — and this cost an ending.
   It was `animation: sdIn .5s forwards`, and Pip's picture also takes
   `.sd-fix` (a brightness flash) when she repaints it. Two rules setting
   `animation` on one element: the later one wins outright, so the flash
   replaced the arrival, the `forwards` that was holding opacity:1 went with
   it, and her whole picture vanished at the exact moment the game paid off.
   A state held by a transition cannot be taken over like that. */
.sd-pic{position:relative;width:93%;aspect-ratio:2/1;border-radius:18px;
  background:#FFF8E8;--a1:#FFCE3D;--a2:#6EC6F0;
  opacity:0;transform:translateY(16px);
  transition:opacity .45s var(--spring),transform .45s var(--spring),box-shadow .4s}
.sd-pic.in{opacity:1;transform:none}
.sd-pic.sd-hers.in{transition-delay:.16s}
.sd-inner{position:absolute;inset:5px;border-radius:13px;overflow:hidden}
.sd-art{width:100%;height:100%;display:block}

/* whose picture it is — a drawn face, never a word */
.sd-who{position:absolute;left:-10px;top:-10px;width:46px;height:46px;border-radius:50%;
  background:#FFF8E8;border:3px solid #C9A06A;box-shadow:0 3px 0 rgba(0,0,0,.14);
  z-index:4;overflow:hidden;padding:2px}
.sd-hers .sd-who{border-color:#6EC6F0}
.sd-who svg{width:100%;height:100%;display:block;overflow:visible}

.sd-hit{position:absolute;padding:0;border:0;background:transparent;cursor:pointer;
  border-radius:14px;z-index:2}
.sd-hit.sd-got{pointer-events:none}
.sd-hit.sd-beg{background:rgba(255,206,61,.34);
  animation:sdBeg .9s ease-in-out 3}
@keyframes sdBeg{0%,100%{transform:none}50%{transform:scale(1.12)}}
.sd-ring{position:absolute;border:4px solid #FF8FA6;border-radius:50%;
  box-shadow:0 0 0 3px rgba(255,255,255,.85) inset;pointer-events:none;z-index:3;
  animation:sdRing .5s var(--spring) both}
.sd-ring.gone{animation:sdGone .6s ease-out forwards}
@keyframes sdRing{from{opacity:0;transform:scale(1.7)}to{opacity:1;transform:none}}
@keyframes sdGone{to{opacity:0;transform:scale(.5)}}
/* Pip repaints hers to match his */
.sd-pic.sd-fix{animation:sdFix .9s ease-in-out}
@keyframes sdFix{0%,100%{filter:none}45%{filter:brightness(1.7) saturate(.4)}}
.sd-pic.sd-match{box-shadow:0 0 0 5px rgba(138,211,127,.6)}
.sd-scene.sd-done .sd-pics{gap:0}
.sd-scene.sd-done .sd-pic{box-shadow:0 0 0 6px rgba(255,206,61,.65)}
.sd-scene.sd-done .sd-pic.sd-his.in{transform:translateY(4%) rotate(-1.5deg)}
.sd-scene.sd-done .sd-pic.sd-hers.in{transform:translateY(-4%) rotate(1.5deg)}
