/* Served from this folder, not from Google: no player IP goes to a third party. License in fonts/OFL.txt */
@font-face {
  font-family: 'Press Start 2P';
  src: url('fonts/press-start-2p-latin-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

:root {
  --night: #0c1220;
  --night2: #161f36;
  --accent: #ffbf47;
  --danger: #ff5a5a;
  --safe: #3ddc84;
  --text: #f1f3f8;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0;
  height: 100%;
  background: #000;
  color: var(--text);
  font-family: -apple-system, "Segoe UI", Roboto, sans-serif;
  overflow: hidden;
  touch-action: manipulation;
}

#viewport {
  position: relative;
  width: 100%;
  height: 100vh;
  height: 100dvh;
  max-width: 480px;
  margin: 0 auto;
  overflow: hidden;
  background: linear-gradient(180deg, var(--night2), var(--night));
  /* dragging the racket is a gesture, not a scroll: stop the browser from claiming it */
  touch-action: none;
}

.scene { position: absolute; inset: 0; }
.hidden { display: none !important; }

.wall {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, #2a3350, #0c1220);
  transition: filter .6s ease;
}
.wall.dark { filter: brightness(.35); }

.night-outside {
  background: linear-gradient(180deg, #050914, #000);
}

.floor {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 22%;
  background: linear-gradient(180deg, #1a1410, #0a0806);
}

/* The bedroom scene is laid out from these knobs. The walk distance used to be a fixed
   145px while the bed was placed in %, so on a narrow phone the character finished his walk
   standing on top of the bed. Everything that has to stay clear of everything else is
   derived from the same variables now, and they shrink together on a small screen. */
#scene-door {
  --door-x: 32%;
  --door-w: 150px;
  --walk: 130px;
  --switch-x: 86px;
  --bed-size: 160px;
  --bed-right: -7%;
}
@media (max-width: 420px) {
  #scene-door {
    --door-x: 23%;
    --door-w: 120px;
    --walk: 96px;
    --switch-x: 62px;
    --bed-size: 124px;
    --bed-right: -9%;
  }
}

/* Door frame. The perspective here is what makes the leaf swing in real 3D. */
.door-frame {
  position: absolute;
  left: var(--door-x);
  bottom: 22%;
  width: var(--door-w);
  height: 260px;
  transform: translateX(-50%);
  border: 10px solid #4a2f1c;
  border-bottom: none;
  border-radius: 4px 4px 0 0;
  box-shadow: 0 0 0 2px #2a1a0f, inset 0 0 12px rgba(0,0,0,.6);
  perspective: 700px;
  background: #000;
}

/* Lit hallway behind the door, only visible once the leaf swings open */
.door-hall {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at 50% 35%, #ffe3a3 0%, #e9a94f 45%, #7a4a1f 100%);
}

/* Character standing in the doorway, behind the leaf */
.door-char {
  position: absolute;
  left: 50%;
  bottom: 0;
  font-size: 150px;
  line-height: 1;
  transform: translateX(-50%) scale(.72);
  transform-origin: 50% 100%;
  filter: brightness(.25) drop-shadow(0 0 10px rgba(0,0,0,.6)); /* backlit: silhouette */
  opacity: 0;
  transition: transform 1.2s ease-in-out, filter 1.2s ease, opacity .4s ease;
}
.door-char.show { opacity: 1; }
/* Steps into the room and stops beside the switch (right of the frame) */
.door-char.walk {
  transform: translateX(calc(-50% + var(--walk))) scale(.85);
  filter: brightness(1) drop-shadow(0 0 10px rgba(0,0,0,.6));
}
/* A slight lean toward the switch reads as "pressing it" */
.door-char.walk.press {
  transform: translateX(calc(-50% + var(--walk) - 9px)) scale(.85) rotate(-6deg);
  transition-duration: .25s;
}

/* The bed is in the room from the very start */
.room-bed {
  position: absolute;
  right: var(--bed-right);
  bottom: 12%;
  font-size: var(--bed-size);
  line-height: 1;
  transition: filter .5s ease;
}

/* Small wall switch, right of the door, at hand height */
.light-switch {
  position: absolute;
  left: calc(var(--door-x) + var(--switch-x));
  bottom: calc(22% + 80px);
  width: 16px;
  height: 24px;
  background: #e9e4d8;
  border-radius: 3px;
  box-shadow: 0 1px 3px rgba(0,0,0,.5);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: filter .5s ease;
}
.switch-lever {
  width: 6px;
  height: 10px;
  background: #bdb6a6;
  border-radius: 2px;
  transform: translateY(-3px);
  transition: transform .15s ease;
}
.light-switch.off .switch-lever { transform: translateY(3px); }

/* Lights out: the whole room dims, hallway included */
#scene-door .wall, #scene-door .floor, .door-hall { transition: filter .5s ease; }
#scene-door.dark .wall,
#scene-door.dark .floor,
#scene-door.dark .door-hall,
#scene-door.dark .room-bed,
#scene-door.dark .light-switch { filter: brightness(.3); }
#scene-door.dark .door-char { filter: brightness(.35); }
#scene-door.dark .door-light { opacity: 0; }

/* Door leaf: swings on the left hinge, opening into the room */
.door-leaf {
  position: absolute;
  inset: 0;
  z-index: 2;
  transform-origin: 0% 50%;
  transform: rotateY(0deg);
  transition: transform 1.3s cubic-bezier(.45,.05,.3,1);
  background: linear-gradient(90deg, #8b5a34, #9c6a40 50%, #7d4f2c);
  box-shadow: inset 0 0 0 2px rgba(0,0,0,.25);
}
.door-leaf.open { transform: rotateY(-105deg); }
.door-panel {
  position: absolute;
  left: 16px;
  right: 16px;
  border: 3px solid rgba(0,0,0,.22);
  border-radius: 3px;
  box-shadow: inset 0 0 8px rgba(0,0,0,.25);
}
.door-panel.top    { top: 16px;  height: 42%; }
.door-panel.bottom { bottom: 16px; height: 38%; }
.door-knob {
  position: absolute;
  right: 12px;
  top: 52%;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 35%, #fff2b0, #c99a2e 60%, #6b4e12);
}

/* Strip of light spilling onto the floor when the door opens */
.door-light {
  position: absolute;
  left: var(--door-x);
  bottom: 0;
  width: var(--door-w);
  height: 22%;
  transform: translateX(-50%) perspective(200px) rotateX(55deg);
  transform-origin: 50% 0%;
  background: linear-gradient(180deg, rgba(255,214,140,.55), rgba(255,214,140,0));
  opacity: 0;
  transition: opacity 1s ease .3s;
  pointer-events: none;
}
.door-light.on { opacity: 1; }

.lamp-wrap {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
}
.lamp-cord {
  width: 2px;
  height: 90px;
  background: rgba(255,255,255,.3);
}
.lamp {
  font-size: 46px;
  margin-top: -6px;
  /* The emoji ships with the screw base pointing down; rotate it so the round belly
     faces down, hanging from the cord like a real ceiling bulb. */
  transform: rotate(180deg);
  filter: drop-shadow(0 0 18px rgba(255,214,120,.8));
  transition: filter .5s ease;
}
.lamp.off {
  filter: brightness(.3) drop-shadow(0 0 0 rgba(0,0,0,0));
}

.switch-btn, .primary-btn {
  font-family: inherit;
  font-weight: 700;
  border: none;
  border-radius: 12px;
  cursor: pointer;
  color: #14151a;
  background: var(--accent);
  padding: 12px 20px;
  font-size: 15px;
}

.switch-btn {
  position: absolute;
  bottom: 12%;
  left: 50%;
  transform: translateX(-50%);
}

#bed {
  position: absolute;
  bottom: 10%;
  left: 50%;
  transform: translateX(-50%);
  font-size: 160px;
  line-height: 1;
}

/* --- Morning: she climbs out of bed and stretches (#riser), the bed stays empty --- */
.riser {
  position: absolute;
  left: calc(50% - 150px);
  bottom: 10%;
  font-size: 110px;
  line-height: 1;
  z-index: 7;
  transform-origin: 50% 100%;
}
/* out of the bed: starts low and to the right, where she was lying, and stands up beside it */
.riser.getup { animation: getup 1s cubic-bezier(.3,1.3,.5,1) both; }
@keyframes getup {
  from { transform: translate(70px, 50px) scale(.55) rotate(-70deg); opacity: 0; }
  60%  { opacity: 1; }
  to   { transform: none; opacity: 1; }
}
/* arms up, the body grows a little taller and sways, twice */
.riser.stretch { animation: stretch .9s ease-in-out 2; }
@keyframes stretch {
  0%, 100% { transform: none; }
  40%      { transform: scaleY(1.1) rotate(-4deg); }
  70%      { transform: scaleY(1.06) rotate(4deg); }
}
@media (prefers-reduced-motion: reduce) {
  .riser.getup, .riser.stretch { animation: none; }
}

/* Language picker on the How to play card: EN / PT / ES */
.langs { display: flex; justify-content: center; gap: 8px; margin-bottom: 12px; }
.langs button {
  font: inherit;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: .06em;
  min-width: 48px;
  padding: 6px 10px;
  border-radius: 999px;
  color: var(--text);
  background: transparent;
  border: 1.5px solid rgba(255,255,255,.3);
  cursor: pointer;
}
.langs button[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: #14151a; }

/* Sleeping z's drifting out of her mouth. Three letters on the same animation, staggered by
   delay and growing as they climb, so they read as one continuous stream of breathing.
   The mouth sits at the head end of 🛌, on the left. Exactly where that lands depends on the
   system emoji font, so the two offsets live here as knobs: --mouth-x is measured from the
   center of the bed (negative = toward the head) and --mouth-y up from the floor line. */
.sleep-z {
  --mouth-x: -40px;
  --mouth-y: 92px;
  position: absolute;
  left: calc(50% + var(--mouth-x));
  bottom: calc(10% + var(--mouth-y));
  z-index: 6;
  pointer-events: none;
}
.sleep-z span {
  position: absolute;
  font-weight: 800;
  font-style: italic;
  color: #d3e3ff;
  text-shadow: 0 0 10px rgba(150,195,255,.7);
  opacity: 0;
  animation: z-float 2.7s ease-in-out infinite;
}
.sleep-z span:nth-child(1) { font-size: 17px; animation-delay: 0s; }
.sleep-z span:nth-child(2) { font-size: 22px; animation-delay: .9s; }
.sleep-z span:nth-child(3) { font-size: 27px; animation-delay: 1.8s; }
@keyframes z-float {
  0%   { transform: translate(0, 0) rotate(-10deg) scale(.6); opacity: 0; }
  18%  { opacity: .95; }
  100% { transform: translate(38px, -58px) rotate(14deg) scale(1.1); opacity: 0; }
}

.mosquito {
  position: absolute;
  font-size: 30px;
  cursor: pointer;
  user-select: none;
  filter: drop-shadow(0 0 4px rgba(0,0,0,.5));
  animation: buzz .18s infinite alternate;
  touch-action: manipulation;
  transition: left .22s linear, top .22s linear;
  /* its own layer: the shadow and the buzz are drawn once and only moved, not repainted
     every frame for every mosquito (this is what made a swarm stutter on phones) */
  will-change: transform, left, top;
}
@keyframes buzz {
  from { transform: translate(0,0) rotate(-6deg); }
  to   { transform: translate(3px,-3px) rotate(6deg); }
}

.key-badge {
  position: absolute;
  top: -8px;
  right: -8px;
  background: var(--accent);
  color: #14151a;
  font-size: 11px;
  font-weight: 800;
  line-height: 1;
  padding: 2px 5px;
  border-radius: 6px;
  font-family: monospace;
}

/* Instructions in arcade type, parked beside the window so they stay readable all night
   without sitting in the middle of the play area. The window is 150px wide at left 7%. */
.hint-panel {
  position: absolute;
  left: calc(7% + 160px);
  right: 10px;
  /* clear of the ceiling lamp, which hangs from the top middle: 90px of cord plus a 46px
     bulb. Sitting in front of it worked, but the text read as if it were tangled in the wire. */
  top: 146px;
  /* above the ceiling lamp, which sits at the top middle and would otherwise cut the text */
  z-index: 12;
  font-family: 'Press Start 2P', 'Courier New', monospace;
  font-size: 11px;
  line-height: 1.7;
  color: var(--accent);
  /* hard offset shadow, the way arcade type is normally set, so it stays legible over
     both the dark wall and a mosquito passing behind it */
  text-shadow: 2px 2px 0 rgba(0,0,0,.9), 0 0 10px rgba(255,191,71,.5);
  pointer-events: none;
}
/* tighter than before: the instruction is a full sentence now and runs to more lines */
.hint-line { margin: 0 0 9px; }
.hint-nets { color: var(--safe); } /* green, same as the net lives it points at */
.hint-goal { margin-bottom: 0; opacity: .8; }
@media (max-width: 420px) {
  .hint-panel { left: calc(7% + 156px); font-size: 9px; line-height: 1.9; }
}

/* --- The window: the only piece of sky in the room. It doubles as the clock (the moon
   crosses it over the whole night) and as the finish line (the sun comes up in it). --- */
.window {
  position: absolute;
  left: 7%;
  top: 12%;
  width: 150px;
  pointer-events: none;
}
.window-pane {
  position: relative;
  height: 120px;
  border: 8px solid #4a2f1c;
  border-radius: 3px;
  box-shadow: 0 0 0 2px #2a1a0f, inset 0 0 14px rgba(0,0,0,.7);
  overflow: hidden; /* keeps the moon and the sun inside the glass */
  background: #05080f;
}
.window-sill {
  height: 7px;
  margin: 0 -7px;
  border-radius: 0 0 3px 3px;
  background: linear-gradient(180deg, #5a3a22, #34200f);
  box-shadow: 0 2px 4px rgba(0,0,0,.6);
}
.window-sky {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, #08112a 0%, #122048 70%, #1b2a5c 100%);
  transition: background 2.4s ease;
}
/* stars, faded out by dawn */
.window-sky::after {
  content: '';
  position: absolute;
  inset: 0;
  background-image:
    radial-gradient(1.6px 1.6px at 22% 24%, #fff, transparent),
    radial-gradient(1.4px 1.4px at 63% 14%, #fff, transparent),
    radial-gradient(1.1px 1.1px at 82% 42%, #fff, transparent),
    radial-gradient(1.1px 1.1px at 34% 56%, #fff, transparent),
    radial-gradient(1.5px 1.5px at 49% 72%, #fff, transparent);
  opacity: .85;
  transition: opacity 1.8s ease;
}
/* mullions, drawn over the glass so the moon passes behind them */
.window-bars { position: absolute; inset: 0; z-index: 3; }
.window-bars::before,
.window-bars::after { content: ''; position: absolute; background: #4a2f1c; }
.window-bars::before { left: 50%; top: 0; bottom: 0; width: 6px; margin-left: -3px; }
.window-bars::after  { top: 50%; left: 0; right: 0; height: 6px; margin-top: -3px; }

/* The moon is the timer. Its duration comes from WIN_TIME_MS in JS, and pausing the game
   pauses this animation too, which is the whole reason the clock lives in CSS and not in a timer. */
.moon {
  position: absolute;
  z-index: 2;
  font-size: 22px;
  line-height: 1;
  filter: drop-shadow(0 0 10px rgba(255,235,170,.6));
  animation-name: moon-cross;
  animation-timing-function: linear;
  animation-fill-mode: both;
  animation-play-state: paused;
}
@keyframes moon-cross {
  0%   { left: -18%; top: 62%; opacity: 0; }
  10%  { opacity: 1; }
  50%  { left: 40%;  top: 6%;  opacity: 1; }
  90%  { opacity: 1; }
  100% { left: 98%;  top: 62%; opacity: 0; }
}

/* Dawn: sky warms up, stars go out, sun climbs into the frame */
.sun {
  position: absolute;
  left: 34%;
  top: 115%;
  z-index: 2;
  font-size: 26px;
  line-height: 1;
  opacity: 0;
  filter: drop-shadow(0 0 16px rgba(255,190,90,.9));
}
.window.dawn .window-sky {
  background: linear-gradient(180deg, #3a5ca0 0%, #e98a3c 60%, #ffd27a 100%);
}
.window.dawn .window-sky::after { opacity: 0; }
.window.dawn .sun { animation: sun-rise 2.4s ease-out forwards; }
@keyframes sun-rise {
  0%   { top: 115%; opacity: 0; }
  25%  { opacity: 1; }
  100% { top: 24%;  opacity: 1; }
}
/* the room itself catches the morning light */
#scene-bed.dawn .wall.dark { filter: brightness(.85); transition: filter 2.4s ease; }

/* --- Getting bitten: without this the health bar just shrinks in silence and the player
   never learns what cost them the night. --- */
#hurt-flash {
  position: absolute;
  inset: 0;
  z-index: 18;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(ellipse at 50% 60%, transparent 38%, rgba(255,60,60,.55) 100%);
}
#hurt-flash.on { animation: hurt-pulse .45s ease-out; }
@keyframes hurt-pulse {
  0%   { opacity: 0; }
  15%  { opacity: 1; }
  100% { opacity: 0; }
}

#hud-health.shake { animation: hud-shake .4s ease; }
@keyframes hud-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-4px); }
  40% { transform: translateX(4px); }
  60% { transform: translateX(-3px); }
  80% { transform: translateX(2px); }
}

/* "-1" floating up from the spot where the bite landed */
.bite-tag {
  position: absolute;
  z-index: 19;
  pointer-events: none;
  font-size: 20px;
  font-weight: 800;
  color: var(--danger);
  text-shadow: 0 2px 6px rgba(0,0,0,.75);
  animation: bite-rise .9s ease-out forwards;
}
@keyframes bite-rise {
  0%   { transform: translateY(0) scale(.8);    opacity: 0; }
  20%  { transform: translateY(-6px) scale(1.15); opacity: 1; }
  100% { transform: translateY(-46px) scale(1);  opacity: 0; }
}

#hud {
  position: absolute;
  top: 10px;
  left: 10px;
  right: 10px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  z-index: 20;
  font-size: 13px;
  font-weight: 700;
}
/* Hearts instead of a bar: the bar was anonymous, and this audience should not have to
   read a label to know what it is losing. */
.hearts {
  display: flex;
  gap: 3px;
  font-size: 17px;
  line-height: 1;
}
.hearts span {
  transition: filter .3s ease, opacity .3s ease, transform .3s ease;
}
.hearts span.lost {
  filter: grayscale(1) brightness(.55);
  opacity: .35;
  transform: scale(.85);
}
/* the one just lost gets a beat of its own, so the player sees WHICH heart went */
.hearts span.breaking { animation: heart-break .5s ease; }
@keyframes heart-break {
  0%   { transform: scale(1.4); filter: none; opacity: 1; }
  35%  { transform: scale(.68) rotate(-14deg); }
  70%  { transform: scale(.95) rotate(6deg); }
  100% { transform: scale(.85); }
}

/* --- The net bar, at the bottom. The bedroom scene stops above it (see #scene-bed), so the
   bed and the net over it never hide behind the buttons. --- */
:root { --bar-h: 104px; }
#scene-bed { bottom: var(--bar-h); }
.net-bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--bar-h);
  padding: 6px 14px 10px;
  z-index: 20;
  background: linear-gradient(180deg, #121a30, #0a0f1d);
  border-top: 1px solid rgba(255,255,255,.12);
}
.net-tip {
  margin: 0 0 6px;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .04em;
  text-align: center;
  color: var(--safe);
}
.net-lives { display: flex; gap: 10px; }
.net-item {
  flex: 1;
  min-width: 0;
  display: grid;
  grid-template-columns: 34px 1fr;
  grid-template-rows: auto auto;
  column-gap: 8px;
  align-items: center;
  padding: 8px;
  font-family: inherit;
  text-align: left;
  color: var(--text);
  background: rgba(61,220,132,.12);
  border: 2px solid rgba(61,220,132,.7);
  border-radius: 12px;
  cursor: pointer;
  transition: opacity .3s ease, transform .15s ease, background .2s ease;
}
.net-item:active { transform: scale(.96); }
/* Net drawn in CSS: green cloth with a mesh weave, no emoji */
.net-swatch {
  grid-row: 1 / span 2;
  width: 34px;
  height: 28px;
  border: 2px solid rgba(255,255,255,.55);
  border-radius: 6px;
  background-color: rgba(61,220,132,.35);
  background-image:
    repeating-linear-gradient(45deg,  rgba(255,255,255,.45) 0 1px, transparent 1px 6px),
    repeating-linear-gradient(-45deg, rgba(255,255,255,.45) 0 1px, transparent 1px 6px);
}
.net-name { font-size: 14px; font-weight: 800; line-height: 1.1; }
.net-what { font-size: 12px; line-height: 1.2; opacity: .8; }
/* The 2X net: blue instead of green. A resistant mosquito can tell the difference, so the
   player has to be able to as well. */
.net-item.ig2 {
  background: rgba(90,170,255,.14);
  border-color: rgba(120,200,255,.85);
  box-shadow: inset 0 0 10px rgba(120,200,255,.35); /* inside: the last button sits at the screen edge */
}
.net-item.ig2 .net-swatch {
  border-color: rgba(120,200,255,.85);
  background-color: rgba(90,170,255,.4);
  background-image:
    repeating-linear-gradient(45deg,  rgba(220,240,255,.5) 0 1px, transparent 1px 6px),
    repeating-linear-gradient(-45deg, rgba(220,240,255,.5) 0 1px, transparent 1px 6px);
}
/* While she explains them, the one she is talking about lights up. The highlight is drawn
   INSIDE the button (inset ring, no scale, no outer glow): anything outside got cut off by
   the edge of the screen on the first and last button. */
.net-item.spot {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 2px var(--accent);
  background: rgba(255,191,71,.18);
}
.net-item:focus { outline: none; }
.net-item:focus-visible { outline: 2px dashed var(--accent); outline-offset: -5px; }
@media (max-width: 380px) {
  .net-item { grid-template-columns: 1fr; padding: 6px; text-align: center; }
  .net-swatch { grid-row: auto; justify-self: center; width: 30px; height: 20px; margin-bottom: 2px; }
  .net-what { font-size: 12px; }
}

/* A resistant mosquito: bigger and darker, with a sickly glow. It survives an ordinary net. */
/* --- The swatter: an electric mosquito racket that follows the pointer. Killing happens on
   pointermove, never on pointerdown, for the same reason the mosquito uses click: on touch a
   removal fired from pointerdown leaves a stray synthetic click to land on whatever is
   underneath. Dragging sidesteps that entirely. --- */
.racket {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 15;
  pointer-events: none; /* it must never eat a tap meant for a mosquito */
  will-change: transform;
}
.racket-head {
  width: 62px;
  height: 72px;
  border: 4px solid #2f3a52;
  border-radius: 50%;
  background-color: rgba(120,220,255,.10);
  background-image:
    repeating-linear-gradient(0deg,  rgba(150,235,255,.35) 0 1px, transparent 1px 7px),
    repeating-linear-gradient(90deg, rgba(150,235,255,.35) 0 1px, transparent 1px 7px);
  box-shadow: 0 0 14px rgba(120,220,255,.4), inset 0 0 18px rgba(120,220,255,.2);
}
.racket-handle {
  width: 11px;
  height: 36px;
  margin: -3px auto 0;
  border-radius: 0 0 7px 7px;
  background: linear-gradient(90deg, #3d4a63, #212a3c);
  box-shadow: 0 2px 4px rgba(0,0,0,.6);
}
/* the zap when it connects */
.racket.swing .racket-head { animation: racket-zap .22s ease-out; }
@keyframes racket-zap {
  0%   { box-shadow: 0 0 34px rgba(190,250,255,.95), inset 0 0 34px rgba(190,250,255,.75);
         border-color: #cfefff; }
  100% { box-shadow: 0 0 14px rgba(120,220,255,.4), inset 0 0 18px rgba(120,220,255,.2);
         border-color: #2f3a52; }
}
@media (max-width: 420px) {
  .racket-head { width: 56px; height: 64px; }
  .racket-handle { height: 32px; }
}
/* Phone only: a bigger racket. A finger hides what is under it and moves less precisely
   than a mouse, so the head has to reach further. (game.js measures it from here.) */
.phone-mode .racket-head { width: 70px; height: 80px; }
.phone-mode .racket-handle { width: 12px; height: 38px; }

/* Bigger on a phone: a fingertip covers about 9mm, so a 30px target is a coin toss */
.phone-mode .mosquito { font-size: 38px; }
.phone-mode .mosquito.resistant { font-size: 48px; }

.mosquito.resistant {
  font-size: 40px;
  filter: brightness(.62) saturate(.45) drop-shadow(0 0 8px rgba(90,230,150,.85));
}
/* bounced off an ordinary net, still alive */
.mosquito.repelled { animation: repelled .4s ease-out; }
@keyframes repelled {
  0%   { transform: scale(1.25) rotate(-14deg); }
  100% { transform: scale(1) rotate(0deg); }
}
/* the dual-ingredient net in action over the bed */
.bed-net.ig2 {
  border-color: rgba(190,230,255,.7);
  background-color: rgba(150,205,255,.12);
  background-image:
    repeating-linear-gradient(0deg,  rgba(210,235,255,.32) 0 1px, transparent 1px 5px),
    repeating-linear-gradient(90deg, rgba(210,235,255,.32) 0 1px, transparent 1px 5px);
  box-shadow: 0 0 20px rgba(120,190,255,.35), inset 0 0 24px rgba(120,190,255,.2);
}
.net-item.used {
  opacity: .3;
  cursor: default;
  pointer-events: none;
  box-shadow: none;
}
/* Reminder: the nets still left pulse and the tip blinks */
.net-bar.pulse .net-item:not(.used) { animation: pulse 1s infinite; }
.net-bar.pulse .net-tip { animation: tip-blink 1s infinite; }
@keyframes pulse {
  /* inset, for the same reason as .spot: an outer ring was cut off at the screen edges */
  0%, 100% { box-shadow: inset 0 0 0 0 rgba(255,191,71,0); border-color: rgba(255,191,71,.6); }
  50% { box-shadow: inset 0 0 0 3px rgba(255,191,71,.9); border-color: var(--accent); }
}
@keyframes tip-blink {
  0%, 100% { opacity: 1; }
  50%      { opacity: .3; }
}

/* --- Net lowered over the bed (#bed: centered, bottom 10%, 160px) --- */
.bed-net {
  position: absolute;
  left: 50%;
  bottom: calc(10% - 6px);
  width: 210px;
  height: 200px;
  margin-left: -105px;
  z-index: 5;
  pointer-events: none;
  /* tent shape: rounded top fixed to the ceiling, straight hem on the floor */
  border-radius: 50% 50% 6px 6px / 38% 38% 6px 6px;
  border: 1px solid rgba(220,255,235,.55);
  background-color: rgba(190,255,215,.08);
  background-image:
    repeating-linear-gradient(0deg,  rgba(230,255,240,.28) 0 1px, transparent 1px 5px),
    repeating-linear-gradient(90deg, rgba(230,255,240,.28) 0 1px, transparent 1px 5px);
  box-shadow: 0 0 18px rgba(120,255,170,.25), inset 0 0 22px rgba(120,255,170,.15);
  transform-origin: 50% 0%;
  animation: net-drop .9s cubic-bezier(.3,1.4,.5,1) both, net-sway 3.2s ease-in-out .9s infinite;
}
/* the cord holding the net to the ceiling */
.bed-net::before {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 100%;
  width: 1px;
  height: 100vh;
  background: rgba(230,255,240,.35);
}
@keyframes net-drop {
  from { transform: translateY(-110%) scaleY(.3); opacity: 0; }
  to   { transform: translateY(0) scaleY(1); opacity: 1; }
}
@keyframes net-sway {
  0%, 100% { transform: skewX(0deg); }
  50%      { transform: skewX(1.5deg); }
}
/* shake when a mosquito hits it */
.bed-net.hit { animation: net-hit .25s ease-out, net-sway 3.2s ease-in-out .25s infinite; }
@keyframes net-hit {
  0%   { transform: scale(1); box-shadow: 0 0 30px rgba(150,255,190,.6), inset 0 0 30px rgba(150,255,190,.35); }
  40%  { transform: scale(1.02, .985); }
  100% { transform: scale(1); }
}
.bed-net.lifting { animation: net-lift .7s ease-in both; }
@keyframes net-lift {
  to { transform: translateY(-110%) scaleY(.3); opacity: 0; }
}

/* Mosquito killed by the net: stops buzzing, greys out, flips and drops to the floor */
.mosquito.dying {
  animation: none;
  transform: rotate(160deg);
  filter: grayscale(1) brightness(.6);
  transition: transform .3s ease, filter .3s ease;
  cursor: default;
  pointer-events: none;
}
.mosquito.falling {
  transition: top 1s cubic-bezier(.55,0,1,.45), left 1s ease-out, transform 1s ease, opacity 1s ease 3.2s;
  transform: rotate(540deg) scale(.8);
  opacity: 0;
  z-index: 4;
}

/* --- Comic speech cloud. Every spoken line in the game uses this same shape. --- */
.cloud {
  position: absolute;
  left: 50%;
  top: 30%;
  transform: translateX(-50%);
  z-index: 25;
  animation: cloud-pop .45s cubic-bezier(.2,1.5,.4,1) both;
}
/* the name bubble sits higher, so the phone keyboard does not cover it */
.cloud.cloud-high { top: 12%; }
@keyframes cloud-pop {
  from { transform: translateX(-50%) scale(.3); opacity: 0; }
  to   { transform: translateX(-50%) scale(1);  opacity: 1; }
}

.cloud-body {
  /* no z-index here on purpose: a stacking context would make the puffs below paint
     ON TOP of this white background instead of behind it. The .cloud wrapper already
     has z-index, so the negative pseudo-elements stay inside the bubble. */
  position: relative;
  max-width: 250px;
  min-width: 140px;
  padding: 14px 20px;
  background: #fff;
  color: #14151a;
  border-radius: 28px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  text-align: center;
}

/* The scalloped edge: two pseudo-elements tiling white circles along the sides with
   repeat-x / repeat-y. Tiling instead of fixed offsets means the cloud fits any bubble
   size, so a one-line speech and the name form get the same shape for free. */
.cloud-body::before,
.cloud-body::after {
  content: '';
  position: absolute;
  z-index: -1;
  background-image:
    radial-gradient(circle 13px at 13px 13px, #fff 99%, transparent 100%),
    radial-gradient(circle 13px at 13px 13px, #fff 99%, transparent 100%);
  background-size: 26px 26px;
}
/* top and bottom rows, the bottom one offset half a puff so it does not look striped */
.cloud-body::before {
  inset: -13px 10px;
  background-position: 0 top, 13px bottom;
  background-repeat: repeat-x;
}
/* left and right columns */
.cloud-body::after {
  inset: 10px -13px;
  background-position: left 0, right 13px;
  background-repeat: repeat-y;
}

/* Tail of shrinking puffs. Points down-left by default, toward the door. */
.cloud-tail {
  position: absolute;
  background: #fff;
  border-radius: 50%;
}
.cloud-tail.t1 { width: 22px; height: 22px; left: 24px; bottom: -26px; }
.cloud-tail.t2 { width: 14px; height: 14px; left: 8px;  bottom: -46px; }
.cloud-tail.t3 { width: 8px;  height: 8px;  left: -2px; bottom: -60px; }
/* straight down, for whoever is speaking from the middle of the scene (the bed) */
.cloud.tail-center .cloud-tail.t1 { left: calc(50% - 11px); }
.cloud.tail-center .cloud-tail.t2 { left: calc(50% - 7px); }
.cloud.tail-center .cloud-tail.t3 { left: calc(50% - 4px); }

.cloud-text { margin: 0; font-size: 15px; font-weight: 700; line-height: 1.35; }
#name-bubble .cloud-body { width: 232px; }
#name-bubble .cloud-text { font-size: 17px; font-weight: 800; }
.cloud-form { display: flex; gap: 6px; }
.cloud-input {
  flex: 1;
  min-width: 0;
  font-family: inherit;
  font-size: 15px;
  font-weight: 700;
  padding: 8px 10px;
  border: 2px solid #14151a;
  border-radius: 10px;
  background: #f4f5f8;
  color: #14151a;
}
.cloud-input::placeholder { color: #9aa0ad; font-weight: 600; }
.cloud-input:focus { outline: none; border-color: #ff9f1c; }
.cloud-btn {
  font-family: inherit;
  font-weight: 800;
  font-size: 14px;
  padding: 8px 14px;
  border: none;
  border-radius: 10px;
  background: #14151a;
  color: #fff;
  cursor: pointer;
}
.cloud-btn:active { transform: scale(.94); }

/* --- Splash: everything starts tiny in the back and grows toward the viewer --- */
.splash {
  position: absolute;
  inset: 0;
  z-index: 40;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(circle at 50% 50%, #17244d 0%, #060b1a 58%, #000 100%);
  transition: opacity .45s ease;
}
.splash.leaving { opacity: 0; }

/* Rays: a fan of slices from a conic-gradient. One single animation, because two
   competing animations on the same transform property cancel each other out. */
.splash-rays {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 180vmax;
  height: 180vmax;
  margin: -90vmax 0 0 -90vmax;
  background: repeating-conic-gradient(from 0deg,
      rgba(255,191,71,.22) 0deg 3.5deg,
      transparent 3.5deg 13deg);
  animation: rays-forward 2.6s cubic-bezier(.24,.62,.3,1) both;
}
@keyframes rays-forward {
  0%   { transform: scale(.03) rotate(0deg);   opacity: 0; }
  30%  { opacity: .95; }
  100% { transform: scale(1) rotate(26deg);    opacity: .38; }
}

/* Core of light the rays appear to come from */
.splash-glow {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 60vmin;
  height: 60vmin;
  margin: -30vmin 0 0 -30vmin;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255,222,150,.6), rgba(255,191,71,0) 70%);
  animation: glow-forward 2.6s cubic-bezier(.24,.62,.3,1) both;
}
@keyframes glow-forward {
  0%   { transform: scale(.04); opacity: 0; }
  35%  { opacity: 1; }
  100% { transform: scale(1.7); opacity: .5; }
}

.splash-title {
  position: relative;
  z-index: 2;
  text-align: center;
  animation: title-forward 1.5s cubic-bezier(.2,.8,.3,1) .5s both;
}
@keyframes title-forward {
  0%   { transform: scale(.12); opacity: 0; filter: blur(8px); }
  55%  { opacity: 1; }
  100% { transform: scale(1);   opacity: 1; filter: blur(0); }
}
.splash-mosq {
  display: block;
  font-size: 68px;
  line-height: 1;
  filter: drop-shadow(0 6px 14px rgba(0,0,0,.6));
}
.splash-name {
  margin: 10px 0 4px;
  /* "Mosquito Night" is wider than the old title: shrink it on a narrow screen
     instead of wrapping or overflowing. */
  font-size: clamp(28px, 9vw, 40px);
  white-space: nowrap;
  font-weight: 800;
  letter-spacing: .5px;
  color: #fff;
  text-shadow: 0 0 22px rgba(255,191,71,.65), 0 4px 10px rgba(0,0,0,.7);
}
.splash-sub {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  color: var(--accent);
}
.splash-skip {
  position: absolute;
  bottom: 9%;
  left: 0;
  right: 0;
  z-index: 2;
  margin: 0;
  text-align: center;
  font-size: 13px;
  opacity: 0;
  animation: skip-in .6s ease 2.2s forwards;
}
@keyframes skip-in { to { opacity: .6; } }

@media (prefers-reduced-motion: reduce) {
  .splash-rays, .splash-glow, .splash-title, .splash-skip {
    animation-duration: .01ms;
    animation-delay: 0ms;
  }
}


/* "How to play" screen, before the cutscene */
.howto-title { margin: 0 0 12px; font-size: 20px; }
/* The goal, stated before the controls: what you are trying to do beats which button to press */
.howto-goal {
  margin: 0 0 14px;
  padding: 12px 14px;
  border-radius: 12px;
  background: rgba(255,191,71,.12);
  border: 1px solid rgba(255,191,71,.4);
  font-size: 15px;
  font-weight: 700;
  line-height: 1.4;
}
.howto-goal-icon { display: block; font-size: 30px; margin-bottom: 6px; }
.howto-list {
  list-style: none;
  padding: 0;
  margin: 0 0 16px;
  text-align: left;
}
.howto-list li {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 0;
  font-size: 15px;
}
.howto-key {
  flex: 0 0 auto;
  min-width: 64px;
  padding: 6px 8px;
  border-radius: 8px;
  background: var(--accent);
  color: #14151a;
  font-weight: 800;
  font-size: 13px;
  text-align: center;
}

.overlay {
  position: absolute;
  inset: 0;
  background: rgba(0,0,0,.72);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 30;
  padding: 24px;
}
.card {
  background: #1b2340;
  border-radius: 18px;
  padding: 24px;
  text-align: center;
  max-width: 320px;
  /* the end cards carry a paragraph of real numbers now, and a short phone is not tall */
  max-height: 100%;
  overflow-y: auto;
}
/* The facts. Deliberately quieter than the result above them: the player reads this after
   the game has already given them a reason to care, not instead of playing. */
.card-fact {
  margin: 14px 0 4px;
  padding-top: 12px;
  border-top: 1px solid rgba(255,255,255,.15);
  font-size: 12.5px;
  line-height: 1.5;
  opacity: .82;
  text-align: left;
}
/* Same idea on the way in, but one line only: stakes, not a lecture */
.howto-stakes {
  margin: -6px 0 14px;
  font-size: 12.5px;
  line-height: 1.45;
  opacity: .8;
}
/* Who made this and where the numbers come from: small, but always on the card */
.card-credit {
  margin: 14px 0 0;
  font-size: 12px;
  line-height: 1.4;
  opacity: .6;
}
/* "Sources" inside the credit line: reads as a link, works as a button */
.link-btn {
  font: inherit;
  color: var(--accent);
  background: none;
  border: none;
  padding: 0;
  text-decoration: underline;
  cursor: pointer;
}
.sources { z-index: 35; } /* over the card that opened it */
.sources-list {
  margin: 0;
  padding-left: 18px;
  text-align: left;
  font-size: 13px;
  line-height: 1.45;
}
.sources-list li { margin-bottom: 10px; }
.sources-list a { color: var(--accent); }

/* Notifications: one line under the HUD, slides in, leaves by itself. Never takes a tap. */
.toast {
  position: absolute;
  top: 92px;
  left: 50%;
  transform: translate(-50%, -8px);
  max-width: calc(100% - 32px);
  padding: 8px 14px;
  border-radius: 12px;
  background: rgba(20, 26, 48, .92);
  border: 1px solid rgba(255,255,255,.18);
  color: var(--text);
  font-size: 14px;
  font-weight: 700;
  text-align: center;
  z-index: 22;
  opacity: 0;
  pointer-events: none;
  transition: opacity .25s ease, transform .25s ease;
}
.toast.on { opacity: 1; transform: translate(-50%, 0); }
.toast.danger { border-color: var(--danger); }

/* Sound controls, top right under the hearts, above every card */
.sndbox {
  position: absolute;
  right: 10px;
  top: 40px;
  z-index: 45;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  border-radius: 999px;
  background: rgba(0,0,0,.45);
}
.snd {
  font-size: 18px;
  line-height: 1;
  background: none;
  border: none;
  padding: 4px;
  cursor: pointer;
}
.sndbox input[type=range] { width: 72px; accent-color: var(--accent); touch-action: auto; }

.card-icon { font-size: 40px; margin-bottom: 8px; }
/* the trophy lands with a bounce, so winning feels like an event and not a screen change */
.card-icon.trophy {
  font-size: 56px;
  filter: drop-shadow(0 0 20px rgba(255,200,80,.7));
  animation: trophy-drop .7s cubic-bezier(.2,1.6,.4,1) both;
}
@keyframes trophy-drop {
  0%   { transform: translateY(-40px) scale(.4) rotate(-14deg); opacity: 0; }
  60%  { opacity: 1; }
  100% { transform: translateY(0) scale(1) rotate(0deg); opacity: 1; }
}
.win-title {
  margin: 0 0 4px;
  font-size: 26px;
  font-weight: 800;
  color: var(--accent);
}
.card p { font-size: 15px; line-height: 1.5; }
.final-score { font-weight: 700; color: var(--accent); }
.primary-btn { margin-top: 8px; width: 100%; }
